Skip to content

startdo-ing/url-shortener

Repository files navigation

URL Shortener

Production-oriented URL shortener built as two Bun-based apps with one shared SQLite database.

Architecture

  • apps/redirect-service: hot-path redirect server for short-link resolution and click capture
  • apps/management-web: Astro + Svelte dashboard for auth, domains, links, and operations
  • packages/shared-db: shared Drizzle schema, client, migrations, and backup tooling

The architecture is intentionally split so redirect latency stays isolated from dashboard traffic. See docs/PRODUCT-PLAN.md and docs/adr/ADR-001-two-app-architecture.md for the product and architecture decisions.

Feature Overview

Redirect Service Features

  • Short-link resolution by host + slug with 301 / 302 / 307 redirects
  • Password-protected short links (HTML password prompt, POST verify, then redirect)
  • Link-state enforcement: disabled links return 404, expired links return 410
  • Request rate limiting (per IP sliding window)
  • Click-event capture for analytics (non-blocking async write)
  • Prometheus metrics endpoint with optional bearer token protection
  • Health endpoint with DB probe

Management Web Features

  • Keycloak OIDC sign-in flow with first-admin bootstrap
  • Local RBAC (admin / member) for dashboard actions
  • Domain management: create, verify DNS, enable/disable, set primary, delete
  • Link management: create, search/filter, update, delete
  • Optional link password support in create/update forms
  • Analytics dashboard: top links and recent click events
  • Public JSON API for links (/api/links, /api/links/:id)
  • Admin-managed API tokens with rotation, removal, and usage counts
  • Health + metrics endpoints for operational monitoring

Feature Matrix

Feature App Endpoint / Page
Short-link redirect redirect-service GET /<slug>
Password-protected redirect redirect-service GET /<slug>, POST /<slug>
Expired / disabled link enforcement redirect-service GET /<slug>
Click-event capture redirect-service redirect request path
Redirect metrics redirect-service GET /metrics
Redirect health probe redirect-service GET /health
OIDC sign-in management-web GET /auth
First-admin bootstrap management-web /setup/first-admin
Dashboard home management-web GET /dashboard
Domain management management-web /domains
Link management UI management-web /links
Analytics dashboard management-web GET /analytics
API token management management-web GET /users
Public links API management-web /api/links, /api/links/:id
Management metrics management-web GET /metrics
Management health probe management-web GET /health

Requirements

  • Bun 1.3+
  • A writable SQLite database path shared by both apps
  • Keycloak for dashboard authentication

Local Setup

  1. Install dependencies:
bun install
  1. Run migrations:
bun run db:migrate
  1. Set environment variables.

apps/management-web:

APP_URL=http://localhost:3000
DATABASE_PATH=../../dev.sqlite
KEYCLOAK_URL=https://auth.startdo.ing
KEYCLOAK_REALM=startdoing
KEYCLOAK_CLIENT_ID=url-shortener-management-web
KEYCLOAK_CLIENT_SECRET=replace-with-client-secret
METRICS_BEARER_TOKEN=replace-with-an-internal-scrape-token
SESSION_SECRET=replace-with-a-random-string-at-least-32-characters-long

apps/redirect-service:

DATABASE_PATH=../../dev.sqlite
PORT=8000
METRICS_BEARER_TOKEN=replace-with-an-internal-scrape-token
CLICK_EVENT_RETENTION_DAYS=90
  1. Start each app in its own terminal:
bun run redirect:dev
bun run web:dev

Endpoints:

  • Management UI: http://localhost:3000
  • Redirect service: http://localhost:8000

Useful Commands

bun run redirect:dev
bun run web:dev
bun run redirect:lint
bun run web:lint
bun run db:lint
bun run redirect:typecheck
bun run web:typecheck
bun run db:typecheck
bun run test
bun run check-all
bun run db:backup
bun run db:studio

Runtime Endpoints

Human UI

management-web

  • GET /auth: Open the Keycloak sign-in flow.
  • GET /dashboard: Open the authenticated dashboard.
  • GET /links: Open the link management UI.
  • GET /domains: Open the domain management UI.
  • GET /users: Open the user management UI (admin only).
  • POST /users: Manage users and API tokens (admin only).
  • GET /analytics: Open the analytics dashboard.
  • GET /setup/first-admin: Open the first-admin bootstrap flow.

redirect-service

  • GET /<slug>: Resolve a short link or render the password prompt.
  • POST /<slug>: Submit the password for a protected link.

Machine API

management-web

  • GET /api/links: List links. Auth via session cookie or an admin-managed bearer token.
  • POST /api/links: Create a link.
  • GET /api/links/:id: Get one link.
  • PATCH /api/links/:id: Update a link.
  • DELETE /api/links/:id: Delete a link.

Ops Endpoints

redirect-service

  • GET /health: Run a database-backed health probe.
  • GET /metrics: Return Prometheus metrics. Disabled unless METRICS_BEARER_TOKEN is set, then requires Authorization: Bearer <token>.

management-web

  • GET /health: Run a database-backed health probe.
  • GET /metrics: Return Prometheus metrics. Disabled unless METRICS_BEARER_TOKEN is set, then requires Authorization: Bearer <token>.

Public API Quickstart

Use the management API with either a signed-in session cookie or a token created by an admin on /users.

export API_TOKEN="paste-a-token-created-in-the-users-page"

curl -s -H "Authorization: Bearer $API_TOKEN" \
	http://localhost:3000/api/links

Create a link:

curl -s -X POST http://localhost:3000/api/links \
	-H "Authorization: Bearer $API_TOKEN" \
	-H "Content-Type: application/json" \
	-d '{
		"domainId": "<domain-id>",
		"slug": "docs",
		"targetUrl": "https://example.com/docs",
		"httpCode": 302,
		"status": "active",
		"password": "optional-password"
	}'

Bearer-token requests run as the admin user who created the token. Token usage count and last-used time are updated on successful API authentication.

Testing

  • Redirect-service behavior is covered with focused request-handler tests.
  • Management repositories use SQLite-backed integration tests.
  • Run app-local tests when working on a specific surface:
cd apps/redirect-service && bun test
cd apps/management-web && bun test

Operational Notes

  • Both apps must point at the same DATABASE_PATH.
  • SQLite WAL mode is enabled by the shared DB client for concurrent reads and writes.
  • Redirect targets are limited to absolute http and https URLs and reject embedded credentials.

Further Docs

About

This app is intended solely for my personal use!

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors