Open-Source Cold Email & Warmup Platform
Warmbly 是開源的冷郵件平台:溫暖您的收件箱、發送活動、並對每個回覆進行分流。可在我們的雲端運行或自行託管整個系統。
AI 翻譯
WhatsApp and email marketing in one app. Bring new customers into WhatsApp with Status ads, confirm cash-on-delivery orders from Shopify and WooCommerce, and reach people on the channel they actually
翻譯準備中
行銷TypeScript · PLpgSQLGitHub @AbdulHannan20411上線中營運者未確認
試用過了嗎? 點一下就好,不需要登入。
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證WhatsApp and email marketing in one app. Bring new customers into WhatsApp with Status ads, confirm cash-on-delivery orders from Shopify and WooCommerce, and reach people on the channel they actually check.
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗The public website for NextReach, a WhatsApp marketing and sales SaaS for businesses in Pakistan. It has:
/account) where people follow their queries and reply;/admin): dashboard, live inbox, replies, notes, saved replies,
customers and settings.The NextReach product itself is a separate app. Every "Start free trial" and "Sign in" link goes to
NEXT_PUBLIC_APP_URL; this site never handles app accounts.
Standalone mode (before the app is live). Leave NEXT_PUBLIC_APP_URL and NEXTREACH_API_URL
empty and the site works on its own: every "Start free trial" and plan button opens the query
form (so each request lands in Admin → Queries), app sign-in links are hidden, and Pricing
shows your plans from content/fallback-plans.ts with no warning. When the app launches, set both
variables on Vercel and redeploy; no code changes are needed.
Stack: Next.js 16 (App Router, Cache Components, Server Actions), React 19, TypeScript (strict), Tailwind CSS v4 + shadcn/ui, Motion, next-intl, Supabase (Auth, Postgres + RLS, Storage, Realtime), Zod + React Hook Form, Resend, Vitest and Playwright.
Requirements: Node.js 22.12 or newer (.nvmrc pins 24, the current LTS) and npm 10+.
npm install
cp .env.example .env.local # fill in what you have; see section 2
npm run dev # http://localhost:3000 → redirects to /en
Every variable is optional for browsing the marketing pages. Without Supabase, the query form, sign-in, the portal and the admin area show a friendly error. Without the pricing API, Pricing shows the fallback plans with a "prices may have changed" note.
Put real values in .env.local (never committed). .env.example lists every variable with a
placeholder. On Vercel, set the same names under Project → Settings → Environment Variables.
| Variable | Required | Where it's used |
|---|---|---|
NEXT_PUBLIC_SITE_URL | Production | This site's public URL, e.g. https://nextreach.pk. Canonical URLs, sitemap, Open Graph images and email links. |
NEXT_PUBLIC_APP_URL | When the app is live | The NextReach app. "Start free trial" / "Sign in" links, and plan buttons (${APP_URL}/pricing, /pricing/custom). Empty = standalone mode (see above). |
NEXTREACH_API_URL | When the app is live | Base URL of the NextReach API (/api/v1/public/plans, /api/v1/public/custom-plan). Server only. Empty = plans from content/fallback-plans.ts. |
REVALIDATE_SECRET | For instant updates | Shared secret for POST /api/revalidate-pricing (section 6). Use a long random string. |
NEXT_PUBLIC_SUPABASE_URL | Yes, for accounts | Supabase project URL. |
NEXT_PUBLIC_SUPABASE_ANON_KEY | Yes, for accounts | Supabase publishable (anon) key. Safe in the browser; Row Level Security protects the data. |
SUPABASE_SERVICE_ROLE_KEY | Yes, for accounts | Supabase secret (service role) key. Server only. Also keys the form tokens and IP hashing. |
SUPABASE_DB_URL | Scripts only | Postgres connection string for migrations, types and DB tests. Never read by the app. |
RESEND_API_KEY | For real email | Resend API key. Without it, emails are printed to the server log instead. |
EMAIL_FROM | With Resend | Sender, e.g. NextReach <[email protected]>. The domain must be verified in Resend. |
TURNSTILE_SITE_KEY | Optional | Cloudflare Turnstile site key. Turnstile is on only when both Turnstile keys are set. |
TURNSTILE_SECRET_KEY | Optional | Cloudflare Turnstile secret key. Server only. |
NEXT_PUBLIC_ANALYTICS_ID |
npm run check:bundle (part of npm run ci) fails the build if any server-only name or value
appears in the browser bundle.
NEXT_PUBLIC_SUPABASE_ANON_KEY) and the secret key (SUPABASE_SERVICE_ROLE_KEY).SUPABASE_DB_URL.
URL-encode special characters in the password (for example @ → %40).npm run db:push # applies supabase/migrations (tables, RLS, triggers, storage, realtime)
npm run db:seed # optional: starter saved replies in English and Urdu
npm run test:db # optional: proves the RLS rules and triggers work on your project
The migrations create:
profiles, queries (with NR-YYYY-##### references from a sequence),
query_messages, query_attachments, query_events (activity log), saved_replies,
admin_settings and rate_limits. Every table has Row Level Security with explicit policies.query-attachments bucket. Files are only reachable through
60-second signed URLs created after an ownership check.queries and query_messages are published, so the portal and inbox update live.These can't be set from migrations. In Authentication:
| Setting | Value |
|---|---|
| URL Configuration → Site URL | Your site, e.g. https://nextreach.pk (locally http://localhost:3000). |
| URL Configuration → Redirect URLs | https://nextreach.pk/** and http://localhost:3000/** (add preview domains if you use them, e.g. https://*.vercel.app/**). |
| Sign In / Providers → Email | Email provider on, Confirm email on. |
| Sign In / Providers → Email → Password | Minimum length 8. |
| Emails → Templates → Confirm signup | Paste supabase/templates/confirm-signup.html. Subject: Confirm your email | NextReach. |
| Emails → Templates → Reset password | Paste supabase/templates/reset-password.html. Subject: Reset your password | NextReach. |
| Emails → SMTP settings | Recommended: use Resend's SMTP (smtp.resend.com, port 465, user resend, password = your Resend API key). |
The templates switch between English and Urdu using the language chosen at sign-up. They link to
/[locale]/auth/confirm with a token_hash, which works on any device. Supabase's built-in email
service is heavily rate-limited, so set up SMTP before launch.
Add a new file to supabase/migrations/, then run npm run db:push and npm run db:types
(this regenerates lib/supabase/database.types.ts). Keep npm run test:db passing.
The role can never be set from the browser. Sign up on the site, confirm your email, then:
npm run make-superadmin -- [email protected]
Or in the Supabase SQL editor:
select public.promote_to_superadmin('[email protected]');
Sign out and back in, then open /en/admin. Anyone who isn't an active Super Admin gets a 404 there.
Email (Resend). Set RESEND_API_KEY and EMAIL_FROM (on a domain verified in Resend). The site
sends:
Each email has HTML and plain-text versions in the recipient's language. Without a key, emails are logged to the server console.
Spam protection. Every submission passes these checks:
Analytics (optional). Set NEXT_PUBLIC_ANALYTICS_ID to your Umami Cloud website ID. Visitors
then see a short notice, and the Umami script loads only after they accept. Their choice is stored
in the browser, and a "Cookie settings" link in the footer lets them change it. With the variable
empty, nothing loads and no notice is shown. The CSP allows Umami's domains only when analytics is
on.
Pricing reads GET ${NEXTREACH_API_URL}/api/v1/public/plans and /api/v1/public/custom-plan.
Responses are wrapped as { data, message, traceId } and validated with Zod.
content/fallback-plans.ts, with a "Prices may have changed" note. The page
never crashes.curl -X POST https://nextreach.pk/api/revalidate-pricing -H "x-revalidate-secret: $REVALIDATE_SECRET"
next build) and output settings.SUPABASE_SERVICE_ROLE_KEY,
TURNSTILE_SECRET_KEY, RESEND_API_KEY and REVALIDATE_SECRET as Sensitive. Set
NEXT_PUBLIC_SITE_URL to the production domain (for previews you can leave the default).syd1 for Supabase in ap-southeast-2). Database calls then stay
short.npm run make-superadmin -- [email protected] locally against the production project;/en/contact, then reply to it from /en/admin.The site sends security headers on every response: CSP, HSTS (2 years, preload-ready),
X-Content-Type-Options, Referrer-Policy, X-Frame-Options / frame-ancestors 'none',
COOP and Permissions-Policy. Before submitting the domain to the HSTS preload list, make sure
every subdomain serves HTTPS.
| Script | What it does |
|---|---|
npm run dev | Development server |
npm run build / npm run start | Production build / serve it |
npm run lint | ESLint (zero warnings allowed) |
npm run format | Prettier write (format:check to verify) |
npm run typecheck | Generate route types, then tsc --noEmit |
npm run test | Vitest unit tests |
npm run test:db | RLS, grant and trigger tests against SUPABASE_DB_URL (rolled back) |
npm run test:e2e | Playwright end-to-end tests against a production build |
npm run lighthouse | Lighthouse (mobile) on Home, Pricing and Contact, with the brief's targets |
npm run check:bundle | Fails if a server secret appears in the client bundle (after build) |
npm run ci | lint → format:check → typecheck → test → build → check:bundle |
npm run db:push | Apply pending migrations (-- --dry-run to preview) |
npm run db:seed | Insert starter saved replies (safe to rerun) |
npm run db:types | Regenerate lib/supabase/database.types.ts |
npm run make-superadmin -- <email> | Promote a signed-up account to Super Admin |
Unit tests (tests/unit, Vitest) cover:
Database tests (tests/db, npm run test:db) impersonate visitors, customers and Super Admins
to prove the RLS rules, grants, triggers, rate limiting and dashboard stats. Each test runs in a
transaction that is rolled back.
End-to-end tests (tests/e2e, Playwright) run against next start on port 3100 and a local
mock pricing API (port 3999), at desktop size and at 360 px (mobile):
| Spec | Covers |
|---|---|
smoke.spec.ts | The full journey: send a query signed out → sign up with that email and see it linked → a Super Admin replies → the customer sees it → Urdu RTL. |
a11y.spec.ts | axe-core WCAG 2.2 A/AA checks on every public page in both languages and both themes, plus the portal and admin pages. |
marketing.spec.ts, foundation.spec.ts | Pages, headers, no horizontal scroll at 360 px, theme, language switching, 404s, menus. |
pricing-api.spec.ts | Live plans, fallback when the API is down, revalidation and JSON-LD (switches the mock API between modes). |
queries.spec.ts, auth.spec.ts, portal.spec.ts, admin.spec.ts | The query form, accounts, portal and admin area in depth. |
Specs that need Supabase create their own test users through the admin API and delete everything afterwards. They are skipped when Supabase isn't configured. Run with:
npm run build && npm run test:e2e
Lighthouse. npm run build && npm run lighthouse audits Home, Pricing and Contact as a mobile
device. It runs each page three times and checks the median against the targets: Performance ≥ 90,
Accessibility ≥ 95, Best Practices ≥ 95 and SEO ≥ 95. Reports are saved to lighthouse-reports/.
Options:
LIGHTHOUSE_PAGES=/ur,/en/faq audits other pages;LIGHTHOUSE_URL=https://… audits a deployed site;LIGHTHOUSE_RUNS=5 changes the number of runs.Lighthouse varies with machine load, so compare medians, not single runs.
app/[locale]/(marketing) public pages (+ opengraph-image.tsx per page)
app/[locale]/(auth) sign-in, sign-up, forgot/reset password, email confirm, sign-out
app/[locale]/account customer portal
app/[locale]/admin Super Admin area
app/api/ revalidate-pricing
app/sitemap.ts, robots.ts sitemap.xml and robots.txt
components/ UI primitives, layout, marketing sections, motion, analytics
features/ queries (form, actions, emails), auth, portal, admin
lib/ Supabase clients, API client, i18n, SEO, OG images, security, env
content/ typed feature, FAQ, use-case and fallback-plan content
messages/en.json, ur.json all copy
supabase/ migrations, seed, email templates
tests/ unit, db and e2e tests
messages/en.json and messages/ur.json. A test keeps them in sync, and
ESLint rejects literal strings in JSX.ms-, me-, ps-, pe-, start-, end-, text-start).
ESLint rejects physical ones (ml-, pr-, left-, text-right, …).Link from @/lib/i18n/navigation (keeps the locale), not next/link.z from @/lib/validation/zod. It is configured jitless so it never probes
eval, which the CSP blocks. ESLint enforces this.@/lib/env/server (guarded by server-only). Never
commit .env.local.pageMetadata() from lib/seo.ts for new pages. It sets the canonical URL and
hreflang. Add the path to publicPaths for the sitemap, and add an opengraph-image.tsx beside
the page. Urdu text in OG images is shaped with HarfBuzz (lib/og/shaped-text.ts).TODO: comments where real
content belongs (testimonials, customer logos, legal review, social profiles in JSON-LD)./account and /admin.'unsafe-inline' scripts are allowed. Everything else is locked down, including no eval,
no framing and no foreign form targets.| Problem | Fix |
|---|---|
| Confirmation link says the link expired | The redirect URL isn't allowed: add your domain to Supabase Redirect URLs (section 3.3). |
| No emails arrive | Check RESEND_API_KEY / EMAIL_FROM and the Resend domain. Without them, look for [email] lines in the server log. |
/admin shows "page not found" | Your account isn't a Super Admin yet (section 4), or it is suspended. |
| The inbox or portal doesn't update live | Realtime is published by the migrations; check the browser can reach wss://<project>.supabase.co (CSP and network). |
| Pricing shows "Prices may have changed" | NEXTREACH_API_URL is set but the API returned an error; see [nextreach-api] lines in the server log. |
npm run db:push can't connect | Use the Session pooler string and URL-encode the password. |
| Optional |
| Umami Cloud website ID. Leave empty to disable analytics and the consent notice. |