ja-shortener

Just An URL Shortener

A simple, fast, and secure URL shortener service built with Django. This service allows you to create short URLs from long ones, track visits, and manage your shortened URLs through a beautiful admin interface.

Table of Contents

Features

Why this project?

I created this project because I needed a simple, lightweight, and quickly deployable URL shortener. While there are many URL shorteners available, I couldn’t find one that met my specific requirements:

After searching extensively and not finding a solution that ticked all these boxes, I decided to create my own. This project is the result of that effort - a URL shortener that’s both powerful and simple to use.

Whether you’re looking to deploy it for personal use or in a production environment, Just An URL Shortener provides all the essential features without the bloat. It’s designed to be maintainable, secure, and efficient.

How it works?

After installing the project, you can access the admin interface at http://localhost:8000/admin/ (you must configure the ADMIN_URL environment variable to change the default admin URL).

At the admin interface, you can:

No technical knowledge is required - it’s designed to be user-friendly while providing powerful features under the hood.

Screenshots

Some screenshots of the admin interface:

Screenshot 1 Screenshot 1 Screenshot 1

Quick Start with Docker

The simplest way to run Ja Shortener is using Docker:

docker run -d \
  -p 8080:8080 \
  -e SECRET_KEY=your-secret-key \
  -e SUPERUSER_USERNAME=admin \
  -e SUPERUSER_EMAIL=admin@example.com \
  -e SUPERUSER_PASSWORD=admin123 \
  cr0hn/ja-shortener

Docker Compose

For a more complete setup with PostgreSQL and Redis:

version: '3.8'

services:
  postgres:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: ja_shortener
      POSTGRES_USER: ja_shortener
      POSTGRES_PASSWORD: ja_shortener_password
    volumes:
      - postgres_data:/var/lib/postgresql/data

  redis:
    image: redis:7-alpine
    command: redis-server --requirepass redis_password
    volumes:
      - redis_data:/data

  ja-shortener:
    image: cr0hn/ja-shortener:latest
    environment:
      - SECRET_KEY=your-secret-key
      - DATABASE_URL=postgresql://ja_shortener:ja_shortener_password@postgres:5432/ja_shortener
      - REDIS_URL=redis://:redis_password@redis:6379/0
      - SUPERUSER_USERNAME=admin
      - SUPERUSER_EMAIL=admin@example.com
      - SUPERUSER_PASSWORD=admin123
    depends_on:
      - postgres
      - redis

volumes:
  postgres_data:
  redis_data:

Save as docker-compose.yml and run:

docker-compose up -d

Helm Chart

Ja Shortener is available as a Helm chart on ArtifactHub.

Add the Helm repository:

helm repo add cr0hn https://cr0hn.github.io/helm-charts
helm repo update

Install the chart:

# Basic installation
helm install ja-shortener cr0hn/ja-shortener

# Installation with custom values
helm install ja-shortener cr0hn/ja-shortener -f values.yaml

# Installation in a specific namespace
helm install ja-shortener cr0hn/ja-shortener -n your-namespace

Customize the installation:

Create a values.yaml file:

# Basic configuration
replicaCount: 2

# Django configuration
django:
  secretKey: "your-secret-key"
  admin:
    username: "admin"
    email: "admin@example.com"
    password: "admin123"

# Enable autoscaling
autoscaling:
  enabled: true
  minReplicas: 2
  maxReplicas: 10
  targetCPUUtilizationPercentage: 80

# Configure ingress
ingress:
  enabled: true
  hosts:
    - host: your-domain.com
      paths:
        - path: /
          pathType: Prefix

Upgrade the installation:

helm upgrade ja-shortener cr0hn/ja-shortener -f values.yaml

Uninstall the chart:

helm uninstall ja-shortener

Configuration

The application can be configured using environment variables. Here’s a complete list of available options:

Variable Description Default Value Required
SECRET_KEY Django secret key for security Random generated key Yes
DEBUG Enable debug mode False No
ALLOWED_HOSTS List of allowed hosts * No
CSRF_TRUSTED_ORIGINS List of trusted origins for CSRF http://localhost No
DATABASE_URL Database connection URL SQLite (development) No
REDIS_URL Redis connection URL None (disabled) No
SENTRY_DSN Sentry DSN for error tracking None (disabled) No
ADMIN_URL Custom admin URL path admin/ No
ENABLE_VISITS_TRACKING Enable visit tracking True No
GUNICORN_WORKERS Number of Gunicorn workers 5 No
GUNICORN_LOG_LEVEL Gunicorn log level INFO No
SUPERUSER_USERNAME Superuser username None Yes
SUPERUSER_EMAIL Superuser email None Yes
SUPERUSER_PASSWORD Superuser password None Yes

Backup Configuration

The application uses django-dbbackup for database backups. Here are the available configuration options:

Variable Description Default Value Required
ENABLE_BACKUP Enable automatic backups False No
BACKUP_TYPE Type of backup storage (s3/local) None Yes (if ENABLE_BACKUP=True)

S3 Backup Configuration

