Refactor project to transition from Rust backend to Node.js with Fastify; update Dockerfile and Docker configurations for new build process; enhance local development instructions in CONTRIBUTING.md; implement health checks in Docker Compose; update pnpm-lock.yaml with new dependencies for API and shared packages; revise README.md to reflect new stack and development setup.
Build, Test, and Push CFDM Docker Image / test (push) Has been cancelled
Build, Test, and Push CFDM Docker Image / build-and-push (push) Has been cancelled
Build, Test, and Push CFDM Docker Image / create-release (push) Has been cancelled
Build, Test, and Push CFDM Docker Image / update-wiki (push) Has been cancelled
Build, Test, and Push CFDM Docker Image / test (push) Has been cancelled
Build, Test, and Push CFDM Docker Image / build-and-push (push) Has been cancelled
Build, Test, and Push CFDM Docker Image / create-release (push) Has been cancelled
Build, Test, and Push CFDM Docker Image / update-wiki (push) Has been cancelled
This commit is contained in:
1 parent
0a0a1a92f1
commit
d11414666f
2065 files changed
+15782
-16699
No files matched your search
@@ -0,0 +1,65 @@
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { readFileSync, readdirSync } from "node:fs";
|
||||
import Database from "better-sqlite3";
|
||||
import { drizzle } from "drizzle-orm/better-sqlite3";
|
||||
import { schema } from "./schema.js";
|
||||
|
||||
export type Sqlite = Database.Database;
|
||||
export type Db = ReturnType<typeof drizzle<typeof schema>>;
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
export function resolveDatabasePath(databaseUrl: string): string {
|
||||
const url = databaseUrl.startsWith("sqlite:")
|
||||
? databaseUrl.slice("sqlite:".length)
|
||||
: databaseUrl;
|
||||
return url;
|
||||
}
|
||||
|
||||
export function createDb(databaseUrl: string): { db: Db; sqlite: Sqlite } {
|
||||
const path = resolveDatabasePath(databaseUrl);
|
||||
const sqlite = new Database(path);
|
||||
sqlite.pragma("journal_mode = WAL");
|
||||
sqlite.pragma("synchronous = NORMAL");
|
||||
sqlite.pragma("foreign_keys = ON");
|
||||
const db = drizzle(sqlite, { schema });
|
||||
return { db, sqlite };
|
||||
}
|
||||
|
||||
export function createMemoryDb(): { db: Db; sqlite: Sqlite } {
|
||||
const sqlite = new Database(":memory:");
|
||||
sqlite.pragma("foreign_keys = ON");
|
||||
const db = drizzle(sqlite, { schema });
|
||||
return { db, sqlite };
|
||||
}
|
||||
|
||||
export function runMigrations(sqlite: Sqlite): void {
|
||||
const migrationsDir = join(__dirname, "..", "migrations");
|
||||
const files = readdirSync(migrationsDir)
|
||||
.filter((f) => f.endsWith(".sql"))
|
||||
.sort();
|
||||
|
||||
sqlite.exec(
|
||||
`CREATE TABLE IF NOT EXISTS _migrations (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
)`,
|
||||
);
|
||||
|
||||
for (const file of files) {
|
||||
const applied = sqlite
|
||||
.prepare("SELECT 1 FROM _migrations WHERE name = ?")
|
||||
.get(file);
|
||||
if (applied) continue;
|
||||
|
||||
const sql = readFileSync(join(migrationsDir, file), "utf-8");
|
||||
sqlite.exec(sql);
|
||||
sqlite.prepare("INSERT INTO _migrations (name) VALUES (?)").run(file);
|
||||
}
|
||||
}
|
||||
|
||||
export function healthCheck(sqlite: Sqlite): void {
|
||||
sqlite.prepare("SELECT 1").get();
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
export class NotFoundError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
this.name = "NotFoundError";
|
||||
}
|
||||
}
|
||||
|
||||
export class ConflictError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
this.name = "ConflictError";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export * from "./schema.js";
|
||||
export * from "./client.js";
|
||||
export * from "./errors.js";
|
||||
export * as repos from "./repos.js";
|
||||
export type { DnsListFilter } from "./repos.js";
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,225 @@
|
||||
import { sql } from "drizzle-orm";
|
||||
import {
|
||||
integer,
|
||||
primaryKey,
|
||||
sqliteTable,
|
||||
text,
|
||||
} from "drizzle-orm/sqlite-core";
|
||||
|
||||
export const groups = sqliteTable("groups", {
|
||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||
name: text("name").notNull(),
|
||||
slug: text("slug").notNull().unique(),
|
||||
created_at: text("created_at")
|
||||
.notNull()
|
||||
.default(sql`datetime('now')`),
|
||||
updated_at: text("updated_at")
|
||||
.notNull()
|
||||
.default(sql`datetime('now')`),
|
||||
});
|
||||
|
||||
export const services = sqliteTable("services", {
|
||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||
name: text("name").notNull(),
|
||||
slug: text("slug").notNull().unique(),
|
||||
service_group_id: integer("service_group_id").references(
|
||||
() => serviceGroups.id,
|
||||
{ onDelete: "set null" },
|
||||
),
|
||||
subdomain: text("subdomain"),
|
||||
enabled: integer("enabled", { mode: "boolean" }).notNull().default(false),
|
||||
created_at: text("created_at")
|
||||
.notNull()
|
||||
.default(sql`datetime('now')`),
|
||||
updated_at: text("updated_at")
|
||||
.notNull()
|
||||
.default(sql`datetime('now')`),
|
||||
});
|
||||
|
||||
export const serviceGroups = sqliteTable("service_groups", {
|
||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||
name: text("name").notNull(),
|
||||
type: text("type").notNull().default("custom"),
|
||||
icon: text("icon"),
|
||||
domain: text("domain"),
|
||||
enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
|
||||
created_at: text("created_at")
|
||||
.notNull()
|
||||
.default(sql`datetime('now')`),
|
||||
updated_at: text("updated_at")
|
||||
.notNull()
|
||||
.default(sql`datetime('now')`),
|
||||
});
|
||||
|
||||
export const domains = sqliteTable("domains", {
|
||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||
group_id: integer("group_id").references(() => groups.id, {
|
||||
onDelete: "set null",
|
||||
}),
|
||||
zone_name: text("zone_name").notNull().unique(),
|
||||
cf_zone_id: text("cf_zone_id").notNull(),
|
||||
status: text("status").notNull().default("active"),
|
||||
last_synced_at: text("last_synced_at"),
|
||||
created_at: text("created_at")
|
||||
.notNull()
|
||||
.default(sql`datetime('now')`),
|
||||
updated_at: text("updated_at")
|
||||
.notNull()
|
||||
.default(sql`datetime('now')`),
|
||||
});
|
||||
|
||||
export const subdomains = sqliteTable("subdomains", {
|
||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||
domain_id: integer("domain_id")
|
||||
.notNull()
|
||||
.references(() => domains.id, { onDelete: "cascade" }),
|
||||
name: text("name").notNull(),
|
||||
fqdn: text("fqdn").notNull(),
|
||||
created_at: text("created_at")
|
||||
.notNull()
|
||||
.default(sql`datetime('now')`),
|
||||
updated_at: text("updated_at")
|
||||
.notNull()
|
||||
.default(sql`datetime('now')`),
|
||||
});
|
||||
|
||||
export const dnsRecords = sqliteTable("dns_records", {
|
||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||
domain_id: integer("domain_id")
|
||||
.notNull()
|
||||
.references(() => domains.id, { onDelete: "cascade" }),
|
||||
cf_record_id: text("cf_record_id"),
|
||||
record_type: text("record_type").notNull(),
|
||||
name: text("name").notNull(),
|
||||
content: text("content").notNull(),
|
||||
ttl: integer("ttl").notNull().default(1),
|
||||
proxied: integer("proxied", { mode: "boolean" }).notNull().default(false),
|
||||
priority: integer("priority"),
|
||||
sync_status: text("sync_status").notNull().default("pending_push"),
|
||||
origin: text("origin").notNull().default("local"),
|
||||
last_error: text("last_error"),
|
||||
created_at: text("created_at")
|
||||
.notNull()
|
||||
.default(sql`datetime('now')`),
|
||||
updated_at: text("updated_at")
|
||||
.notNull()
|
||||
.default(sql`datetime('now')`),
|
||||
});
|
||||
|
||||
export const serviceBindings = sqliteTable("service_bindings", {
|
||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||
domain_id: integer("domain_id")
|
||||
.notNull()
|
||||
.references(() => domains.id, { onDelete: "cascade" }),
|
||||
service_id: integer("service_id")
|
||||
.notNull()
|
||||
.references(() => services.id, { onDelete: "cascade" }),
|
||||
hostname: text("hostname").notNull().default("@"),
|
||||
dns_record_id: integer("dns_record_id").references(() => dnsRecords.id, {
|
||||
onDelete: "set null",
|
||||
}),
|
||||
created_at: text("created_at")
|
||||
.notNull()
|
||||
.default(sql`datetime('now')`),
|
||||
updated_at: text("updated_at")
|
||||
.notNull()
|
||||
.default(sql`datetime('now')`),
|
||||
});
|
||||
|
||||
export const serviceIps = sqliteTable("service_ips", {
|
||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||
service_id: integer("service_id")
|
||||
.notNull()
|
||||
.references(() => services.id, { onDelete: "cascade" }),
|
||||
ip: text("ip").notNull(),
|
||||
created_at: text("created_at")
|
||||
.notNull()
|
||||
.default(sql`datetime('now')`),
|
||||
});
|
||||
|
||||
export const serviceBindingRecords = sqliteTable(
|
||||
"service_binding_records",
|
||||
{
|
||||
binding_id: integer("binding_id")
|
||||
.notNull()
|
||||
.references(() => serviceBindings.id, { onDelete: "cascade" }),
|
||||
dns_record_id: integer("dns_record_id")
|
||||
.notNull()
|
||||
.references(() => dnsRecords.id, { onDelete: "cascade" }),
|
||||
},
|
||||
(t) => [primaryKey({ columns: [t.binding_id, t.dns_record_id] })],
|
||||
);
|
||||
|
||||
export const serviceBindingIps = sqliteTable(
|
||||
"service_binding_ips",
|
||||
{
|
||||
binding_id: integer("binding_id")
|
||||
.notNull()
|
||||
.references(() => serviceBindings.id, { onDelete: "cascade" }),
|
||||
ip: text("ip").notNull(),
|
||||
},
|
||||
(t) => [primaryKey({ columns: [t.binding_id, t.ip] })],
|
||||
);
|
||||
|
||||
export const serviceGroupDnsRecords = sqliteTable(
|
||||
"service_group_dns_records",
|
||||
{
|
||||
group_id: integer("group_id")
|
||||
.notNull()
|
||||
.references(() => serviceGroups.id, { onDelete: "cascade" }),
|
||||
dns_record_id: integer("dns_record_id")
|
||||
.notNull()
|
||||
.references(() => dnsRecords.id, { onDelete: "cascade" }),
|
||||
},
|
||||
(t) => [primaryKey({ columns: [t.group_id, t.dns_record_id] })],
|
||||
);
|
||||
|
||||
export const certificates = sqliteTable("certificates", {
|
||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||
domain_id: integer("domain_id")
|
||||
.notNull()
|
||||
.references(() => domains.id, { onDelete: "cascade" }),
|
||||
subdomain_id: integer("subdomain_id").references(() => subdomains.id, {
|
||||
onDelete: "set null",
|
||||
}),
|
||||
hostname: text("hostname").notNull().unique(),
|
||||
expires_at: text("expires_at"),
|
||||
last_checked_at: text("last_checked_at"),
|
||||
last_error: text("last_error"),
|
||||
status: text("status").notNull().default("unknown"),
|
||||
created_at: text("created_at")
|
||||
.notNull()
|
||||
.default(sql`datetime('now')`),
|
||||
updated_at: text("updated_at")
|
||||
.notNull()
|
||||
.default(sql`datetime('now')`),
|
||||
});
|
||||
|
||||
export const syncJobs = sqliteTable("sync_jobs", {
|
||||
id: text("id").primaryKey(),
|
||||
status: text("status").notNull().default("pending"),
|
||||
domain_id: integer("domain_id").references(() => domains.id, {
|
||||
onDelete: "set null",
|
||||
}),
|
||||
message: text("message"),
|
||||
created_at: text("created_at")
|
||||
.notNull()
|
||||
.default(sql`datetime('now')`),
|
||||
finished_at: text("finished_at"),
|
||||
});
|
||||
|
||||
export const schema = {
|
||||
groups,
|
||||
services,
|
||||
serviceGroups,
|
||||
domains,
|
||||
subdomains,
|
||||
dnsRecords,
|
||||
serviceBindings,
|
||||
serviceIps,
|
||||
serviceBindingRecords,
|
||||
serviceBindingIps,
|
||||
serviceGroupDnsRecords,
|
||||
certificates,
|
||||
syncJobs,
|
||||
};
|
||||
Reference in new issue
Block a user