No description
  • Rust 66.1%
  • HTML 25.4%
  • CSS 4.9%
  • JavaScript 1.4%
  • Just 1.4%
  • Other 0.8%
Find a file
2026-04-16 18:35:42 +02:00
.claude feat(frontend): restyle index.html to match a8n-tools light theme 2026-03-17 02:40:32 +01:00
.forgejo/workflows chore: Forgot to add release workflow 2026-04-16 18:31:46 +02:00
.idea resolved merge conflict from landingPage 2026-03-13 00:17:05 -04:00
assets Updated readme 2025-11-19 10:05:15 -05:00
docs fix: use CSS class instead of inline style for contrast button active state 2026-04-08 21:41:30 +02:00
examples feat: Change the Docker port and sync with other repos 2026-03-15 22:55:48 +01:00
oci-build Add comprehensive test coverage across all modules 2026-03-17 20:35:14 +01:00
src fix: add server route for theme.js static file 2026-03-26 19:44:14 +01:00
static fix: use CSS class instead of inline style for contrast button active state 2026-04-08 21:41:30 +02:00
tests test: Add 35 tests covering previously untested code paths 2026-03-23 20:49:44 +01:00
.dockerignore feat: Migrate to buildkit (docker buildx) from buildah 2026-02-22 11:29:45 -05:00
.env.saas feat: Add structured logging with tracing 2026-03-19 20:43:36 +01:00
.env.standalone feat: Add structured logging with tracing 2026-03-19 20:43:36 +01:00
.gitignore chore: add .superpowers/ to .gitignore 2026-04-08 21:51:25 +02:00
Cargo.lock chore: Update Cargo.lock for tracing dependencies 2026-03-19 20:43:58 +01:00
Cargo.toml test: Add 35 tests covering previously untested code paths 2026-03-23 20:49:44 +01:00
CLAUDE.md docs: update CLAUDE.md and README.md for per-developer instances 2026-04-02 20:38:23 +02:00
compose.dev.yml feat: add per-developer compose.dev.yml with Traefik routing 2026-04-02 16:58:39 +02:00
compose.yml Add comprehensive test coverage across all modules 2026-03-17 20:35:14 +01:00
Dockerfile Add comprehensive test coverage across all modules 2026-03-17 20:35:14 +01:00
init.sql Planning the development process with claude 2025-11-17 09:09:55 -05:00
justfile chore: Remove unnecessary release-test 2026-04-16 18:35:13 +02:00
README.md docs: update CLAUDE.md and README.md for per-developer instances 2026-04-02 20:38:23 +02:00
vibe-coding.md Planning the development process with claude 2025-11-17 09:09:55 -05:00

rus

Rust URL Shortener - A fast, secure URL shortening service built with Rust and Actix-web. Supports two deployment modes: standalone with built-in auth, or SaaS for integration with a parent application.

URL Shortener Homepage

Features

  • JWT Authentication - Secure user registration and login with Argon2id password hashing
  • SQLite Persistence - Reliable data storage with SQLite (bundled, zero setup)
  • Click Tracking - Per-click history with configurable retention and analytics
  • QR Code Generation - Generate QR codes for shortened URLs (PNG and SVG)
  • Custom Names - Give your shortened URLs memorable names
  • URL Management - Create, rename, delete, and monitor URLs
  • Admin Panel - User management and abuse report review
  • Abuse Reporting - Public abuse reporting for malicious URLs
  • Account Security - Login attempt tracking with configurable lockout
  • Rate Limiting - Built-in request rate limiting via actix-governor
  • Refresh Tokens - Seamless token refresh without re-login
  • Dual Build Modes - Standalone or SaaS deployment
  • Docker Support - Multi-stage Dockerfile with dependency caching

Build Modes

Standalone (default)

Full-featured URL shortener with built-in user management:

  • User registration and login with JWT authentication
  • Password hashing with Argon2id (with automatic bcrypt migration)
  • Admin user management
  • Account lockout protection
  • Refresh token rotation
cargo build --release --features standalone

SaaS

Lightweight version for integration with a parent application:

  • No built-in user management (uses external auth via access_token cookie)
  • User identity extracted from parent app's JWT
  • No registration/login routes
  • Dashboard redirects to parent app if no valid session
