refactor: Modularize server into separate concerns
Split monolithic index.ts (~1400 lines) into modular structure: - config.ts: Server configuration and constants - db/: Database initialization, migrations, and seeds - routes/: API handlers by domain (themes, canvas, components, etc.) - services/: Terminal WebSocket server - utils/: CORS helpers Entry point now only coordinates initialization.
This commit is contained in:
16
server/db/index.ts
Normal file
16
server/db/index.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Database } from 'bun:sqlite'
|
||||
import { DB_PATH } from '../config'
|
||||
import { runMigrations } from './migrations'
|
||||
import { runSeeds } from './seeds'
|
||||
|
||||
// Create database instance
|
||||
export const db = new Database(DB_PATH)
|
||||
|
||||
// Initialize database
|
||||
export function initDatabase() {
|
||||
runMigrations(db)
|
||||
runSeeds(db)
|
||||
console.log('[DB] SQLite initialized:', DB_PATH)
|
||||
}
|
||||
|
||||
export { Database }
|
||||
107
server/db/migrations.ts
Normal file
107
server/db/migrations.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import type { Database } from 'bun:sqlite'
|
||||
|
||||
export function runMigrations(db: Database) {
|
||||
// History table
|
||||
db.run(`
|
||||
CREATE TABLE IF NOT EXISTS history (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
timestamp TEXT DEFAULT CURRENT_TIMESTAMP,
|
||||
tool_name TEXT NOT NULL,
|
||||
args TEXT,
|
||||
result TEXT
|
||||
)
|
||||
`)
|
||||
|
||||
// Config table
|
||||
db.run(`
|
||||
CREATE TABLE IF NOT EXISTS config (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT
|
||||
)
|
||||
`)
|
||||
|
||||
// Vue components table
|
||||
db.run(`
|
||||
CREATE TABLE IF NOT EXISTS vue_components (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
template TEXT NOT NULL,
|
||||
setup TEXT,
|
||||
style TEXT,
|
||||
props TEXT,
|
||||
imports TEXT,
|
||||
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TEXT DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
`)
|
||||
|
||||
// Themes table
|
||||
db.run(`
|
||||
CREATE TABLE IF NOT EXISTS themes (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
is_default INTEGER DEFAULT 0,
|
||||
is_system INTEGER DEFAULT 0,
|
||||
variables TEXT NOT NULL,
|
||||
metadata TEXT,
|
||||
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TEXT DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
`)
|
||||
|
||||
// Project canvas table
|
||||
db.run(`
|
||||
CREATE TABLE IF NOT EXISTS project_canvas (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
type TEXT NOT NULL DEFAULT 'project',
|
||||
theme_id TEXT,
|
||||
config TEXT,
|
||||
tools TEXT,
|
||||
is_default INTEGER DEFAULT 0,
|
||||
is_system INTEGER DEFAULT 0,
|
||||
show_in_toolbar INTEGER DEFAULT 0,
|
||||
toolbar_icon TEXT,
|
||||
toolbar_order INTEGER DEFAULT 99,
|
||||
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TEXT DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
`)
|
||||
|
||||
// Canvas-components relation table
|
||||
db.run(`
|
||||
CREATE TABLE IF NOT EXISTS canvas_components (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
canvas_id TEXT NOT NULL,
|
||||
component_id TEXT NOT NULL,
|
||||
position INTEGER DEFAULT 0,
|
||||
props TEXT,
|
||||
layout TEXT,
|
||||
is_visible INTEGER DEFAULT 1,
|
||||
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE(canvas_id, component_id)
|
||||
)
|
||||
`)
|
||||
|
||||
// Run column migrations for existing tables
|
||||
runColumnMigrations(db)
|
||||
}
|
||||
|
||||
function runColumnMigrations(db: Database) {
|
||||
// Add toolbar columns to project_canvas if missing
|
||||
const alterStatements = [
|
||||
'ALTER TABLE project_canvas ADD COLUMN show_in_toolbar INTEGER DEFAULT 0',
|
||||
'ALTER TABLE project_canvas ADD COLUMN toolbar_icon TEXT',
|
||||
'ALTER TABLE project_canvas ADD COLUMN toolbar_order INTEGER DEFAULT 99'
|
||||
]
|
||||
|
||||
for (const sql of alterStatements) {
|
||||
try {
|
||||
db.run(sql)
|
||||
} catch {
|
||||
// Column already exists
|
||||
}
|
||||
}
|
||||
}
|
||||
134
server/db/seeds.ts
Normal file
134
server/db/seeds.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
import type { Database } from 'bun:sqlite'
|
||||
|
||||
const darkTheme = {
|
||||
id: 'theme-dark',
|
||||
name: 'Dark',
|
||||
description: 'Default dark theme',
|
||||
is_default: 1,
|
||||
is_system: 1,
|
||||
variables: JSON.stringify({
|
||||
colors: {
|
||||
'bg-primary': '#0f0f14',
|
||||
'bg-secondary': '#16161d',
|
||||
'bg-hover': '#1e1e28',
|
||||
'bg-tertiary': '#252530',
|
||||
'border-color': '#2a2a3a',
|
||||
'border-hover': '#3a3a4a'
|
||||
},
|
||||
text: {
|
||||
'text-primary': '#e4e4e7',
|
||||
'text-secondary': '#a1a1aa',
|
||||
'text-muted': '#52525b',
|
||||
'text-inverse': '#0f0f14'
|
||||
},
|
||||
accent: {
|
||||
'accent': '#6366f1',
|
||||
'accent-hover': '#818cf8',
|
||||
'accent-muted': 'rgba(99, 102, 241, 0.2)',
|
||||
'accent-text': '#ffffff'
|
||||
},
|
||||
semantic: {
|
||||
'success': '#22c55e',
|
||||
'success-bg': 'rgba(34, 197, 94, 0.1)',
|
||||
'warning': '#eab308',
|
||||
'warning-bg': 'rgba(234, 179, 8, 0.1)',
|
||||
'error': '#ef4444',
|
||||
'error-bg': 'rgba(239, 68, 68, 0.1)',
|
||||
'info': '#3b82f6',
|
||||
'info-bg': 'rgba(59, 130, 246, 0.1)'
|
||||
},
|
||||
spacing: {
|
||||
'radius-sm': '4px',
|
||||
'radius-md': '8px',
|
||||
'radius-lg': '12px',
|
||||
'radius-full': '9999px'
|
||||
},
|
||||
typography: {
|
||||
'font-sans': "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
|
||||
'font-mono': "'JetBrains Mono', 'Fira Code', Consolas, monospace"
|
||||
},
|
||||
effects: {
|
||||
'shadow-sm': '0 1px 2px rgba(0,0,0,0.2)',
|
||||
'shadow-md': '0 4px 6px rgba(0,0,0,0.3)',
|
||||
'shadow-lg': '0 10px 15px rgba(0,0,0,0.4)',
|
||||
'transition-fast': '0.15s ease',
|
||||
'transition-normal': '0.2s ease'
|
||||
}
|
||||
}),
|
||||
metadata: JSON.stringify({ author: 'system', version: '1.0.0', tags: ['dark', 'default'] })
|
||||
}
|
||||
|
||||
const lightTheme = {
|
||||
id: 'theme-light',
|
||||
name: 'Light',
|
||||
description: 'Clean light theme',
|
||||
is_default: 0,
|
||||
is_system: 1,
|
||||
variables: JSON.stringify({
|
||||
colors: {
|
||||
'bg-primary': '#ffffff',
|
||||
'bg-secondary': '#f4f4f5',
|
||||
'bg-hover': '#e4e4e7',
|
||||
'bg-tertiary': '#d4d4d8',
|
||||
'border-color': '#d4d4d8',
|
||||
'border-hover': '#a1a1aa'
|
||||
},
|
||||
text: {
|
||||
'text-primary': '#18181b',
|
||||
'text-secondary': '#52525b',
|
||||
'text-muted': '#a1a1aa',
|
||||
'text-inverse': '#ffffff'
|
||||
},
|
||||
accent: {
|
||||
'accent': '#4f46e5',
|
||||
'accent-hover': '#4338ca',
|
||||
'accent-muted': 'rgba(79, 70, 229, 0.1)',
|
||||
'accent-text': '#ffffff'
|
||||
},
|
||||
semantic: {
|
||||
'success': '#16a34a',
|
||||
'success-bg': 'rgba(22, 163, 74, 0.1)',
|
||||
'warning': '#ca8a04',
|
||||
'warning-bg': 'rgba(202, 138, 4, 0.1)',
|
||||
'error': '#dc2626',
|
||||
'error-bg': 'rgba(220, 38, 38, 0.1)',
|
||||
'info': '#2563eb',
|
||||
'info-bg': 'rgba(37, 99, 235, 0.1)'
|
||||
},
|
||||
spacing: {
|
||||
'radius-sm': '4px',
|
||||
'radius-md': '8px',
|
||||
'radius-lg': '12px',
|
||||
'radius-full': '9999px'
|
||||
},
|
||||
typography: {
|
||||
'font-sans': "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
|
||||
'font-mono': "'JetBrains Mono', 'Fira Code', Consolas, monospace"
|
||||
},
|
||||
effects: {
|
||||
'shadow-sm': '0 1px 2px rgba(0,0,0,0.05)',
|
||||
'shadow-md': '0 4px 6px rgba(0,0,0,0.07)',
|
||||
'shadow-lg': '0 10px 15px rgba(0,0,0,0.1)',
|
||||
'transition-fast': '0.15s ease',
|
||||
'transition-normal': '0.2s ease'
|
||||
}
|
||||
}),
|
||||
metadata: JSON.stringify({ author: 'system', version: '1.0.0', tags: ['light'] })
|
||||
}
|
||||
|
||||
export function runSeeds(db: Database) {
|
||||
// Check if system themes exist
|
||||
const existingThemes = db.query('SELECT COUNT(*) as count FROM themes WHERE is_system = 1').get() as { count: number }
|
||||
|
||||
if (existingThemes.count === 0) {
|
||||
const stmt = db.prepare(`
|
||||
INSERT INTO themes (id, name, description, is_default, is_system, variables, metadata)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
`)
|
||||
|
||||
stmt.run(darkTheme.id, darkTheme.name, darkTheme.description, darkTheme.is_default, darkTheme.is_system, darkTheme.variables, darkTheme.metadata)
|
||||
stmt.run(lightTheme.id, lightTheme.name, lightTheme.description, lightTheme.is_default, lightTheme.is_system, lightTheme.variables, lightTheme.metadata)
|
||||
|
||||
console.log('[DB] System themes created')
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user