Skip to content

Commit 2140f75

Browse files
committed
v3 Migration Guide
1 parent a5a827c commit 2140f75

File tree

4 files changed

+326
-2
lines changed

4 files changed

+326
-2
lines changed

MIGRATION_GUIDE.md

Lines changed: 158 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,162 @@
1-
# Drizzle Kit Migration Guide
1+
# Migration Guide
22

3-
This project now uses Drizzle Kit for database migrations, providing better schema management and migration tracking.
3+
This guide covers database migrations and version upgrades for Gitea Mirror.
4+
5+
## Version 3.0 Migration Guide
6+
7+
### Overview of v3 Changes
8+
9+
Version 3.0 introduces significant security improvements and authentication changes:
10+
- **Token Encryption**: All GitHub and Gitea tokens are now encrypted in the database
11+
- **Better Auth**: Complete authentication system overhaul with session-based auth
12+
- **SSO/OIDC Support**: Enterprise authentication options
13+
- **Enhanced Security**: Improved error handling and security practices
14+
15+
### Breaking Changes in v3
16+
17+
#### 1. Authentication System Overhaul
18+
- Users now log in with **email** instead of username
19+
- Session-based authentication replaces JWT tokens
20+
- New auth endpoints: `/api/auth/[...all]` instead of `/api/auth/login`
21+
- Password reset may be required for existing users
22+
23+
#### 2. Token Encryption
24+
- All stored GitHub and Gitea tokens are encrypted using AES-256-GCM
25+
- Requires encryption secret configuration
26+
- Existing unencrypted tokens must be migrated
27+
28+
#### 3. Environment Variables
29+
**Required changes:**
30+
- `JWT_SECRET``BETTER_AUTH_SECRET` (backward compatible)
31+
- New: `BETTER_AUTH_URL` (required)
32+
- New: `ENCRYPTION_SECRET` (recommended)
33+
34+
#### 4. Database Schema Updates
35+
New tables added:
36+
- `sessions` - User session management
37+
- `accounts` - Authentication accounts
38+
- `verification_tokens` - Email verification
39+
- `oauth_applications` - OAuth app registrations
40+
- `sso_providers` - SSO configuration
41+
42+
### Migration Steps from v2 to v3
43+
44+
**⚠️ IMPORTANT: Backup your database before upgrading!**
45+
46+
```bash
47+
cp data/gitea-mirror.db data/gitea-mirror.db.backup
48+
```
49+
50+
#### Automated Migration (Docker Compose)
51+
52+
For Docker Compose users, v3 migration is **fully automated**:
53+
54+
1. **Update your docker-compose.yml** to use v3:
55+
```yaml
56+
services:
57+
gitea-mirror:
58+
image: ghcr.io/raylabshq/gitea-mirror:v3
59+
```
60+
61+
2. **Pull and restart the container**:
62+
```bash
63+
docker compose pull
64+
docker compose down
65+
docker compose up -d
66+
```
67+
68+
**That's it!** The container will automatically:
69+
- ✅ Generate BETTER_AUTH_SECRET (from existing JWT_SECRET if available)
70+
- ✅ Generate ENCRYPTION_SECRET for token encryption
71+
- ✅ Create Better Auth database tables
72+
- ✅ Migrate existing users to Better Auth system
73+
- ✅ Encrypt all stored GitHub/Gitea tokens
74+
- ✅ Apply all necessary database migrations
75+
76+
#### Manual Migration (Non-Docker)
77+
78+
#### Step 1: Update Environment Variables
79+
Add to your `.env` file:
80+
```bash
81+
# Set your application URL (required)
82+
BETTER_AUTH_URL=http://localhost:4321 # or your production URL
83+
84+
# Optional: These will be auto-generated if not provided
85+
# BETTER_AUTH_SECRET=your-existing-jwt-secret # Will use existing JWT_SECRET
86+
# ENCRYPTION_SECRET=your-48-character-secret # Will be auto-generated
87+
```
88+
89+
#### Step 2: Stop the Application
90+
```bash
91+
# Stop your running instance
92+
pkill -f "bun run start" # or your process manager command
93+
```
94+
95+
#### Step 3: Update to v3
96+
```bash
97+
# Pull latest changes
98+
git pull origin v3
99+
100+
# Install dependencies
101+
bun install
102+
```
103+
104+
#### Step 4: Run Migrations
105+
```bash
106+
# Option 1: Automatic migration on startup
107+
bun run build
108+
bun run start # Migrations run automatically
109+
110+
# Option 2: Manual migration
111+
bun run migrate:better-auth # Migrate users to Better Auth
112+
bun run migrate:encrypt-tokens # Encrypt stored tokens
113+
```
114+
115+
### Post-Migration Tasks
116+
117+
1. **All users must log in again** - Sessions are invalidated
118+
2. **Users log in with email** - Not username anymore
119+
3. **Check token encryption** - Verify GitHub/Gitea connections still work
120+
4. **Update API integrations** - Switch to new auth endpoints
121+
122+
### Troubleshooting v3 Migration
123+
124+
#### Users Can't Log In
125+
- Ensure they're using email, not username
126+
- They may need to reset password if migration failed
127+
- Check Better Auth migration logs
128+
129+
#### Token Decryption Errors
130+
- Verify ENCRYPTION_SECRET is set correctly
131+
- Re-run token encryption migration
132+
- Users may need to re-enter tokens
133+
134+
#### Database Errors
135+
- Ensure all migrations completed
136+
- Check disk space for new tables
137+
- Review migration logs in console
138+
139+
### Rollback Procedure
140+
If migration fails:
141+
```bash
142+
# Stop application
143+
pkill -f "bun run start"
144+
145+
# Restore database backup
146+
cp data/gitea-mirror.db.backup data/gitea-mirror.db
147+
148+
# Checkout previous version
149+
git checkout v2.22.0
150+
151+
# Restart with old version
152+
bun run start
153+
```
154+
155+
---
156+
157+
## Drizzle Kit Migration Guide
158+
159+
This project uses Drizzle Kit for database migrations, providing better schema management and migration tracking.
4160

