Skip to content

amadr-95/linkshade

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

218 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

LinkShade

A modern URL shortening service built with Spring Boot and PostgreSQL, featuring OAuth authentication, rate limiting, and comprehensive URL management capabilities.

Java Spring Boot PostgreSQL License: MIT


πŸ“‘ Table of Contents


🎯 Overview

LinkShade is a production-ready URL shortening service designed with scalability, security, and user experience in mind. It provides both public and authenticated access, with robust rate limiting, OAuth integration, and comprehensive URL management features.

Purpose

  • Simplify URL Sharing: Convert long URLs into short, manageable links
  • Track Engagement: Monitor clicks
  • Secure Access Control: Support for both public and private URLs with user authentication
  • Multi-tenant Support: User-specific URL management with admin capabilities
  • Enterprise-Ready: Built with production best practices including rate limiting, validation, and error handling

✨ Key Features

Core Functionality

  • βœ… URL Shortening: Generate short, unique identifiers for long URLs
  • βœ… Custom Short Codes: Create personalized short URL identifiers
  • βœ… Click Tracking: Monitor usage statistics for each shortened URL
  • βœ… URL Expiration: Automatic expiration with configurable time periods
  • βœ… Bulk Operations: Delete or reactivate multiple URLs at once
  • βœ… User Profiles: Manage URLs fields
  • βœ… Admin Dashboard: Administrative interface for user and URL management

Authentication & Security

  • βœ… OAuth 2.0 Integration: Login with Google and GitHub
  • βœ… Rate Limiting: Separate limits for anonymous and authenticated users
  • βœ… Private URLs: User-specific URL access control
  • βœ… Input Validation: Comprehensive validation for URLs and user input

UI/UX

  • βœ… Responsive Design: Bootstrap 5-based modern interface
  • βœ… Interactive Carousel: Product feature showcase
  • βœ… Pagination & Sorting: Efficient data browsing with customizable views
  • βœ… Error Pages: Custom error handling with user-friendly messages

πŸ›  Technology Stack

Backend

  • Java 21: Latest LTS version with modern language features
  • Spring Boot 3.5.7: Enterprise-grade application framework
  • Spring Security: OAuth 2.0 and authentication/authorization
  • Spring Data JPA: Database abstraction and ORM
  • Hibernate: JPA implementation for entity management
  • Gradle: Dependency management and build automation

Database

  • PostgreSQL: Primary relational database
  • Flyway: Database version control and migration management

Frontend

  • Thymeleaf: Server-side template engine
  • Bootstrap 5.3.5: Responsive UI framework
  • Bootstrap Icons 1.11.3: Icon library
  • JavaScript: Client-side interactivity

DevOps & Tools

  • Docker: Containerization for consistent environments
  • Docker Compose: Multi-container orchestration
  • Lombok: Boilerplate code reduction

πŸ— Architecture

Domain-Driven Design

The application follows a layered architecture with clear separation of concerns:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚         Presentation Layer              β”‚
β”‚  (Controllers, Templates, Interceptors) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚          Service Layer                  β”‚
β”‚  (Business Logic, Validation, Mapping)  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚         Repository Layer                β”‚
β”‚    (Data Access, JPA Repositories)      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚          Database Layer                 β”‚
β”‚           (PostgreSQL)                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Core Entities

The system is built around two primary domain entities:

  • User: Manages authenticated user information, OAuth provider data, and relationships to shortened URLs
  • ShortUrl: Stores shortened URLs with metadata including clicks, expiration dates, and privacy settings

πŸ“‹ Prerequisites

Before running the application, ensure you have the following installed:

Tool Version Purpose
Java JDK 21+ Required for running the application
Gradle 8.12+ Build and dependency management (or use included Gradle Wrapper)
Docker Latest For containerized PostgreSQL and application deployment
Docker Compose Latest For multi-container orchestration
Git Latest Version control

πŸš€ Getting Started

Get up and running in 5 minutes (using docker compose):

# Clone the repository
git clone https://github.com/yourusername/linkshade.git
cd linkshade

# Set up environment variables (optional - see table below)
cp .env.example .env.local

πŸ“Š Environment Variables Reference

Variable Description Required Default Example
GITHUB_CLIENT_ID GitHub OAuth client ID No* - abc123def456
GITHUB_CLIENT_SECRET GitHub OAuth client secret No* - xyz789uvw456
GOOGLE_CLIENT_ID Google OAuth client ID No* - 123456.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET Google OAuth client secret No* - GOCSPX-abc123
SPRING_DATASOURCE_URL PostgreSQL JDBC URL Yes - jdbc:postgresql://localhost:5432/linkshade-db
SPRING_DATASOURCE_USERNAME Database username Yes - postgres
SPRING_DATASOURCE_PASSWORD Database password Yes - postgres

* Required for OAuth login functionality. Application will work without OAuth but users won't be able to log in.

Note

If you don't mind having this feature, skip this part and continue with running the app from here

To enable OAuth authentication, you'll need:

Google OAuth Configuration

  1. Go to Google Cloud Console
  2. Create a new project or select an existing one
  3. Enable the Google+ API
  4. Create OAuth 2.0 credentials:
    • Application type: Web application
    • Authorized redirect URIs: http://localhost:8080/login/oauth2/code/google
  5. Copy the Client ID and Client Secret to your .env.local file

GitHub OAuth Configuration

  1. Go to GitHub Developer Settings
  2. Click "New OAuth App"
  3. Fill in the application details:
    • Application name: LinkShade
    • Homepage URL: http://localhost:8080
    • Authorization callback URL: http://localhost:8080/login/oauth2/code/github
  4. Copy the Client ID and Client Secret to your .env.local file