Variable Description Default Value Required
BACKUP_ACCESS_KEY AWS Access Key None Yes (for S3)
BACKUP_SECRET_KEY AWS Secret Key None Yes (for S3)
BACKUP_BUCKET_NAME S3 Bucket name None Yes (for S3)
BACKUP_DEFAULT_ACL S3 ACL for backups private No
BACKUP_REGION AWS Region None Yes (for S3)
BACKUP_ENDPOINT_URL Custom S3 endpoint URL None No

Local Backup Configuration

Variable Description Default Value Required
BACKUP_LOCATION Local directory for backups /data/backups No

Backup Commands

Once configured, you can use the following commands to manage your backups:

Manual Backup

# Create a database backup
python manage.py dbbackup

# Create a compressed backup
python manage.py dbbackup --compress

# Create an encrypted backup
python manage.py dbbackup --encrypt

# Create a backup with both compression and encryption
python manage.py dbbackup --compress --encrypt

Restore Backup

# Restore the latest backup
python manage.py dbrestore

# Restore a specific backup file
python manage.py dbrestore --input-filename=backup-filename.dump

# Restore a compressed backup
python manage.py dbrestore --uncompress

# Restore an encrypted backup
python manage.py dbrestore --decrypt --passphrase=your-passphrase

List Backups

# List all backups
python manage.py listbackups

# List only database backups
python manage.py listbackups --content-type=db

# List only compressed backups
python manage.py listbackups --compressed

Automated Backups

To automate backups, you can use cron jobs or systemd timers. Here are some examples:

Using Cron

Add to your crontab (crontab -e):

# Daily backup at 2 AM
0 2 * * * cd /path/to/project && python manage.py dbbackup --compress >> /var/log/backups.log 2>&1

# Weekly backup on Sunday at 3 AM
0 3 * * 0 cd /path/to/project && python manage.py dbbackup --compress --servername=weekly >> /var/log/backups.log 2>&1

Using Docker with Cron

version: '3.8'

services:
  postgres:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: ja_shortener
      POSTGRES_USER: ja_shortener
      POSTGRES_PASSWORD: ja_shortener_password
    volumes:
      - postgres_data:/var/lib/postgresql/data

  ja-shortener:
    image: cr0hn/ja-shortener:latest
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - DATABASE_URL=postgresql://ja_shortener:ja_shortener_password@postgres:5432/ja_shortener
      - ENABLE_BACKUP=True
      - BACKUP_TYPE=s3
      - BACKUP_ACCESS_KEY=your-aws-access-key
      - BACKUP_SECRET_KEY=your-aws-secret-key
      - BACKUP_BUCKET_NAME=your-backup-bucket
      - BACKUP_REGION=us-east-1
    command: >
      sh -c "echo '0 2 * * * python manage.py dbbackup --compress >> /var/log/backups.log 2>&1' > /etc/crontabs/root &&
             crond -f -l 8"

volumes:
  postgres_data:

Using Systemd Timer

Create a service file /etc/systemd/system/ja-shortener-backup.service:

[Unit]
Description=Ja Shortener Database Backup
After=network.target

[Service]
Type=oneshot
User=ja_shortener
WorkingDirectory=/path/to/project
ExecStart=/usr/bin/python manage.py dbbackup --compress

Create a timer file /etc/systemd/system/ja-shortener-backup.timer:

[Unit]
Description=Run Ja Shortener backup daily

[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true

[Install]
WantedBy=timers.target

Enable and start the timer:

sudo systemctl enable ja-shortener-backup.timer
sudo systemctl start ja-shortener-backup.timer

Important Considerations

High Availability (HA) Setup

When deploying in a High Availability environment, please consider the following:

  1. Session Management:
    • If running multiple instances, you MUST configure Redis for session management
    • Without Redis, users will be logged out when requests hit different instances
    • Example Redis configuration:
      REDIS_URL=redis://your-redis-host:6379/0
      
  2. Database Backups:
    • Regular backups are crucial for data safety
    • Consider using S3 or similar cloud storage for backup redundancy
    • Test restore procedures regularly
    • Keep backup encryption keys secure
  3. Load Balancing:
    • Ensure sticky sessions are enabled in your load balancer
    • Configure proper health checks
    • Consider using a CDN for static files
  4. Security Considerations:
    • Always use HTTPS in production
    • Keep your SECRET_KEY secure and rotate it periodically
    • Regularly update dependencies
    • Monitor for suspicious activities
  5. Performance:
    • Redis caching is recommended for high-traffic deployments
    • Adjust GUNICORN_WORKERS based on your server’s CPU cores
    • Monitor memory usage and adjust accordingly
  6. Monitoring:
    • Enable Sentry for error tracking
    • Set up proper logging
    • Monitor database connections
    • Track URL redirection performance

Production Checklist

Before going to production, ensure you have:

License

This project is licensed under the Functionary Source License (FSL). See the LICENSE file for details.

License Terms

This project is available under the Functionary Source License (FSL). You are free to:

However, you are NOT allowed to:

If you need to use this software in a way that conflicts with these terms, please contact me at cr0hncr0hn.com to discuss licensing options.

Author

Contributing

We welcome contributions! Please see our Contributing Guide for details.