/** * 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>(), 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], }), }))