Option 1

Run the database in Docker and the application from your IDE or Gradle:

Step 1: Start PostgreSQL

docker compose up -d linkshade-db

Step 2: Run the Application

Note

You need to add the env.local path to the run configuration, otherwise env variables won't be loaded and app won't be able to connect to the database

Run LinkshadeApplication.java or

./gradlew bootRun

Step 3: Access the Application

Open your browser and navigate to:

http://localhost:8080

Step 4: Stop the Database (when finished)

docker compose down

To remove all data:

docker compose down -v

Option 2 (Recommended)

Run both the database and application as Docker containers:

Step 1: Build and Start All Services

docker compose up -d --build

Tip

Omit --build flag if no code changes were made since the last build.

Step 2: Verify Services are Running

docker compose ps

Expected output:

NAME              IMAGE              STATUS         PORTS
linkshade-app     linkshade-app      Up             0.0.0.0:8080->8080/tcp
linkshade-db      postgres           Up (healthy)   0.0.0.0:5432->5432/tcp

Step 3: View Logs

All services:

docker compose logs -f

Specific service:

docker compose logs -f [NAME]

Step 4: Access the Application

http://localhost:8080

Step 5: Stop All Services

docker compose down

To remove all data (including database volumes):

docker compose down -v

πŸ“ Project Structure

linkshade/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ main/
β”‚   β”‚   β”œβ”€β”€ java/de/linkshade/
β”‚   β”‚   β”‚   β”œβ”€β”€ config/              # Application configuration
β”‚   β”‚   β”‚   β”œβ”€β”€ domain/              # Domain entities and DTOs
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ entities/        # JPA entities
β”‚   β”‚   β”‚   β”‚   └── dto/             # Data Transfer Objects
β”‚   β”‚   β”‚   β”œβ”€β”€ exceptions/          # Custom exceptions and handlers
β”‚   β”‚   β”‚   β”œβ”€β”€ repositories/        # Spring Data JPA repositories
β”‚   β”‚   β”‚   β”œβ”€β”€ security/            # Security configuration and OAuth
β”‚   β”‚   β”‚   β”œβ”€β”€ services/            # Business logic layer
β”‚   β”‚   β”‚   β”œβ”€β”€ utils/               # Utility classes
β”‚   β”‚   β”‚   β”œβ”€β”€ validation/          # Custom validators
β”‚   β”‚   β”‚   └── web/                 # Controllers and interceptors
β”‚   β”‚   └── resources/
β”‚   β”‚       β”œβ”€β”€ db/migration/        # Flyway migration scripts
β”‚   β”‚       β”œβ”€β”€ static/              # Static assets (CSS, JS, images)
β”‚   β”‚       β”œβ”€β”€ templates/           # Thymeleaf templates
β”‚   β”‚       β”œβ”€β”€ application.yml      # Main configuration
β”‚   β”‚       └── application-prod.yml # Production configuration
β”‚   └── test/                        # Test files
β”œβ”€β”€ docker-compose.yml               # Docker Compose configuration
β”œβ”€β”€ Dockerfile                       # Multi-stage Docker build
β”œβ”€β”€ build.gradle                     # Gradle build configuration
β”œβ”€β”€ settings.gradle                  # Gradle settings
β”œβ”€β”€ .env.example                     # Environment variables template
└── README.md                        # This file

πŸ—„ Database Management

Flyway Migrations

LinkShade uses Flyway for database version control and migration management. This ensures:

  • Version Control: Track all database schema changes
  • Consistency: Apply migrations uniformly across all environments
  • Rollback Safety: Maintain migration history for audit trails
  • Team Collaboration: Avoid schema conflicts

Migration Structure

Flyway migrations are located in src/main/resources/db/migration/:

db/migration/
β”œβ”€β”€ V1__create_schema.sql      # Initial schema creation
β”œβ”€β”€ V2__insert_data.sql        # Seed data
└── V{n}__{description}.sql   # Subsequent migrations

Naming Convention

  • Format: V{version}__{description}.sql
  • Example: V3__add_user_roles.sql
  • Rules:
    • Version must be numeric and unique
    • Double underscore separates version from description
    • Description uses underscores for spaces

⚑ Performance Optimization

Preventing the N+1 Select Problem

The application implements best practices to avoid the N+1 query problem:

spring:
  jpa:
    open-in-view: false

Benefits:

  • βœ… Closes EntityManager at transaction boundary
  • βœ… Prevents lazy loading outside transaction context
  • βœ… Forces explicit data fetching strategies
  • βœ… Releases database connections promptly
  • βœ… Reduces database load

Implementation Strategy:

  1. Lazy Loading by Default: All relationships are marked with FetchType.LAZY
  2. Explicit Fetching: Use @EntityGraph or JPQL JOIN FETCH for related data
  3. Single Query Execution: Ensures only one query per operation instead of N+1

Example:

@EntityGraph(attributePaths = {"user"})
@Query("SELECT u FROM ShortUrl u WHERE u.id = :id")
Optional<ShortUrl> findByIdWithUser(@Param("id") Long id);

🀝 Contributing

Contributions are welcome! Please follow these guidelines:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Development Roadmap

  • Comprehensive unit test coverage
  • Integration test suite
  • QR code generation for shortened URLs

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.


Made with ❀️ using Spring Boot

⬆ Back to Top

About

Fast and secure URL shortening service. Create custom short links with just a few clicks.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors