diff --git a/.dockerignore b/.dockerignore index 650dea2..6da289f 100644 --- a/.dockerignore +++ b/.dockerignore @@ -5,10 +5,10 @@ # Node.js node_modules +bun.lockb npm-debug.log yarn-debug.log yarn-error.log -pnpm-debug.log # Build outputs dist @@ -62,4 +62,3 @@ logs # Cache .cache .npm -.pnpm-store diff --git a/.github/workflows/README.md b/.github/workflows/README.md index d78b898..aba0aa2 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -24,8 +24,7 @@ This workflow runs on all branches and pull requests. It: - On push to any branch (except changes to README.md and docs) - On pull requests to any branch (except changes to README.md and docs) -**Key features:** -- Uses pnpm for faster dependency installation +- Uses Bun for dependency installation - Caches dependencies to speed up builds - Uploads build artifacts for 7 days diff --git a/.github/workflows/astro-build-test.yml b/.github/workflows/astro-build-test.yml index 448190f..d7e23f3 100644 --- a/.github/workflows/astro-build-test.yml +++ b/.github/workflows/astro-build-test.yml @@ -21,26 +21,20 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Install pnpm - uses: pnpm/action-setup@v3 + - name: Setup Bun + uses: oven-sh/setup-bun@v1 with: - version: 10 - run_install: false - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 'lts/*' - cache: 'pnpm' + bun-version: '1.2.9' + cache: true - name: Install dependencies - run: pnpm install + run: bun install - name: Run tests - run: pnpm test + run: bunx vitest run - name: Build Astro project - run: pnpm build + run: bunx astro build - name: Upload build artifacts uses: actions/upload-artifact@v4 diff --git a/.github/workflows/docker-scan.yml b/.github/workflows/docker-scan.yml index 005e612..5265cd4 100644 --- a/.github/workflows/docker-scan.yml +++ b/.github/workflows/docker-scan.yml @@ -7,14 +7,14 @@ on: - 'Dockerfile' - '.dockerignore' - 'package.json' - - 'pnpm-lock.yaml' + - 'bun.lockb' pull_request: branches: [ main ] paths: - 'Dockerfile' - '.dockerignore' - 'package.json' - - 'pnpm-lock.yaml' + - 'bun.lockb' schedule: - cron: '0 0 * * 0' # Run weekly on Sunday at midnight diff --git a/README.md b/README.md index dabdc28..64b63da 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ ```bash docker compose --profile production up -d # or -pnpm setup && pnpm dev +bun run setup && bun run dev ```

