Skip to content

Commit 70df3f4

Browse files
add database migration script
1 parent a4c9e3e commit 70df3f4

File tree

4 files changed

+108
-6
lines changed

4 files changed

+108
-6
lines changed

.env.example

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,13 +98,26 @@ AWS_BEDROCK_CONFIG=
9898
# Include this environment variable if you want more logging for debugging locally
9999
VITE_LOG_LEVEL=debug
100100

101+
# Database Configuration
102+
# Required: Set this to your PostgreSQL connection string
103+
# IMPORTANT: URL-encode special characters in passwords ($ becomes %24, ! becomes %21, etc.)
104+
# For local development with PostgreSQL:
105+
# DATABASE_URL=postgresql://username:password@localhost:5432/database_name
106+
#
107+
# For production (Supabase example):
108+
# DATABASE_URL=postgresql://postgres:[password]@db.[project-ref].supabase.co:5432/postgres?sslmode=require
109+
#
110+
# For Docker PostgreSQL (not committed to repo):
111+
# DATABASE_URL=postgresql://username:password@db:5432/database_name
112+
DATABASE_URL=
113+
101114
# Get your GitHub Personal Access Token here -
102115
# https://github.com/settings/tokens
103116
# This token is used for:
104117
# 1. Importing/cloning GitHub repositories without rate limiting
105118
# 2. Accessing private repositories
106119
# 3. Automatic GitHub authentication (no need to manually connect in the UI)
107-
#
120+
#
108121
# For classic tokens, ensure it has these scopes: repo, read:org, read:user
109122
# For fine-grained tokens, ensure it has Repository and Organization access
110123
VITE_GITHUB_ACCESS_TOKEN=
@@ -114,6 +127,14 @@ VITE_GITHUB_ACCESS_TOKEN=
114127
# Classic tokens are recommended for broader access
115128
VITE_GITHUB_TOKEN_TYPE=classic
116129

130+
# Email verification with Resend
131+
# Get your API key at https://resend.com/api-keys
132+
RESEND_API_KEY=
133+
134+
# Application URL for email verification links
135+
# Should be your production domain (e.g., https://codinit.dev)
136+
APP_URL=
137+
117138
# Example Context Values for qwen2.5-coder:32b
118139
#
119140
# DEFAULT_NUM_CTX=32768 # Consumes 36GB of VRAM

app/lib/db/health.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { db } from '~/IPC/db';
2+
import { createScopedLogger } from '~/utils/logger';
3+
4+
const logger = createScopedLogger('DatabaseHealth');
5+
6+
export async function checkDatabaseHealth(): Promise<{
7+
healthy: boolean;
8+
error?: string;
9+
}> {
10+
try {
11+
await db.execute('SELECT 1');
12+
logger.debug('Database health check passed');
13+
return { healthy: true };
14+
} catch (error) {
15+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
16+
logger.error('Database health check failed', { error: errorMessage });
17+
return {
18+
healthy: false,
19+
error: errorMessage,
20+
};
21+
}
22+
}
23+
24+
export async function ensureDatabaseConnected(): Promise<void> {
25+
const health = await checkDatabaseHealth();
26+
27+
if (!health.healthy) {
28+
throw new Error(`Database connection failed: ${health.error}`);
29+
}
30+
}

