Skip to content

Contributing Development

fuomag9 edited this page Apr 18, 2026 · 4 revisions

Contributing & Development

How to build and contribute to Caddy Proxy Manager.

Table of Contents

  1. Getting Started
  2. Development Environment
  3. Code Style
  4. Testing
  5. Database Changes
  6. Pull Request Process
  7. Project Structure

Getting Started

Prerequisites

  • Node.js: Version 25+ (check .nvmrc)
  • Docker: For running Caddy
  • Git: Version control
  • Code editor: VS Code recommended

Fork and Clone

  1. Fork the repository on GitHub

  2. Clone your fork:

    git clone https://github.com/YOUR-USERNAME/caddy-proxy-manager.git
    cd caddy-proxy-manager
  3. Add upstream remote:

    git remote add upstream https://github.com/fuomag9/caddy-proxy-manager.git

Development Environment

Local Development Setup

Step 1: Install dependencies

npm install

Step 2: Create environment file

cp .env.example .env

Step 3: Configure for development

# .env
NODE_ENV=development  # Allows default admin/admin credentials
SESSION_SECRET="dev-secret-change-in-production"
ADMIN_USERNAME="admin"
ADMIN_PASSWORD="admin"
CADDY_API_URL="http://localhost:2019"
DATABASE_URL="file:./data/caddy-proxy-manager.db"

Step 4: Start Caddy in Docker

docker compose up caddy -d

Step 5: Start Next.js dev server

npm run dev

Step 6: Access application


Development Workflow

Working on features:

  1. Create feature branch:

    git checkout -b feature/your-feature-name
  2. Make changes and test locally

  3. Run linter:

    npm run lint
  4. Build to verify:

    npm run build
  5. Commit changes:

    git add .
    git commit -m "Add feature: description"
  6. Push to your fork:

    git push origin feature/your-feature-name
  7. Open Pull Request on GitHub


Hot Reload

Next.js dev server supports hot reload:

  • Edit files in src/
  • Changes reflect immediately
  • No rebuild needed

Restart required for:

  • Environment variable changes
  • Database schema changes
  • Dependency changes

Code Style

TypeScript

Strict typing required:

  • No any types
  • Use proper type definitions
  • Prefer interfaces over types for objects

Example:

// Good
interface ProxyHost {
  id: number;
  domain: string;
  upstream: string;
}

// Bad
const data: any = fetchData();

Formatting

Use Prettier (configured in .prettierrc):

# Format all files
npm run format

# Check formatting
npm run format:check

VS Code integration:

  • Install Prettier extension
  • Enable "Format on Save"

File Organization

Follow existing patterns:

src/
├── app/              # Next.js app directory
│   ├── (dashboard)/  # Dashboard routes
│   ├── login/        # Login page
│   └── api/          # API routes
├── lib/              # Shared utilities
│   ├── actions.ts    # Server actions
│   ├── db/           # Database schema
│   └── caddy.ts      # Caddy integration
└── components/       # React components

Naming Conventions

Files:

  • Components: PascalCase.tsx
  • Utilities: kebab-case.ts
  • Pages: page.tsx

Variables:

  • Constants: UPPER_SNAKE_CASE
  • Variables: camelCase
  • Components: PascalCase

Functions:

  • Regular: camelCase
  • Server actions: camelCase
  • Async: prefix with async or suffix with Async

Testing

Manual Testing

Test checklist:

  1. Functionality works as expected
  2. UI renders correctly
  3. Forms validate properly
  4. Errors handled gracefully
  5. Console errors checked
  6. Different browsers tested (Chrome, Firefox, Safari)

Build Testing

Always test production build:

npm run build
npm run start

Verify:

  • No build errors
  • All pages accessible
  • Production mode restrictions work
  • Environment validation working

Docker Testing

Test Docker builds:

docker compose up --build -d
docker compose logs web

Verify:

  • Containers start successfully
  • Health checks pass
  • Production mode enforced

Database Changes

Using Drizzle ORM

Schema location: src/lib/db/schema.ts

Making Schema Changes

Step 1: Update schema

// src/lib/db/schema.ts
export const proxyHosts = sqliteTable("proxy_hosts", {
  id: integer("id").primaryKey({ autoIncrement: true }),
  domain: text("domain").notNull(),
  upstream: text("upstream").notNull(),
  // Add new field
  description: text("description"),
});

Step 2: Generate migration

npm run db:generate

This creates migration file in drizzle/ directory.

Step 3: Test migration