@@ -63,9 +63,9 @@ Easily configure your GitHub and Gitea connections, set up automatic mirroring s See the [Quick Start Guide](docs/quickstart.md) for detailed instructions on getting up and running quickly. -### Prerequisites +-### Prerequisites -- Node.js 22 or later +- Bun 1.2.9 or later - A GitHub account with a personal access token - A Gitea instance with an access token @@ -92,7 +92,7 @@ Before running the application in production mode for the first time, you need t ```bash # Initialize the database for production mode -pnpm setup +bun run setup ``` This will create the necessary tables. On first launch, you'll be guided through creating your admin account with a secure password. @@ -110,7 +110,7 @@ Gitea Mirror provides multi-architecture Docker images that work on both ARM64 ( docker compose --profile production up -d # For development mode (requires configuration) -# Ensure you have run pnpm setup first +# Ensure you have run bun run setup first docker compose -f docker-compose.dev.yml up -d ``` @@ -206,40 +206,40 @@ git clone https://github.com/arunavo4/gitea-mirror.git cd gitea-mirror # Quick setup (installs dependencies and initializes the database) -pnpm setup +bun run setup # Development Mode Options # Run in development mode -pnpm dev +bun run dev # Run in development mode with clean database (removes existing DB first) -pnpm dev:clean +bun run dev:clean # Production Mode Options # Build the application -pnpm build +bun run build # Preview the production build -pnpm preview +bun run preview # Start the production server (default) -pnpm start +bun run start # Start the production server with a clean setup -pnpm start:fresh +bun run start:fresh # Database Management # Initialize the database -pnpm init-db +bun run init-db # Reset users for testing first-time signup -pnpm reset-users +bun run reset-users # Check database status -pnpm check-db +bun run check-db ``` ### Configuration @@ -262,10 +262,10 @@ Key configuration options include: ```bash # Install dependencies -pnpm setup +bun run setup # Start the development server -pnpm dev +bun run dev ``` @@ -359,7 +359,7 @@ docker compose -f docker-compose.dev.yml up -d ## Technologies Used - **Frontend**: Astro, React, Shadcn UI, Tailwind CSS v4 -- **Backend**: Node.js + - **Backend**: Bun - **Database**: SQLite (default) or PostgreSQL - **Caching/Queue**: Redis - **API Integration**: GitHub API (Octokit), Gitea API @@ -467,33 +467,19 @@ Try the following steps: > For better Redis connection handling, you can modify the `src/lib/redis.ts` file to include retry logic and better error handling: ```typescript -import Redis from "ioredis"; +import { RedisClient } from "bun"; // Connect to Redis using REDIS_URL environment variable or default to redis://redis:6379 -const redisUrl = process.env.REDIS_URL ?? 'redis://redis:6379'; +const redisUrl = process.env.REDIS_URL ?? "redis://redis:6379"; console.log(`Connecting to Redis at: ${redisUrl}`); -// Configure Redis client with connection options -const redisOptions = { - retryStrategy: (times) => { - // Retry with exponential backoff up to 30 seconds - const delay = Math.min(times * 100, 3000); - console.log(`Redis connection attempt ${times} failed. Retrying in ${delay}ms...`); - return delay; - }, - maxRetriesPerRequest: 5, - enableReadyCheck: true, - connectTimeout: 10000, +const redis = new RedisClient(redisUrl, { autoReconnect: true }); + +redis.onconnect = () => console.log("Redis client connected"); +redis.onclose = err => { + if (err) console.error("Redis client error:", err); }; - -export const redis = new Redis(redisUrl, redisOptions); -export const redisPublisher = new Redis(redisUrl, redisOptions); -export const redisSubscriber = new Redis(redisUrl, redisOptions); - -// Log connection events -redis.on('connect', () => console.log('Redis client connected')); -redis.on('error', (err) => console.error('Redis client error:', err)); ``` diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 2065dc1..c746662 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -5,19 +5,19 @@ set -e # Ensure data directory exists mkdir -p /app/data -# If pnpm is available, run setup (for dev images), else run node init directly -if command -v pnpm >/dev/null 2>&1; then - echo "Running pnpm setup (if needed)..." - pnpm setup || true +# If bun is available, run setup (for dev images) +if command -v bun >/dev/null 2>&1; then + echo "Running bun setup (if needed)..." + bun run setup || true fi # Initialize the database if it doesn't exist if [ ! -f "/app/data/gitea-mirror.db" ]; then echo "Initializing database..." if [ -f "dist/scripts/init-db.js" ]; then - node dist/scripts/init-db.js + bun dist/scripts/init-db.js elif [ -f "dist/scripts/manage-db.js" ]; then - node dist/scripts/manage-db.js init + bun dist/scripts/manage-db.js init else echo "Warning: Could not find database initialization scripts in dist/scripts." echo "Creating and initializing database manually..." @@ -119,9 +119,9 @@ EOF else echo "Database already exists, checking for issues..." if [ -f "dist/scripts/fix-db-issues.js" ]; then - node dist/scripts/fix-db-issues.js + bun dist/scripts/fix-db-issues.js elif [ -f "dist/scripts/manage-db.js" ]; then - node dist/scripts/manage-db.js fix + bun dist/scripts/manage-db.js fix fi # Since the application is not used by anyone yet, we've removed the schema updates and migrations @@ -130,4 +130,4 @@ fi # Start the application echo "Starting Gitea Mirror..." -exec node ./dist/server/entry.mjs +exec bun ./dist/server/entry.mjs diff --git a/package.json b/package.json index 9e206c4..3acfe17 100644 --- a/package.json +++ b/package.json @@ -3,31 +3,30 @@ "type": "module", "version": "1.0.0", "engines": { - "node": ">=22.0.0" + "bun": ">=1.2.9" }, "scripts": { - "setup": "pnpm install && pnpm manage-db init", - "dev": "astro dev", - "dev:clean": "pnpm cleanup-db && pnpm manage-db init && astro dev", - "build": "astro build", + "setup": "bun install && bun run manage-db init", + "dev": "bunx astro dev", + "dev:clean": "bun run cleanup-db && bun run manage-db init && bunx astro dev", + "build": "bunx astro build", "cleanup-db": "rm -f gitea-mirror.db data/gitea-mirror.db", - "manage-db": "tsx scripts/manage-db.ts", - "init-db": "tsx scripts/manage-db.ts init", - "check-db": "tsx scripts/manage-db.ts check", - "fix-db": "tsx scripts/manage-db.ts fix", - "reset-users": "tsx scripts/manage-db.ts reset-users", - "preview": "astro preview", - "start": "node dist/server/entry.mjs", - "start:fresh": "pnpm cleanup-db && pnpm manage-db init && node dist/server/entry.mjs", - "test": "vitest run", - "test:watch": "vitest", - "astro": "astro" + "manage-db": "bun scripts/manage-db.ts", + "init-db": "bun scripts/manage-db.ts init", + "check-db": "bun scripts/manage-db.ts check", + "fix-db": "bun scripts/manage-db.ts fix", + "reset-users": "bun scripts/manage-db.ts reset-users", + "preview": "bunx astro preview", + "start": "bun dist/server/entry.mjs", + "start:fresh": "bun run cleanup-db && bun run manage-db init && bun dist/server/entry.mjs", + "test": "bunx vitest run", + "test:watch": "bunx vitest", + "astro": "bunx astro" }, "dependencies": { "@astrojs/mdx": "^4.2.6", "@astrojs/node": "^9.2.1", "@astrojs/react": "^4.2.7", - "@libsql/client": "^0.15.4", "@octokit/rest": "^21.1.1", "@radix-ui/react-avatar": "^1.1.4", "@radix-ui/react-checkbox": "^1.1.5", @@ -54,7 +53,6 @@ "cmdk": "^1.1.1", "drizzle-orm": "^0.41.0", "fuse.js": "^7.1.0", - "ioredis": "^5.6.1", "jsonwebtoken": "^9.0.2", "lucide-react": "^0.488.0", "next-themes": "^0.4.6", @@ -73,15 +71,13 @@ "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.3.0", "@types/bcryptjs": "^3.0.0", - "@types/better-sqlite3": "^7.6.13", "@types/jsonwebtoken": "^9.0.9", "@types/superagent": "^8.1.9", "@types/uuid": "^10.0.0", "@vitejs/plugin-react": "^4.4.0", - "better-sqlite3": "^9.6.0", "jsdom": "^26.1.0", "tsx": "^4.19.3", "vitest": "^3.1.1" }, - "packageManager": "pnpm@10.10.0" + "packageManager": "bun@1.2.9" } diff --git a/scripts/README-docker.md b/scripts/README-docker.md index 952a4a8..02216fc 100644 --- a/scripts/README-docker.md +++ b/scripts/README-docker.md @@ -43,7 +43,7 @@ The script uses environment variables from the `.env` file in the project root: 3. Using with docker-compose: ```bash # Ensure dependencies are installed and database is initialized - pnpm setup + bun run setup # First build the image ./scripts/build-docker.sh --load diff --git a/scripts/README.md b/scripts/README.md index 94e3f12..3dcae9e 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -19,38 +19,38 @@ This is a consolidated database management tool that handles all database-relate You can execute the database management tool using your package manager with various commands: ```bash -# Checks database status (default action if no command is specified, equivalent to 'pnpm check-db') -pnpm manage-db +# Checks database status (default action if no command is specified, equivalent to 'bun run check-db') +bun run manage-db # Check database status -pnpm check-db +bun run check-db # Initialize the database (only if it doesn't exist) -pnpm init-db +bun run init-db # Fix database location issues -pnpm fix-db +bun run fix-db # Automatic check, fix, and initialize if needed -pnpm db-auto +bun run db-auto # Reset all users (for testing signup flow) -pnpm reset-users +bun run reset-users # Update the database schema to the latest version -pnpm update-schema +bun run update-schema # Remove database files completely -pnpm cleanup-db +bun run cleanup-db # Complete setup (install dependencies and initialize database) -pnpm setup +bun run setup # Start development server with a fresh database -pnpm dev:clean +bun run dev:clean # Start production server with a fresh database -pnpm start:fresh +bun run start:fresh ``` ## Database File Location diff --git a/scripts/docker-diagnostics.sh b/scripts/docker-diagnostics.sh old mode 100755 new mode 100644 index 77ca842..3972a91 --- a/scripts/docker-diagnostics.sh +++ b/scripts/docker-diagnostics.sh @@ -105,12 +105,12 @@ echo -e "${BLUE} Recommendations ${NC}" echo -e "${BLUE}=====================================================${NC}" echo -e "\n${YELLOW}For local development:${NC}" -echo -e "1. ${GREEN}pnpm setup${NC} (initialize database and install dependencies)" +echo -e "1. ${GREEN}bun run setup${NC} (initialize database and install dependencies)" echo -e "2. ${GREEN}./scripts/build-docker.sh --load${NC} (build and load into Docker)" echo -e "3. ${GREEN}docker-compose -f docker-compose.dev.yml up -d${NC} (start the development container)" echo -e "\n${YELLOW}For production deployment (using Docker Compose):${NC}" -echo -e "1. ${GREEN}pnpm setup${NC} (if not already done, to ensure database schema is ready)" +echo -e "1. ${GREEN}bun run setup${NC} (if not already done, to ensure database schema is ready)" echo -e "2. ${GREEN}docker-compose --profile production up -d${NC} (start the production container)" echo -e "\n${YELLOW}For CI/CD builds:${NC}" diff --git a/scripts/manage-db.ts b/scripts/manage-db.ts index eb84bf4..4827773 100644 --- a/scripts/manage-db.ts +++ b/scripts/manage-db.ts @@ -168,7 +168,7 @@ async function checkDatabase() { ); console.warn("This file should be in the data directory."); console.warn( - 'Run "pnpm manage-db fix" to fix this issue or "pnpm cleanup-db" to remove it.' + 'Run "bun run manage-db fix" to fix this issue or "bun run cleanup-db" to remove it.' ); } @@ -215,12 +215,12 @@ async function checkDatabase() { } catch (error) { console.error("❌ Error connecting to the database:", error); console.warn( - 'The database file might be corrupted. Consider running "pnpm manage-db init" to recreate it.' + 'The database file might be corrupted. Consider running "bun run manage-db init" to recreate it.' ); } } else { console.warn("⚠️ WARNING: Database file not found in data directory."); - console.warn('Run "pnpm manage-db init" to create it.'); + console.warn('Run "bun run manage-db init" to create it.'); } } @@ -235,10 +235,10 @@ async function initializeDatabase() { if (fs.existsSync(dataDbFile)) { console.log("⚠️ Database already exists at data/gitea-mirror.db"); console.log( - 'If you want to recreate the database, run "pnpm cleanup-db" first.' + 'If you want to recreate the database, run "bun run cleanup-db" first.' ); console.log( - 'Or use "pnpm manage-db reset-users" to just remove users without recreating tables.' + 'Or use "bun run manage-db reset-users" to just remove users without recreating tables.' ); // Check if we can connect to it @@ -457,7 +457,7 @@ async function resetUsers() { if (!doesDbExist) { console.log( - "❌ Database file doesn't exist. Run 'pnpm manage-db init' first to create it." + "❌ Database file doesn't exist. Run 'bun run manage-db init' first to create it." ); return; } @@ -629,7 +629,7 @@ async function fixDatabaseIssues() { console.warn( "⚠️ WARNING: Production database file not found in data directory." ); - console.warn('Run "pnpm manage-db init" to create it.'); + console.warn('Run "bun run manage-db init" to create it.'); } else { console.log("✅ Production database file found in data directory."); @@ -641,7 +641,7 @@ async function fixDatabaseIssues() { } catch (error) { console.error("❌ Error connecting to the database:", error); console.warn( - 'The database file might be corrupted. Consider running "pnpm manage-db init" to recreate it.' + 'The database file might be corrupted. Consider running "bun run manage-db init" to recreate it.' ); } } @@ -692,7 +692,7 @@ Available commands: reset-users - Remove all users and their data auto - Automatic mode: check, fix, and initialize if needed -Usage: pnpm manage-db [command] +Usage: bun run manage-db [command] `); } } diff --git a/src/content/docs/architecture.md b/src/content/docs/architecture.md index 7b8b7a9..4045f4b 100644 --- a/src/content/docs/architecture.md +++ b/src/content/docs/architecture.md @@ -22,7 +22,7 @@ The application is built using: - React: Component library for interactive UI elements - Shadcn UI: UI component library built on Tailwind CSS - SQLite: Database for storing configuration and state -- Node.js: Runtime environment for the backend +- Bun: Runtime environment for the backend ## Architecture Diagram @@ -30,7 +30,7 @@ The application is built using: graph TD subgraph "Gitea Mirror" Frontend["Frontend
(Astro)"] - Backend["Backend
(Node.js)"] + Backend["Backend
(Bun)"] Database["Database
(SQLite)"] Frontend <--> Backend @@ -60,9 +60,9 @@ Key frontend components: - **Configuration**: Settings for GitHub and Gitea connections - **Activity Log**: Detailed log of mirroring operations -### Backend (Node.js) +### Backend (Bun) -The backend is built with Node.js and provides API endpoints for the frontend to interact with. It handles: +The backend is built with Bun and provides API endpoints for the frontend to interact with. It handles: - Authentication and user management - GitHub API integration diff --git a/src/content/docs/configuration.md b/src/content/docs/configuration.md index 381ff41..ce4c713 100644 --- a/src/content/docs/configuration.md +++ b/src/content/docs/configuration.md @@ -23,7 +23,7 @@ The following environment variables can be used to configure Gitea Mirror: | Variable | Description | Default Value | Example | |----------|-------------|---------------|---------| -| `NODE_ENV` | Node environment (development, production, test) | `development` | `production` | +| `NODE_ENV` | Runtime environment (development, production, test) | `development` | `production` | | `DATABASE_URL` | SQLite database URL | `sqlite://data/gitea-mirror.db` | `sqlite://path/to/your/database.db` | | `JWT_SECRET` | Secret key for JWT authentication | `your-secret-key-change-this-in-production` | `your-secure-random-string` | | `HOST` | Server host | `localhost` | `0.0.0.0` | diff --git a/src/content/docs/quickstart.md b/src/content/docs/quickstart.md index 8efa109..2e75493 100644 --- a/src/content/docs/quickstart.md +++ b/src/content/docs/quickstart.md @@ -16,7 +16,7 @@ Before you begin, make sure you have: 1. A GitHub account with a personal access token 2. A Gitea instance with an access token -3. Docker and docker-compose (recommended) or Node.js 18+ installed +3. Docker and docker-compose (recommended) or Bun 1.2.9+ installed ## Installation Options @@ -51,7 +51,7 @@ If you prefer to run the application directly on your system: 2. Run the quick setup script: ```bash - pnpm setup + bun run setup ``` This installs dependencies and initializes the database. @@ -59,13 +59,13 @@ If you prefer to run the application directly on your system: **Development Mode:** ```bash - pnpm dev + bun run dev ``` **Production Mode:** ```bash - pnpm build - pnpm start + bun run build + bun run start ``` 4. Access the application at [http://localhost:4321](http://localhost:4321) diff --git a/src/lib/config.ts b/src/lib/config.ts index 8583353..8968830 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -4,7 +4,7 @@ // Environment variables export const ENV = { - // Node environment (development, production, test) + // Runtime environment (development, production, test) NODE_ENV: process.env.NODE_ENV || "development", // Database URL - use SQLite by default diff --git a/src/lib/db/index.ts b/src/lib/db/index.ts index 0ae592a..bb9307f 100644 --- a/src/lib/db/index.ts +++ b/src/lib/db/index.ts @@ -1,6 +1,6 @@ import { z } from "zod"; -import { createClient } from "@libsql/client"; -import { drizzle } from "drizzle-orm/libsql"; +import { Database } from "bun:sqlite"; +import { drizzle } from "drizzle-orm/bun-sqlite"; import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core"; import path from "path"; @@ -11,11 +11,24 @@ const dataDir = path.join(process.cwd(), "data"); const dbUrl = process.env.DATABASE_URL || `file:${path.join(dataDir, "gitea-mirror.db")}`; -// Create a client connection to the database -export const client = createClient({ url: dbUrl }); +// Create a SQLite database instance using Bun's native driver +export const sqlite = new Database(dbUrl); + +// Simple async wrapper around Bun's SQLite API for compatibility +export const client = { + async execute(sql: string, params?: any[]) { + const stmt = sqlite.query(sql); + if (/^\s*select/i.test(sql)) { + const rows = stmt.all(params ?? []); + return { rows } as { rows: any[] }; + } + stmt.run(params ?? []); + return { rows: [] } as { rows: any[] }; + }, +}; // Create a drizzle instance -export const db = drizzle(client); +export const db = drizzle(sqlite); // Define the tables export const users = sqliteTable("users", { diff --git a/src/lib/redis.ts b/src/lib/redis.ts index 6c59d9d..a5686e7 100644 --- a/src/lib/redis.ts +++ b/src/lib/redis.ts @@ -1,30 +1,23 @@ -import Redis from "ioredis"; +import { RedisClient } from "bun"; // Connect to Redis using REDIS_URL environment variable or default to redis://redis:6379 // This ensures we have a fallback URL when running with Docker Compose -const redisUrl = process.env.REDIS_URL ?? 'redis://redis:6379'; +const redisUrl = process.env.REDIS_URL ?? "redis://redis:6379"; console.log(`Connecting to Redis at: ${redisUrl}`); // Configure Redis client with connection options -const redisOptions = { - retryStrategy: (times: number) => { - // Retry with exponential backoff up to 30 seconds - const delay = Math.min(times * 100, 3000); - console.log(`Redis connection attempt ${times} failed. Retrying in ${delay}ms...`); - return delay; - }, - maxRetriesPerRequest: 5, - enableReadyCheck: true, - connectTimeout: 10000, +function createClient() { + return new RedisClient(redisUrl, { + autoReconnect: true, + }); +} + +export const redis = createClient(); +export const redisPublisher = createClient(); +export const redisSubscriber = createClient(); + +redis.onconnect = () => console.log("Connected to Redis server"); +redis.onclose = (err) => { + if (err) console.error("Disconnected from Redis server:", err); }; - -export const redis = new Redis(redisUrl, redisOptions); -export const redisPublisher = new Redis(redisUrl, redisOptions); // For publishing -export const redisSubscriber = new Redis(redisUrl, redisOptions); // For subscribing - -// Log connection events -redis.on('connect', () => console.log('Redis client connected')); -redis.on('error', (err) => console.error('Redis client error:', err)); -redis.on('ready', () => console.log('Redis client ready')); -redis.on('reconnecting', () => console.log('Redis client reconnecting...'));