feat: auth foundation, similar tools, costs, redundancy, anonymous voting

This commit is contained in:
root
2026-07-29 21:59:29 +02:00
parent 8b3b7c9955
commit 59badeaa48
22 changed files with 793 additions and 15 deletions
+71
View File
@@ -55,6 +55,7 @@ async function seedAdminUser(): Promise<void> {
username: adminUsername,
passwordHash,
role: "admin",
tier: "enterprise",
});
logger.info({ username: adminUsername }, "Admin user created");
} catch (err) {
@@ -62,9 +63,79 @@ async function seedAdminUser(): Promise<void> {
}
}
async function ensureToolRelationsTable(): Promise<void> {
try {
const exists = await db.execute(
sql`SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = 'tool_relations')`,
);
const rows = exists.rows as [{ exists: boolean }];
if (rows[0]?.exists) return;
await db.execute(
sql`CREATE TABLE "tool_relations" ("id" serial NOT NULL, "tool_id" integer NOT NULL REFERENCES "tools"("id") ON DELETE CASCADE, "related_tool_id" integer NOT NULL REFERENCES "tools"("id") ON DELETE CASCADE, "relation_type" text NOT NULL DEFAULT 'similar', "notes" text, "created_by" integer REFERENCES "users"("id"), "created_at" timestamp with time zone NOT NULL DEFAULT NOW())`,
);
await db.execute(sql`ALTER TABLE "tool_relations" ADD PRIMARY KEY ("id")`);
logger.info("tool_relations table created");
} catch (err) {
logger.error({ err }, "Failed to ensure tool_relations table");
}
}
async function ensureToolCostsTable(): Promise<void> {
try {
const exists = await db.execute(
sql`SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = 'tool_costs')`,
);
const rows = exists.rows as [{ exists: boolean }];
if (rows[0]?.exists) return;
await db.execute(
sql`CREATE TABLE "tool_costs" ("id" serial NOT NULL, "tool_id" integer NOT NULL REFERENCES "tools"("id") ON DELETE CASCADE, "license_type" text NOT NULL DEFAULT 'free', "cost" numeric(10,2), "currency" text DEFAULT 'EUR', "renewal_date" timestamp with time zone, "notes" text, "created_by" integer REFERENCES "users"("id"), "created_at" timestamp with time zone NOT NULL DEFAULT NOW())`,
);
await db.execute(sql`ALTER TABLE "tool_costs" ADD PRIMARY KEY ("id")`);
logger.info("tool_costs table created");
} catch (err) {
logger.error({ err }, "Failed to ensure tool_costs table");
}
}
async function ensureVoterTokenColumn(): Promise<void> {
try {
const exists = await db.execute(
sql`SELECT EXISTS (SELECT FROM information_schema.columns WHERE table_name = 'ratings' AND column_name = 'voter_token')`,
);
const rows = exists.rows as [{ exists: boolean }];
if (rows[0]?.exists) return;
await db.execute(sql`ALTER TABLE "ratings" ADD COLUMN "voter_token" text`);
logger.info("voter_token column added to ratings");
} catch (err) {
logger.error({ err }, "Failed to add voter_token column");
}
}
async function ensureAdminTier(): Promise<void> {
try {
const result = await db
.update(usersTable)
.set({ tier: "enterprise" })
.where(sql`role = 'admin' AND (tier IS NULL OR tier = 'free')`)
.returning({ id: usersTable.id, username: usersTable.username });
if (result.length > 0) {
logger.info({ count: result.length }, "Admin users upgraded to enterprise tier");
}
} catch (err) {
logger.error({ err }, "Failed to upgrade admin tier");
}
}
async function start(): Promise<void> {
await ensureSessionsTable();
await ensureToolRelationsTable();
await ensureToolCostsTable();
await ensureVoterTokenColumn();
await seedAdminUser();
await ensureAdminTier();
app.listen(port, (err) => {
if (err) {