132 lines
3.2 KiB
TypeScript
132 lines
3.2 KiB
TypeScript
import Database from "better-sqlite3";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
|
export type BuildStatus =
|
|
| "pending"
|
|
| "queued"
|
|
| "in_progress"
|
|
| "completed"
|
|
| "failed";
|
|
|
|
export interface BuildRecord {
|
|
id: string;
|
|
app_name: string;
|
|
app_name_en: string;
|
|
app_identifier: string;
|
|
status: BuildStatus;
|
|
workflow_run_id: number | null;
|
|
windows_url: string | null;
|
|
android_url: string | null;
|
|
error: string | null;
|
|
created_at: string;
|
|
updated_at: string;
|
|
}
|
|
|
|
let db: Database.Database | null = null;
|
|
|
|
export function getDb(): Database.Database {
|
|
if (db) return db;
|
|
|
|
const dataDir = path.resolve(__dirname, "../../data");
|
|
fs.mkdirSync(dataDir, { recursive: true });
|
|
const dbPath = path.join(dataDir, "web2app.db");
|
|
db = new Database(dbPath);
|
|
const schema = fs.readFileSync(path.join(__dirname, "schema.sql"), "utf8");
|
|
db.exec(schema);
|
|
migrateBuildsTable(db);
|
|
return db;
|
|
}
|
|
|
|
function migrateBuildsTable(database: Database.Database) {
|
|
const columns = database
|
|
.prepare("PRAGMA table_info(builds)")
|
|
.all() as Array<{ name: string }>;
|
|
|
|
if (!columns.some((column) => column.name === "app_name_en")) {
|
|
database.exec("ALTER TABLE builds ADD COLUMN app_name_en TEXT NOT NULL DEFAULT ''");
|
|
}
|
|
}
|
|
|
|
export function insertBuild(record: {
|
|
id: string;
|
|
appName: string;
|
|
appNameEn: string;
|
|
appIdentifier: string;
|
|
}): BuildRecord {
|
|
const database = getDb();
|
|
database
|
|
.prepare(
|
|
`INSERT INTO builds (id, app_name, app_name_en, app_identifier, status)
|
|
VALUES (@id, @appName, @appNameEn, @appIdentifier, 'pending')`,
|
|
)
|
|
.run(record);
|
|
|
|
return getBuild(record.id)!;
|
|
}
|
|
|
|
export function getBuild(id: string): BuildRecord | null {
|
|
const row = getDb()
|
|
.prepare("SELECT * FROM builds WHERE id = ?")
|
|
.get(id) as BuildRecord | undefined;
|
|
return row ?? null;
|
|
}
|
|
|
|
export function listBuilds(limit = 20): BuildRecord[] {
|
|
return getDb()
|
|
.prepare("SELECT * FROM builds ORDER BY created_at DESC LIMIT ?")
|
|
.all(limit) as BuildRecord[];
|
|
}
|
|
|
|
export function updateBuild(
|
|
id: string,
|
|
patch: Partial<
|
|
Pick<
|
|
BuildRecord,
|
|
| "status"
|
|
| "workflow_run_id"
|
|
| "windows_url"
|
|
| "android_url"
|
|
| "error"
|
|
>
|
|
>,
|
|
): BuildRecord | null {
|
|
const fields: string[] = [];
|
|
const params: Record<string, unknown> = { id };
|
|
|
|
for (const [key, value] of Object.entries(patch)) {
|
|
if (value !== undefined) {
|
|
fields.push(`${key} = @${key}`);
|
|
params[key] = value;
|
|
}
|
|
}
|
|
|
|
if (fields.length === 0) return getBuild(id);
|
|
|
|
fields.push("updated_at = datetime('now')");
|
|
getDb()
|
|
.prepare(`UPDATE builds SET ${fields.join(", ")} WHERE id = @id`)
|
|
.run(params);
|
|
|
|
return getBuild(id);
|
|
}
|
|
|
|
export function toPublicBuild(record: BuildRecord) {
|
|
return {
|
|
id: record.id,
|
|
appName: record.app_name,
|
|
appNameEn: record.app_name_en || record.app_name,
|
|
appIdentifier: record.app_identifier,
|
|
status: record.status,
|
|
workflowRunId: record.workflow_run_id,
|
|
windowsUrl: record.windows_url,
|
|
androidUrl: record.android_url,
|
|
error: record.error,
|
|
createdAt: record.created_at,
|
|
updatedAt: record.updated_at,
|
|
};
|
|
}
|