cargo build --release --no-default-features --features saas

Prerequisites

  • Rust 1.93 or higher (or use Docker)

Installation

  1. Clone the repository:
git clone https://github.com/joshrandall8478/rus.git
cd rus
  1. Copy and edit the environment file:
cp .env.standalone .env
# Edit .env and set JWT_SECRET
  1. Build and run:
cargo build --release
cargo run --release

The application starts on http://localhost:4001.

Docker Deployment

Each developer gets their own HTTPS instance at https://{USER}-rus.a8n.run (where {USER} is your OS username), routed via Traefik:

just dev                 # Start standalone instance
just dev saas            # Start SaaS mode instance
just dev-stop            # Stop instance

For local development without Traefik (cargo-watch with hot-reload on localhost:4001):

just dev-local           # Start local dev server
just dev-local-stop      # Stop local dev server

Usage

Web Interface

  1. Sign Up - Create an account at /signup.html
  2. Log In - Authenticate at /login.html
  3. Dashboard - Manage your URLs at /dashboard.html:
    • Shorten new URLs
    • View click statistics and history
    • Generate QR codes
    • Rename URLs with custom names
    • Copy short URLs to clipboard
    • Delete URLs
  4. Admin - Manage users and abuse reports at /admin.html (admin only)
  5. Report - Report abusive URLs at /report.html

API Endpoints

Public

Method Path Description
POST /api/register Register a new user (standalone only)
POST /api/login Login, returns JWT + refresh token (standalone only)
POST /api/token/refresh Refresh an expired JWT (standalone only)
GET /{short_code} Redirect to original URL
POST /api/report Report an abusive URL

Protected (Bearer Token)

Method Path Description
POST /api/shorten Shorten a URL
GET /api/urls List user's URLs
GET /api/stats/{code} Get URL statistics
GET /api/stats/{code}/clicks Get click history
DELETE /api/urls/{code} Delete a URL
PATCH /api/urls/{code}/name Rename a URL
GET /api/qr/{code} Generate QR code (PNG)
GET /api/qr/{code}/svg Generate QR code (SVG)
GET /api/config Get public configuration

Admin (Bearer Token, admin users only)

Method Path Description
GET /api/admin/users List all users
DELETE /api/admin/users/{id} Delete a user
PATCH /api/admin/users/{id}/admin Toggle admin status
GET /api/admin/reports List abuse reports
PATCH /api/admin/reports/{id} Resolve an abuse report

Example Usage

Using cURL

Register:

curl -X POST http://localhost:4001/api/register \
  -H "Content-Type: application/json" \
  -d '{"username":"test","password":"password123"}'

Login and save token:

TOKEN=$(curl -s -X POST http://localhost:4001/api/login \
  -H "Content-Type: application/json" \
  -d '{"username":"test","password":"password123"}' | jq -r '.token')

Shorten a URL:

curl -X POST http://localhost:4001/api/shorten \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"url":"https://github.com/joshrandall8478/rus"}'

Get your URLs:

curl http://localhost:4001/api/urls \
  -H "Authorization: Bearer $TOKEN"

Project Structure

rus/
├── src/
│   ├── main.rs              # Entry point, route configuration
│   ├── config.rs            # Environment-based configuration
│   ├── db.rs                # Database connection and schema
│   ├── models.rs            # Data models and request/response types
│   ├── security.rs          # Password validation, account lockout
│   ├── auth/
│   │   ├── mod.rs
│   │   ├── jwt.rs           # JWT creation and validation
│   │   └── middleware.rs    # Auth middleware
│   ├── handlers/
│   │   ├── mod.rs
│   │   ├── auth.rs          # Registration, login (standalone)
│   │   ├── admin.rs         # User management (standalone)
│   │   ├── abuse.rs         # Abuse reporting
│   │   ├── pages.rs         # Static page serving
│   │   ├── saas_auth.rs     # Cookie-based auth (saas)
│   │   └── urls.rs          # URL CRUD, redirect, statistics
│   └── url/
│       ├── mod.rs
│       ├── shortener.rs     # Short code generation
│       └── qr.rs            # QR code generation
├── static/
│   ├── index.html           # Landing page
│   ├── login.html           # Login page
│   ├── signup.html          # Registration page
│   ├── dashboard.html       # URL management dashboard
│   ├── admin.html           # Admin panel
│   ├── report.html          # Abuse report form
│   ├── setup.html           # Initial setup page
│   ├── 404.html             # Custom 404 error page
│   ├── styles.css           # Global styles
│   └── auth.js              # Authentication utilities
├── oci-build/
│   ├── setup.nu             # Nushell build script
│   └── get-tags.nu          # Image tag derivation from git describe
├── data/
│   └── rus.db               # SQLite database (auto-created)
├── Cargo.toml
├── Dockerfile
├── compose.yml
├── compose.dev.yml          # Per-developer Traefik compose
├── justfile                 # Task runner recipes
├── .env.standalone          # Env template for standalone mode
└── .env.saas                # Env template for saas mode

