Redrx
Shorten URLs, generate QR codes, and track clicks with Redrx. The advanced open-source URL shortener with password protection, device targeting, and detailed analytics.
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
MarketingGitHub @AbdulHannan20411OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Repository information has not been collected yet.
No updates detected yet. GitHub releases will appear here.
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.
Taken from the repository README
Loading the full README.
Read the full 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. |