A production-ready QR code link management platform with geo-routing, bypass URLs, and real-time analytics. Create short, scannable links that intelligently redirect users based on their geographic location.
Live: https://qrlive.vercel.app Repository: https://github.com/hthmkt12/qrlive Database: Supabase (ybxmpuirarncxmenprzf)
- Auth System: Email/password signup, login, logout with session persistence
- QR Link Management: Create, edit, delete, and toggle QR links with auto-generated 6-character codes or custom 3-20 character short codes
- Protected & Expiring Links: Optional password protection plus expiration dates enforced by the redirect edge function
- Geo-Routing: Route visitors to different URLs based on 15 supported countries (US, GB, JP, KR, DE, FR, VN, TH, SG, AU, CA, BR, IN, CN, RU)
- Bypass URLs: Optional override URLs for accessing geo-blocked content (priority: bypass_url → target_url → default_url)
- Click Analytics: Real-time tracking with date range filtering, country distribution, referer breakdown, and bot filtering
- Click Webhooks: Optional per-link
click.createdPOST notifications for recorded clicks
- Vietnamese UI: All text localized for Vietnamese users
- Dark/Light Theme: Toggle between themes with next-themes
- Responsive Design: Works on desktop and mobile (tailored grid layout)
- Smooth Animations: Framer Motion animations for link creation/deletion
- Loading States: Skeleton screens instead of spinners
- Toast Notifications: Immediate feedback on actions
- Hardened CSP: No
unsafe-inlinescripts; restrictedconnect-src,object-src 'none',form-action 'self' - Security Headers: HSTS (preload), X-Frame-Options DENY, X-Content-Type-Options nosniff, strict Referrer-Policy, Permissions-Policy
| Layer | Technologies |
|---|---|
| Frontend | React 18 + TypeScript + Vite |
| Styling | Tailwind CSS + shadcn/ui (45 Radix UI components) |
| State | TanStack React Query v5 |
| Forms | react-hook-form + zod validation |
| Auth | Supabase Auth (email/password) |
| Database | Supabase Postgres with RLS policies |
| Edge Functions | Supabase Edge Functions (Deno runtime) |
| Charts | Recharts (7-day, country, referer) |
| QR Codes | qrcode.react |
| Deploy | Vercel (frontend) + Supabase (backend) |
- Node.js 18+ (install via nvm)
- npm 9+
- Supabase CLI (optional, for local testing)
# 1. Clone repository
git clone https://github.com/hthmkt12/qrlive.git
cd qrlive
# 2. Install dependencies
npm install
# 3. Create environment file
cp .env.example .env.local
# Fill in VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY
# 4. Start development server
npm run dev
# Open http://localhost:5173Create .env.local:
VITE_SUPABASE_URL=https://ybxmpuirarncxmenprzf.supabase.co
VITE_SUPABASE_PUBLISHABLE_KEY=<your-publishable-key>
Get credentials from Supabase Dashboard.
src/
├── pages/ # Route pages (Index, Auth, NotFound) — lazy-loaded
├── components/ # Reusable UI + business components
│ ├── LinkCard.tsx
│ ├── CreateLinkDialog.tsx
│ ├── EditLinkDialog.tsx
│ ├── StatsPanel.tsx # Shell (12 KB) — lazy-loads StatsCharts
│ ├── StatsCharts.tsx # Recharts visualizations (loaded on demand)
│ ├── DashboardHeader.tsx
│ ├── DashboardMetrics.tsx
│ └── ui/ # 45 shadcn/ui components
├── contexts/ # Auth context provider
├── hooks/ # useLinks, useLinkMutations, useMobile
├── lib/ # Database ops, schemas, types, utilities
├── integrations/ # Supabase client
├── test/ # 289 app tests across 24 files (jsdom project)
└── App.tsx # Root component + routing (code-split)
supabase/
├── functions/redirect/
│ ├── click-webhook.ts # click.created payload builder + delivery helper
│ ├── redirect-handler.ts # Runtime-agnostic handler logic (testable)
│ ├── redirect-password.ts # Password form + verification helpers
│ └── index.ts # Thin Deno wrapper + background webhook queueing
├── functions/proxy/ # Content-fetch proxy (FALLBACK/TESTING ONLY)
└── migrations/ # 13 database migrations
cloudflare-worker/ # Redirect-domain gateway (r.yourdomain.com → Supabase edge)
proxy-gateway/ # Canonical bypass gateway for bypass_url (Fly.io Tokyo)
docs/ # Documentation (see below)
All documentation is in the docs/ directory:
| Document | Purpose |
|---|---|
| project-overview-pdr.md | Project goals, features, user stories, acceptance criteria |
| system-architecture.md | Architecture diagrams, data flow, database schema, edge function flow |
| code-standards.md | File structure, naming conventions, patterns, testing |
| deployment-guide.md | Step-by-step setup: local, Supabase, Vercel, edge functions |
| project-roadmap.md | Status, completed features, known issues, future plans |
| codebase-summary.md | Quick reference for developers and LLMs |
| user-guide.md | Hướng dẫn sử dụng cho người dùng (Vietnamese) |
| openapi.yaml | Machine-readable contract for redirect endpoints, proxy surfaces, and click webhooks |
# Development
npm run dev # Start Vite dev server (http://localhost:5173)
npm run typecheck # TypeScript type checking
npm run lint # ESLint code quality
# Testing
npm run test # Run full test suite (334 Vitest tests across 29 files)
npm run test:coverage # Run Vitest with coverage for app, redirect logic, cache helpers, and Worker
npm run test:watch # Watch mode for development
# Building
npm run build # Production build (vite build)
npm run preview # Preview production build locally
# Local Supabase (optional)
supabase start # Start local Postgres + Auth + Edge Functions
supabase db push # Apply migrations
# Proxy gateway (optional, for bypass_url deployment)
npm run gateway:install # Install proxy-gateway dependencies
npm run gateway:dev # Run always-on gateway locally
npm run gateway:check # Smoke-check gateway module loading
npm run gateway:test # Run proxy-gateway smoke tests334 unit/integration tests across 29 test files covering:
- Schemas & Validation: Zod validation for links, auth, geo routes, and optional webhook URLs
- Database & Data Layer: db utilities, mutations, query helpers, and query keys
- Auth Context & Hooks: session lifecycle, query wiring, and password hashing helpers
- Pages & UI Components: Index/Auth/NotFound plus dialogs, cards, stats, QR preview, analytics controls
- Redirect Logic: simulator coverage plus direct handler tests for password, expiration, geo-routing, bot filtering, duplicate suppression, and click webhooks
- Cloudflare Worker: proxy contract, auth header injection, POST forwarding, and fail-fast errors
Run tests:
npm run test # Run all 334 tests
npm run gateway:test # Proxy-gateway smoke tests (3 tests)
npm run test:watch # Watch mode- Supabase Auth manages sessions (JWT tokens)
- Auth context always resolves loading state (prevents flash)
- RLS policies enforce user data isolation
- Geo detection via Cloudflare
cf-ipcountryheader (edge-only) - Redirect priority: bypass_url → target_url → default_url
- Fallback to default_url if geo data unavailable
- Validates short code format (
^[A-Z0-9_-]{3,20}$) - Rate limits to 1 click per IP per 60 seconds
- Filters bots/crawlers (skip click recording)
- Records click events for analytics
- Queues optional
click.createdwebhooks in the background after recorded clicks - Returns 302 redirect with no-cache headers
- RLS policies on all tables (owner-only for user data)
- Service role used only in edge function (for public INSERT)
- Atomic geo route updates via Postgres RPC function
- Detailed analytics use server-side aggregate RPCs, but very high-volume or long-range reporting will eventually want pre-aggregated rollups or caching
- Full deployed edge-to-webhook verification is still a manual smoke test rather than an automated production monitor
- ✅ Password-protected links (PBKDF2-HMAC-SHA256 with legacy SHA-256 compatibility)
- ✅ Link expiration dates
- ✅ Advanced analytics (date range filtering, country/referer breakdown)
- ✅ Route-level code splitting and lazy-loaded analytics charts
- Team collaboration
- Slack / Zapier integrations
- Mobile apps (iOS/Android)
See project-roadmap.md for full details.
vercel --prod
# Or auto-deploy: push to main branch on GitHub# Apply new migrations when the release includes schema changes
supabase db push
# Public redirect endpoint
supabase functions deploy redirect --no-verify-jwt
# Deploy when cache invalidation logic changes ship
supabase functions deploy link-cache-invalidateIf browser Sentry monitoring is enabled for the release, set VITE_SENTRY_DSN in Vercel and trigger a new production deploy so the bundled frontend picks it up.
cd proxy-gateway
npm install
npm run test
npm run startSee deployment-guide.md for detailed instructions.
| Metric | Target | Actual |
|---|---|---|
| Redirect Latency | <100ms | ~50ms (Cloudflare edge) |
| Page Load | <2s | ~1.5s (Vercel CDN) |
| Build Time | <30s | ~5s (passes; Vite chunk-size warning remains) |
| Tests | — | 308/308 passing (25 files) |
| Uptime | 99.9% | 100% (current) |
| Chunk | Size | Gzip |
|---|---|---|
| Main bundle | 490 KB | 147 KB |
| Index page | 213 KB | 70 KB |
| StatsCharts (lazy) | 393 KB | 107 KB |
| StatsPanel shell | 12 KB | 4 KB |
| Schemas | 86 KB | 24 KB |
- ✅ RLS (Row-Level Security) on all database tables
- ✅ Password hashing: PBKDF2-HMAC-SHA256 (600k iterations) with legacy SHA-256 compatibility
- ✅ Dashboard never exposes password hashes (uses
has_passwordboolean) - ✅ URL protocol validation (blocks javascript:, data: injection)
- ✅ Rate limiting (1 click per IP per 60s)
- ✅ Bot filtering (skips analytics for crawlers)
- ✅ CORS headers on edge function
- ✅ Session persistence via Supabase JS client (
localStoragein current app config)
- Read code-standards.md
- Create feature branch:
git checkout -b feature/my-feature - Make changes, test, lint:
npm run test && npm run lint - Commit with conventional format:
git commit -m "feat: add new feature" - Push and submit PR
- Docs: See
docs/directory - Issues: https://github.com/hthmkt12/qrlive/issues
- Supabase Docs: https://supabase.com/docs
- Vercel Docs: https://vercel.com/docs
- React: https://react.dev
- Tailwind: https://tailwindcss.com
Project by hthmkt12. See LICENSE file for details.
Last Updated: 2026-03-17 Status: Production-Ready v1.7 Maintainer: hthmkt12