feat: auth foundation, similar tools, costs, redundancy, anonymous voting
This commit is contained in:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user