docs: document new features, ops env vars, and API surface; refresh roadmap
- README: feature map updated (redirect types, protected links, QR vector export, audience analytics, API keys, admin observability, operations); API reference covers new endpoints and params. Roadmap now lists self-serve accounts, security hardening (rate limiting/CSRF/SSRF guard), Stripe billing, custom domains, and remaining power features. - INSTALL: new environment variables (WEBHOOK_URL, GEOIP_DB_PATH, GEO_API_FALLBACK, IP_ANONYMIZE, CLICK_RETENTION_DAYS, BACKUPS, BACKUP_DIR, BACKUP_KEEP) with GeoLite2 setup notes and the ip-api.com licensing caveat. - api-docs page: API-key authentication, new link/analytics/QR params, aggregate analytics, admin overview/audit/keys/purge, unlock route. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -18,27 +18,32 @@ A managed URL shortener with QR code generation, deep click analytics, and tag-b
|
||||
|
||||
| Feature area | What's included |
|
||||
|---|---|
|
||||
| **Short links** | Random & custom codes, link expiry, pinned links, one-click copy, auto-fetch page title |
|
||||
| **QR codes** | QR generation for any link or URL, logo overlay, dot-shape presets, inline thumbnail, copy to clipboard |
|
||||
| **Analytics** | Per-link daily click charts, referrer / device / browser / country breakdowns, hourly 7×24 heatmap, raw click CSV export |
|
||||
| **Short links** | Random & custom codes, link expiry, pinned links, one-click copy, auto-fetch page title, per-link redirect type (302 default / 301 opt-in) |
|
||||
| **Protected links** | Per-link password protection with a branded unlock page; branded visitor pages for missing, expired, and quota-limited links |
|
||||
| **QR codes** | QR generation for any link or URL, logo overlay, dot-shape presets, inline thumbnail, copy to clipboard, **SVG / PDF / PNG export**, selectable error-correction level (L/M/Q/H) |
|
||||
| **Analytics** | Per-link daily click charts, unique-visitor counts, bot/crawler filtering, referrer / device / browser / country breakdowns, hourly 7×24 heatmap, raw click CSV export |
|
||||
| **Audience analytics** | Aggregate dashboard across the whole account or org, filterable by tag — one chart for a whole campaign |
|
||||
| **Tags** | Tag links for filtering; tags are scoped per user — org members share a namespace, solo users and admins each have their own |
|
||||
| **Bulk tools** | Bulk delete, bulk tag, bulk expire; CSV import & export |
|
||||
| **Auth** | Session-cookie login, 30-day HttpOnly cookie, works behind Cloudflare and Nginx Proxy Manager |
|
||||
| **Auth** | Session-cookie login, 30-day HttpOnly cookie, **API keys** (`Authorization: Bearer qrk_…`) with read/write scopes, works behind Cloudflare and Nginx Proxy Manager |
|
||||
| **Multi-user** | Per-user link isolation, admin user-management panel, username + password login |
|
||||
| **Organizations** | Group users into orgs that share link/tag visibility and pool usage quotas under a shared plan |
|
||||
| **Plan tiers** | Free / Starter / Pro / Team plans with enforced limits on active links, monthly clicks, and analytics history; org plan overrides member plans |
|
||||
| **Admin tier** | Admin accounts sit above all plans with unlimited quotas and an isolated workspace; can filter into any user's links on demand |
|
||||
| **Admin observability** | Platform overview (totals, 30-day series, top links), per-user/org quota usage with near-limit warnings, audit log of admin actions, webhook notifications for contact messages |
|
||||
| **Landing & portal** | Marketing landing page, SaaS pricing page, contact form, admin inbox with unread badge |
|
||||
| **Operations** | Async click logging (redirects never block), local GeoLite2 geolocation, daily SQLite backups (`VACUUM INTO`), click-data retention & IP anonymisation options, hard-purge of deleted links, multi-worker-safe uptime tracking |
|
||||
| **Deployment** | `APP_NAME` / `BASE_URL` env vars, four colour themes, single Docker container, Unraid-ready |
|
||||
|
||||
### Upcoming
|
||||
### Roadmap
|
||||
|
||||
| Feature area | What's planned |
|
||||
|---|---|
|
||||
| **Self-serve accounts** | Public signup with email verification, change-own-password, account page with plan & usage |
|
||||
| **Security hardening** | Rate limiting (login, contact, QR endpoints), CSRF tokens, SSRF guard on title fetching |
|
||||
| **Billing** | Stripe checkout & subscriptions, upgrade/downgrade UI, self-serve account portal |
|
||||
| **Custom domains** | CNAME-based short domains, SSL provisioning, domain verification, Team tier multi-domain support |
|
||||
| **Power features** | API key auth, password-protected links, UTM parameter builder, custom 404 & expired-link pages |
|
||||
| **Link organisation** | Folders/groups, duplicate link, dead-link detection, per-link redirect type (301 vs 302) |
|
||||
| **Power features** | UTM parameter builder, dead-link detection, folders/groups, duplicate link |
|
||||
|
||||
---
|
||||
|
||||
@@ -48,26 +53,30 @@ A managed URL shortener with QR code generation, deep click analytics, and tag-b
|
||||
|
||||
## 🔌 API Reference
|
||||
|
||||
All write endpoints require an active session (log in via the web UI or `POST /api/auth/login`).
|
||||
Authenticated endpoints accept either an active session (log in via the web UI or `POST /api/auth/login`) **or an API key**: `Authorization: Bearer qrk_…`. Keys are created in the dashboard (or via `POST /api/keys`) with a `read` or `write` scope — read-only keys can only call GET endpoints.
|
||||
|
||||
### Auth
|
||||
### Auth & API keys
|
||||
|
||||
| Method | Endpoint | Auth | Description |
|
||||
|---|---|---|---|
|
||||
| POST | `/api/auth/login` | — | Login — `{"username": "...", "password": "..."}`, sets session cookie; returns `plan`, `plan_limits`, `org_id` |
|
||||
| POST | `/api/auth/logout` | ✓ | Clear session |
|
||||
| GET | `/api/auth/me` | ✓ | Returns `{"authenticated": true, "username": "...", "is_admin": bool, "org_id": int\|null, "plan": "...", "plan_limits": {…}}` |
|
||||
| GET | `/api/keys` | ✓ | List your API keys (prefix, scope, last used — never the full token) |
|
||||
| POST | `/api/keys` | ✓ | Create key — `{name, scope: "read"\|"write"}`; response includes `token` **once** |
|
||||
| DELETE | `/api/keys/:id` | ✓ | Revoke a key (admins can revoke any key) |
|
||||
|
||||
### Links
|
||||
|
||||
| Method | Endpoint | Auth | Description |
|
||||
|---|---|---|---|
|
||||
| POST | `/api/shorten` | ✓ | Create a short link |
|
||||
| POST | `/api/shorten` | ✓ | Create a short link — `{url, custom_code?, title?, expires_at?, tags?, redirect_type?: 301\|302, password?}` |
|
||||
| GET | `/api/links` | ✓ | List links — supports `?q=`, `?tag=`, `?page=`, `?per_page=`; org members see all links within their org; admin sees own links only, optionally filtered with `?user=<username>` |
|
||||
| GET | `/api/links/:code` | ✓ | Link detail — includes `created_by` username |
|
||||
| PATCH | `/api/links/:code` | ✓ | Edit link — `url`, `title`, `expires_at`, `tags`, `is_pinned` |
|
||||
| DELETE | `/api/links/:code` | ✓ | Delete link |
|
||||
| GET | `/api/links/:code/analytics` | ✓ | Click analytics — supports `?days=7\|30\|90` (capped to plan's max window); returns `daily`, `referrers`, `devices`, `browsers`, `countries`, `heatmap` (7×24 array), and `max_days` |
|
||||
| GET | `/api/links/:code` | ✓ | Link detail — includes `created_by`, `redirect_type`, `has_password` |
|
||||
| PATCH | `/api/links/:code` | ✓ | Edit link — `url`, `title`, `expires_at`, `tags`, `is_pinned`, `redirect_type` (301/302), `password` (empty string removes protection) |
|
||||
| DELETE | `/api/links/:code` | ✓ | Delete link (soft — recoverable until purged by an admin) |
|
||||
| GET | `/api/links/:code/analytics` | ✓ | Click analytics — supports `?days=7\|30\|90` (capped to plan's max window) and `?exclude_bots=1`; returns `daily` (with per-day `unique`), `unique_visitors`, `bot_clicks`, `referrers`, `devices`, `browsers`, `countries`, `heatmap` (7×24 array), and `max_days` |
|
||||
| GET | `/api/analytics/aggregate` | ✓ | Account/org-wide analytics — `?days=`, `?tag=`, `?exclude_bots=1`; returns `daily`, `unique_visitors`, `referrers`, `devices`, `browsers`, `countries`, `top_links` |
|
||||
| GET | `/api/links/:code/clicks/export` | ✓ | Download raw click events as CSV — columns: `timestamp`, `referrer`, `device`, `browser`, `country` |
|
||||
|
||||
### Utilities
|
||||
@@ -78,14 +87,15 @@ All write endpoints require an active session (log in via the web UI or `POST /a
|
||||
| GET | `/api/plan` | ✓ | Current user's effective plan, limits, and live usage — `{plan, limits: {max_links, monthly_clicks, analytics_days}, usage: {active_links, monthly_clicks}}`; org members see pooled usage across all org members; admin plan is always `"admin"` (unlimited) |
|
||||
| GET | `/api/tags` | ✓ | Tags scoped to the requesting user — own tags only for solo users and admins; all org member tags for org members |
|
||||
| GET | `/api/fetch-title` | ✓ | Server-side page title fetch — `?url=`. Returns `{"title":"…"}` |
|
||||
| GET | `/api/qr/:code` | — | QR PNG for a short link |
|
||||
| GET | `/api/qr/custom` | — | QR PNG for any URL — `?url=`, `?fg=`, `?bg=`, `?size=`, `?style=` |
|
||||
| POST | `/api/qr/custom` | — | QR PNG with logo overlay — `{url, fg, bg, size, style, logo}` (logo as base64) |
|
||||
| GET | `/api/qr/:code` | — | QR for a short link — `?format=png\|svg\|pdf`, `?ec=L\|M\|Q\|H`, `?fg=`, `?bg=`, `?size=`, `?style=` (raster only), `?download=1` |
|
||||
| GET | `/api/qr/custom` | — | QR for any URL — same params as above plus `?url=` |
|
||||
| POST | `/api/qr/custom` | — | QR with logo overlay — `{url, fg, bg, size, style, ec, format, logo}` (logo as base64; forces error correction H) |
|
||||
| POST | `/api/links/bulk` | ✓ | Bulk operations — `{action: "delete"\|"tag"\|"expire", codes: […]}` |
|
||||
| GET | `/api/links/export` | ✓ | Download all links as CSV |
|
||||
| POST | `/api/links/import` | ✓ | Import links from CSV — `{csv: "…"}` |
|
||||
| GET | `/api/health` | — | Health check — `{"status":"ok"}` |
|
||||
| GET | `/:code` | — | Redirect to destination URL |
|
||||
| GET | `/:code` | — | Redirect to destination URL (302 by default; 301 if configured per link; password-protected links show a branded unlock page) |
|
||||
| POST | `/:code/unlock` | — | Password submission for protected links (form field `password`) |
|
||||
|
||||
### Admin
|
||||
|
||||
@@ -103,6 +113,10 @@ All write endpoints require an active session (log in via the web UI or `POST /a
|
||||
| GET | `/api/admin/messages` | Admin | List all contact/portal messages, newest first |
|
||||
| DELETE | `/api/admin/messages/:id` | Admin | Delete a message |
|
||||
| PATCH | `/api/admin/messages/:id/read` | Admin | Mark a message as read |
|
||||
| GET | `/api/admin/overview` | Admin | Platform totals, 30-day click series, top links, per-user quota usage with near-limit flags |
|
||||
| GET | `/api/admin/audit` | Admin | Audit log of admin actions — `?limit=` (default 200) |
|
||||
| GET | `/api/admin/keys` | Admin | List all API keys across users (prefix, scope, last used) |
|
||||
| POST | `/api/admin/purge` | Admin | Hard-delete soft-deleted links older than `{days}` — frees short codes, removes click history |
|
||||
|
||||
### Contact
|
||||
|
||||
|
||||
Reference in New Issue
Block a user