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
@@ -241,6 +241,7 @@ export interface AuthUser {
/** @nullable */
preferredUsername?: string | null;
role?: AuthUserRole;
tier?: "free" | "premium" | "enterprise";
isLocal?: boolean;
}
+1 -1
View File
@@ -1,4 +1,4 @@
export * from "./generated/api";
export * from "./generated/api.schemas";
export { setBaseUrl, setAuthTokenGetter } from "./custom-fetch";
export { setBaseUrl, setAuthTokenGetter, customFetch } from "./custom-fetch";
export type { AuthTokenGetter } from "./custom-fetch";
+2
View File
@@ -3,3 +3,5 @@ export * from "./ratings";
export * from "./users";
export * from "./audit-logs";
export * from "./sessions";
export * from "./tool-relations";
export * from "./tool-costs";
+1
View File
@@ -10,6 +10,7 @@ export const ratingsTable = pgTable("ratings", {
usability: integer("usability").notNull(),
comment: text("comment"),
reviewerName: text("reviewer_name"),
voterToken: text("voter_token"),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
});
+15
View File
@@ -0,0 +1,15 @@
import { pgTable, text, serial, integer, numeric, timestamp } from "drizzle-orm/pg-core";
import { toolsTable } from "./tools";
import { usersTable } from "./users";
export const toolCostsTable = pgTable("tool_costs", {
id: serial("id").primaryKey(),
toolId: integer("tool_id").notNull().references(() => toolsTable.id, { onDelete: "cascade" }),
licenseType: text("license_type", { enum: ["free", "subscription", "one_time", "usage_based"] }).notNull().default("free"),
cost: numeric("cost", { precision: 10, scale: 2 }),
currency: text("currency").default("EUR"),
renewalDate: timestamp("renewal_date", { withTimezone: true }),
notes: text("notes"),
createdBy: integer("created_by").references(() => usersTable.id),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
});
+13
View File
@@ -0,0 +1,13 @@
import { pgTable, text, serial, integer, timestamp } from "drizzle-orm/pg-core";
import { toolsTable } from "./tools";
import { usersTable } from "./users";
export const toolRelationsTable = pgTable("tool_relations", {
id: serial("id").primaryKey(),
toolId: integer("tool_id").notNull().references(() => toolsTable.id, { onDelete: "cascade" }),
relatedToolId: integer("related_tool_id").notNull().references(() => toolsTable.id, { onDelete: "cascade" }),
relationType: text("relation_type", { enum: ["similar", "replaces", "superseded_by"] }).notNull().default("similar"),
notes: text("notes"),
createdBy: integer("created_by").references(() => usersTable.id),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
});
+5 -1
View File
@@ -5,9 +5,13 @@ import { z } from "zod/v4";
export const usersTable = pgTable("users", {
id: serial("id").primaryKey(),
username: text("username").notNull().unique(),
passwordHash: text("password_hash").notNull(),
email: text("email"),
role: text("role").notNull().default("user"),
passwordHash: text("password_hash"),
tier: text("tier").notNull().default("free"),
authProvider: text("auth_provider").notNull().default("local"),
authProviderId: text("auth_provider_id"),
displayName: text("display_name"),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
});