- Bun 1.3.6 server setup - MariaDB schema (projects, agents, tasks) - Auto-migrations on startup - WebSocket support - Health check endpoint Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
148 lines
4.2 KiB
TypeScript
148 lines
4.2 KiB
TypeScript
/**
|
|
* Database Schema - Drizzle ORM
|
|
* MariaDB 11.4 LTS
|
|
*/
|
|
|
|
import { relations } from 'drizzle-orm'
|
|
import {
|
|
mysqlTable,
|
|
varchar,
|
|
text,
|
|
timestamp,
|
|
json,
|
|
int,
|
|
mysqlEnum,
|
|
boolean,
|
|
bigint,
|
|
index,
|
|
} from 'drizzle-orm/mysql-core'
|
|
|
|
// ============================================
|
|
// PROJECTS TABLE
|
|
// ============================================
|
|
|
|
export const projects = mysqlTable('projects', {
|
|
id: varchar('id', { length: 36 }).primaryKey(),
|
|
name: varchar('name', { length: 255 }).notNull(),
|
|
description: text('description'),
|
|
|
|
// Gitea
|
|
giteaRepoId: int('gitea_repo_id'),
|
|
giteaRepoUrl: varchar('gitea_repo_url', { length: 512 }),
|
|
giteaOwner: varchar('gitea_owner', { length: 100 }),
|
|
giteaRepoName: varchar('gitea_repo_name', { length: 100 }),
|
|
defaultBranch: varchar('default_branch', { length: 100 }).default('main'),
|
|
|
|
// K8s
|
|
k8sNamespace: varchar('k8s_namespace', { length: 63 }).notNull().unique(),
|
|
|
|
// Infrastructure
|
|
dockerImage: varchar('docker_image', { length: 512 }),
|
|
envVars: json('env_vars').$type<Record<string, string>>(),
|
|
replicas: int('replicas').default(1),
|
|
cpuLimit: varchar('cpu_limit', { length: 20 }).default('500m'),
|
|
memoryLimit: varchar('memory_limit', { length: 20 }).default('512Mi'),
|
|
|
|
// Status
|
|
status: mysqlEnum('status', ['active', 'paused', 'archived']).default('active'),
|
|
|
|
// Timestamps
|
|
createdAt: timestamp('created_at').defaultNow(),
|
|
updatedAt: timestamp('updated_at').defaultNow().onUpdateNow(),
|
|
}, (table) => ({
|
|
statusIdx: index('idx_status').on(table.status),
|
|
}))
|
|
|
|
// ============================================
|
|
// AGENTS TABLE
|
|
// ============================================
|
|
|
|
export const agents = mysqlTable('agents', {
|
|
id: varchar('id', { length: 36 }).primaryKey(),
|
|
|
|
// K8s
|
|
podName: varchar('pod_name', { length: 253 }).notNull().unique(),
|
|
k8sNamespace: varchar('k8s_namespace', { length: 63 }).default('agents'),
|
|
|
|
// Status
|
|
status: mysqlEnum('status', ['idle', 'busy', 'error', 'offline']).default('idle'),
|
|
currentTaskId: varchar('current_task_id', { length: 36 }),
|
|
|
|
// Metrics
|
|
tasksCompleted: int('tasks_completed').default(0),
|
|
lastHeartbeat: timestamp('last_heartbeat'),
|
|
|
|
// Timestamps
|
|
createdAt: timestamp('created_at').defaultNow(),
|
|
updatedAt: timestamp('updated_at').defaultNow().onUpdateNow(),
|
|
}, (table) => ({
|
|
statusIdx: index('idx_status').on(table.status),
|
|
}))
|
|
|
|
// ============================================
|
|
// TASKS TABLE
|
|
// ============================================
|
|
|
|
export const tasks = mysqlTable('tasks', {
|
|
id: varchar('id', { length: 36 }).primaryKey(),
|
|
projectId: varchar('project_id', { length: 36 }).notNull().references(() => projects.id, { onDelete: 'cascade' }),
|
|
|
|
// Task info
|
|
title: varchar('title', { length: 255 }).notNull(),
|
|
description: text('description'),
|
|
priority: mysqlEnum('priority', ['low', 'medium', 'high', 'urgent']).default('medium'),
|
|
|
|
// State
|
|
state: mysqlEnum('state', [
|
|
'backlog',
|
|
'in_progress',
|
|
'needs_input',
|
|
'ready_to_test',
|
|
'approved',
|
|
'staging',
|
|
'production',
|
|
]).default('backlog'),
|
|
|
|
// Assignment
|
|
assignedAgentId: varchar('assigned_agent_id', { length: 36 }).references(() => agents.id, { onDelete: 'set null' }),
|
|
|
|
// Git
|
|
branchName: varchar('branch_name', { length: 255 }),
|
|
prUrl: varchar('pr_url', { length: 512 }),
|
|
|
|
// Preview
|
|
previewUrl: varchar('preview_url', { length: 512 }),
|
|
|
|
// Timestamps
|
|
createdAt: timestamp('created_at').defaultNow(),
|
|
updatedAt: timestamp('updated_at').defaultNow().onUpdateNow(),
|
|
}, (table) => ({
|
|
projectStateIdx: index('idx_project_state').on(table.projectId, table.state),
|
|
}))
|
|
|
|
// ============================================
|
|
// RELATIONS
|
|
// ============================================
|
|
|
|
export const projectsRelations = relations(projects, ({ many }) => ({
|
|
tasks: many(tasks),
|
|
}))
|
|
|
|
export const tasksRelations = relations(tasks, ({ one }) => ({
|
|
project: one(projects, {
|
|
fields: [tasks.projectId],
|
|
references: [projects.id],
|
|
}),
|
|
assignedAgent: one(agents, {
|
|
fields: [tasks.assignedAgentId],
|
|
references: [agents.id],
|
|
}),
|
|
}))
|
|
|
|
export const agentsRelations = relations(agents, ({ one }) => ({
|
|
currentTask: one(tasks, {
|
|
fields: [agents.currentTaskId],
|
|
references: [tasks.id],
|
|
}),
|
|
}))
|