mirror of
https://github.com/RayLabsHQ/gitea-mirror.git
synced 2025-12-06 11:36:44 +03:00
v3 Migration Guide
This commit is contained in:
@@ -1,6 +1,162 @@
|
|||||||
# Drizzle Kit Migration Guide
|
# Migration Guide
|
||||||
|
|
||||||
This project now uses Drizzle Kit for database migrations, providing better schema management and migration tracking.
|
This guide covers database migrations and version upgrades for Gitea Mirror.
|
||||||
|
|
||||||
|
## Version 3.0 Migration Guide
|
||||||
|
|
||||||
|
### Overview of v3 Changes
|
||||||
|
|
||||||
|
Version 3.0 introduces significant security improvements and authentication changes:
|
||||||
|
- **Token Encryption**: All GitHub and Gitea tokens are now encrypted in the database
|
||||||
|
- **Better Auth**: Complete authentication system overhaul with session-based auth
|
||||||
|
- **SSO/OIDC Support**: Enterprise authentication options
|
||||||
|
- **Enhanced Security**: Improved error handling and security practices
|
||||||
|
|
||||||
|
### Breaking Changes in v3
|
||||||
|
|
||||||
|
#### 1. Authentication System Overhaul
|
||||||
|
- Users now log in with **email** instead of username
|
||||||
|
- Session-based authentication replaces JWT tokens
|
||||||
|
- New auth endpoints: `/api/auth/[...all]` instead of `/api/auth/login`
|
||||||
|
- Password reset may be required for existing users
|
||||||
|
|
||||||
|
#### 2. Token Encryption
|
||||||
|
- All stored GitHub and Gitea tokens are encrypted using AES-256-GCM
|
||||||
|
- Requires encryption secret configuration
|
||||||
|
- Existing unencrypted tokens must be migrated
|
||||||
|
|
||||||
|
#### 3. Environment Variables
|
||||||
|
**Required changes:**
|
||||||
|
- `JWT_SECRET` → `BETTER_AUTH_SECRET` (backward compatible)
|
||||||
|
- New: `BETTER_AUTH_URL` (required)
|
||||||
|
- New: `ENCRYPTION_SECRET` (recommended)
|
||||||
|
|
||||||
|
#### 4. Database Schema Updates
|
||||||
|
New tables added:
|
||||||
|
- `sessions` - User session management
|
||||||
|
- `accounts` - Authentication accounts
|
||||||
|
- `verification_tokens` - Email verification
|
||||||
|
- `oauth_applications` - OAuth app registrations
|
||||||
|
- `sso_providers` - SSO configuration
|
||||||
|
|
||||||
|
### Migration Steps from v2 to v3
|
||||||
|
|
||||||
|
**⚠️ IMPORTANT: Backup your database before upgrading!**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp data/gitea-mirror.db data/gitea-mirror.db.backup
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Automated Migration (Docker Compose)
|
||||||
|
|
||||||
|
For Docker Compose users, v3 migration is **fully automated**:
|
||||||
|
|
||||||
|
1. **Update your docker-compose.yml** to use v3:
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
gitea-mirror:
|
||||||
|
image: ghcr.io/raylabshq/gitea-mirror:v3
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Pull and restart the container**:
|
||||||
|
```bash
|
||||||
|
docker compose pull
|
||||||
|
docker compose down
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
**That's it!** The container will automatically:
|
||||||
|
- ✅ Generate BETTER_AUTH_SECRET (from existing JWT_SECRET if available)
|
||||||
|
- ✅ Generate ENCRYPTION_SECRET for token encryption
|
||||||
|
- ✅ Create Better Auth database tables
|
||||||
|
- ✅ Migrate existing users to Better Auth system
|
||||||
|
- ✅ Encrypt all stored GitHub/Gitea tokens
|
||||||
|
- ✅ Apply all necessary database migrations
|
||||||
|
|
||||||
|
#### Manual Migration (Non-Docker)
|
||||||
|
|
||||||
|
#### Step 1: Update Environment Variables
|
||||||
|
Add to your `.env` file:
|
||||||
|
```bash
|
||||||
|
# Set your application URL (required)
|
||||||
|
BETTER_AUTH_URL=http://localhost:4321 # or your production URL
|
||||||
|
|
||||||
|
# Optional: These will be auto-generated if not provided
|
||||||
|
# BETTER_AUTH_SECRET=your-existing-jwt-secret # Will use existing JWT_SECRET
|
||||||
|
# ENCRYPTION_SECRET=your-48-character-secret # Will be auto-generated
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Step 2: Stop the Application
|
||||||
|
```bash
|
||||||
|
# Stop your running instance
|
||||||
|
pkill -f "bun run start" # or your process manager command
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Step 3: Update to v3
|
||||||
|
```bash
|
||||||
|
# Pull latest changes
|
||||||
|
git pull origin v3
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
bun install
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Step 4: Run Migrations
|
||||||
|
```bash
|
||||||
|
# Option 1: Automatic migration on startup
|
||||||
|
bun run build
|
||||||
|
bun run start # Migrations run automatically
|
||||||
|
|
||||||
|
# Option 2: Manual migration
|
||||||
|
bun run migrate:better-auth # Migrate users to Better Auth
|
||||||
|
bun run migrate:encrypt-tokens # Encrypt stored tokens
|
||||||
|
```
|
||||||
|
|
||||||
|
### Post-Migration Tasks
|
||||||
|
|
||||||
|
1. **All users must log in again** - Sessions are invalidated
|
||||||
|
2. **Users log in with email** - Not username anymore
|
||||||
|
3. **Check token encryption** - Verify GitHub/Gitea connections still work
|
||||||
|
4. **Update API integrations** - Switch to new auth endpoints
|
||||||
|
|
||||||
|
### Troubleshooting v3 Migration
|
||||||
|
|
||||||
|
#### Users Can't Log In
|
||||||
|
- Ensure they're using email, not username
|
||||||
|
- They may need to reset password if migration failed
|
||||||
|
- Check Better Auth migration logs
|
||||||
|
|
||||||
|
#### Token Decryption Errors
|
||||||
|
- Verify ENCRYPTION_SECRET is set correctly
|
||||||
|
- Re-run token encryption migration
|
||||||
|
- Users may need to re-enter tokens
|
||||||
|
|
||||||
|
#### Database Errors
|
||||||
|
- Ensure all migrations completed
|
||||||
|
- Check disk space for new tables
|
||||||
|
- Review migration logs in console
|
||||||
|
|
||||||
|
### Rollback Procedure
|
||||||
|
If migration fails:
|
||||||
|
```bash
|
||||||
|
# Stop application
|
||||||
|
pkill -f "bun run start"
|
||||||
|
|
||||||
|
# Restore database backup
|
||||||
|
cp data/gitea-mirror.db.backup data/gitea-mirror.db
|
||||||
|
|
||||||
|
# Checkout previous version
|
||||||
|
git checkout v2.22.0
|
||||||
|
|
||||||
|
# Restart with old version
|
||||||
|
bun run start
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Drizzle Kit Migration Guide
|
||||||
|
|
||||||
|
This project uses Drizzle Kit for database migrations, providing better schema management and migration tracking.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ services:
|
|||||||
- HOST=0.0.0.0
|
- HOST=0.0.0.0
|
||||||
- PORT=4321
|
- PORT=4321
|
||||||
- BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET:-your-secret-key-change-this-in-production}
|
- BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET:-your-secret-key-change-this-in-production}
|
||||||
|
- BETTER_AUTH_URL=${BETTER_AUTH_URL:-http://localhost:4321}
|
||||||
|
# Optional: ENCRYPTION_SECRET will be auto-generated if not provided
|
||||||
|
# - ENCRYPTION_SECRET=${ENCRYPTION_SECRET:-}
|
||||||
# GitHub/Gitea Mirror Config
|
# GitHub/Gitea Mirror Config
|
||||||
- GITHUB_USERNAME=${GITHUB_USERNAME:-}
|
- GITHUB_USERNAME=${GITHUB_USERNAME:-}
|
||||||
- GITHUB_TOKEN=${GITHUB_TOKEN:-}
|
- GITHUB_TOKEN=${GITHUB_TOKEN:-}
|
||||||
|
|||||||
@@ -88,6 +88,32 @@ if [ "$BETTER_AUTH_SECRET" = "your-secret-key-change-this-in-production" ] || [
|
|||||||
echo "BETTER_AUTH_SECRET has been set to a secure random value"
|
echo "BETTER_AUTH_SECRET has been set to a secure random value"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Generate a secure ENCRYPTION_SECRET if one isn't provided
|
||||||
|
ENCRYPTION_SECRET_FILE="/app/data/.encryption_secret"
|
||||||
|
|
||||||
|
if [ -z "$ENCRYPTION_SECRET" ]; then
|
||||||
|
# Check if we have a previously generated secret
|
||||||
|
if [ -f "$ENCRYPTION_SECRET_FILE" ]; then
|
||||||
|
echo "Using previously generated ENCRYPTION_SECRET"
|
||||||
|
export ENCRYPTION_SECRET=$(cat "$ENCRYPTION_SECRET_FILE")
|
||||||
|
else
|
||||||
|
echo "Generating a secure random ENCRYPTION_SECRET"
|
||||||
|
# Generate a 48-character secret for encryption
|
||||||
|
if command -v openssl >/dev/null 2>&1; then
|
||||||
|
GENERATED_ENCRYPTION_SECRET=$(openssl rand -base64 36)
|
||||||
|
else
|
||||||
|
# Fallback to using /dev/urandom if openssl is not available
|
||||||
|
echo "OpenSSL not found, using fallback method for encryption secret generation"
|
||||||
|
GENERATED_ENCRYPTION_SECRET=$(head -c 36 /dev/urandom | base64 | tr -d '\n' | head -c 48)
|
||||||
|
fi
|
||||||
|
export ENCRYPTION_SECRET="$GENERATED_ENCRYPTION_SECRET"
|
||||||
|
# Save the secret to a file for persistence across container restarts
|
||||||
|
echo "$GENERATED_ENCRYPTION_SECRET" > "$ENCRYPTION_SECRET_FILE"
|
||||||
|
chmod 600 "$ENCRYPTION_SECRET_FILE"
|
||||||
|
fi
|
||||||
|
echo "ENCRYPTION_SECRET has been set to a secure random value"
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Skip dependency installation entirely for pre-built images
|
# Skip dependency installation entirely for pre-built images
|
||||||
@@ -256,6 +282,69 @@ else
|
|||||||
else
|
else
|
||||||
echo "Warning: Could not find mirror_jobs table update script."
|
echo "Warning: Could not find mirror_jobs table update script."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Run v3 migrations if needed
|
||||||
|
echo "Checking for v3 migrations..."
|
||||||
|
|
||||||
|
# Check if we need to run Better Auth migration (check if accounts table exists)
|
||||||
|
if ! sqlite3 /app/data/gitea-mirror.db "SELECT name FROM sqlite_master WHERE type='table' AND name='accounts';" | grep -q accounts; then
|
||||||
|
echo "🔄 v3 Migration: Creating Better Auth tables..."
|
||||||
|
# Create Better Auth tables
|
||||||
|
sqlite3 /app/data/gitea-mirror.db <<EOF
|
||||||
|
CREATE TABLE IF NOT EXISTS accounts (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
userId TEXT NOT NULL,
|
||||||
|
accountId TEXT NOT NULL,
|
||||||
|
providerId TEXT NOT NULL,
|
||||||
|
accessToken TEXT,
|
||||||
|
refreshToken TEXT,
|
||||||
|
expiresAt INTEGER,
|
||||||
|
password TEXT,
|
||||||
|
createdAt INTEGER NOT NULL,
|
||||||
|
updatedAt INTEGER NOT NULL,
|
||||||
|
FOREIGN KEY (userId) REFERENCES users(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS sessions (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
userId TEXT NOT NULL,
|
||||||
|
token TEXT NOT NULL,
|
||||||
|
expiresAt INTEGER NOT NULL,
|
||||||
|
createdAt INTEGER NOT NULL,
|
||||||
|
updatedAt INTEGER NOT NULL,
|
||||||
|
FOREIGN KEY (userId) REFERENCES users(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS verification_tokens (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
identifier TEXT NOT NULL,
|
||||||
|
token TEXT NOT NULL,
|
||||||
|
expires INTEGER NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_accounts_userId ON accounts(userId);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_sessions_token ON sessions(token);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_verification_identifier_token ON verification_tokens(identifier, token);
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run Better Auth user migration
|
||||||
|
if [ -f "dist/scripts/migrate-better-auth.js" ]; then
|
||||||
|
echo "🔄 v3 Migration: Migrating users to Better Auth..."
|
||||||
|
bun dist/scripts/migrate-better-auth.js
|
||||||
|
elif [ -f "scripts/migrate-better-auth.ts" ]; then
|
||||||
|
echo "🔄 v3 Migration: Migrating users to Better Auth..."
|
||||||
|
bun scripts/migrate-better-auth.ts
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run token encryption migration
|
||||||
|
if [ -f "dist/scripts/migrate-tokens-encryption.js" ]; then
|
||||||
|
echo "🔄 v3 Migration: Encrypting stored tokens..."
|
||||||
|
bun dist/scripts/migrate-tokens-encryption.js
|
||||||
|
elif [ -f "scripts/migrate-tokens-encryption.ts" ]; then
|
||||||
|
echo "🔄 v3 Migration: Encrypting stored tokens..."
|
||||||
|
bun scripts/migrate-tokens-encryption.ts
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Extract version from package.json and set as environment variable
|
# Extract version from package.json and set as environment variable
|
||||||
|
|||||||
76
scripts/migrate-better-auth.ts
Normal file
76
scripts/migrate-better-auth.ts
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
#!/usr/bin/env bun
|
||||||
|
import { db } from "../src/lib/db";
|
||||||
|
import { users, accounts } from "../src/lib/db/schema";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
import bcrypt from "bcryptjs";
|
||||||
|
|
||||||
|
console.log("🔄 Starting Better Auth migration...");
|
||||||
|
|
||||||
|
async function migrateToBetterAuth() {
|
||||||
|
try {
|
||||||
|
// Check if migration is needed
|
||||||
|
const existingAccounts = await db.select().from(accounts).limit(1);
|
||||||
|
if (existingAccounts.length > 0) {
|
||||||
|
console.log("✓ Better Auth migration already completed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all users with password hashes
|
||||||
|
const allUsers = await db.select().from(users);
|
||||||
|
|
||||||
|
if (allUsers.length === 0) {
|
||||||
|
console.log("ℹ️ No users to migrate");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`📊 Found ${allUsers.length} users to migrate`);
|
||||||
|
|
||||||
|
// Migrate each user
|
||||||
|
for (const user of allUsers) {
|
||||||
|
try {
|
||||||
|
// Skip users without passwords (shouldn't happen but be safe)
|
||||||
|
if (!user.password) {
|
||||||
|
console.log(`⚠️ Skipping user ${user.email} - no password hash found`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Better Auth account entry
|
||||||
|
await db.insert(accounts).values({
|
||||||
|
id: crypto.randomUUID(),
|
||||||
|
userId: user.id,
|
||||||
|
accountId: user.email, // Use email as account ID
|
||||||
|
providerId: "credential", // Better Auth credential provider
|
||||||
|
accessToken: null,
|
||||||
|
refreshToken: null,
|
||||||
|
expiresAt: null,
|
||||||
|
password: user.password, // Move password hash to accounts table
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remove password from users table (Better Auth manages it now)
|
||||||
|
await db.update(users)
|
||||||
|
.set({ password: null })
|
||||||
|
.where(eq(users.id, user.id));
|
||||||
|
|
||||||
|
console.log(`✓ Migrated user: ${user.email}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`❌ Failed to migrate user ${user.email}:`, error);
|
||||||
|
// Continue with other users even if one fails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("✅ Better Auth migration completed successfully");
|
||||||
|
|
||||||
|
// Verify migration
|
||||||
|
const migratedAccounts = await db.select().from(accounts);
|
||||||
|
console.log(`📊 Total accounts after migration: ${migratedAccounts.length}`);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("❌ Better Auth migration failed:", error);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run migration
|
||||||
|
migrateToBetterAuth();
|
||||||
Reference in New Issue
Block a user