Skip to content

PythonPlumber/zly

Repository files navigation

Zly

Next-gen open-source URL shortener and marketing platform.

Zly is a full-featured URL shortening platform with analytics, custom domains, QR codes, link in bio pages, A/B testing, team collaboration, email campaigns, an admin panel, and a developer API, all self hosted.

Features

Category Capabilities
Short Links Custom slugs, password protection, scheduling (activate/expire), bulk import/export
Analytics Per-link and workspace dashboards; clicks over time, browser/OS/device, top referrers; Chart.js visualizations
QR Codes Auto-generated QR codes per link; PNG and SVG download; customizable colors
Link in Bio Profile pages with curated link collections; midnight/dark/light themes
A/B Testing Weighted destination variants with randomized traffic splitting; variant-level analytics
Custom Domains DNS TXT verification; workspace-scoped custom domains; CNAME-ready
Email Campaigns Contact management, email templates, campaign sending with open/click tracking
Teams Multi-user workspaces; owner/member roles; invite flow with accept/decline
Admin Panel User management, system stats, audit log with color-coded events
API Full REST API with auto-generated OpenAPI docs; scoped API keys per workspace
Security bcrypt password hashing; JWT auth with refresh tokens; rate limiting; CSRF protection; security headers (CSP, HSTS)

Tech Stack

Component Technology
Framework FastAPI (Python 3.12+)
Database PostgreSQL via SQLAlchemy 2.0 (async)
Cache Redis 7 (optional, graceful fallback)
Auth bcrypt + PyJWT
Frontend Jinja2 + HTMX + Tailwind CSS (CDN) + Chart.js
Queue arq (background jobs)
Email aiosmtplib (async SMTP)
Migrations Alembic
Containers Docker, Docker Compose
Reverse Proxy Caddy (auto HTTPS)

Quick Start

Zero config Local Dev (no Docker, no PostgreSQL, no Redis)

# Clone
git clone https://github.com/pythonplumber/zly.git
cd zly

# Install with dev dependencies
pip install -e ".[dev]"

# Start the dev server (auto-creates tables)
uvicorn app.main:app --reload

# Open http://localhost:8000/dashboard

That's it. The app defaults to SQLite and gracefully handles Redis being unavailable.

Deployment

Zly is built for self hosting. A $5 to $10/month VPS with Docker Compose gives you full control, persistent storage, Redis caching, and auto-HTTPS via Caddy with no platform lock-in.

Recommended: VPS with Docker Compose

# SSH into your VPS (Ubuntu 24+)
ssh root@your-server

# Clone and deploy
git clone https://github.com/pythonplumber/zly.git
cd zly
cp infrastructure/.env.example .env
# Edit .env with secure secrets

# Start everything
docker compose -f infrastructure/docker-compose.yml up -d

# Run migrations
docker compose exec fastapi alembic upgrade head

Full guide in docs/DEPLOYMENT.md.

Configuration

All configuration lives in app/config.py and is driven by environment variables:

Variable Default Description
DATABASE_URL sqlite+aiosqlite:///./zly.db Database connection string
REDIS_URL redis://localhost:6379/0 Redis connection string (optional, graceful fallback)
SECRET_KEY change-me-in-production General purpose secret
JWT_SECRET change-me-in-production JWT signing key (min 32 bytes)
JWT_ALGORITHM HS256 JWT signing algorithm
ACCESS_TOKEN_EXPIRE_MINUTES 15 JWT token lifetime (minutes)
CORS_ORIGINS http://localhost:8000 Comma-separated allowed origins
DEFAULT_DOMAIN localhost:8000 Base domain for short URLs

API Overview

Interactive OpenAPI docs at /docs (auto-generated by FastAPI). All routes prefixed /api/v1.

Auth

  • POST /api/v1/auth/register: Register a new user
  • POST /api/v1/auth/login: Login, receive JWT access + refresh tokens
  • POST /api/v1/auth/refresh: Refresh JWT token
  • POST /api/v1/auth/forgot-password: Request password reset email
  • POST /api/v1/auth/reset-password: Reset password with token
  • GET /api/v1/auth/oauth/{provider}: OAuth login (Google, GitHub)

Users

  • GET /api/v1/users/me: Get current user
  • PATCH /api/v1/users/me: Update profile
  • POST /api/v1/users/me/change-password: Change password
  • POST /api/v1/users/me/set-password: Set password (OAuth users)

Links

  • POST /api/v1/links: Create a short link
  • GET /api/v1/links?workspace_id=: List links for a workspace
  • GET /api/v1/links/{id}: Get link details
  • PATCH /api/v1/links/{id}: Update a link
  • DELETE /api/v1/links/{id}: Delete a link
  • POST /api/v1/links/{id}/qrcode: Generate QR code (PNG or SVG)
  • POST /api/v1/links/{id}/verify-password: Verify link password

