feat: schéma Drizzle/SQLite (machines, snapshots, executions)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
import { defineConfig } from "drizzle-kit";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
dialect: "sqlite",
|
||||||
|
schema: "./server/db/schema.ts",
|
||||||
|
out: "./server/db/migrations",
|
||||||
|
});
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// server/db/client.ts
|
||||||
|
import Database from "better-sqlite3";
|
||||||
|
import { drizzle } from "drizzle-orm/better-sqlite3";
|
||||||
|
import { mkdirSync } from "node:fs";
|
||||||
|
import { dirname } from "node:path";
|
||||||
|
import { env } from "../env.js";
|
||||||
|
import * as schema from "./schema.js";
|
||||||
|
|
||||||
|
mkdirSync(dirname(env.dbPath), { recursive: true });
|
||||||
|
const sqlite = new Database(env.dbPath);
|
||||||
|
sqlite.pragma("journal_mode = WAL");
|
||||||
|
sqlite.pragma("foreign_keys = ON");
|
||||||
|
|
||||||
|
export const db = drizzle(sqlite, { schema });
|
||||||
|
export { schema };
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// server/db/migrate.ts
|
||||||
|
import { migrate } from "drizzle-orm/better-sqlite3/migrator";
|
||||||
|
import { db } from "./client.js";
|
||||||
|
|
||||||
|
export function runMigrations(): void {
|
||||||
|
migrate(db, { migrationsFolder: "./server/db/migrations" });
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
CREATE TABLE `executions` (
|
||||||
|
`id` text PRIMARY KEY NOT NULL,
|
||||||
|
`machine_id` text NOT NULL,
|
||||||
|
`action` text NOT NULL,
|
||||||
|
`mode` text DEFAULT 'manual' NOT NULL,
|
||||||
|
`started_at` text NOT NULL,
|
||||||
|
`finished_at` text,
|
||||||
|
`status` text NOT NULL,
|
||||||
|
`result_json` text,
|
||||||
|
`report_path` text,
|
||||||
|
`raw_log_path` text,
|
||||||
|
FOREIGN KEY (`machine_id`) REFERENCES `machines`(`id`) ON UPDATE no action ON DELETE cascade
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `machines` (
|
||||||
|
`id` text PRIMARY KEY NOT NULL,
|
||||||
|
`name` text NOT NULL,
|
||||||
|
`hostname` text NOT NULL,
|
||||||
|
`port` integer DEFAULT 22 NOT NULL,
|
||||||
|
`os_family` text DEFAULT 'unknown' NOT NULL,
|
||||||
|
`username` text NOT NULL,
|
||||||
|
`enc_password` text NOT NULL,
|
||||||
|
`enc_sudo_password` text,
|
||||||
|
`apt_proxy_mode` text DEFAULT 'direct' NOT NULL,
|
||||||
|
`apt_proxy_url` text,
|
||||||
|
`status` text DEFAULT 'unknown' NOT NULL,
|
||||||
|
`last_checked_at` text,
|
||||||
|
`created_at` text NOT NULL
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `snapshots` (
|
||||||
|
`id` text PRIMARY KEY NOT NULL,
|
||||||
|
`machine_id` text NOT NULL,
|
||||||
|
`checked_at` text NOT NULL,
|
||||||
|
`status` text NOT NULL,
|
||||||
|
`payload_json` text NOT NULL,
|
||||||
|
FOREIGN KEY (`machine_id`) REFERENCES `machines`(`id`) ON UPDATE no action ON DELETE cascade
|
||||||
|
);
|
||||||
@@ -0,0 +1,277 @@
|
|||||||
|
{
|
||||||
|
"version": "6",
|
||||||
|
"dialect": "sqlite",
|
||||||
|
"id": "6aec3f17-e17f-4e7c-950c-c11592a58541",
|
||||||
|
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||||
|
"tables": {
|
||||||
|
"executions": {
|
||||||
|
"name": "executions",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"machine_id": {
|
||||||
|
"name": "machine_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
"name": "action",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"mode": {
|
||||||
|
"name": "mode",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'manual'"
|
||||||
|
},
|
||||||
|
"started_at": {
|
||||||
|
"name": "started_at",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"finished_at": {
|
||||||
|
"name": "finished_at",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"name": "status",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"result_json": {
|
||||||
|
"name": "result_json",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"report_path": {
|
||||||
|
"name": "report_path",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"raw_log_path": {
|
||||||
|
"name": "raw_log_path",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"executions_machine_id_machines_id_fk": {
|
||||||
|
"name": "executions_machine_id_machines_id_fk",
|
||||||
|
"tableFrom": "executions",
|
||||||
|
"tableTo": "machines",
|
||||||
|
"columnsFrom": [
|
||||||
|
"machine_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"machines": {
|
||||||
|
"name": "machines",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"hostname": {
|
||||||
|
"name": "hostname",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"port": {
|
||||||
|
"name": "port",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": 22
|
||||||
|
},
|
||||||
|
"os_family": {
|
||||||
|
"name": "os_family",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'unknown'"
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"name": "username",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"enc_password": {
|
||||||
|
"name": "enc_password",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"enc_sudo_password": {
|
||||||
|
"name": "enc_sudo_password",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"apt_proxy_mode": {
|
||||||
|
"name": "apt_proxy_mode",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'direct'"
|
||||||
|
},
|
||||||
|
"apt_proxy_url": {
|
||||||
|
"name": "apt_proxy_url",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"name": "status",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'unknown'"
|
||||||
|
},
|
||||||
|
"last_checked_at": {
|
||||||
|
"name": "last_checked_at",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"snapshots": {
|
||||||
|
"name": "snapshots",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"machine_id": {
|
||||||
|
"name": "machine_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"checked_at": {
|
||||||
|
"name": "checked_at",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"name": "status",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"payload_json": {
|
||||||
|
"name": "payload_json",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"snapshots_machine_id_machines_id_fk": {
|
||||||
|
"name": "snapshots_machine_id_machines_id_fk",
|
||||||
|
"tableFrom": "snapshots",
|
||||||
|
"tableTo": "machines",
|
||||||
|
"columnsFrom": [
|
||||||
|
"machine_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"views": {},
|
||||||
|
"enums": {},
|
||||||
|
"_meta": {
|
||||||
|
"schemas": {},
|
||||||
|
"tables": {},
|
||||||
|
"columns": {}
|
||||||
|
},
|
||||||
|
"internal": {
|
||||||
|
"indexes": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"version": "7",
|
||||||
|
"dialect": "sqlite",
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"idx": 0,
|
||||||
|
"version": "6",
|
||||||
|
"when": 1780599514478,
|
||||||
|
"tag": "0000_brainy_dakota_north",
|
||||||
|
"breakpoints": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
// server/db/schema.ts
|
||||||
|
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
|
||||||
|
|
||||||
|
export const machines = sqliteTable("machines", {
|
||||||
|
id: text("id").primaryKey(),
|
||||||
|
name: text("name").notNull(),
|
||||||
|
hostname: text("hostname").notNull(),
|
||||||
|
port: integer("port").notNull().default(22),
|
||||||
|
osFamily: text("os_family").notNull().default("unknown"),
|
||||||
|
username: text("username").notNull(),
|
||||||
|
encPassword: text("enc_password").notNull(),
|
||||||
|
encSudoPassword: text("enc_sudo_password"),
|
||||||
|
aptProxyMode: text("apt_proxy_mode").notNull().default("direct"),
|
||||||
|
aptProxyUrl: text("apt_proxy_url"),
|
||||||
|
status: text("status").notNull().default("unknown"),
|
||||||
|
lastCheckedAt: text("last_checked_at"),
|
||||||
|
createdAt: text("created_at").notNull(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const snapshots = sqliteTable("snapshots", {
|
||||||
|
id: text("id").primaryKey(),
|
||||||
|
machineId: text("machine_id").notNull().references(() => machines.id, { onDelete: "cascade" }),
|
||||||
|
checkedAt: text("checked_at").notNull(),
|
||||||
|
status: text("status").notNull(),
|
||||||
|
payloadJson: text("payload_json").notNull(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const executions = sqliteTable("executions", {
|
||||||
|
id: text("id").primaryKey(),
|
||||||
|
machineId: text("machine_id").notNull().references(() => machines.id, { onDelete: "cascade" }),
|
||||||
|
action: text("action").notNull(),
|
||||||
|
mode: text("mode").notNull().default("manual"),
|
||||||
|
startedAt: text("started_at").notNull(),
|
||||||
|
finishedAt: text("finished_at"),
|
||||||
|
status: text("status").notNull(),
|
||||||
|
resultJson: text("result_json"),
|
||||||
|
reportPath: text("report_path"),
|
||||||
|
rawLogPath: text("raw_log_path"),
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user