package.json

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,15 @@
1111
"email": "gerome.e24@gmail.com"
1212
},
1313
"scripts": {
14-
"deploy": "npm run build && wrangler pages deploy",
1514
"build": "cross-env NODE_OPTIONS=--max-old-space-size=4096 remix vite:build",
1615
"dev": "node pre-start.cjs && remix vite:dev",
1716
"test": "vitest --run",
1817
"test:watch": "vitest",
1918
"lint": "eslint --cache --cache-location ./node_modules/.cache/eslint app",
20-
"lint:fix": "npm run lint -- --fix && prettier app --write",
19+
"lint:fix": "pnpm run lint -- --fix && prettier app --write",
2120
"start:windows": "wrangler pages dev ./build/client",
2221
"start:unix": "bindings=$(./bindings.sh) && wrangler pages dev ./build/client $bindings",
23-
"start": "node -e \"const { spawn } = require('child_process'); const isWindows = process.platform === 'win32'; const cmd = isWindows ? 'npm run start:windows' : 'npm run start:unix'; const child = spawn(cmd, { shell: true, stdio: 'inherit' }); child.on('exit', code => process.exit(code));\"",
22+
"start": "node -e \"const { spawn } = require('child_process'); const isWindows = process.platform === 'win32'; const cmd = isWindows ? 'pnpm run start:windows' : 'pnpm run start:unix'; const child = spawn(cmd, { shell: true, stdio: 'inherit' }); child.on('exit', code => process.exit(code));\"",
2423
"dockerstart": "bindings=$(./bindings.sh) && wrangler pages dev ./build/client $bindings --ip 0.0.0.0 --port 5173 --no-show-interactive-dev-session",
2524
"dockerrun": "docker run -it -d --name codinit-ai-live -p 5173:5173 --env-file .env codinit-ai",
2625
"dockerbuild:prod": "docker build -t codinit-ai:production -t codinit-ai:latest --target codinit-ai-production .",
@@ -42,7 +41,11 @@
4241
"electron:build:linux": "rimraf dist && pnpm electron:build:renderer && pnpm electron:build:deps && electron-builder --linux",
4342
"electron:build:dist": "rimraf dist && pnpm electron:build:renderer && pnpm electron:build:deps && electron-builder -m -w -l",
4443
"email:dev": "email dev --dir emails --port 3000",
45-
"email:export": "email export --dir emails --outDir .react-email"
44+
"email:export": "email export --dir emails --outDir .react-email",
45+
"db:migrate": "tsx scripts/migrate.ts",
46+
"db:generate": "drizzle-kit generate",
47+
"db:studio": "drizzle-kit studio",
48+
"db:push": "drizzle-kit push"
4649
},
4750
"engines": {
4851
"node": ">=20.15.1"
@@ -87,6 +90,7 @@
8790
"@openrouter/ai-sdk-provider": "^0.0.5",
8891
"@phosphor-icons/react": "^2.1.10",
8992
"@playwright/test": "^1.56.1",
93+
"@radix-ui/colors": "^3.0.0",
9094
"@radix-ui/react-checkbox": "^1.3.3",
9195
"@radix-ui/react-collapsible": "^1.1.12",
9296
"@radix-ui/react-context-menu": "^2.2.16",
@@ -124,7 +128,7 @@
124128
"cmdk": "^1.1.1",
125129
"date-fns": "^3.6.0",
126130
"diff": "^5.2.0",
127-
"dotenv": "^16.6.1",
131+
"drizzle-orm": "^0.44.7",
128132
"electron-log": "^5.4.3",
129133
"electron-store": "^10.1.0",
130134
"electron-updater": "^6.6.2",
@@ -145,6 +149,7 @@
145149
"nanostores": "^0.10.3",
146150
"ollama-ai-provider": "^0.15.2",
147151
"path-browserify": "^1.0.1",
152+
"postgres": "^3.4.7",
148153
"react": "^18.3.1",
149154
"react-chartjs-2": "^5.3.0",
150155
"react-dnd": "^16.0.1",
@@ -190,13 +195,16 @@
190195
"@types/file-saver": "^2.0.7",
191196
"@types/js-cookie": "^3.0.6",
192197
"@types/path-browserify": "^1.0.3",
198+
"@types/pg": "^8.15.6",
193199
"@types/react": "^18.3.25",
194200
"@types/react-dom": "^18.3.7",
195201
"@types/react-window": "^1.8.8",
196202
"@vitejs/plugin-react": "^4.7.0",
197203
"@vitest/browser": "^3.1.0",
198204
"concurrently": "^8.2.2",
199205
"cross-env": "^7.0.3",
206+
"dotenv": "^16.6.1",
207+
"drizzle-kit": "^0.31.7",
200208
"electron": "^35.7.5",
201209
"electron-builder": "^26.0.12",
202210
"eslint": "^9.38.0",
@@ -212,6 +220,7 @@
212220
"rimraf": "^4.4.1",
213221
"sass-embedded": "^1.93.2",
214222
"stream-browserify": "^3.0.0",
223+
"tsx": "^4.19.2",
215224
"typescript": "^5.9.3",
216225
"unified": "^11.0.5",
217226
"unocss": "^0.61.9",

scripts/migrate.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { drizzle } from 'drizzle-orm/postgres-js';
2+
import { migrate } from 'drizzle-orm/postgres-js/migrator';
3+
import postgres from 'postgres';
4+
import * as dotenv from 'dotenv';
5+
import * as path from 'path';
6+
import { fileURLToPath } from 'url';
7+
8+
const __filename = fileURLToPath(import.meta.url);
9+
const __dirname = path.dirname(__filename);
10+
11+
dotenv.config({ path: path.resolve(__dirname, '../.env') });
12+
13+
async function runMigrations() {
14+
const connectionString = process.env.DATABASE_URL;
15+
16+
if (!connectionString) {
17+
console.error('❌ DATABASE_URL environment variable is not set');
18+
process.exit(1);
19+
}
20+
21+
console.log('🔄 Connecting to database...');
22+
23+
const sql = postgres(connectionString, {
24+
ssl: 'require',
25+
max: 1,
26+
});
27+
28+
const db = drizzle(sql);
29+
30+
try {
31+
console.log('🔄 Running migrations...');
32+
await migrate(db, { migrationsFolder: path.resolve(__dirname, '../app/IPC/db/migrations') });
33+
console.log('✅ Migrations completed successfully');
34+
} catch (error) {
35+
console.error('❌ Migration failed:', error);
36+
process.exit(1);
37+
} finally {
38+
await sql.end();
39+
}
40+
}
41+
42+
runMigrations();

0 commit comments

Comments
 (0)