Analytics

  • GET /api/v1/links/{id}/analytics: Per-link analytics
  • GET /api/v1/workspaces/{id}/analytics/summary: Workspace-level summary

A/B Testing

  • POST /api/v1/links/{id}/variants: Add an A/B variant
  • GET /api/v1/links/{id}/variants: List variants
  • PATCH /api/v1/variants/{id}: Update a variant
  • DELETE /api/v1/variants/{id}: Delete a variant

Workspaces and Teams

  • GET/POST /api/v1/workspaces: List/create workspaces
  • GET/PUT/DELETE /api/v1/workspaces/{id}: Workspace detail/update/delete
  • POST /api/v1/workspaces/{id}/invites: Invite a member
  • GET /api/v1/workspaces/{id}/invites: List invites
  • GET /api/v1/workspaces/{id}/members: List members

Email Campaigns

  • GET/POST /api/v1/workspaces/{id}/email/contacts: Manage contacts
  • GET/POST /api/v1/workspaces/{id}/email/templates: Manage templates
  • GET/POST /api/v1/workspaces/{id}/email/campaigns: Manage campaigns
  • POST /api/v1/workspaces/{id}/email/campaigns/{id}/send: Send campaign
  • GET /api/v1/workspaces/{id}/email/campaigns/{id}/stats: Campaign stats

Bio Pages

  • POST /api/v1/workspaces/{id}/bio: Create bio page
  • GET /api/v1/workspaces/{id}/bio: Get bio page
  • PUT /api/v1/workspaces/{id}/bio: Update bio page

Custom Domains

  • POST /api/v1/workspaces/{id}/domains: Add a custom domain
  • GET /api/v1/workspaces/{id}/domains: List domains
  • POST /api/v1/workspaces/{id}/domains/{did}/verify: Verify domain (TXT record)
  • DELETE /api/v1/workspaces/{id}/domains/{did}: Remove a domain

API Keys

  • POST /api/v1/workspaces/{id}/api-keys: Create API key
  • GET /api/v1/workspaces/{id}/api-keys: List API keys
  • DELETE /api/v1/api-keys/{id}: Delete API key

Admin

  • GET /api/v1/admin/users: List users (paginated)
  • GET /api/v1/admin/users/{id}: Get user details
  • PATCH /api/v1/admin/users/{id}: Update user (superuser, active)
  • GET /api/v1/admin/stats: System statistics
  • GET /api/v1/admin/audit-logs: Audit log (paginated, filterable)

Sessions

  • GET /api/v1/sessions: List active sessions
  • DELETE /api/v1/sessions/{jti}: Revoke a session
  • POST /api/v1/sessions/revoke-all: Revoke all sessions

Redirect

  • GET /{short_code}: Redirect to destination URL

Project Structure

zly/
  app/               FastAPI application
    api/             Route handlers
    core/            Config, security, dependencies, rate limiter
    models/          SQLAlchemy ORM models
    schemas/         Pydantic schemas
    services/        Business logic
    templates/       Jinja2 HTML templates (auth, dashboard, bio, errors, partials)
    routes/          Dashboard and auth HTML page routes
    db.py            Database engine
    config.py        Settings
    cli.py           CLI management commands
    main.py          App factory
  api/index.py       Vercel serverless entrypoint
  infrastructure/    Docker, Caddy, env config
  migrations/        Alembic migrations
  tests/             Pytest suite (210+)
  worker/            arq background worker
  docs/              Deployment and architecture guides
  .github/           CI workflow
  pyproject.toml     Project config

Architecture

See docs/ARCHITECTURE.md for detailed architecture documentation covering:

  • Data model relationships and migration strategy
  • Routing flow (redirect engine, caching, analytics recording)
  • Authentication and authorization model
  • Async job patterns and Redis integration
  • Multi-tenancy via workspaces and teams
  • Rate limiting and security middleware

Development

Running Tests

pytest -v          # 210+ tests covering all features
pytest --cov=app   # With coverage

Code Quality

ruff check .       # Linting (E, F, I, N, W, UP)
mypy app/          # Strict type checking

Database Migrations

# Create a new migration
alembic revision --autogenerate -m "description"

# Apply migrations
alembic upgrade head

# Rollback
alembic downgrade -1

License

MIT, see LICENSE.

About

Next-gen open-source URL shortener and marketing platform with analytics, custom domains, QR codes, Link-in-Bio pages, and A/B testing.

Topics

Resources

License

Stars

Watchers

Forks

Contributors

Languages