5161
## Overview
6162

docker-compose.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ services:
2929
- HOST=0.0.0.0
3030
- PORT=4321
3131
- BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET:-your-secret-key-change-this-in-production}
32+
- BETTER_AUTH_URL=${BETTER_AUTH_URL:-http://localhost:4321}
33+
# Optional: ENCRYPTION_SECRET will be auto-generated if not provided
34+
# - ENCRYPTION_SECRET=${ENCRYPTION_SECRET:-}
3235
# GitHub/Gitea Mirror Config
3336
- GITHUB_USERNAME=${GITHUB_USERNAME:-}
3437
- GITHUB_TOKEN=${GITHUB_TOKEN:-}

docker-entrypoint.sh

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,32 @@ if [ "$BETTER_AUTH_SECRET" = "your-secret-key-change-this-in-production" ] || [
8888
echo "BETTER_AUTH_SECRET has been set to a secure random value"
8989
fi
9090

91+
# Generate a secure ENCRYPTION_SECRET if one isn't provided
92+
ENCRYPTION_SECRET_FILE="/app/data/.encryption_secret"
93+
94+
if [ -z "$ENCRYPTION_SECRET" ]; then
95+
# Check if we have a previously generated secret
96+
if [ -f "$ENCRYPTION_SECRET_FILE" ]; then
97+
echo "Using previously generated ENCRYPTION_SECRET"
98+
export ENCRYPTION_SECRET=$(cat "$ENCRYPTION_SECRET_FILE")
99+
else
100+
echo "Generating a secure random ENCRYPTION_SECRET"
101+
# Generate a 48-character secret for encryption
102+
if command -v openssl >/dev/null 2>&1; then
103+
GENERATED_ENCRYPTION_SECRET=$(openssl rand -base64 36)
104+
else
105+
# Fallback to using /dev/urandom if openssl is not available
106+
echo "OpenSSL not found, using fallback method for encryption secret generation"
107+
GENERATED_ENCRYPTION_SECRET=$(head -c 36 /dev/urandom | base64 | tr -d '\n' | head -c 48)
108+
fi
109+
export ENCRYPTION_SECRET="$GENERATED_ENCRYPTION_SECRET"
110+
# Save the secret to a file for persistence across container restarts
111+
echo "$GENERATED_ENCRYPTION_SECRET" > "$ENCRYPTION_SECRET_FILE"
112+
chmod 600 "$ENCRYPTION_SECRET_FILE"
113+
fi
114+
echo "ENCRYPTION_SECRET has been set to a secure random value"
115+
fi
116+
91117

92118

93119
# Skip dependency installation entirely for pre-built images
@@ -256,6 +282,69 @@ else
256282
else
257283
echo "Warning: Could not find mirror_jobs table update script."
258284
fi
285+
286+
# Run v3 migrations if needed
287+
echo "Checking for v3 migrations..."
288+
289+
# Check if we need to run Better Auth migration (check if accounts table exists)
290+
if ! sqlite3 /app/data/gitea-mirror.db "SELECT name FROM sqlite_master WHERE type='table' AND name='accounts';" | grep -q accounts; then
291+
echo "🔄 v3 Migration: Creating Better Auth tables..."
292+
# Create Better Auth tables
293+
sqlite3 /app/data/gitea-mirror.db <<EOF
294+
CREATE TABLE IF NOT EXISTS accounts (
295+
id TEXT PRIMARY KEY,
296+
userId TEXT NOT NULL,
297+
accountId TEXT NOT NULL,
298+
providerId TEXT NOT NULL,
299+
accessToken TEXT,
300+
refreshToken TEXT,
301+
expiresAt INTEGER,
302+
password TEXT,
303+
createdAt INTEGER NOT NULL,
304+
updatedAt INTEGER NOT NULL,
305+
FOREIGN KEY (userId) REFERENCES users(id)
306+
);
307+
308+
CREATE TABLE IF NOT EXISTS sessions (
309+
id TEXT PRIMARY KEY,
310+
userId TEXT NOT NULL,
311+
token TEXT NOT NULL,
312+
expiresAt INTEGER NOT NULL,
313+
createdAt INTEGER NOT NULL,
314+
updatedAt INTEGER NOT NULL,
315+
FOREIGN KEY (userId) REFERENCES users(id)
316+
);
317+
318+
CREATE TABLE IF NOT EXISTS verification_tokens (
319+
id TEXT PRIMARY KEY,
320+
identifier TEXT NOT NULL,
321+
token TEXT NOT NULL,
322+
expires INTEGER NOT NULL
323+
);
324+
325+
CREATE INDEX IF NOT EXISTS idx_accounts_userId ON accounts(userId);
326+
CREATE INDEX IF NOT EXISTS idx_sessions_token ON sessions(token);
327+
CREATE INDEX IF NOT EXISTS idx_verification_identifier_token ON verification_tokens(identifier, token);
328+
EOF
329+
fi
330+
331+
# Run Better Auth user migration
332+
if [ -f "dist/scripts/migrate-better-auth.js" ]; then
333+
echo "🔄 v3 Migration: Migrating users to Better Auth..."
334+
bun dist/scripts/migrate-better-auth.js
335+
elif [ -f "scripts/migrate-better-auth.ts" ]; then
336+
echo "🔄 v3 Migration: Migrating users to Better Auth..."
337+
bun scripts/migrate-better-auth.ts
338+
fi
339+
340+
# Run token encryption migration
341+
if [ -f "dist/scripts/migrate-tokens-encryption.js" ]; then
342+
echo "🔄 v3 Migration: Encrypting stored tokens..."
343+
bun dist/scripts/migrate-tokens-encryption.js
344+
elif [ -f "scripts/migrate-tokens-encryption.ts" ]; then
345+
echo "🔄 v3 Migration: Encrypting stored tokens..."
346+
bun scripts/migrate-tokens-encryption.ts
347+
fi
259348
fi
260349

261350
# Extract version from package.json and set as environment variable

scripts/migrate-better-auth.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#!/usr/bin/env bun
2+
import { db } from "../src/lib/db";
3+
import { users, accounts } from "../src/lib/db/schema";
4+
import { eq } from "drizzle-orm";
5+
import bcrypt from "bcryptjs";
6+
7+
console.log("🔄 Starting Better Auth migration...");
8+
9+
async function migrateToBetterAuth() {
10+
try {
11+
// Check if migration is needed
12+
const existingAccounts = await db.select().from(accounts).limit(1);
13+
if (existingAccounts.length > 0) {
14+
console.log("✓ Better Auth migration already completed");
15+
return;
16+
}
17+
18+
// Get all users with password hashes
19+
const allUsers = await db.select().from(users);
20+
21+
if (allUsers.length === 0) {
22+
console.log("ℹ️ No users to migrate");
23+
return;
24+
}
25+
26+
console.log(`📊 Found ${allUsers.length} users to migrate`);
27+
28+
// Migrate each user
29+
for (const user of allUsers) {
30+
try {
31+
// Skip users without passwords (shouldn't happen but be safe)
32+
if (!user.password) {
33+
console.log(`⚠️ Skipping user ${user.email} - no password hash found`);
34+
continue;
35+
}
36+
37+
// Create Better Auth account entry
38+
await db.insert(accounts).values({
39+
id: crypto.randomUUID(),
40+
userId: user.id,
41+
accountId: user.email, // Use email as account ID
42+
providerId: "credential", // Better Auth credential provider
43+
accessToken: null,
44+
refreshToken: null,
45+
expiresAt: null,
46+
password: user.password, // Move password hash to accounts table
47+
createdAt: new Date(),
48+
updatedAt: new Date()
49+
});
50+
51+
// Remove password from users table (Better Auth manages it now)
52+
await db.update(users)
53+
.set({ password: null })
54+
.where(eq(users.id, user.id));
55+
56+
console.log(`✓ Migrated user: ${user.email}`);
57+
} catch (error) {
58+
console.error(`❌ Failed to migrate user ${user.email}:`, error);
59+
// Continue with other users even if one fails
60+
}
61+
}
62+
63+
console.log("✅ Better Auth migration completed successfully");
64+
65+
// Verify migration
66+
const migratedAccounts = await db.select().from(accounts);
67+
console.log(`📊 Total accounts after migration: ${migratedAccounts.length}`);
68+
69+
} catch (error) {
70+
console.error("❌ Better Auth migration failed:", error);
71+
process.exit(1);
72+
}
73+
}
74+
75+
// Run migration
76+
migrateToBetterAuth();

0 commit comments

Comments
 (0)