284 lines
7.3 KiB
Markdown
284 lines
7.3 KiB
Markdown
# QR-First URL Shortener SaaS (Designer + Short Links + Tracking)
|
||
|
||
Note: These specs are a draft and need review.
|
||
|
||
## 0) Product Definition
|
||
|
||
One-liner: Create branded short links and highly customizable QR codes, then track scans/clicks with actionable analytics.
|
||
|
||
Primary users:
|
||
- Solo creators / small businesses
|
||
- Marketing teams in SMB/PME
|
||
- Agencies managing multiple clients
|
||
|
||
Core value: Beautiful QR designs + brandable short domains + trustworthy tracking in one place.
|
||
|
||
## 1) MVP Scope (what ships first)
|
||
|
||
### 1.1 User Capabilities (MVP)
|
||
|
||
Auth & Account
|
||
- Sign up / sign in (email + password; optional SSO later)
|
||
- Email verification
|
||
- Password reset
|
||
- Basic account settings
|
||
|
||
Projects / Workspaces
|
||
- Default workspace per user
|
||
- Create “Projects” to organize links/QRs (e.g., “Restaurant menus”, “Flyers Q1”)
|
||
|
||
Short Link Creation
|
||
- Create short link: https://d.om/abc123 or custom slug …/menu
|
||
- Destination URL validation
|
||
- Optional UTM builder (preset templates)
|
||
- Enable/disable link
|
||
- Expiration date (optional)
|
||
- Password protection (optional) — may be “Pro” if you want
|
||
|
||
QR Code Designer
|
||
- Generate QR from a short link (default) or direct URL
|
||
- Styling:
|
||
- Colors (foreground/background)
|
||
- Error correction level (L/M/Q/H)
|
||
- Quiet zone padding
|
||
- Shape presets (modules/eyes) (start with a few presets)
|
||
- Center logo upload (PNG/SVG) with size + margin controls
|
||
- Export:
|
||
- PNG and SVG
|
||
- Size presets (e.g., 256/512/1024/2048)
|
||
- Print-ready options (e.g., “high contrast” toggle)
|
||
|
||
Tracking & Analytics (MVP)
|
||
- Track events: click (short link) and scan (QR)
|
||
- Dashboard:
|
||
- Total events, uniques, last 24h / 7d / 30d
|
||
- Time series
|
||
- Top referrers (for clicks)
|
||
- Geo (country) and device (desktop/mobile) high-level
|
||
- Per-link analytics and per-QR analytics
|
||
|
||
Basic Admin
|
||
- Subscription status
|
||
- Usage quotas (links/QRs/events)
|
||
|
||
## 2) Non-Goals for MVP (explicitly out)
|
||
|
||
- Team roles/permissions (RBAC) beyond “owner”
|
||
- A/B routing, smart rules, rotation, geo routing
|
||
- Deep campaign automation
|
||
- Enterprise SSO, SCIM
|
||
- Offline QR scan tracking (impossible without network in most cases)
|
||
|
||
## 3) Plans & Monetization (recommended)
|
||
|
||
Free
|
||
- 1 workspace
|
||
- 25 short links
|
||
- 25 QR designs
|
||
- 10k events/month
|
||
- 1 custom QR logo upload (or allow unlimited but watermark exports)
|
||
|
||
Pro (individual/SMB)
|
||
- Custom domains (1–3)
|
||
- Higher limits
|
||
- No watermark
|
||
- UTM templates
|
||
- Expiring links / password links (if Pro)
|
||
|
||
Business
|
||
- Multiple workspaces
|
||
- Team seats (later)
|
||
- Higher retention and export presets
|
||
|
||
## 4) Core Entities (Data Model)
|
||
|
||
### 4.1 Entities
|
||
|
||
User
|
||
- id, email, password_hash, verified_at, created_at
|
||
|
||
Workspace
|
||
- id, owner_user_id, name, plan, created_at
|
||
|
||
Project
|
||
- id, workspace_id, name, created_at
|
||
|
||
Domain
|
||
- id, workspace_id, hostname, status (pending/verified/active), verification_token, created_at
|
||
|
||
ShortLink
|
||
- id
|
||
- workspace_id, project_id (nullable)
|
||
- domain_id (nullable; else default platform domain)
|
||
- slug
|
||
- destination_url
|
||
- title (nullable)
|
||
- status (active/disabled)
|
||
- expires_at (nullable)
|
||
- password_hash (nullable)
|
||
- created_at, updated_at
|
||
|
||
QRCodeDesign
|
||
- id
|
||
- workspace_id, project_id (nullable)
|
||
- shortlink_id (nullable; recommended default)
|
||
- style_json (colors, shapes, ecc level, etc.)
|
||
- logo_asset_id (nullable)
|
||
- created_at, updated_at
|
||
|
||
Event
|
||
- id (or bigint)
|
||
- workspace_id
|
||
- shortlink_id
|
||
- qrcode_id (nullable but strongly recommended to tag scans)
|
||
- type: click | scan
|
||
- ts
|
||
- ip_hash (privacy-safe)
|
||
- user_agent
|
||
- referrer
|
||
- country_code (nullable)
|
||
- device_type (nullable)
|
||
- dedupe_key (nullable)
|
||
- raw_json (optional for debug, or drop)
|
||
|
||
Asset
|
||
- id, workspace_id, type (logo), storage_key, mime, size, created_at
|
||
|
||
### 4.2 Key Design Choice
|
||
|
||
How do we distinguish “scan” vs “click”?
|
||
|
||
When exporting a QR, embed a URL like:
|
||
https://d.om/s/abc123?qr=<qrcode_id>
|
||
|
||
The redirect endpoint records scan when it detects qr=<id>, then redirects to destination, which will also produce a click unless you decide “scan implies click” and record only one.
|
||
|
||
Recommendation: record one event per redirect request:
|
||
- If qr present → type=scan
|
||
- Else → type=click
|
||
|
||
## 5) System Behavior (Routes & Flows)
|
||
|
||
### 5.1 Public Redirect
|
||
|
||
GET /{slug}
|
||
|
||
Resolve domain + slug → short link
|
||
|
||
Validate:
|
||
- exists
|
||
- active
|
||
- not expired
|
||
- if password-protected → show password page
|
||
|
||
Log event (scan/click)
|
||
|
||
Redirect 301/302 (configurable later; MVP use 302)
|
||
|
||
### 5.2 QR Export
|
||
|
||
QR code is generated from:
|
||
|
||
Redirect URL including qrcode id: https://{domain}/{slug}?qr={qrcode_id}
|
||
|
||
## 6) Functional Requirements (MVP checklist)
|
||
|
||
Link Management
|
||
- Create, edit, disable, delete (soft delete preferred)
|
||
- Slug uniqueness per domain
|
||
- Auto-slug generator (base62)
|
||
- Destination URL allowlist/denylist (prevent abuse)
|
||
|
||
QR Designer
|
||
- Live preview
|
||
- Save design
|
||
- Export SVG/PNG
|
||
- Logo upload with validation (size/mime)
|
||
|
||
Analytics
|
||
- Views for:
|
||
- Workspace overview
|
||
- Project overview
|
||
- Per short link
|
||
- Per QR design
|
||
- Time filters: 24h / 7d / 30d / custom range
|
||
- Unique definition:
|
||
- Unique per day per link based on ip_hash + UA hash (privacy-safe and approximate)
|
||
|
||
## 7) Non-Functional Requirements
|
||
|
||
Performance
|
||
- Redirect endpoint P95 < 100ms (excluding DNS/TLS)
|
||
- Event write must not block redirect (use async queue if possible)
|
||
|
||
Availability
|
||
- Redirect is the critical path; should stay up even if dashboard is down
|
||
- Graceful degradation: if analytics store is down, still redirect
|
||
|
||
Security
|
||
- Rate limit public endpoints
|
||
- Abuse prevention: phishing/malware reporting flow (later), basic filters now
|
||
- Domain verification to prevent takeover
|
||
- Strict CSP on app pages
|
||
|
||
Privacy & Compliance (Canada / Quebec friendly baseline)
|
||
- Avoid storing raw IP; store hashed IP with rotating salt (e.g., monthly)
|
||
- Provide retention configuration per plan (e.g., 30/180/365 days)
|
||
|
||
## 8) Architecture (pragmatic MVP)
|
||
|
||
Components
|
||
- Web App: dashboard + designer (Vue/React)
|
||
- API: CRUD for links/qr/projects/domains, analytics queries
|
||
- Redirect Edge: fastest path for /{slug} (can be same API initially)
|
||
|
||
Storage
|
||
- PostgreSQL for core entities
|
||
- Analytics:
|
||
- MVP: PostgreSQL events table partitioned by month
|
||
- Later: ClickHouse/BigQuery for scale
|
||
|
||
Background Jobs
|
||
- Domain verification checks
|
||
- Event enrichment (geo/device parsing)
|
||
- Cleanup & retention tasks
|
||
|
||
## 9) API Surface (minimal)
|
||
|
||
- POST /auth/register|login|forgot|reset
|
||
- GET/POST /workspaces
|
||
- GET/POST /projects
|
||
- GET/POST /links
|
||
- GET/POST /qrcodes
|
||
- POST /domains + verification status
|
||
- GET /analytics/overview
|
||
- GET /analytics/link/{id}
|
||
- GET /analytics/qrcode/{id}
|
||
|
||
## 10) UI Pages (MVP)
|
||
|
||
- Login / Register / Reset
|
||
- Workspace switcher
|
||
- Projects list
|
||
- Links list + create/edit
|
||
- QR designer (create/edit) with preview
|
||
- Analytics dashboard (overview + per link + per QR)
|
||
- Domains page (add/verify)
|
||
|
||
## 11) Pricing/Quotas Enforcement
|
||
|
||
Enforce at API level:
|
||
- max links, max QR codes, max events/month, max custom domains
|
||
|
||
Stripe integration later; MVP can be “manual Pro” toggle or Stripe from day 1 if you want.
|
||
|
||
## 12) Implementation Notes (key decisions)
|
||
|
||
- Use one redirect URL as canonical; QR adds ?qr= for attribution.
|
||
- Event logging should be non-blocking:
|
||
- MVP: write to DB async (background queue) or “fire-and-forget” with retry
|
||
- Plan for domain verification:
|
||
- Require DNS TXT record or CNAME to verify ownership
|
||
- Short link collision:
|
||
- Slug uniqueness per domain enforced in DB
|