The previous name 'skipStarredIssues' was misleading as it now skips ALL metadata (not just issues) for starred repositories. The new name 'starredCodeOnly' better reflects the actual behavior - mirroring only source code for starred repos. Changes: - Renamed skipStarredIssues → starredCodeOnly in all files - Updated UI label from "Don't fetch issues" to "Code-only mode" - Updated description to clarify it skips ALL metadata types: issues, PRs, labels, milestones, wiki, and releases - Updated database schema, types, config mapper, and all components - Updated Helm charts, CI configs, and documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
10 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
DONT HALLUCIATE THINGS. IF YOU DONT KNOW LOOK AT THE CODE OR ASK FOR DOCS
NEVER MENTION CLAUDE CODE ANYWHERE.
Project Overview
Gitea Mirror is a web application that automatically mirrors repositories from GitHub to self-hosted Gitea instances. It uses Astro for SSR, React for UI, SQLite for data storage, and Bun as the JavaScript runtime.
Essential Commands
Development
bun run dev # Start development server (port 3000)
bun run build # Build for production
bun run preview # Preview production build
Testing
bun test # Run all tests
bun test:watch # Run tests in watch mode
bun test:coverage # Run tests with coverage
Database Management
bun run init-db # Initialize database
bun run reset-users # Reset user accounts (development)
bun run cleanup-db # Remove database files
Production
bun run start # Start production server
Architecture & Key Concepts
Technology Stack
- Frontend: Astro (SSR) + React + Tailwind CSS v4 + Shadcn UI
- Backend: Bun runtime + SQLite + Drizzle ORM
- APIs: GitHub (Octokit) and Gitea APIs
- Auth: Better Auth with email/password, SSO, and OIDC provider support
Project Structure
/src/pages/api/- API endpoints (Astro API routes)/src/components/- React components organized by feature/src/lib/db/- Database queries and schema (Drizzle ORM)/src/hooks/- Custom React hooks for data fetching/data/- SQLite database storage location
Key Architectural Patterns
- API Routes: All API endpoints follow the pattern
/api/[resource]/[action]and usecreateSecureErrorResponsefor consistent error handling:
import { createSecureErrorResponse } from '@/lib/utils/error-handler';
export async function POST({ request }: APIContext) {
try {
// Implementation
} catch (error) {
return createSecureErrorResponse(error);
}
}
-
Database Queries: Located in
/src/lib/db/queries/organized by domain (users, repositories, etc.) -
Real-time Updates: Server-Sent Events (SSE) endpoint at
/api/eventsfor live dashboard updates -
Authentication System:
- Built on Better Auth library
- Three authentication methods:
- Email & Password (traditional auth)
- SSO (authenticate via external OIDC providers)
- OIDC Provider (act as OIDC provider for other apps)
- Session-based authentication with secure cookies
- First user signup creates admin account
- Protected routes use Better Auth session validation
-
Mirror Process:
- Discovers repos from GitHub (user/org)
- Creates/updates mirror in Gitea
- Tracks status in database
- Supports scheduled automatic mirroring
-
Mirror Strategies: Four ways to organize repositories in Gitea:
- preserve: Maintains GitHub structure (default)
- Organization repos → Same organization name in Gitea
- Personal repos → Under your Gitea username
- single-org: All repos go to one organization
- All repos → Single configured organization
- flat-user: All repos go under user account
- All repos → Under your Gitea username
- mixed: Hybrid approach
- Organization repos → Preserve structure
- Personal repos → Single configured organization
- Starred repos always go to separate organization (starredReposOrg, default: "starred")
- Routing logic in
getGiteaRepoOwner()function
- preserve: Maintains GitHub structure (default)
Database Schema (SQLite)
users- User accounts and authenticationconfigs- GitHub/Gitea connection settingsrepositories- Repository mirror status and metadataorganizations- Organization structure preservationmirror_jobs- Scheduled mirror operationsevents- Activity log and notifications
Testing Approach
- Uses Bun's native test runner (
bun:test) - Test files use
.test.tsor.test.tsxextension - Setup file at
/src/tests/setup.bun.ts - Mock utilities available for API testing.
Development Tips
- Environment variables in
.env(copy from.env.example) - BETTER_AUTH_SECRET required for session signing
- Database auto-initializes on first run
- Use
bun run dev:cleanfor fresh database start - Tailwind CSS v4 configured with Vite plugin
Authentication Setup
- Better Auth handles all authentication
- Configuration in
/src/lib/auth.ts(server) and/src/lib/auth-client.ts(client) - Auth endpoints available at
/api/auth/* - SSO providers configured through the web UI
- OIDC provider functionality for external applications
Common Tasks
Adding a new API endpoint:
- Create file in
/src/pages/api/[resource]/[action].ts - Use
createSecureErrorResponsefor error handling - Add corresponding database query in
/src/lib/db/queries/ - Update types in
/src/types/if needed
Adding a new component:
- Create in appropriate
/src/components/[feature]/directory - Use Shadcn UI components from
/src/components/ui/ - Follow existing naming patterns (e.g.,
RepositoryCard,ConfigTabs)
Modifying database schema:
- Update schema in
/src/lib/db/schema.ts - Run
bun run init-dbto recreate database - Update related queries in
/src/lib/db/queries/
Configuration Options
GitHub Configuration (UI Fields)
Basic Settings (githubConfig)
- username: GitHub username
- token: GitHub personal access token (requires repo and admin:org scopes)
- privateRepositories: Include private repositories
- mirrorStarred: Mirror starred repositories
Gitea Configuration (UI Fields)
- url: Gitea instance URL
- username: Gitea username
- token: Gitea access token
- organization: Destination organization (for single-org/mixed strategies)
- starredReposOrg: Organization for starred repositories (default: "starred")
- visibility: Organization visibility - "public", "private", "limited"
- mirrorStrategy: Repository organization strategy (set via UI)
- preserveOrgStructure: Automatically set based on mirrorStrategy
Schedule Configuration (scheduleConfig)
- enabled: Enable automatic mirroring (default: false)
- interval: Cron expression or seconds (default: "0 2 * * *" - 2 AM daily)
- concurrent: Allow concurrent mirror operations (default: false)
- batchSize: Number of repos to process in parallel (default: 10)
Database Cleanup Configuration (cleanupConfig)
- enabled: Enable automatic cleanup (default: false)
- retentionDays: Days to keep events (stored as seconds internally)
Mirror Options (UI Fields)
- mirrorReleases: Mirror GitHub releases to Gitea
- mirrorLFS: Mirror Git LFS (Large File Storage) objects
- Requires LFS enabled on Gitea server (LFS_START_SERVER = true)
- Requires Git v2.1.2+ on server
- mirrorMetadata: Enable metadata mirroring (master toggle)
- metadataComponents (only available when mirrorMetadata is enabled):
- issues: Mirror issues
- pullRequests: Mirror pull requests
- labels: Mirror labels
- milestones: Mirror milestones
- wiki: Mirror wiki content
Advanced Options (UI Fields)
- skipForks: Skip forked repositories (default: false)
- starredCodeOnly: Skip issues for starred repositories (default: false) - enables "Lightweight mode" for starred repos
Repository Statuses
Repositories can have the following statuses:
- imported: Repository discovered from GitHub
- mirroring: Currently being mirrored to Gitea
- mirrored: Successfully mirrored
- syncing: Repository being synchronized
- synced: Successfully synchronized
- failed: Mirror/sync operation failed
- skipped: Skipped due to filters or conditions
- ignored: User explicitly marked to ignore (won't be mirrored/synced)
- deleting: Repository being deleted
- deleted: Repository deleted
Scheduling and Synchronization (Issue #72 Fixes)
Fixed Issues
- Mirror Interval Bug: Added
mirror_intervalparameter to Gitea API calls when creating mirrors (previously defaulted to 24h) - Auto-Discovery: Scheduler now automatically discovers and imports new GitHub repositories
- Interval Updates: Sync operations now update existing mirrors' intervals to match configuration
- Repository Cleanup: Integrated automatic cleanup of orphaned repositories (repos removed from GitHub)
Environment Variables for Auto-Import
- AUTO_IMPORT_REPOS: Set to
falseto disable automatic repository discovery (default: enabled)
How Scheduling Works
- Scheduler Service: Runs every minute to check for scheduled tasks
- Sync Interval: Configured via
GITEA_MIRROR_INTERVALor UI (e.g., "8h", "30m", "1d") - Auto-Import: Checks GitHub for new repositories during each scheduled sync
- Auto-Cleanup: Removes repositories that no longer exist in GitHub (if enabled)
- Mirror Interval Update: Updates Gitea's internal mirror interval during sync operations
Authentication Configuration
SSO Provider Configuration
- issuerUrl: OIDC issuer URL (e.g., https://accounts.google.com)
- domain: Email domain for this provider
- providerId: Unique identifier for the provider
- clientId: OAuth client ID from provider
- clientSecret: OAuth client secret from provider
- authorizationEndpoint: OAuth authorization URL (auto-discovered if supported)
- tokenEndpoint: OAuth token exchange URL (auto-discovered if supported)
- jwksEndpoint: JSON Web Key Set URL (optional, auto-discovered)
- userInfoEndpoint: User information endpoint (optional, auto-discovered)
OIDC Provider Settings (for external apps)
- allowedRedirectUris: Comma-separated list of allowed redirect URIs
- clientId: Generated client ID for the application
- clientSecret: Generated client secret for the application
- scopes: Available scopes (openid, profile, email)
Environment Variables
- BETTER_AUTH_SECRET: Secret key for signing sessions (required)
- BETTER_AUTH_URL: Base URL for authentication (default: http://localhost:4321)
Security Guidelines
- Confidentiality Guidelines:
- Dont ever say Claude Code or generated with AI anyhwere.
- Never commit without the explicict ask