# Delete dev database
rm -rf data/caddy-proxy-manager.db*

# Restart dev server (runs migrations)
npm run dev

Step 4: Verify migration

# Check migration applied
sqlite3 data/caddy-proxy-manager.db
.schema proxy_hosts
.exit

Migration Best Practices

  1. Never edit existing migrations - create new ones
  2. Test on clean database before PR
  3. Include migration files in commit
  4. Document breaking changes in PR description

Pull Request Process

Before Submitting

  • Code follows project style
  • TypeScript types are correct (no any)
  • Linting passes: npm run lint
  • Build succeeds: npm run build
  • Tested locally in development mode
  • Tested locally in production mode
  • Database migrations included (if schema changed)
  • No secrets committed

Pull Request Template

## Description
Brief description of changes

## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update

## Testing
- [ ] Tested in development mode
- [ ] Tested in production build
- [ ] Tested in Docker

## Checklist
- [ ] Code follows style guidelines
- [ ] Self-reviewed code
- [ ] Commented complex logic
- [ ] Updated documentation
- [ ] No console errors
- [ ] Database migrations tested

## Screenshots (if UI changes)
[Add screenshots]

Review Process

  1. Automated checks run (future: CI/CD)
  2. Maintainer review code
  3. Feedback addressed if needed
  4. Approved and merged

Project Structure

Directory Layout

caddy-proxy-manager/
├── src/                    # Application source
│   ├── app/               # Next.js App Router
│   │   ├── (dashboard)/  # Dashboard layout & pages
│   │   ├── api/          # API routes
│   │   └── login/        # Login page
│   ├── lib/              # Shared utilities
│   │   ├── actions.ts    # Server actions
│   │   ├── auth.ts       # Authentication
│   │   ├── caddy.ts      # Caddy API client
│   │   ├── config.ts     # Config & validation
│   │   ├── db/           # Database
│   │   │   ├── schema.ts # Drizzle schema
│   │   │   └── index.ts  # DB connection
│   │   └── models/       # Type-safe models
│   └── components/       # React components
│       └── ui/          # Shadcn/ui components
├── docker/               # Docker configurations
│   ├── caddy/           # Caddy Dockerfile
│   └── web/             # Web Dockerfile
├── drizzle/             # Database migrations
├── public/              # Static assets
└── scripts/             # Build scripts

Key Files

Configuration:

  • next.config.mjs - Next.js configuration
  • drizzle.config.ts - Database configuration
  • tsconfig.json - TypeScript configuration
  • .env.example - Environment variable template

Docker:

  • docker-compose.yml - Development compose file
  • docker/web/Dockerfile - Web application image
  • docker/caddy/Dockerfile - Caddy server image

Database:

  • src/lib/db/schema.ts - Database schema (Drizzle)
  • src/lib/db/index.ts - Database connection

Architecture Overview

Tech Stack

  • Framework: Next.js 16 (App Router)
  • Runtime: React 19
  • Language: TypeScript
  • Database: SQLite with Drizzle ORM
  • UI: shadcn/ui with Tailwind CSS
  • Forms: React Hook Form
  • Authentication: Better Auth
  • Proxy Server: Caddy

Design Patterns

Server Actions:

  • Used for mutations (create, update, delete)
  • Defined in src/lib/actions.ts
  • Type-safe with Zod validation

Models:

  • Type-safe data layer in src/lib/models/
  • Abstracts database operations
  • Handles Caddy API integration

Components:

  • React Server Components by default
  • Client Components when needed (forms, interactivity)
  • shadcn/ui + Tailwind CSS for consistent design

Security Contribution Guidelines

Never Commit Secrets

  • No API tokens
  • No passwords
  • No SESSION_SECRET values
  • No .env files

Use .env.example with placeholder values.

Input Validation

Always validate user input:

import { z } from "zod";

const schema = z.object({
  domain: z.string().min(1).regex(/^[a-z0-9.-]+$/),
  upstream: z.string().url(),
});

Database Queries

Use parameterized queries:

// Good
db.select().from(proxyHosts).where(eq(proxyHosts.id, id));

// Bad (SQL injection)
db.execute(`SELECT * FROM proxy_hosts WHERE id = ${id}`);

Principle of Least Privilege

  • Minimize permissions
  • Validate authorization
  • Use type safety
  • Handle errors gracefully

Getting Help

Resources

Communication


Related Documentation


Ready to contribute? See CONTRIBUTING.md in the repository root for quick start guide!

Clone this wiki locally