| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Modern, self-hosted issue tracker with Kotlin/Spring Boot backend and Next.js frontend. Features a powerful Kanban board, backlog management, advanced search, and drag & drop functionality.
Board View (Kanban)
Backlog View
Archive View
issue-tracker/ ├── backend/ │ └── src/main/ │ ├── kotlin/com/issuetracker/ │ │ ├── domain/ # Entities (Board, Task, BacklogCategory) │ │ ├── dto/ # Request/Response DTOs │ │ ├── repository/ # R2DBC repositories │ │ ├── service/ # Business logic │ │ ├── web/ # REST controllers │ │ ├── mcp/ # MCP server, tools and confirmation guard │ │ └── exception/ # Custom exceptions │ └── resources/ │ ├── db/migration/ # Flyway SQL migrations (V1__, V2__, ...) │ ├── application.yml # Base config │ ├── application-dev.yml # Local PostgreSQL │ ├── application-prod.yml # Env-var driven │ └── static/ # Deployed frontend (auto-generated) ├── frontend/ │ ├── app/ # Next.js App Router pages │ ├── features/ # Feature modules (see below) │ ├── shared/ # Cross-feature components, hooks, utils │ ├── core/ # Error types and error boundary │ ├── types/ # Shared type barrel (re-exports feature types) │ ├── e2e/ # Playwright specs, fixtures and helpers │ ├── out/ # Build output (→ backend/static) │ └── next.config.mjs # Static export config ├── gradlew # Gradle wrapper ├── build.gradle.kts # Root build config ├── docs/MCP.md # MCP endpoint reference ├── CLAUDE.md # Development guidelines ├── FEATURES.md # Detailed feature documentation └── openapi.json # REST API specification
The easiest way to run Issue Tracker is using Docker Compose with PostgreSQL:
docker-compose.yml:
version: '3.8'
services:
postgres:
image: postgres:16-alpine
container_name: issue-tracker-db
environment:
POSTGRES_DB: issuetracker
POSTGRES_USER: issuetracker
POSTGRES_PASSWORD: changeme
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- '5432:5432'
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U issuetracker']
interval: 10s
timeout: 5s
retries: 5
issue-tracker:
image: ghcr.io/freefair/issue-tracker:latest
container_name: issue-tracker
depends_on:
postgres:
condition: service_healthy
ports:
- '8080:8080'
environment:
# Spring Profile
SPRING_PROFILES_ACTIVE: prod
# Database Configuration
DB_HOST: postgres
DB_PORT: 5432
DB_NAME: issuetracker
DB_USER: issuetracker
DB_PASSWORD: changeme
# CORS Configuration (set to your domain in production)
CORS_ALLOWED_ORIGINS: http://localhost:8080
restart: unless-stopped
volumes:
postgres_data:Start:
docker-compose up -dView logs:
docker-compose logs -f issue-trackerStop:
docker-compose downAccess the application at http://localhost:8080
If you already have a PostgreSQL database:
docker run -d \
--name issue-tracker \
-p 8080:8080 \
-e SPRING_PROFILES_ACTIVE=prod \
-e DB_HOST=your-db-host \
-e DB_PORT=5432 \
-e DB_NAME=issuetracker \
-e DB_USER=your-db-user \
-e DB_PASSWORD=your-db-password \
-e CORS_ALLOWED_ORIGINS=https://yourdomain.com \
ghcr.io/freefair/issue-tracker:latestFor development or custom builds, see the Getting Started section below.
Setup PostgreSQL database:
# Create database and user
psql -U postgres -c "CREATE DATABASE issuetracker;"
psql -U postgres -c "CREATE USER issuetracker WITH PASSWORD 'postgres';"
psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE issuetracker TO issuetracker;"Build and deploy frontend:
cd frontend
npm install
npm run build # Builds to out/
npm run deploy # Copies out/ → backend/src/main/resources/static/Start backend:
cd ..
./gradlew bootRun # Uses dev profile with localhost PostgreSQLAccess application:
Frontend hot reload:
cd frontend
npm run dev # Starts Next.js dev server on http://localhost:3000Backend auto-restart:
./gradlew bootRun --continuousFull deployment (after changes):
# Kill existing backend
pkill -f "gradle.*bootRun"
# Build and deploy frontend
cd frontend && npm run deploy
# Restart backend
cd ..
./gradlew bootRun > /tmp/backend.log 2>&1 &
# Verify
sleep 5 && curl -s http://localhost:8080/api/boards | jqGET /api/boards # List all boards
GET /api/boards/{id} # Get board by ID
POST /api/boards # Create board
PUT /api/boards/{id} # Update board
DELETE /api/boards/{id} # Delete board (cascades to tasks)GET /api/boards/{boardId}/tasks # List tasks for board
GET /api/boards/{boardId}/tasks?status=TODO # Filter by status
GET /api/tasks/{id} # Get task by ID
POST /api/boards/{boardId}/tasks # Create task
PATCH /api/tasks/{id} # Update task (partial)
PATCH /api/tasks/{id}/move # Move task (status + position)
DELETE /api/tasks/{id} # Delete taskGET /api/tasks/search?boardId={id}&q={query} # Search in board
GET /api/tasks/search/global?q={query} # Search across all boardsGET /api/boards/{boardId}/tags # Get all tags for board
GET /api/boards/{boardId}/tags?q={query} # Filter tagsGET /api/boards/{boardId}/backlog-categories # List categories
POST /api/boards/{boardId}/backlog-categories # Create category
GET /api/backlog-categories/{id} # Get category
PATCH /api/backlog-categories/{id} # Update category
DELETE /api/backlog-categories/{id} # Delete categoryBesides REST, the same actions are available over the Model Context Protocol, so an AI agent can drive the tracker directly:
claude mcp add --transport http issue-tracker http://localhost:8080/mcp19 tools cover boards, tasks and backlog categories. Destructive ones ask for confirmation first. Full reference: docs/MCP.md.
Create Board:
curl -X POST http://localhost:8080/api/boards \
-H "Content-Type: application/json" \
-d '{"name": "My Project", "description": "Main development board"}'Create Task:
curl -X POST http://localhost:8080/api/boards/{boardId}/tasks \
-H "Content-Type: application/json" \
-d '{
"title": "Implement user authentication",
"description": "Add session-based auth",
"status": "TODO",
"position": 0,
"tags": ["backend", "security"]
}'Update Task:
curl -X PATCH http://localhost:8080/api/tasks/{taskId} \
-H "Content-Type: application/json" \
-d '{"status": "IN_PROGRESS", "position": 1}'Search Tasks:
curl "http://localhost:8080/api/tasks/search?boardId={id}&q=authentication"
curl "http://localhost:8080/api/tasks/search/global?q=Tag:backend"Tables:
Key Relationships:
Database schema managed with Flyway. Migrations located in:
backend/src/main/resources/db/migration/ ├── V1__initial_schema.sql ├── V2__sample_data.sql └── V3__create_backlog_categories.sql
Creating new migrations:
Reset database:
# Drop and recreate database
psql -U postgres -c "DROP DATABASE IF EXISTS issuetracker;"
psql -U postgres -c "CREATE DATABASE issuetracker;"
psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE issuetracker TO issuetracker;"
# Restart application - Flyway will run migrations
pkill -f gradle
./gradlew bootRunFrontend:
Backend:
Database Configuration:
Application Configuration:
MCP Configuration (see docs/MCP.md):
Java Version:
# Using jenv
jenv local 21
# Or set JAVA_HOME
export JAVA_HOME=/path/to/java-21./deploy/deploy.shBuilds the working tree for aarch64, streams the image to the Pi over SSH and runs it there as a Compose stack together with PostgreSQL and an nginx reverse proxy. Reachable afterwards at http://issue-tracker.raspi01.local/. Use --dry-run to see what would happen. Details in deploy/README.md.
# 1. Build frontend (static export)
cd frontend
npm run build
npm run deploy
# 2. Build backend JAR (includes frontend)
cd ..
./gradlew build
# 3. JAR location
# backend/build/libs/backend-*.jarjava -jar backend/build/libs/backend-*.jarRequired:
PostgreSQL Database Setup:
First, create the database:
CREATE DATABASE issuetracker;
CREATE USER issuetracker WITH PASSWORD 'your_secure_password';
GRANT ALL PRIVILEGES ON DATABASE issuetracker TO issuetracker;Run with PostgreSQL:
export SPRING_PROFILES_ACTIVE=prod
export DB_HOST=localhost
export DB_PORT=5432
export DB_NAME=issuetracker
export DB_USER=issuetracker
export DB_PASSWORD=your_secure_password
export CORS_ALLOWED_ORIGINS=https://tracker.example.com
java -jar backend/build/libs/backend-*.jarOr use a .env file and docker-compose (see Installation)
R2DBC Persistable Pattern:
Reactive Streams:
Optimistic UI Updates:
URL State Management:
Drag & Drop (@dnd-kit/react):
View backend logs:
tail -f /tmp/backend.logCheck health:
curl http://localhost:8080/actuator/health
curl http://localhost:8080/api/boardsFrontend dev server:
cd frontend
npm run dev
# Access at http://localhost:3000Clean build:
rm -rf frontend/out frontend/.next backend/src/main/resources/staticBackend — JUnit 5, with repository tests running against an embedded PostgreSQL (io.zonky.test:embedded-postgres, no Docker daemon required):
./gradlew test
# HTML report: backend/build/reports/tests/test/index.htmlFrontend — Vitest with Testing Library for units, services and hooks:
cd frontend
npm test # single run
npm run test:watch # watch mode
npm run test:coverage # with coverage reportEnd-to-end and accessibility — Playwright. The suite starts the backend itself; it only needs PostgreSQL on localhost:5432:
docker compose -f compose.dev.yml up -d --wait # start the database first
cd frontend
npm run test:e2e # full suite
npm run test:a11y # accessibility checks only
npm run test:e2e:ui # interactive runner with trace viewer
npm run test:e2e:report # open the report of the last runEach test creates its own board and removes it afterwards, so the specs run in parallel and leave the sample data alone.
Static checks:
cd frontend
npm run type-check # TypeScript, strict mode
npm run lint # ESLint, fails on any warning
npm run format:check # PrettierAll of the above run in CI on every push and pull request — see .github/workflows/README.md.
Current State (Development):
Production Requirements:
MIT
For detailed development guidelines, see CLAUDE.md. For feature documentation, see FEATURES.md. For API details, see openapi.json.
| Back | FazBrowse Home | New Git URL |