Environment Variables

Shared (both modes)

Variable Description Default
DB_PATH Path to SQLite database file ./data/rus.db
HOST Server bind address 0.0.0.0
APP_PORT Server port 4001
HOST_URL Public URL for shortened links http://localhost:4001
MAX_URL_LENGTH Maximum URL length 2048
CLICK_RETENTION_DAYS Days to retain click history 30
RUST_LOG Log level info

Standalone only

Variable Description Default
JWT_SECRET Base64-encoded 32-byte secret for JWT signing Required
JWT_EXPIRY JWT expiry in hours 1
REFRESH_TOKEN_EXPIRY Refresh token expiry in days 7
ACCOUNT_LOCKOUT_ATTEMPTS Failed attempts before lockout 5
ACCOUNT_LOCKOUT_DURATION Lockout duration in minutes 30
ALLOW_REGISTRATION Allow public signups true

SaaS only

Variable Description Default
SAAS_JWT_SECRET JWT secret for validating parent app tokens Required

Database Schema

users (standalone only)

  • userID - Primary key
  • username - Unique username
  • password - Argon2id hashed password (legacy bcrypt hashes migrated on login)
  • is_admin - Admin flag (0/1)
  • created_at - Account creation timestamp

urls

  • id - Primary key
  • user_id - Foreign key to users
  • original_url - The original long URL
  • short_code - Unique 6-character code (indexed)
  • name - Optional custom name
  • clicks - Click counter
  • created_at - URL creation timestamp

click_history

  • id - Primary key
  • url_id - Foreign key to urls
  • clicked_at - Click timestamp

refresh_tokens (standalone only)

  • id - Primary key
  • user_id - Foreign key to users
  • token - Unique refresh token
  • expires_at - Expiry timestamp

login_attempts (standalone only)

  • id - Primary key
  • username - Attempted username
  • attempted_at - Attempt timestamp
  • success - Whether login succeeded (0/1)

abuse_reports

  • id - Primary key
  • short_code - Reported URL code
  • reporter_email - Optional reporter email
  • reason - Report reason
  • description - Optional description
  • status - Report status (pending/resolved)
  • created_at, resolved_at, resolved_by

Technology Stack

Development

just dev                     # Traefik-routed instance (standalone)
just dev saas                # Traefik-routed instance (saas)
just dev-local               # Local dev with hot-reload
just test                    # Run tests (standalone)
just test-saas               # Run tests (saas)
just lint                    # Clippy
just fmt                     # Format

Short Code Generation

  • 6-character alphanumeric codes (A-Z, a-z, 0-9)
  • 62^6 = ~56.8 billion possible combinations
  • Collision detection ensures unique codes

Security

  • JWT-based authentication with short-lived tokens
  • Argon2id password hashing (with transparent bcrypt migration on login)
  • Refresh token rotation
  • Account lockout after configurable failed attempts
  • Rate limiting on API endpoints
  • Protected API endpoints with user-scoped access
  • SQL injection prevention via parameterized queries
  • Foreign key enforcement enabled

Contributing

Contributions are welcome! Feel free to:

  • Report bugs
  • Suggest features
  • Submit pull requests

License

This project is open source and available under the MIT License.


Made with Rust