From 1b64ccd2bfa73df1c3f8b0bf6b2699f078d3e691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Balogh=20Barnab=C3=A1s?= Date: Sun, 14 Dec 2025 14:43:20 +0100 Subject: [PATCH 1/6] Add AI pre-review for devlogs and projects --- .env.example | 4 +- README.md | 5 + drizzle/0004_ai_prechecks.sql | 32 ++ drizzle/meta/0004_snapshot.json | 277 ++++++++++++++++++ drizzle/meta/_journal.json | 7 + src/lib/components/Devlog.svelte | 31 +- src/lib/server/ai/ensure.ts | 123 ++++++++ src/lib/server/ai/reviewer.ts | 141 +++++++++ src/lib/server/db/schema.ts | 34 +++ .../admin/review/[id]/+page.server.ts | 71 ++++- .../dashboard/admin/review/[id]/+page.svelte | 53 +++- .../projects/[id]/ship/+page.server.ts | 25 ++ 12 files changed, 794 insertions(+), 9 deletions(-) create mode 100644 drizzle/0004_ai_prechecks.sql create mode 100644 drizzle/meta/0004_snapshot.json create mode 100644 src/lib/server/ai/ensure.ts create mode 100644 src/lib/server/ai/reviewer.ts diff --git a/.env.example b/.env.example index 079887e..2203f07 100644 --- a/.env.example +++ b/.env.example @@ -25,4 +25,6 @@ IDV_DOMAIN=account.hackclub.com IDV_CLIENT_ID=changeme IDV_CLIENT_SECRET=changeme -SENTRY_AUTH_TOKEN=changeme \ No newline at end of file +SENTRY_AUTH_TOKEN=changeme + +AI_API_KEY=sk-... diff --git a/README.md b/README.md index 7a4b8a2..ce79478 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,11 @@ You can also run this to get a GUI for the database: npm run db:studio ``` +### AI prereview (devlogs) + +- The review panel now shows an AI pre-check on shipped projects’ devlogs. It only flags obvious over-reporting of time. +- Set `AI_API_KEY` to your Hack Club AI key (it uses the https://ai.hackclub.com proxy under the hood). No key = no AI hints in review. + ## Deploying Create a `.env` file containing all the required credentials, look at `.env.example` for an example. diff --git a/drizzle/0004_ai_prechecks.sql b/drizzle/0004_ai_prechecks.sql new file mode 100644 index 0000000..6575a50 --- /dev/null +++ b/drizzle/0004_ai_prechecks.sql @@ -0,0 +1,32 @@ +CREATE TABLE "ai_devlog_review" ( + "id" serial PRIMARY KEY NOT NULL, + "devlogId" integer NOT NULL, + "projectId" integer NOT NULL, + "approved" boolean NOT NULL, + "rationale" text NOT NULL, + "prompt" text NOT NULL, + "model" text NOT NULL, + "createdAt" timestamp DEFAULT now() NOT NULL, + "updatedAt" timestamp DEFAULT now() NOT NULL, + CONSTRAINT "ai_devlog_review_devlogId_unique" UNIQUE ("devlogId") +); +--> statement-breakpoint + +ALTER TABLE "ai_devlog_review" ADD CONSTRAINT "ai_devlog_review_devlogId_devlog_id_fk" FOREIGN KEY ("devlogId") REFERENCES "devlog"("id") ON DELETE no action ON UPDATE no action; +--> statement-breakpoint +ALTER TABLE "ai_devlog_review" ADD CONSTRAINT "ai_devlog_review_projectId_project_id_fk" FOREIGN KEY ("projectId") REFERENCES "project"("id") ON DELETE no action ON UPDATE no action; +--> statement-breakpoint + +CREATE TABLE "ai_project_review" ( + "id" serial PRIMARY KEY NOT NULL, + "projectId" integer NOT NULL, + "overallApproved" boolean NOT NULL, + "summary" text NOT NULL, + "prompt" text NOT NULL, + "model" text NOT NULL, + "createdAt" timestamp DEFAULT now() NOT NULL, + "updatedAt" timestamp DEFAULT now() NOT NULL, + CONSTRAINT "ai_project_review_projectId_unique" UNIQUE ("projectId") +); +--> statement-breakpoint +ALTER TABLE "ai_project_review" ADD CONSTRAINT "ai_project_review_projectId_project_id_fk" FOREIGN KEY ("projectId") REFERENCES "project"("id") ON DELETE no action ON UPDATE no action; diff --git a/drizzle/meta/0004_snapshot.json b/drizzle/meta/0004_snapshot.json new file mode 100644 index 0000000..674cb20 --- /dev/null +++ b/drizzle/meta/0004_snapshot.json @@ -0,0 +1,277 @@ +{ + "id": "5c6f3ce1-9e64-4e8c-93ab-9fd1d3ae1b70", + "prevId": "dd1faf34-f215-4e5a-95a9-66c21e151248", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.ai_devlog_review": { + "name": "ai_devlog_review", + "schema": "", + "columns": { + "id": { "name": "id", "type": "serial", "primaryKey": true, "notNull": true }, + "devlogId": { "name": "devlogId", "type": "integer", "primaryKey": false, "notNull": true }, + "projectId": { "name": "projectId", "type": "integer", "primaryKey": false, "notNull": true }, + "approved": { "name": "approved", "type": "boolean", "primaryKey": false, "notNull": true }, + "rationale": { "name": "rationale", "type": "text", "primaryKey": false, "notNull": true }, + "prompt": { "name": "prompt", "type": "text", "primaryKey": false, "notNull": true }, + "model": { "name": "model", "type": "text", "primaryKey": false, "notNull": true }, + "createdAt": { "name": "createdAt", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, + "updatedAt": { "name": "updatedAt", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + }, + "indexes": {}, + "foreignKeys": { + "ai_devlog_review_devlogId_devlog_id_fk": { + "name": "ai_devlog_review_devlogId_devlog_id_fk", + "tableFrom": "ai_devlog_review", + "tableTo": "devlog", + "columnsFrom": ["devlogId"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "ai_devlog_review_projectId_project_id_fk": { + "name": "ai_devlog_review_projectId_project_id_fk", + "tableFrom": "ai_devlog_review", + "tableTo": "project", + "columnsFrom": ["projectId"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "ai_devlog_review_devlogId_unique": { + "name": "ai_devlog_review_devlogId_unique", + "nullsNotDistinct": false, + "columns": ["devlogId"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ai_project_review": { + "name": "ai_project_review", + "schema": "", + "columns": { + "id": { "name": "id", "type": "serial", "primaryKey": true, "notNull": true }, + "projectId": { "name": "projectId", "type": "integer", "primaryKey": false, "notNull": true }, + "overallApproved": { "name": "overallApproved", "type": "boolean", "primaryKey": false, "notNull": true }, + "summary": { "name": "summary", "type": "text", "primaryKey": false, "notNull": true }, + "prompt": { "name": "prompt", "type": "text", "primaryKey": false, "notNull": true }, + "model": { "name": "model", "type": "text", "primaryKey": false, "notNull": true }, + "createdAt": { "name": "createdAt", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, + "updatedAt": { "name": "updatedAt", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + }, + "indexes": {}, + "foreignKeys": { + "ai_project_review_projectId_project_id_fk": { + "name": "ai_project_review_projectId_project_id_fk", + "tableFrom": "ai_project_review", + "tableTo": "project", + "columnsFrom": ["projectId"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "ai_project_review_projectId_unique": { + "name": "ai_project_review_projectId_unique", + "nullsNotDistinct": false, + "columns": ["projectId"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.devlog": { + "name": "devlog", + "schema": "", + "columns": { + "id": { "name": "id", "type": "serial", "primaryKey": true, "notNull": true }, + "userId": { "name": "userId", "type": "integer", "primaryKey": false, "notNull": false }, + "projectId": { "name": "projectId", "type": "integer", "primaryKey": false, "notNull": true }, + "description": { "name": "description", "type": "text", "primaryKey": false, "notNull": true }, + "timeSpent": { "name": "timeSpent", "type": "integer", "primaryKey": false, "notNull": true }, + "image": { "name": "image", "type": "text", "primaryKey": false, "notNull": true }, + "model": { "name": "model", "type": "text", "primaryKey": false, "notNull": true }, + "deleted": { "name": "deleted", "type": "boolean", "primaryKey": false, "notNull": true, "default": false }, + "createdAt": { "name": "createdAt", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, + "updatedAt": { "name": "updatedAt", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + }, + "indexes": {}, + "foreignKeys": { + "devlog_userId_user_id_fk": { + "name": "devlog_userId_user_id_fk", + "tableFrom": "devlog", + "tableTo": "user", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "devlog_projectId_project_id_fk": { + "name": "devlog_projectId_project_id_fk", + "tableFrom": "devlog", + "tableTo": "project", + "columnsFrom": ["projectId"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project": { + "name": "project", + "schema": "", + "columns": { + "id": { "name": "id", "type": "serial", "primaryKey": true, "notNull": true }, + "userId": { "name": "userId", "type": "integer", "primaryKey": false, "notNull": true }, + "name": { "name": "name", "type": "text", "primaryKey": false, "notNull": false }, + "description": { "name": "description", "type": "text", "primaryKey": false, "notNull": false }, + "url": { "name": "url", "type": "text", "primaryKey": false, "notNull": false }, + "status": { "name": "status", "type": "status", "typeSchema": "public", "primaryKey": false, "notNull": true, "default": "'building'" }, + "deleted": { "name": "deleted", "type": "boolean", "primaryKey": false, "notNull": true, "default": false }, + "createdAt": { "name": "createdAt", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, + "updatedAt": { "name": "updatedAt", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + }, + "indexes": {}, + "foreignKeys": { + "project_userId_user_id_fk": { + "name": "project_userId_user_id_fk", + "tableFrom": "project", + "tableTo": "user", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { "name": "id", "type": "serial", "primaryKey": true, "notNull": true }, + "token": { "name": "token", "type": "text", "primaryKey": false, "notNull": true }, + "userId": { "name": "userId", "type": "integer", "primaryKey": false, "notNull": true }, + "expiresAt": { "name": "expiresAt", "type": "timestamp", "primaryKey": false, "notNull": true } + }, + "indexes": {}, + "foreignKeys": { + "session_userId_user_id_fk": { + "name": "session_userId_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.t1_review": { + "name": "t1_review", + "schema": "", + "columns": { + "id": { "name": "id", "type": "serial", "primaryKey": true, "notNull": true }, + "userId": { "name": "userId", "type": "integer", "primaryKey": false, "notNull": true }, + "projectId": { "name": "projectId", "type": "integer", "primaryKey": false, "notNull": true }, + "feedback": { "name": "feedback", "type": "text", "primaryKey": false, "notNull": false }, + "notes": { "name": "notes", "type": "text", "primaryKey": false, "notNull": false }, + "action": { "name": "action", "type": "t1_review_action", "typeSchema": "public", "primaryKey": false, "notNull": true }, + "timestamp": { "name": "timestamp", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + }, + "indexes": {}, + "foreignKeys": { + "t1_review_userId_user_id_fk": { + "name": "t1_review_userId_user_id_fk", + "tableFrom": "t1_review", + "tableTo": "user", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "t1_review_projectId_project_id_fk": { + "name": "t1_review_projectId_project_id_fk", + "tableFrom": "t1_review", + "tableTo": "project", + "columnsFrom": ["projectId"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { "name": "id", "type": "serial", "primaryKey": true, "notNull": true }, + "idvId": { "name": "idvId", "type": "text", "primaryKey": false, "notNull": true }, + "slackId": { "name": "slackId", "type": "text", "primaryKey": false, "notNull": true }, + "profilePicture": { "name": "profilePicture", "type": "text", "primaryKey": false, "notNull": true }, + "name": { "name": "name", "type": "text", "primaryKey": false, "notNull": true }, + "hackatimeTrust": { "name": "hackatimeTrust", "type": "hackatime_trust", "typeSchema": "public", "primaryKey": false, "notNull": true }, + "trust": { "name": "trust", "type": "trust", "typeSchema": "public", "primaryKey": false, "notNull": true, "default": "'blue'" }, + "clay": { "name": "clay", "type": "real", "primaryKey": false, "notNull": true, "default": 0 }, + "brick": { "name": "brick", "type": "real", "primaryKey": false, "notNull": true, "default": 0 }, + "shopScore": { "name": "shopScore", "type": "real", "primaryKey": false, "notNull": true, "default": 0 }, + "hasBasePrinter": { "name": "hasBasePrinter", "type": "boolean", "primaryKey": false, "notNull": true, "default": false }, + "hasT1Review": { "name": "hasT1Review", "type": "boolean", "primaryKey": false, "notNull": true, "default": false }, + "hasT2Review": { "name": "hasT2Review", "type": "boolean", "primaryKey": false, "notNull": true, "default": false }, + "hasAdmin": { "name": "hasAdmin", "type": "boolean", "primaryKey": false, "notNull": true, "default": false }, + "createdAt": { "name": "createdAt", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, + "lastLoginAt": { "name": "lastLoginAt", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_idvId_unique": { "name": "user_idvId_unique", "nullsNotDistinct": false, "columns": ["idvId"] }, + "user_slackId_unique": { "name": "user_slackId_unique", "nullsNotDistinct": false, "columns": ["slackId"] } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.hackatime_trust": { "name": "hackatime_trust", "schema": "public", "values": ["green", "blue", "yellow", "red"] }, + "public.project_audit_log_type": { "name": "project_audit_log_type", "schema": "public", "values": ["create", "update", "delete"] }, + "public.status": { "name": "status", "schema": "public", "values": ["building", "submitted", "t1_approved", "printing", "printed", "t2_approved", "finalized", "rejected", "rejected_locked"] }, + "public.t1_review_action": { "name": "t1_review_action", "schema": "public", "values": ["approve", "approve_no_print", "add_comment", "reject", "reject_lock"] }, + "public.trust": { "name": "trust", "schema": "public", "values": ["green", "blue", "yellow", "red"] } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { "columns": {}, "schemas": {}, "tables": {} } +} diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index c01da65..d37d529 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -29,6 +29,13 @@ "when": 1765151231107, "tag": "0003_confused_captain_marvel", "breakpoints": true + }, + { + "idx": 4, + "version": "7", + "when": 1765478400000, + "tag": "0004_ai_prechecks", + "breakpoints": true } ] } \ No newline at end of file diff --git a/src/lib/components/Devlog.svelte b/src/lib/components/Devlog.svelte index d704abf..742da20 100644 --- a/src/lib/components/Devlog.svelte +++ b/src/lib/components/Devlog.svelte @@ -2,14 +2,22 @@ // https://github.com/3daddict/stl-viewer/ and https://tonybox.net/posts/simple-stl-viewer/ for stl viewer code import relativeDate from 'tiny-relative-date'; - import { SquarePen, Trash } from '@lucide/svelte'; + import { AlertCircle, CheckCircle2, SquarePen, Trash } from '@lucide/svelte'; import * as THREE from 'three'; import { OBJLoader, STLLoader, ThreeMFLoader } from 'three/examples/jsm/Addons.js'; import { OrbitControls } from 'three/examples/jsm/Addons.js'; import { onMount } from 'svelte'; import { page } from '$app/state'; - let { devlog, projectId, showModifyButtons, allowDelete = true, projectName = null, user = null } = $props(); + let { + devlog, + projectId, + showModifyButtons, + allowDelete = true, + projectName = null, + user = null, + aiReview = null + } = $props(); // Necessary for camera/plane rotation let degree = Math.PI / 180; @@ -294,6 +302,17 @@ class="themed-box relative flex flex-col p-3 shadow-lg/20 transition-all" id={`devlog-${devlog.id}`} > + {#if aiReview} +
+ {#if aiReview.approved} + + AI ok + {:else} + + AI flagged + {/if} +
+ {/if}

{#if user} {/if} + {#if aiReview && !aiReview.approved && aiReview.rationale} +

+
+ +
{aiReview.rationale}
+
+
+ {/if} diff --git a/src/lib/server/ai/ensure.ts b/src/lib/server/ai/ensure.ts new file mode 100644 index 0000000..039616a --- /dev/null +++ b/src/lib/server/ai/ensure.ts @@ -0,0 +1,123 @@ +import { env } from '$env/dynamic/private'; +import { db } from '$lib/server/db/index.js'; +import { aiDevlogReview, aiProjectReview } from '$lib/server/db/schema.js'; +import type { Devlog, Project } from '$lib/server/db/schema.js'; +import { reviewDevlogs } from '$lib/server/ai/reviewer.js'; +import { eq, sql } from 'drizzle-orm'; + +type ProjectForReview = Pick; +type DevlogForReview = Pick & { + s3PublicUrl: string; +}; + +export async function ensureAiReviewsForProject({ + project, + devlogs +}: { + project: ProjectForReview; + devlogs: DevlogForReview[]; +}): Promise<{ + projectReview: typeof aiProjectReview.$inferSelect | null; + devlogReviews: typeof aiDevlogReview.$inferSelect[]; +}> { + if (devlogs.length === 0) { + return { projectReview: null, devlogReviews: [] }; + } + + const devlogIds = devlogs.map((log) => log.id); + + const existingDevlogReviews = await db + .select() + .from(aiDevlogReview) + .where(eq(aiDevlogReview.projectId, project.id)); + + // Build quick lookup + const existingMap = new Map(existingDevlogReviews.map((r) => [r.devlogId, r])); + + const [existingProjectReview] = await db + .select() + .from(aiProjectReview) + .where(eq(aiProjectReview.projectId, project.id)); + + if (!env.AI_API_KEY) { + return { + projectReview: existingProjectReview ?? null, + devlogReviews: existingDevlogReviews + }; + } + + const needsDevlogReview = devlogs.some((log) => { + const review = existingMap.get(log.id); + return !review || log.updatedAt > review.updatedAt; + }); + + const needsProjectReview = !existingProjectReview || project.updatedAt > existingProjectReview.updatedAt; + + if (!needsDevlogReview && !needsProjectReview) { + return { + projectReview: existingProjectReview ?? null, + devlogReviews: existingDevlogReviews + }; + } + + const { projectReview, devlogReviews } = await reviewDevlogs({ project, devlogs }); + + for (const review of devlogReviews) { + await db + .insert(aiDevlogReview) + .values({ + devlogId: review.devlogId, + projectId: review.projectId, + approved: review.approved, + rationale: review.rationale, + prompt: review.prompt, + model: review.model + }) + .onConflictDoUpdate({ + target: aiDevlogReview.devlogId, + set: { + projectId: review.projectId, + approved: review.approved, + rationale: review.rationale, + prompt: review.prompt, + model: review.model, + updatedAt: sql`now()` + } + }); + } + + await db + .insert(aiProjectReview) + .values({ + projectId: project.id, + overallApproved: projectReview.overallApproved, + summary: projectReview.summary, + prompt: projectReview.prompt, + model: projectReview.model + }) + .onConflictDoUpdate({ + target: aiProjectReview.projectId, + set: { + overallApproved: projectReview.overallApproved, + summary: projectReview.summary, + prompt: projectReview.prompt, + model: projectReview.model, + updatedAt: sql`now()` + } + }); + + const latestDevlogReviews = await db + .select() + .from(aiDevlogReview) + .where(eq(aiDevlogReview.projectId, project.id)); + + const [latestProjectReview] = await db + .select() + .from(aiProjectReview) + .where(eq(aiProjectReview.projectId, project.id)); + + return { + projectReview: latestProjectReview ?? null, + devlogReviews: latestDevlogReviews + }; +} diff --git a/src/lib/server/ai/reviewer.ts b/src/lib/server/ai/reviewer.ts new file mode 100644 index 0000000..c27cc3a --- /dev/null +++ b/src/lib/server/ai/reviewer.ts @@ -0,0 +1,141 @@ +import { env } from '$env/dynamic/private'; +import type { AiDevlogReview, AiProjectReview, Devlog, Project } from '$lib/server/db/schema.js'; + +const model = 'qwen/qwen3-vl-235b-a22b-instruct'; + +const systemPrompt = `You are a fabrication reviewer for Hack Club Construct. Keep feedback concise and professional. + +Important policy: +- Only check for overstated time (over-reporting). If the devlog likely took LESS time than claimed, do NOT flag it. +- Under-reporting (claiming fewer minutes than actual) is acceptable; do NOT penalize or mention it. +- Flag only when claimed minutes seem clearly more than reasonable for the pictured/modelled work and description. +`; + +type ReviewResponse = { + overallVerdict: 'approve' | 'flag'; + overallSummary: string; + devlogs: { + id: number; + approve: boolean; + explanation: string; + }[]; +}; + +type ReviewInput = { + project: Pick; + devlogs: (Pick & { + s3PublicUrl: string; + })[]; +}; + +async function callHackClubAI(message: string) { + if (!env.AI_API_KEY) { + throw new Error('AI_API_KEY is missing'); + } + + const res = await fetch('https://ai.hackclub.com/proxy/v1/chat/completions', { + method: 'POST', + headers: { + Authorization: `Bearer ${env.AI_API_KEY}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + model, + temperature: 0.25, + max_tokens: 800, + messages: [ + { role: 'system', content: systemPrompt }, + { role: 'user', content: message } + ] + }) + }); + + if (!res.ok) { + const text = await res.text(); + throw new Error(`AI request failed: ${res.status} ${text}`); + } + + const json = await res.json(); + const content: string | undefined = json?.choices?.[0]?.message?.content; + if (!content) { + throw new Error('AI response missing content'); + } + + return content.trim(); +} + +async function reviewProjectOverall(project: ReviewInput['project'], devlogs: ReviewInput['devlogs']) { + const totals = { + devlogCount: devlogs.length, + totalMinutes: devlogs.reduce((sum, l) => sum + (l.timeSpent ?? 0), 0), + avgPerLog: devlogs.length > 0 ? Math.round(devlogs.reduce((s, l) => s + (l.timeSpent ?? 0), 0) / devlogs.length) : 0 + }; + const devlogSummaries = devlogs.map((l) => ({ + id: l.id, + minutes: l.timeSpent, + createdAt: l.createdAt, + description: (l.description ?? '').slice(0, 220) + })); + const payload = { + project: { + id: project.id, + name: project.name, + description: project.description, + status: project.status + }, + totals, + devlogs: devlogSummaries + }; + const userPrompt = `Create a concise professional overview for moderator review. Include: 1) brief summary of the project intent; 2) time analysis (total, average, count); 3) highlights/risks from logs (only mention potential OVER-REPORTING, ignore under-reporting); 4) recommended next steps. Then decide if it's ready for approval. +Reply strict JSON: {"overallVerdict":"approve|flag","overview":string}. +${JSON.stringify(payload, null, 2)}`; + const rawContent = await callHackClubAI(userPrompt); + const sanitizedContent = rawContent.replace(/```json|```/g, '').trim(); + let parsed: { overallVerdict: 'approve' | 'flag'; overview: string }; + try { parsed = JSON.parse(sanitizedContent); } catch { throw new Error(`AI response was not JSON: ${rawContent}`); } + return { + overallApproved: parsed.overallVerdict === 'approve', + summary: parsed.overview, + prompt: `${systemPrompt}\n${userPrompt}`, + model + } satisfies Pick; +} + +async function reviewSingleDevlog(projectId: number, log: ReviewInput['devlogs'][number]) { + const payload = { + id: log.id, + description: log.description, + minutes: log.timeSpent, + imageUrl: `${log.s3PublicUrl}/${log.image}`, + modelUrl: log.model ? `${log.s3PublicUrl}/${log.model}` : null, + createdAt: log.createdAt + }; + const userPrompt = `Judge if claimed minutes are OVERSTATED for this single devlog. Approve unless minutes appear clearly higher than reasonable. Ignore possible under-reporting. +Reply strict JSON: {"id":number,"approve":boolean,"explanation":string}. +${JSON.stringify(payload, null, 2)}`; + const rawContent = await callHackClubAI(userPrompt); + const sanitizedContent = rawContent.replace(/```json|```/g, '').trim(); + let parsed: ReviewResponse['devlogs'][number]; + try { parsed = JSON.parse(sanitizedContent); } catch { throw new Error(`AI response was not JSON: ${rawContent}`); } + return { + devlogId: parsed.id, + projectId, + approved: parsed.approve, + rationale: parsed.explanation, + prompt: `${systemPrompt}\n${userPrompt}`, + model + } satisfies Pick; +} + +export async function reviewDevlogs({ project, devlogs }: ReviewInput): Promise<{ + projectReview: Pick; + devlogReviews: Pick[]; +}> { + const projectReview = await reviewProjectOverall(project, devlogs); + const devlogReviews: Pick[] = []; + for (const log of devlogs) { + const r = await reviewSingleDevlog(project.id, log); + devlogReviews.push(r); + } + return { projectReview, devlogReviews }; +} diff --git a/src/lib/server/db/schema.ts b/src/lib/server/db/schema.ts index 43e44e0..eef05cd 100644 --- a/src/lib/server/db/schema.ts +++ b/src/lib/server/db/schema.ts @@ -143,9 +143,43 @@ export const devlog = pgTable('devlog', { updatedAt: timestamp().notNull().defaultNow() }); +export const aiDevlogReview = pgTable('ai_devlog_review', { + id: serial().primaryKey(), + devlogId: integer() + .notNull() + .references(() => devlog.id) + .unique(), + projectId: integer() + .notNull() + .references(() => project.id), + approved: boolean().notNull(), + rationale: text().notNull(), + prompt: text().notNull(), + model: text().notNull(), + createdAt: timestamp().notNull().defaultNow(), + updatedAt: timestamp().notNull().defaultNow() +}); + +export const aiProjectReview = pgTable('ai_project_review', { + id: serial().primaryKey(), + projectId: integer() + .notNull() + .references(() => project.id) + .unique(), + overallApproved: boolean().notNull(), + summary: text().notNull(), + prompt: text().notNull(), + model: text().notNull(), + createdAt: timestamp().notNull().defaultNow(), + updatedAt: timestamp().notNull().defaultNow() +}); + export type Session = typeof session.$inferSelect; export type User = typeof user.$inferSelect; export type Project = typeof project.$inferSelect; +export type Devlog = typeof devlog.$inferSelect; export type T1Review = typeof t1Review.$inferSelect; +export type AiDevlogReview = typeof aiDevlogReview.$inferSelect; +export type AiProjectReview = typeof aiProjectReview.$inferSelect; // export type T2Review = typeof t2Review.$inferSelect; diff --git a/src/routes/dashboard/admin/review/[id]/+page.server.ts b/src/routes/dashboard/admin/review/[id]/+page.server.ts index bec1696..2a62cd0 100644 --- a/src/routes/dashboard/admin/review/[id]/+page.server.ts +++ b/src/routes/dashboard/admin/review/[id]/+page.server.ts @@ -1,9 +1,20 @@ +import { env } from '$env/dynamic/private'; import { db } from '$lib/server/db/index.js'; -import { project, user, devlog, t1Review } from '$lib/server/db/schema.js'; +import { aiDevlogReview, aiProjectReview, devlog, project, t1Review, user } from '$lib/server/db/schema.js'; import { error, redirect } from '@sveltejs/kit'; -import { eq, and, asc, sql } from 'drizzle-orm'; +import { and, asc, desc, eq, sql } from 'drizzle-orm'; import type { Actions } from './$types'; import { sendSlackDM } from '$lib/server/slack.js'; +import { ensureAiReviewsForProject } from '$lib/server/ai/ensure.js'; + +const REVIEWABLE_STATUSES = new Set([ + 'submitted', + 't1_approved', + 'printing', + 'printed', + 't2_approved', + 'finalized' +]); export async function load({ locals, params }) { if (!locals.user) { @@ -63,7 +74,55 @@ export async function load({ locals, params }) { .select() .from(devlog) .where(and(eq(devlog.projectId, queriedProject.project.id), eq(devlog.deleted, false))) - .orderBy(asc(devlog.createdAt)); + .orderBy(desc(devlog.createdAt)); + + const devlogIds = devlogs.map((log) => log.id); + + let latestDevlogReviews: typeof aiDevlogReview.$inferSelect[] = []; + let latestProjectReview: typeof aiProjectReview.$inferSelect | undefined; + + try { + latestDevlogReviews = await db + .select() + .from(aiDevlogReview) + .where(eq(aiDevlogReview.projectId, queriedProject.project.id)); + + // filter reviews to only those matching current devlogs + const devlogIdSet = new Set(devlogIds); + latestDevlogReviews = latestDevlogReviews.filter((r) => devlogIdSet.has(r.devlogId)); + + [latestProjectReview] = await db + .select() + .from(aiProjectReview) + .where(eq(aiProjectReview.projectId, queriedProject.project.id)); + } catch (err) { + console.error('AI review read failed', err); + latestDevlogReviews = []; + latestProjectReview = undefined; + } + + if (REVIEWABLE_STATUSES.has(queriedProject.project.status)) { + try { + const ensured = await ensureAiReviewsForProject({ + project: { + id: queriedProject.project.id, + name: queriedProject.project.name, + description: queriedProject.project.description, + status: queriedProject.project.status, + updatedAt: queriedProject.project.updatedAt + }, + devlogs: devlogs.map((log) => ({ + ...log, + s3PublicUrl: env.S3_PUBLIC_URL ?? '' + })) + }); + + latestDevlogReviews = ensured.devlogReviews; + latestProjectReview = ensured.projectReview ?? latestProjectReview; + } catch (err) { + console.error('AI review ensure failed', err); + } + } const t1Reviews = await db .select({ @@ -85,7 +144,11 @@ export async function load({ locals, params }) { return { project: queriedProject, devlogs, - t1Reviews + t1Reviews, + ai: { + projectReview: latestProjectReview ?? null, + devlogReviews: latestDevlogReviews + } }; } diff --git a/src/routes/dashboard/admin/review/[id]/+page.svelte b/src/routes/dashboard/admin/review/[id]/+page.svelte index 79075c8..fe11ccf 100644 --- a/src/routes/dashboard/admin/review/[id]/+page.svelte +++ b/src/routes/dashboard/admin/review/[id]/+page.svelte @@ -2,13 +2,21 @@ import relativeDate from 'tiny-relative-date'; import Devlog from '$lib/components/Devlog.svelte'; import Head from '$lib/components/Head.svelte'; - import { ExternalLink } from '@lucide/svelte'; + import { AlertCircle, CheckCircle2, ExternalLink } from '@lucide/svelte'; import { enhance } from '$app/forms'; import { projectStatuses } from '$lib/utils.js'; let { data } = $props(); let formPending = $state(false); + + const aiDevlogMap = $derived( + Object.fromEntries((data.ai?.devlogReviews ?? []).map((review) => [review.devlogId, review])) + ); + + const totalDevlogs = $derived((data.ai?.devlogReviews ?? []).length); + const totalApproved = $derived((data.ai?.devlogReviews ?? []).filter((r) => r.approved).length); + const totalFlagged = $derived(totalDevlogs - totalApproved); @@ -18,6 +26,42 @@

{data.project.project.name}

+ {#if data.ai?.projectReview} +
+ {#if data.ai.projectReview.overallApproved} + +
+

AI pre-review: approveable

+

{data.ai.projectReview.summary}

+ + {#if totalDevlogs > 0} +

+ {totalApproved === totalDevlogs + ? 'All devlogs approved by AI.' + : `${totalApproved}/${totalDevlogs} devlogs approved by AI`} +

+ {/if} +
+ {:else} + +
+

AI pre-review: needs attention

+

{data.ai.projectReview.summary}

+ + {#if totalDevlogs > 0} +

+ {totalFlagged === 0 + ? 'All devlogs approved by AI.' + : `${totalFlagged} devlog${totalFlagged === 1 ? '' : 's'} flagged by AI`} +

+ {/if} +
+ {/if} +
+ {/if} + + +

Project details

@@ -125,7 +169,12 @@

Journal logs

{#each data.devlogs as devlog} - + {/each}
diff --git a/src/routes/dashboard/projects/[id]/ship/+page.server.ts b/src/routes/dashboard/projects/[id]/ship/+page.server.ts index d80b1ab..c2c9485 100644 --- a/src/routes/dashboard/projects/[id]/ship/+page.server.ts +++ b/src/routes/dashboard/projects/[id]/ship/+page.server.ts @@ -1,9 +1,11 @@ +import { env } from '$env/dynamic/private'; import { db } from '$lib/server/db/index.js'; import { devlog, project, user } from '$lib/server/db/schema.js'; import { error, redirect } from '@sveltejs/kit'; import { eq, and, or, sql } from 'drizzle-orm'; import type { Actions } from './$types'; import { sendSlackDM } from '$lib/server/slack.js'; +import { ensureAiReviewsForProject } from '$lib/server/ai/ensure.js'; export async function load({ params, locals }) { const id: number = parseInt(params.id); @@ -95,6 +97,12 @@ export const actions = { return error(400, { message: 'project must have a description and Printables url' }); } + const projectDevlogs = await db + .select() + .from(devlog) + .where(and(eq(devlog.projectId, queriedProject.id), eq(devlog.deleted, false))) + .orderBy(devlog.createdAt); + await db .update(project) .set({ @@ -113,6 +121,23 @@ export const actions = { `Hiya :wave: Your project has been shipped and is now under review. We'll take a look and get back to you soon! :rocket:` ); + if (projectDevlogs.length > 0) { + // Fire-and-forget AI pre-review so moderators have context ready. + ensureAiReviewsForProject({ + project: { + id: queriedProject.id, + name: queriedProject.name, + description: queriedProject.description, + status: 'submitted', + updatedAt: new Date() + }, + devlogs: projectDevlogs.map((log) => ({ + ...log, + s3PublicUrl: env.S3_PUBLIC_URL ?? '' + })) + }).catch((err) => console.error('AI pre-review failed', err)); + } + return redirect(303, '/dashboard/projects'); } } satisfies Actions; From 8d1e5490fa1db00bc6523aadef7aefb848ac4ac7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Balogh=20Barnab=C3=A1s?= Date: Sun, 14 Dec 2025 15:04:43 +0100 Subject: [PATCH 2/6] Fixed drizzle --- README.md | 2 +- drizzle/0004_ai_prechecks.sql | 32 -- drizzle/0004_milky_iron_patriot.sql | 28 ++ drizzle/meta/0004_snapshot.json | 633 ++++++++++++++++++++++++---- drizzle/meta/_journal.json | 4 +- 5 files changed, 571 insertions(+), 128 deletions(-) delete mode 100644 drizzle/0004_ai_prechecks.sql create mode 100644 drizzle/0004_milky_iron_patriot.sql diff --git a/README.md b/README.md index ce79478..dd47dd9 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ npm run db:studio ### AI prereview (devlogs) - The review panel now shows an AI pre-check on shipped projects’ devlogs. It only flags obvious over-reporting of time. -- Set `AI_API_KEY` to your Hack Club AI key (it uses the https://ai.hackclub.com proxy under the hood). No key = no AI hints in review. +- Set `AI_API_KEY` to your Hack Club AI API key. No key = no AI hints in review. ## Deploying diff --git a/drizzle/0004_ai_prechecks.sql b/drizzle/0004_ai_prechecks.sql deleted file mode 100644 index 6575a50..0000000 --- a/drizzle/0004_ai_prechecks.sql +++ /dev/null @@ -1,32 +0,0 @@ -CREATE TABLE "ai_devlog_review" ( - "id" serial PRIMARY KEY NOT NULL, - "devlogId" integer NOT NULL, - "projectId" integer NOT NULL, - "approved" boolean NOT NULL, - "rationale" text NOT NULL, - "prompt" text NOT NULL, - "model" text NOT NULL, - "createdAt" timestamp DEFAULT now() NOT NULL, - "updatedAt" timestamp DEFAULT now() NOT NULL, - CONSTRAINT "ai_devlog_review_devlogId_unique" UNIQUE ("devlogId") -); ---> statement-breakpoint - -ALTER TABLE "ai_devlog_review" ADD CONSTRAINT "ai_devlog_review_devlogId_devlog_id_fk" FOREIGN KEY ("devlogId") REFERENCES "devlog"("id") ON DELETE no action ON UPDATE no action; ---> statement-breakpoint -ALTER TABLE "ai_devlog_review" ADD CONSTRAINT "ai_devlog_review_projectId_project_id_fk" FOREIGN KEY ("projectId") REFERENCES "project"("id") ON DELETE no action ON UPDATE no action; ---> statement-breakpoint - -CREATE TABLE "ai_project_review" ( - "id" serial PRIMARY KEY NOT NULL, - "projectId" integer NOT NULL, - "overallApproved" boolean NOT NULL, - "summary" text NOT NULL, - "prompt" text NOT NULL, - "model" text NOT NULL, - "createdAt" timestamp DEFAULT now() NOT NULL, - "updatedAt" timestamp DEFAULT now() NOT NULL, - CONSTRAINT "ai_project_review_projectId_unique" UNIQUE ("projectId") -); ---> statement-breakpoint -ALTER TABLE "ai_project_review" ADD CONSTRAINT "ai_project_review_projectId_project_id_fk" FOREIGN KEY ("projectId") REFERENCES "project"("id") ON DELETE no action ON UPDATE no action; diff --git a/drizzle/0004_milky_iron_patriot.sql b/drizzle/0004_milky_iron_patriot.sql new file mode 100644 index 0000000..fde18cc --- /dev/null +++ b/drizzle/0004_milky_iron_patriot.sql @@ -0,0 +1,28 @@ +CREATE TABLE "ai_devlog_review" ( + "id" serial PRIMARY KEY NOT NULL, + "devlogId" integer NOT NULL, + "projectId" integer NOT NULL, + "approved" boolean NOT NULL, + "rationale" text NOT NULL, + "prompt" text NOT NULL, + "model" text NOT NULL, + "createdAt" timestamp DEFAULT now() NOT NULL, + "updatedAt" timestamp DEFAULT now() NOT NULL, + CONSTRAINT "ai_devlog_review_devlogId_unique" UNIQUE("devlogId") +); +--> statement-breakpoint +CREATE TABLE "ai_project_review" ( + "id" serial PRIMARY KEY NOT NULL, + "projectId" integer NOT NULL, + "overallApproved" boolean NOT NULL, + "summary" text NOT NULL, + "prompt" text NOT NULL, + "model" text NOT NULL, + "createdAt" timestamp DEFAULT now() NOT NULL, + "updatedAt" timestamp DEFAULT now() NOT NULL, + CONSTRAINT "ai_project_review_projectId_unique" UNIQUE("projectId") +); +--> statement-breakpoint +ALTER TABLE "ai_devlog_review" ADD CONSTRAINT "ai_devlog_review_devlogId_devlog_id_fk" FOREIGN KEY ("devlogId") REFERENCES "public"."devlog"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "ai_devlog_review" ADD CONSTRAINT "ai_devlog_review_projectId_project_id_fk" FOREIGN KEY ("projectId") REFERENCES "public"."project"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "ai_project_review" ADD CONSTRAINT "ai_project_review_projectId_project_id_fk" FOREIGN KEY ("projectId") REFERENCES "public"."project"("id") ON DELETE no action ON UPDATE no action; \ No newline at end of file diff --git a/drizzle/meta/0004_snapshot.json b/drizzle/meta/0004_snapshot.json index 674cb20..615918c 100644 --- a/drizzle/meta/0004_snapshot.json +++ b/drizzle/meta/0004_snapshot.json @@ -1,5 +1,5 @@ { - "id": "5c6f3ce1-9e64-4e8c-93ab-9fd1d3ae1b70", + "id": "d2bdeceb-7436-4ee1-8240-2ef7ad884aa2", "prevId": "dd1faf34-f215-4e5a-95a9-66c21e151248", "version": "7", "dialect": "postgresql", @@ -8,15 +8,62 @@ "name": "ai_devlog_review", "schema": "", "columns": { - "id": { "name": "id", "type": "serial", "primaryKey": true, "notNull": true }, - "devlogId": { "name": "devlogId", "type": "integer", "primaryKey": false, "notNull": true }, - "projectId": { "name": "projectId", "type": "integer", "primaryKey": false, "notNull": true }, - "approved": { "name": "approved", "type": "boolean", "primaryKey": false, "notNull": true }, - "rationale": { "name": "rationale", "type": "text", "primaryKey": false, "notNull": true }, - "prompt": { "name": "prompt", "type": "text", "primaryKey": false, "notNull": true }, - "model": { "name": "model", "type": "text", "primaryKey": false, "notNull": true }, - "createdAt": { "name": "createdAt", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "updatedAt": { "name": "updatedAt", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "devlogId": { + "name": "devlogId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "approved": { + "name": "approved", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "rationale": { + "name": "rationale", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prompt": { + "name": "prompt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, "foreignKeys": { @@ -24,8 +71,12 @@ "name": "ai_devlog_review_devlogId_devlog_id_fk", "tableFrom": "ai_devlog_review", "tableTo": "devlog", - "columnsFrom": ["devlogId"], - "columnsTo": ["id"], + "columnsFrom": [ + "devlogId" + ], + "columnsTo": [ + "id" + ], "onDelete": "no action", "onUpdate": "no action" }, @@ -33,8 +84,12 @@ "name": "ai_devlog_review_projectId_project_id_fk", "tableFrom": "ai_devlog_review", "tableTo": "project", - "columnsFrom": ["projectId"], - "columnsTo": ["id"], + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], "onDelete": "no action", "onUpdate": "no action" } @@ -44,7 +99,9 @@ "ai_devlog_review_devlogId_unique": { "name": "ai_devlog_review_devlogId_unique", "nullsNotDistinct": false, - "columns": ["devlogId"] + "columns": [ + "devlogId" + ] } }, "policies": {}, @@ -55,14 +112,56 @@ "name": "ai_project_review", "schema": "", "columns": { - "id": { "name": "id", "type": "serial", "primaryKey": true, "notNull": true }, - "projectId": { "name": "projectId", "type": "integer", "primaryKey": false, "notNull": true }, - "overallApproved": { "name": "overallApproved", "type": "boolean", "primaryKey": false, "notNull": true }, - "summary": { "name": "summary", "type": "text", "primaryKey": false, "notNull": true }, - "prompt": { "name": "prompt", "type": "text", "primaryKey": false, "notNull": true }, - "model": { "name": "model", "type": "text", "primaryKey": false, "notNull": true }, - "createdAt": { "name": "createdAt", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "updatedAt": { "name": "updatedAt", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "overallApproved": { + "name": "overallApproved", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prompt": { + "name": "prompt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, "foreignKeys": { @@ -70,8 +169,12 @@ "name": "ai_project_review_projectId_project_id_fk", "tableFrom": "ai_project_review", "tableTo": "project", - "columnsFrom": ["projectId"], - "columnsTo": ["id"], + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], "onDelete": "no action", "onUpdate": "no action" } @@ -81,7 +184,9 @@ "ai_project_review_projectId_unique": { "name": "ai_project_review_projectId_unique", "nullsNotDistinct": false, - "columns": ["projectId"] + "columns": [ + "projectId" + ] } }, "policies": {}, @@ -92,16 +197,69 @@ "name": "devlog", "schema": "", "columns": { - "id": { "name": "id", "type": "serial", "primaryKey": true, "notNull": true }, - "userId": { "name": "userId", "type": "integer", "primaryKey": false, "notNull": false }, - "projectId": { "name": "projectId", "type": "integer", "primaryKey": false, "notNull": true }, - "description": { "name": "description", "type": "text", "primaryKey": false, "notNull": true }, - "timeSpent": { "name": "timeSpent", "type": "integer", "primaryKey": false, "notNull": true }, - "image": { "name": "image", "type": "text", "primaryKey": false, "notNull": true }, - "model": { "name": "model", "type": "text", "primaryKey": false, "notNull": true }, - "deleted": { "name": "deleted", "type": "boolean", "primaryKey": false, "notNull": true, "default": false }, - "createdAt": { "name": "createdAt", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "updatedAt": { "name": "updatedAt", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timeSpent": { + "name": "timeSpent", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, "foreignKeys": { @@ -109,8 +267,12 @@ "name": "devlog_userId_user_id_fk", "tableFrom": "devlog", "tableTo": "user", - "columnsFrom": ["userId"], - "columnsTo": ["id"], + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], "onDelete": "no action", "onUpdate": "no action" }, @@ -118,8 +280,12 @@ "name": "devlog_projectId_project_id_fk", "tableFrom": "devlog", "tableTo": "project", - "columnsFrom": ["projectId"], - "columnsTo": ["id"], + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], "onDelete": "no action", "onUpdate": "no action" } @@ -134,15 +300,65 @@ "name": "project", "schema": "", "columns": { - "id": { "name": "id", "type": "serial", "primaryKey": true, "notNull": true }, - "userId": { "name": "userId", "type": "integer", "primaryKey": false, "notNull": true }, - "name": { "name": "name", "type": "text", "primaryKey": false, "notNull": false }, - "description": { "name": "description", "type": "text", "primaryKey": false, "notNull": false }, - "url": { "name": "url", "type": "text", "primaryKey": false, "notNull": false }, - "status": { "name": "status", "type": "status", "typeSchema": "public", "primaryKey": false, "notNull": true, "default": "'building'" }, - "deleted": { "name": "deleted", "type": "boolean", "primaryKey": false, "notNull": true, "default": false }, - "createdAt": { "name": "createdAt", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "updatedAt": { "name": "updatedAt", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'building'" + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, "foreignKeys": { @@ -150,8 +366,12 @@ "name": "project_userId_user_id_fk", "tableFrom": "project", "tableTo": "user", - "columnsFrom": ["userId"], - "columnsTo": ["id"], + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], "onDelete": "no action", "onUpdate": "no action" } @@ -166,10 +386,30 @@ "name": "session", "schema": "", "columns": { - "id": { "name": "id", "type": "serial", "primaryKey": true, "notNull": true }, - "token": { "name": "token", "type": "text", "primaryKey": false, "notNull": true }, - "userId": { "name": "userId", "type": "integer", "primaryKey": false, "notNull": true }, - "expiresAt": { "name": "expiresAt", "type": "timestamp", "primaryKey": false, "notNull": true } + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } }, "indexes": {}, "foreignKeys": { @@ -177,8 +417,12 @@ "name": "session_userId_user_id_fk", "tableFrom": "session", "tableTo": "user", - "columnsFrom": ["userId"], - "columnsTo": ["id"], + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], "onDelete": "no action", "onUpdate": "no action" } @@ -193,13 +437,50 @@ "name": "t1_review", "schema": "", "columns": { - "id": { "name": "id", "type": "serial", "primaryKey": true, "notNull": true }, - "userId": { "name": "userId", "type": "integer", "primaryKey": false, "notNull": true }, - "projectId": { "name": "projectId", "type": "integer", "primaryKey": false, "notNull": true }, - "feedback": { "name": "feedback", "type": "text", "primaryKey": false, "notNull": false }, - "notes": { "name": "notes", "type": "text", "primaryKey": false, "notNull": false }, - "action": { "name": "action", "type": "t1_review_action", "typeSchema": "public", "primaryKey": false, "notNull": true }, - "timestamp": { "name": "timestamp", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "feedback": { + "name": "feedback", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "t1_review_action", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, "foreignKeys": { @@ -207,8 +488,12 @@ "name": "t1_review_userId_user_id_fk", "tableFrom": "t1_review", "tableTo": "user", - "columnsFrom": ["userId"], - "columnsTo": ["id"], + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], "onDelete": "no action", "onUpdate": "no action" }, @@ -216,8 +501,12 @@ "name": "t1_review_projectId_project_id_fk", "tableFrom": "t1_review", "tableTo": "project", - "columnsFrom": ["projectId"], - "columnsTo": ["id"], + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], "onDelete": "no action", "onUpdate": "no action" } @@ -232,29 +521,133 @@ "name": "user", "schema": "", "columns": { - "id": { "name": "id", "type": "serial", "primaryKey": true, "notNull": true }, - "idvId": { "name": "idvId", "type": "text", "primaryKey": false, "notNull": true }, - "slackId": { "name": "slackId", "type": "text", "primaryKey": false, "notNull": true }, - "profilePicture": { "name": "profilePicture", "type": "text", "primaryKey": false, "notNull": true }, - "name": { "name": "name", "type": "text", "primaryKey": false, "notNull": true }, - "hackatimeTrust": { "name": "hackatimeTrust", "type": "hackatime_trust", "typeSchema": "public", "primaryKey": false, "notNull": true }, - "trust": { "name": "trust", "type": "trust", "typeSchema": "public", "primaryKey": false, "notNull": true, "default": "'blue'" }, - "clay": { "name": "clay", "type": "real", "primaryKey": false, "notNull": true, "default": 0 }, - "brick": { "name": "brick", "type": "real", "primaryKey": false, "notNull": true, "default": 0 }, - "shopScore": { "name": "shopScore", "type": "real", "primaryKey": false, "notNull": true, "default": 0 }, - "hasBasePrinter": { "name": "hasBasePrinter", "type": "boolean", "primaryKey": false, "notNull": true, "default": false }, - "hasT1Review": { "name": "hasT1Review", "type": "boolean", "primaryKey": false, "notNull": true, "default": false }, - "hasT2Review": { "name": "hasT2Review", "type": "boolean", "primaryKey": false, "notNull": true, "default": false }, - "hasAdmin": { "name": "hasAdmin", "type": "boolean", "primaryKey": false, "notNull": true, "default": false }, - "createdAt": { "name": "createdAt", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "lastLoginAt": { "name": "lastLoginAt", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "idvId": { + "name": "idvId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "profilePicture": { + "name": "profilePicture", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hackatimeTrust": { + "name": "hackatimeTrust", + "type": "hackatime_trust", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "trust": { + "name": "trust", + "type": "trust", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'blue'" + }, + "clay": { + "name": "clay", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "brick": { + "name": "brick", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "shopScore": { + "name": "shopScore", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "hasBasePrinter": { + "name": "hasBasePrinter", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "hasT1Review": { + "name": "hasT1Review", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "hasT2Review": { + "name": "hasT2Review", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "hasAdmin": { + "name": "hasAdmin", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "lastLoginAt": { + "name": "lastLoginAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, "foreignKeys": {}, "compositePrimaryKeys": {}, "uniqueConstraints": { - "user_idvId_unique": { "name": "user_idvId_unique", "nullsNotDistinct": false, "columns": ["idvId"] }, - "user_slackId_unique": { "name": "user_slackId_unique", "nullsNotDistinct": false, "columns": ["slackId"] } + "user_idvId_unique": { + "name": "user_idvId_unique", + "nullsNotDistinct": false, + "columns": [ + "idvId" + ] + }, + "user_slackId_unique": { + "name": "user_slackId_unique", + "nullsNotDistinct": false, + "columns": [ + "slackId" + ] + } }, "policies": {}, "checkConstraints": {}, @@ -262,16 +655,70 @@ } }, "enums": { - "public.hackatime_trust": { "name": "hackatime_trust", "schema": "public", "values": ["green", "blue", "yellow", "red"] }, - "public.project_audit_log_type": { "name": "project_audit_log_type", "schema": "public", "values": ["create", "update", "delete"] }, - "public.status": { "name": "status", "schema": "public", "values": ["building", "submitted", "t1_approved", "printing", "printed", "t2_approved", "finalized", "rejected", "rejected_locked"] }, - "public.t1_review_action": { "name": "t1_review_action", "schema": "public", "values": ["approve", "approve_no_print", "add_comment", "reject", "reject_lock"] }, - "public.trust": { "name": "trust", "schema": "public", "values": ["green", "blue", "yellow", "red"] } + "public.hackatime_trust": { + "name": "hackatime_trust", + "schema": "public", + "values": [ + "green", + "blue", + "yellow", + "red" + ] + }, + "public.project_audit_log_type": { + "name": "project_audit_log_type", + "schema": "public", + "values": [ + "create", + "update", + "delete" + ] + }, + "public.status": { + "name": "status", + "schema": "public", + "values": [ + "building", + "submitted", + "t1_approved", + "printing", + "printed", + "t2_approved", + "finalized", + "rejected", + "rejected_locked" + ] + }, + "public.t1_review_action": { + "name": "t1_review_action", + "schema": "public", + "values": [ + "approve", + "approve_no_print", + "add_comment", + "reject", + "reject_lock" + ] + }, + "public.trust": { + "name": "trust", + "schema": "public", + "values": [ + "green", + "blue", + "yellow", + "red" + ] + } }, "schemas": {}, "sequences": {}, "roles": {}, "policies": {}, "views": {}, - "_meta": { "columns": {}, "schemas": {}, "tables": {} } -} + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index d37d529..2ebc2d8 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -33,8 +33,8 @@ { "idx": 4, "version": "7", - "when": 1765478400000, - "tag": "0004_ai_prechecks", + "when": 1765721014407, + "tag": "0004_milky_iron_patriot", "breakpoints": true } ] From 72f06850a363027c20dd05947b0c3b679be24c47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Balogh=20Barnab=C3=A1s?= Date: Sun, 14 Dec 2025 15:16:07 +0100 Subject: [PATCH 3/6] Cleaned up merge conflicts and fixes --- drizzle/0004_huge_clea.sql | 1 + drizzle/0004_milky_iron_patriot.sql | 4 +- drizzle/0005_good_omega_flight.sql | 2 + drizzle/0006_omniscient_moondragon.sql | 2 + drizzle/0007_early_morph.sql | 4 + drizzle/0008_tiny_princess_powerful.sql | 1 + drizzle/0009_clean_supreme_intelligence.sql | 14 + drizzle/meta/0004_snapshot.json | 198 +---- drizzle/meta/0005_snapshot.json | 561 ++++++++++++++ drizzle/meta/0006_snapshot.json | 561 ++++++++++++++ drizzle/meta/0007_snapshot.json | 588 +++++++++++++++ drizzle/meta/0008_snapshot.json | 594 +++++++++++++++ drizzle/meta/0009_snapshot.json | 690 ++++++++++++++++++ drizzle/meta/_journal.json | 53 +- src/lib/server/ai/ensure.ts | 77 +- src/lib/server/ai/reviewer.ts | 10 +- .../admin/review/[id]/+page.server.ts | 10 +- .../dashboard/admin/review/[id]/+page.svelte | 1 - .../projects/[id]/ship/+page.server.ts | 4 +- 19 files changed, 3125 insertions(+), 250 deletions(-) create mode 100644 drizzle/0004_huge_clea.sql create mode 100644 drizzle/0005_good_omega_flight.sql create mode 100644 drizzle/0006_omniscient_moondragon.sql create mode 100644 drizzle/0007_early_morph.sql create mode 100644 drizzle/0008_tiny_princess_powerful.sql create mode 100644 drizzle/0009_clean_supreme_intelligence.sql create mode 100644 drizzle/meta/0005_snapshot.json create mode 100644 drizzle/meta/0006_snapshot.json create mode 100644 drizzle/meta/0007_snapshot.json create mode 100644 drizzle/meta/0008_snapshot.json create mode 100644 drizzle/meta/0009_snapshot.json diff --git a/drizzle/0004_huge_clea.sql b/drizzle/0004_huge_clea.sql new file mode 100644 index 0000000..4cae8f7 --- /dev/null +++ b/drizzle/0004_huge_clea.sql @@ -0,0 +1 @@ +ALTER TABLE "user" ADD COLUMN "isPrinter" boolean DEFAULT false NOT NULL; \ No newline at end of file diff --git a/drizzle/0004_milky_iron_patriot.sql b/drizzle/0004_milky_iron_patriot.sql index fde18cc..839d1ab 100644 --- a/drizzle/0004_milky_iron_patriot.sql +++ b/drizzle/0004_milky_iron_patriot.sql @@ -23,6 +23,6 @@ CREATE TABLE "ai_project_review" ( CONSTRAINT "ai_project_review_projectId_unique" UNIQUE("projectId") ); --> statement-breakpoint -ALTER TABLE "ai_devlog_review" ADD CONSTRAINT "ai_devlog_review_devlogId_devlog_id_fk" FOREIGN KEY ("devlogId") REFERENCES "public"."devlog"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "ai_devlog_review" ADD CONSTRAINT "ai_devlog_review_projectId_project_id_fk" FOREIGN KEY ("projectId") REFERENCES "public"."project"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "ai_devlog_review" ADD CONSTRAINT "ai_devlog_review_devlogId_devlog_id_fk" FOREIGN KEY ("devlogId") REFERENCES "public"."devlog"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "ai_devlog_review" ADD CONSTRAINT "ai_devlog_review_projectId_project_id_fk" FOREIGN KEY ("projectId") REFERENCES "public"."project"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint ALTER TABLE "ai_project_review" ADD CONSTRAINT "ai_project_review_projectId_project_id_fk" FOREIGN KEY ("projectId") REFERENCES "public"."project"("id") ON DELETE no action ON UPDATE no action; \ No newline at end of file diff --git a/drizzle/0005_good_omega_flight.sql b/drizzle/0005_good_omega_flight.sql new file mode 100644 index 0000000..d3307bb --- /dev/null +++ b/drizzle/0005_good_omega_flight.sql @@ -0,0 +1,2 @@ +ALTER TABLE "project" ADD COLUMN "printedBy" serial NOT NULL;--> statement-breakpoint +ALTER TABLE "project" ADD CONSTRAINT "project_printedBy_user_id_fk" FOREIGN KEY ("printedBy") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action; \ No newline at end of file diff --git a/drizzle/0006_omniscient_moondragon.sql b/drizzle/0006_omniscient_moondragon.sql new file mode 100644 index 0000000..c960427 --- /dev/null +++ b/drizzle/0006_omniscient_moondragon.sql @@ -0,0 +1,2 @@ +ALTER TABLE "project" ALTER COLUMN "printedBy" SET DATA TYPE integer;--> statement-breakpoint +ALTER TABLE "project" ALTER COLUMN "printedBy" DROP NOT NULL; \ No newline at end of file diff --git a/drizzle/0007_early_morph.sql b/drizzle/0007_early_morph.sql new file mode 100644 index 0000000..9603d3c --- /dev/null +++ b/drizzle/0007_early_morph.sql @@ -0,0 +1,4 @@ +CREATE TYPE "public"."editor_file_type" AS ENUM('url', 'upload');--> statement-breakpoint +ALTER TABLE "project" ADD COLUMN "editorFileType" "editor_file_type";--> statement-breakpoint +ALTER TABLE "project" ADD COLUMN "editorUrl" text;--> statement-breakpoint +ALTER TABLE "project" ADD COLUMN "uploadedFileUrl" text; \ No newline at end of file diff --git a/drizzle/0008_tiny_princess_powerful.sql b/drizzle/0008_tiny_princess_powerful.sql new file mode 100644 index 0000000..92ba72d --- /dev/null +++ b/drizzle/0008_tiny_princess_powerful.sql @@ -0,0 +1 @@ +ALTER TABLE "project" ADD COLUMN "modelFile" text; \ No newline at end of file diff --git a/drizzle/0009_clean_supreme_intelligence.sql b/drizzle/0009_clean_supreme_intelligence.sql new file mode 100644 index 0000000..531d873 --- /dev/null +++ b/drizzle/0009_clean_supreme_intelligence.sql @@ -0,0 +1,14 @@ +CREATE TABLE "ship" ( + "id" serial PRIMARY KEY NOT NULL, + "userId" integer NOT NULL, + "projectId" integer NOT NULL, + "url" text NOT NULL, + "editorFileType" "editor_file_type" NOT NULL, + "editorUrl" text, + "uploadedFileUrl" text, + "modelFile" text NOT NULL, + "timestamp" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +ALTER TABLE "ship" ADD CONSTRAINT "ship_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "ship" ADD CONSTRAINT "ship_projectId_project_id_fk" FOREIGN KEY ("projectId") REFERENCES "public"."project"("id") ON DELETE no action ON UPDATE no action; \ No newline at end of file diff --git a/drizzle/meta/0004_snapshot.json b/drizzle/meta/0004_snapshot.json index 615918c..61660a2 100644 --- a/drizzle/meta/0004_snapshot.json +++ b/drizzle/meta/0004_snapshot.json @@ -1,198 +1,9 @@ { - "id": "d2bdeceb-7436-4ee1-8240-2ef7ad884aa2", + "id": "3da61c80-84ad-466d-a2f9-f0a5bfbbe420", "prevId": "dd1faf34-f215-4e5a-95a9-66c21e151248", "version": "7", "dialect": "postgresql", "tables": { - "public.ai_devlog_review": { - "name": "ai_devlog_review", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "serial", - "primaryKey": true, - "notNull": true - }, - "devlogId": { - "name": "devlogId", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "projectId": { - "name": "projectId", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "approved": { - "name": "approved", - "type": "boolean", - "primaryKey": false, - "notNull": true - }, - "rationale": { - "name": "rationale", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "prompt": { - "name": "prompt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "model": { - "name": "model", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "createdAt": { - "name": "createdAt", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - }, - "updatedAt": { - "name": "updatedAt", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": {}, - "foreignKeys": { - "ai_devlog_review_devlogId_devlog_id_fk": { - "name": "ai_devlog_review_devlogId_devlog_id_fk", - "tableFrom": "ai_devlog_review", - "tableTo": "devlog", - "columnsFrom": [ - "devlogId" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - }, - "ai_devlog_review_projectId_project_id_fk": { - "name": "ai_devlog_review_projectId_project_id_fk", - "tableFrom": "ai_devlog_review", - "tableTo": "project", - "columnsFrom": [ - "projectId" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "ai_devlog_review_devlogId_unique": { - "name": "ai_devlog_review_devlogId_unique", - "nullsNotDistinct": false, - "columns": [ - "devlogId" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.ai_project_review": { - "name": "ai_project_review", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "serial", - "primaryKey": true, - "notNull": true - }, - "projectId": { - "name": "projectId", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "overallApproved": { - "name": "overallApproved", - "type": "boolean", - "primaryKey": false, - "notNull": true - }, - "summary": { - "name": "summary", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "prompt": { - "name": "prompt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "model": { - "name": "model", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "createdAt": { - "name": "createdAt", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - }, - "updatedAt": { - "name": "updatedAt", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": {}, - "foreignKeys": { - "ai_project_review_projectId_project_id_fk": { - "name": "ai_project_review_projectId_project_id_fk", - "tableFrom": "ai_project_review", - "tableTo": "project", - "columnsFrom": [ - "projectId" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "ai_project_review_projectId_unique": { - "name": "ai_project_review_projectId_unique", - "nullsNotDistinct": false, - "columns": [ - "projectId" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, "public.devlog": { "name": "devlog", "schema": "", @@ -615,6 +426,13 @@ "notNull": true, "default": false }, + "isPrinter": { + "name": "isPrinter", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, "createdAt": { "name": "createdAt", "type": "timestamp", diff --git a/drizzle/meta/0005_snapshot.json b/drizzle/meta/0005_snapshot.json new file mode 100644 index 0000000..722204e --- /dev/null +++ b/drizzle/meta/0005_snapshot.json @@ -0,0 +1,561 @@ +{ + "id": "0b35966d-3a50-463e-8218-bfc4320586bc", + "prevId": "3da61c80-84ad-466d-a2f9-f0a5bfbbe420", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.devlog": { + "name": "devlog", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timeSpent": { + "name": "timeSpent", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "devlog_userId_user_id_fk": { + "name": "devlog_userId_user_id_fk", + "tableFrom": "devlog", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "devlog_projectId_project_id_fk": { + "name": "devlog_projectId_project_id_fk", + "tableFrom": "devlog", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project": { + "name": "project", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'building'" + }, + "printedBy": { + "name": "printedBy", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "project_userId_user_id_fk": { + "name": "project_userId_user_id_fk", + "tableFrom": "project", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "project_printedBy_user_id_fk": { + "name": "project_printedBy_user_id_fk", + "tableFrom": "project", + "tableTo": "user", + "columnsFrom": [ + "printedBy" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_userId_user_id_fk": { + "name": "session_userId_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.t1_review": { + "name": "t1_review", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "feedback": { + "name": "feedback", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "t1_review_action", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "t1_review_userId_user_id_fk": { + "name": "t1_review_userId_user_id_fk", + "tableFrom": "t1_review", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "t1_review_projectId_project_id_fk": { + "name": "t1_review_projectId_project_id_fk", + "tableFrom": "t1_review", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "idvId": { + "name": "idvId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "profilePicture": { + "name": "profilePicture", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hackatimeTrust": { + "name": "hackatimeTrust", + "type": "hackatime_trust", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "trust": { + "name": "trust", + "type": "trust", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'blue'" + }, + "clay": { + "name": "clay", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "brick": { + "name": "brick", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "shopScore": { + "name": "shopScore", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "hasBasePrinter": { + "name": "hasBasePrinter", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "hasT1Review": { + "name": "hasT1Review", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "hasT2Review": { + "name": "hasT2Review", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "hasAdmin": { + "name": "hasAdmin", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "isPrinter": { + "name": "isPrinter", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "lastLoginAt": { + "name": "lastLoginAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_idvId_unique": { + "name": "user_idvId_unique", + "nullsNotDistinct": false, + "columns": [ + "idvId" + ] + }, + "user_slackId_unique": { + "name": "user_slackId_unique", + "nullsNotDistinct": false, + "columns": [ + "slackId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.hackatime_trust": { + "name": "hackatime_trust", + "schema": "public", + "values": [ + "green", + "blue", + "yellow", + "red" + ] + }, + "public.project_audit_log_type": { + "name": "project_audit_log_type", + "schema": "public", + "values": [ + "create", + "update", + "delete" + ] + }, + "public.status": { + "name": "status", + "schema": "public", + "values": [ + "building", + "submitted", + "t1_approved", + "printing", + "printed", + "t2_approved", + "finalized", + "rejected", + "rejected_locked" + ] + }, + "public.t1_review_action": { + "name": "t1_review_action", + "schema": "public", + "values": [ + "approve", + "approve_no_print", + "add_comment", + "reject", + "reject_lock" + ] + }, + "public.trust": { + "name": "trust", + "schema": "public", + "values": [ + "green", + "blue", + "yellow", + "red" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/0006_snapshot.json b/drizzle/meta/0006_snapshot.json new file mode 100644 index 0000000..656a722 --- /dev/null +++ b/drizzle/meta/0006_snapshot.json @@ -0,0 +1,561 @@ +{ + "id": "c12618f2-0cfc-40f2-9a9a-dbf644650700", + "prevId": "0b35966d-3a50-463e-8218-bfc4320586bc", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.devlog": { + "name": "devlog", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timeSpent": { + "name": "timeSpent", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "devlog_userId_user_id_fk": { + "name": "devlog_userId_user_id_fk", + "tableFrom": "devlog", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "devlog_projectId_project_id_fk": { + "name": "devlog_projectId_project_id_fk", + "tableFrom": "devlog", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project": { + "name": "project", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'building'" + }, + "printedBy": { + "name": "printedBy", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "project_userId_user_id_fk": { + "name": "project_userId_user_id_fk", + "tableFrom": "project", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "project_printedBy_user_id_fk": { + "name": "project_printedBy_user_id_fk", + "tableFrom": "project", + "tableTo": "user", + "columnsFrom": [ + "printedBy" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_userId_user_id_fk": { + "name": "session_userId_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.t1_review": { + "name": "t1_review", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "feedback": { + "name": "feedback", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "t1_review_action", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "t1_review_userId_user_id_fk": { + "name": "t1_review_userId_user_id_fk", + "tableFrom": "t1_review", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "t1_review_projectId_project_id_fk": { + "name": "t1_review_projectId_project_id_fk", + "tableFrom": "t1_review", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "idvId": { + "name": "idvId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "profilePicture": { + "name": "profilePicture", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hackatimeTrust": { + "name": "hackatimeTrust", + "type": "hackatime_trust", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "trust": { + "name": "trust", + "type": "trust", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'blue'" + }, + "clay": { + "name": "clay", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "brick": { + "name": "brick", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "shopScore": { + "name": "shopScore", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "hasBasePrinter": { + "name": "hasBasePrinter", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "hasT1Review": { + "name": "hasT1Review", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "hasT2Review": { + "name": "hasT2Review", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "hasAdmin": { + "name": "hasAdmin", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "isPrinter": { + "name": "isPrinter", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "lastLoginAt": { + "name": "lastLoginAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_idvId_unique": { + "name": "user_idvId_unique", + "nullsNotDistinct": false, + "columns": [ + "idvId" + ] + }, + "user_slackId_unique": { + "name": "user_slackId_unique", + "nullsNotDistinct": false, + "columns": [ + "slackId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.hackatime_trust": { + "name": "hackatime_trust", + "schema": "public", + "values": [ + "green", + "blue", + "yellow", + "red" + ] + }, + "public.project_audit_log_type": { + "name": "project_audit_log_type", + "schema": "public", + "values": [ + "create", + "update", + "delete" + ] + }, + "public.status": { + "name": "status", + "schema": "public", + "values": [ + "building", + "submitted", + "t1_approved", + "printing", + "printed", + "t2_approved", + "finalized", + "rejected", + "rejected_locked" + ] + }, + "public.t1_review_action": { + "name": "t1_review_action", + "schema": "public", + "values": [ + "approve", + "approve_no_print", + "add_comment", + "reject", + "reject_lock" + ] + }, + "public.trust": { + "name": "trust", + "schema": "public", + "values": [ + "green", + "blue", + "yellow", + "red" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/0007_snapshot.json b/drizzle/meta/0007_snapshot.json new file mode 100644 index 0000000..bf2c917 --- /dev/null +++ b/drizzle/meta/0007_snapshot.json @@ -0,0 +1,588 @@ +{ + "id": "44bbbdb5-f715-4c21-8bcc-426aa05e11d0", + "prevId": "c12618f2-0cfc-40f2-9a9a-dbf644650700", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.devlog": { + "name": "devlog", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timeSpent": { + "name": "timeSpent", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "devlog_userId_user_id_fk": { + "name": "devlog_userId_user_id_fk", + "tableFrom": "devlog", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "devlog_projectId_project_id_fk": { + "name": "devlog_projectId_project_id_fk", + "tableFrom": "devlog", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project": { + "name": "project", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "editorFileType": { + "name": "editorFileType", + "type": "editor_file_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "editorUrl": { + "name": "editorUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uploadedFileUrl": { + "name": "uploadedFileUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'building'" + }, + "printedBy": { + "name": "printedBy", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "project_userId_user_id_fk": { + "name": "project_userId_user_id_fk", + "tableFrom": "project", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "project_printedBy_user_id_fk": { + "name": "project_printedBy_user_id_fk", + "tableFrom": "project", + "tableTo": "user", + "columnsFrom": [ + "printedBy" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_userId_user_id_fk": { + "name": "session_userId_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.t1_review": { + "name": "t1_review", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "feedback": { + "name": "feedback", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "t1_review_action", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "t1_review_userId_user_id_fk": { + "name": "t1_review_userId_user_id_fk", + "tableFrom": "t1_review", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "t1_review_projectId_project_id_fk": { + "name": "t1_review_projectId_project_id_fk", + "tableFrom": "t1_review", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "idvId": { + "name": "idvId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "profilePicture": { + "name": "profilePicture", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hackatimeTrust": { + "name": "hackatimeTrust", + "type": "hackatime_trust", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "trust": { + "name": "trust", + "type": "trust", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'blue'" + }, + "clay": { + "name": "clay", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "brick": { + "name": "brick", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "shopScore": { + "name": "shopScore", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "hasBasePrinter": { + "name": "hasBasePrinter", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "hasT1Review": { + "name": "hasT1Review", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "hasT2Review": { + "name": "hasT2Review", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "hasAdmin": { + "name": "hasAdmin", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "isPrinter": { + "name": "isPrinter", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "lastLoginAt": { + "name": "lastLoginAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_idvId_unique": { + "name": "user_idvId_unique", + "nullsNotDistinct": false, + "columns": [ + "idvId" + ] + }, + "user_slackId_unique": { + "name": "user_slackId_unique", + "nullsNotDistinct": false, + "columns": [ + "slackId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.editor_file_type": { + "name": "editor_file_type", + "schema": "public", + "values": [ + "url", + "upload" + ] + }, + "public.hackatime_trust": { + "name": "hackatime_trust", + "schema": "public", + "values": [ + "green", + "blue", + "yellow", + "red" + ] + }, + "public.project_audit_log_type": { + "name": "project_audit_log_type", + "schema": "public", + "values": [ + "create", + "update", + "delete" + ] + }, + "public.status": { + "name": "status", + "schema": "public", + "values": [ + "building", + "submitted", + "t1_approved", + "printing", + "printed", + "t2_approved", + "finalized", + "rejected", + "rejected_locked" + ] + }, + "public.t1_review_action": { + "name": "t1_review_action", + "schema": "public", + "values": [ + "approve", + "approve_no_print", + "add_comment", + "reject", + "reject_lock" + ] + }, + "public.trust": { + "name": "trust", + "schema": "public", + "values": [ + "green", + "blue", + "yellow", + "red" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/0008_snapshot.json b/drizzle/meta/0008_snapshot.json new file mode 100644 index 0000000..5848003 --- /dev/null +++ b/drizzle/meta/0008_snapshot.json @@ -0,0 +1,594 @@ +{ + "id": "4feec100-6277-4ff1-a631-a85760e86c86", + "prevId": "44bbbdb5-f715-4c21-8bcc-426aa05e11d0", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.devlog": { + "name": "devlog", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timeSpent": { + "name": "timeSpent", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "devlog_userId_user_id_fk": { + "name": "devlog_userId_user_id_fk", + "tableFrom": "devlog", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "devlog_projectId_project_id_fk": { + "name": "devlog_projectId_project_id_fk", + "tableFrom": "devlog", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project": { + "name": "project", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "editorFileType": { + "name": "editorFileType", + "type": "editor_file_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "editorUrl": { + "name": "editorUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uploadedFileUrl": { + "name": "uploadedFileUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "modelFile": { + "name": "modelFile", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'building'" + }, + "printedBy": { + "name": "printedBy", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "project_userId_user_id_fk": { + "name": "project_userId_user_id_fk", + "tableFrom": "project", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "project_printedBy_user_id_fk": { + "name": "project_printedBy_user_id_fk", + "tableFrom": "project", + "tableTo": "user", + "columnsFrom": [ + "printedBy" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_userId_user_id_fk": { + "name": "session_userId_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.t1_review": { + "name": "t1_review", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "feedback": { + "name": "feedback", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "t1_review_action", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "t1_review_userId_user_id_fk": { + "name": "t1_review_userId_user_id_fk", + "tableFrom": "t1_review", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "t1_review_projectId_project_id_fk": { + "name": "t1_review_projectId_project_id_fk", + "tableFrom": "t1_review", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "idvId": { + "name": "idvId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "profilePicture": { + "name": "profilePicture", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hackatimeTrust": { + "name": "hackatimeTrust", + "type": "hackatime_trust", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "trust": { + "name": "trust", + "type": "trust", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'blue'" + }, + "clay": { + "name": "clay", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "brick": { + "name": "brick", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "shopScore": { + "name": "shopScore", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "hasBasePrinter": { + "name": "hasBasePrinter", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "hasT1Review": { + "name": "hasT1Review", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "hasT2Review": { + "name": "hasT2Review", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "hasAdmin": { + "name": "hasAdmin", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "isPrinter": { + "name": "isPrinter", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "lastLoginAt": { + "name": "lastLoginAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_idvId_unique": { + "name": "user_idvId_unique", + "nullsNotDistinct": false, + "columns": [ + "idvId" + ] + }, + "user_slackId_unique": { + "name": "user_slackId_unique", + "nullsNotDistinct": false, + "columns": [ + "slackId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.editor_file_type": { + "name": "editor_file_type", + "schema": "public", + "values": [ + "url", + "upload" + ] + }, + "public.hackatime_trust": { + "name": "hackatime_trust", + "schema": "public", + "values": [ + "green", + "blue", + "yellow", + "red" + ] + }, + "public.project_audit_log_type": { + "name": "project_audit_log_type", + "schema": "public", + "values": [ + "create", + "update", + "delete" + ] + }, + "public.status": { + "name": "status", + "schema": "public", + "values": [ + "building", + "submitted", + "t1_approved", + "printing", + "printed", + "t2_approved", + "finalized", + "rejected", + "rejected_locked" + ] + }, + "public.t1_review_action": { + "name": "t1_review_action", + "schema": "public", + "values": [ + "approve", + "approve_no_print", + "add_comment", + "reject", + "reject_lock" + ] + }, + "public.trust": { + "name": "trust", + "schema": "public", + "values": [ + "green", + "blue", + "yellow", + "red" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/0009_snapshot.json b/drizzle/meta/0009_snapshot.json new file mode 100644 index 0000000..c9e0173 --- /dev/null +++ b/drizzle/meta/0009_snapshot.json @@ -0,0 +1,690 @@ +{ + "id": "406e729f-c6c1-4acb-b65f-eca9d9cde379", + "prevId": "4feec100-6277-4ff1-a631-a85760e86c86", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.devlog": { + "name": "devlog", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timeSpent": { + "name": "timeSpent", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "devlog_userId_user_id_fk": { + "name": "devlog_userId_user_id_fk", + "tableFrom": "devlog", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "devlog_projectId_project_id_fk": { + "name": "devlog_projectId_project_id_fk", + "tableFrom": "devlog", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project": { + "name": "project", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "editorFileType": { + "name": "editorFileType", + "type": "editor_file_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "editorUrl": { + "name": "editorUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uploadedFileUrl": { + "name": "uploadedFileUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "modelFile": { + "name": "modelFile", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'building'" + }, + "printedBy": { + "name": "printedBy", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "project_userId_user_id_fk": { + "name": "project_userId_user_id_fk", + "tableFrom": "project", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "project_printedBy_user_id_fk": { + "name": "project_printedBy_user_id_fk", + "tableFrom": "project", + "tableTo": "user", + "columnsFrom": [ + "printedBy" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_userId_user_id_fk": { + "name": "session_userId_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ship": { + "name": "ship", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "editorFileType": { + "name": "editorFileType", + "type": "editor_file_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "editorUrl": { + "name": "editorUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uploadedFileUrl": { + "name": "uploadedFileUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "modelFile": { + "name": "modelFile", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "ship_userId_user_id_fk": { + "name": "ship_userId_user_id_fk", + "tableFrom": "ship", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "ship_projectId_project_id_fk": { + "name": "ship_projectId_project_id_fk", + "tableFrom": "ship", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.t1_review": { + "name": "t1_review", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "feedback": { + "name": "feedback", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "t1_review_action", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "t1_review_userId_user_id_fk": { + "name": "t1_review_userId_user_id_fk", + "tableFrom": "t1_review", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "t1_review_projectId_project_id_fk": { + "name": "t1_review_projectId_project_id_fk", + "tableFrom": "t1_review", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "idvId": { + "name": "idvId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "profilePicture": { + "name": "profilePicture", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hackatimeTrust": { + "name": "hackatimeTrust", + "type": "hackatime_trust", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "trust": { + "name": "trust", + "type": "trust", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'blue'" + }, + "clay": { + "name": "clay", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "brick": { + "name": "brick", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "shopScore": { + "name": "shopScore", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "hasBasePrinter": { + "name": "hasBasePrinter", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "hasT1Review": { + "name": "hasT1Review", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "hasT2Review": { + "name": "hasT2Review", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "hasAdmin": { + "name": "hasAdmin", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "isPrinter": { + "name": "isPrinter", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "lastLoginAt": { + "name": "lastLoginAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_idvId_unique": { + "name": "user_idvId_unique", + "nullsNotDistinct": false, + "columns": [ + "idvId" + ] + }, + "user_slackId_unique": { + "name": "user_slackId_unique", + "nullsNotDistinct": false, + "columns": [ + "slackId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.editor_file_type": { + "name": "editor_file_type", + "schema": "public", + "values": [ + "url", + "upload" + ] + }, + "public.hackatime_trust": { + "name": "hackatime_trust", + "schema": "public", + "values": [ + "green", + "blue", + "yellow", + "red" + ] + }, + "public.project_audit_log_type": { + "name": "project_audit_log_type", + "schema": "public", + "values": [ + "create", + "update", + "delete" + ] + }, + "public.status": { + "name": "status", + "schema": "public", + "values": [ + "building", + "submitted", + "t1_approved", + "printing", + "printed", + "t2_approved", + "finalized", + "rejected", + "rejected_locked" + ] + }, + "public.t1_review_action": { + "name": "t1_review_action", + "schema": "public", + "values": [ + "approve", + "approve_no_print", + "add_comment", + "reject", + "reject_lock" + ] + }, + "public.trust": { + "name": "trust", + "schema": "public", + "values": [ + "green", + "blue", + "yellow", + "red" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 2ebc2d8..5a60fe6 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -33,8 +33,57 @@ { "idx": 4, "version": "7", - "when": 1765721014407, - "tag": "0004_milky_iron_patriot", + "when": 1765473667325, + "tag": "0004_huge_clea", + "breakpoints": true + }, + { + "idx": 5, + "version": "7", + "when": 1765491579072, + "tag": "0005_good_omega_flight", + "breakpoints": true + }, + { + "idx": 6, + "version": "7", + "when": 1765495049768, + "tag": "0006_omniscient_moondragon", + "breakpoints": true + }, + { + "idx": 7, + "version": "7", + "when": 1765647041868, + "tag": "0007_early_morph", + "breakpoints": true + }, + { + "idx": 8, + "version": "7", + "when": 1765659003352, + "tag": "0008_tiny_princess_powerful", + "breakpoints": true + }, + { + "idx": 9, + "version": "7", + "when": 1765666086518, + "tag": "0009_clean_supreme_intelligence", + "breakpoints": true + }, + { + "idx": 10, + "version": "7", + "when": 1765721547890, + "tag": "0010_curved_puff_adder", + "breakpoints": true + }, + { + "idx": 11, + "version": "7", + "when": 1765721708131, + "tag": "0011_striped_gwen_stacy", "breakpoints": true } ] diff --git a/src/lib/server/ai/ensure.ts b/src/lib/server/ai/ensure.ts index 039616a..7336896 100644 --- a/src/lib/server/ai/ensure.ts +++ b/src/lib/server/ai/ensure.ts @@ -24,8 +24,6 @@ export async function ensureAiReviewsForProject({ return { projectReview: null, devlogReviews: [] }; } - const devlogIds = devlogs.map((log) => log.id); - const existingDevlogReviews = await db .select() .from(aiDevlogReview) @@ -62,49 +60,52 @@ export async function ensureAiReviewsForProject({ const { projectReview, devlogReviews } = await reviewDevlogs({ project, devlogs }); - for (const review of devlogReviews) { - await db - .insert(aiDevlogReview) + await db.transaction(async (tx) => { + await Promise.all( + devlogReviews.map((review) => + tx + .insert(aiDevlogReview) + .values({ + devlogId: review.devlogId, + projectId: review.projectId, + approved: review.approved, + rationale: review.rationale, + prompt: review.prompt, + model: review.model + }) + .onConflictDoUpdate({ + target: aiDevlogReview.devlogId, + set: { + projectId: review.projectId, + approved: review.approved, + rationale: review.rationale, + prompt: review.prompt, + model: review.model, + updatedAt: sql`now()` + } + }) + ) + ); + await tx + .insert(aiProjectReview) .values({ - devlogId: review.devlogId, - projectId: review.projectId, - approved: review.approved, - rationale: review.rationale, - prompt: review.prompt, - model: review.model + projectId: project.id, + overallApproved: projectReview.overallApproved, + summary: projectReview.summary, + prompt: projectReview.prompt, + model: projectReview.model }) .onConflictDoUpdate({ - target: aiDevlogReview.devlogId, + target: aiProjectReview.projectId, set: { - projectId: review.projectId, - approved: review.approved, - rationale: review.rationale, - prompt: review.prompt, - model: review.model, + overallApproved: projectReview.overallApproved, + summary: projectReview.summary, + prompt: projectReview.prompt, + model: projectReview.model, updatedAt: sql`now()` } }); - } - - await db - .insert(aiProjectReview) - .values({ - projectId: project.id, - overallApproved: projectReview.overallApproved, - summary: projectReview.summary, - prompt: projectReview.prompt, - model: projectReview.model - }) - .onConflictDoUpdate({ - target: aiProjectReview.projectId, - set: { - overallApproved: projectReview.overallApproved, - summary: projectReview.summary, - prompt: projectReview.prompt, - model: projectReview.model, - updatedAt: sql`now()` - } - }); + }); const latestDevlogReviews = await db .select() diff --git a/src/lib/server/ai/reviewer.ts b/src/lib/server/ai/reviewer.ts index c27cc3a..0fac6fb 100644 --- a/src/lib/server/ai/reviewer.ts +++ b/src/lib/server/ai/reviewer.ts @@ -92,7 +92,7 @@ ${JSON.stringify(payload, null, 2)}`; const rawContent = await callHackClubAI(userPrompt); const sanitizedContent = rawContent.replace(/```json|```/g, '').trim(); let parsed: { overallVerdict: 'approve' | 'flag'; overview: string }; - try { parsed = JSON.parse(sanitizedContent); } catch { throw new Error(`AI response was not JSON: ${rawContent}`); } + try { parsed = JSON.parse(sanitizedContent); } catch { throw new Error(`AI response was not JSON. First 200 chars: ${rawContent.slice(0, 200)}`); } return { overallApproved: parsed.overallVerdict === 'approve', summary: parsed.overview, @@ -116,7 +116,7 @@ ${JSON.stringify(payload, null, 2)}`; const rawContent = await callHackClubAI(userPrompt); const sanitizedContent = rawContent.replace(/```json|```/g, '').trim(); let parsed: ReviewResponse['devlogs'][number]; - try { parsed = JSON.parse(sanitizedContent); } catch { throw new Error(`AI response was not JSON: ${rawContent}`); } + try { parsed = JSON.parse(sanitizedContent); } catch { throw new Error(`AI response was not JSON. First 200 chars: ${rawContent.slice(0, 200)}`); } return { devlogId: parsed.id, projectId, @@ -132,10 +132,6 @@ export async function reviewDevlogs({ project, devlogs }: ReviewInput): Promise< devlogReviews: Pick[]; }> { const projectReview = await reviewProjectOverall(project, devlogs); - const devlogReviews: Pick[] = []; - for (const log of devlogs) { - const r = await reviewSingleDevlog(project.id, log); - devlogReviews.push(r); - } + const devlogReviews = await Promise.all(devlogs.map(log => reviewSingleDevlog(project.id, log))); return { projectReview, devlogReviews }; } diff --git a/src/routes/dashboard/admin/review/[id]/+page.server.ts b/src/routes/dashboard/admin/review/[id]/+page.server.ts index 2a62cd0..718872a 100644 --- a/src/routes/dashboard/admin/review/[id]/+page.server.ts +++ b/src/routes/dashboard/admin/review/[id]/+page.server.ts @@ -2,7 +2,7 @@ import { env } from '$env/dynamic/private'; import { db } from '$lib/server/db/index.js'; import { aiDevlogReview, aiProjectReview, devlog, project, t1Review, user } from '$lib/server/db/schema.js'; import { error, redirect } from '@sveltejs/kit'; -import { and, asc, desc, eq, sql } from 'drizzle-orm'; +import { and, asc, desc, eq, inArray, sql } from 'drizzle-orm'; import type { Actions } from './$types'; import { sendSlackDM } from '$lib/server/slack.js'; import { ensureAiReviewsForProject } from '$lib/server/ai/ensure.js'; @@ -76,8 +76,6 @@ export async function load({ locals, params }) { .where(and(eq(devlog.projectId, queriedProject.project.id), eq(devlog.deleted, false))) .orderBy(desc(devlog.createdAt)); - const devlogIds = devlogs.map((log) => log.id); - let latestDevlogReviews: typeof aiDevlogReview.$inferSelect[] = []; let latestProjectReview: typeof aiProjectReview.$inferSelect | undefined; @@ -85,11 +83,7 @@ export async function load({ locals, params }) { latestDevlogReviews = await db .select() .from(aiDevlogReview) - .where(eq(aiDevlogReview.projectId, queriedProject.project.id)); - - // filter reviews to only those matching current devlogs - const devlogIdSet = new Set(devlogIds); - latestDevlogReviews = latestDevlogReviews.filter((r) => devlogIdSet.has(r.devlogId)); + .where(and(eq(aiDevlogReview.projectId, queriedProject.project.id), inArray(aiDevlogReview.devlogId, devlogs.map((log) => log.id)))); [latestProjectReview] = await db .select() diff --git a/src/routes/dashboard/admin/review/[id]/+page.svelte b/src/routes/dashboard/admin/review/[id]/+page.svelte index fe11ccf..c3a47c2 100644 --- a/src/routes/dashboard/admin/review/[id]/+page.svelte +++ b/src/routes/dashboard/admin/review/[id]/+page.svelte @@ -61,7 +61,6 @@ {/if} -

Project details

diff --git a/src/routes/dashboard/projects/[id]/ship/+page.server.ts b/src/routes/dashboard/projects/[id]/ship/+page.server.ts index c2c9485..207c4c1 100644 --- a/src/routes/dashboard/projects/[id]/ship/+page.server.ts +++ b/src/routes/dashboard/projects/[id]/ship/+page.server.ts @@ -2,7 +2,7 @@ import { env } from '$env/dynamic/private'; import { db } from '$lib/server/db/index.js'; import { devlog, project, user } from '$lib/server/db/schema.js'; import { error, redirect } from '@sveltejs/kit'; -import { eq, and, or, sql } from 'drizzle-orm'; +import { eq, and, or, sql, desc } from 'drizzle-orm'; import type { Actions } from './$types'; import { sendSlackDM } from '$lib/server/slack.js'; import { ensureAiReviewsForProject } from '$lib/server/ai/ensure.js'; @@ -101,7 +101,7 @@ export const actions = { .select() .from(devlog) .where(and(eq(devlog.projectId, queriedProject.id), eq(devlog.deleted, false))) - .orderBy(devlog.createdAt); + .orderBy(desc(devlog.createdAt)); await db .update(project) From 699b4debbb6be1e36d6fc0a76f5993d1f7b63702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Balogh=20Barnab=C3=A1s?= Date: Sun, 14 Dec 2025 15:21:45 +0100 Subject: [PATCH 4/6] Add AI review tables --- drizzle/0010_swift_korg.sql | 28 + drizzle/meta/0010_snapshot.json | 879 ++++++++++++++++++++++++++++++++ drizzle/meta/_journal.json | 11 +- src/lib/server/db/schema.ts | 32 ++ 4 files changed, 941 insertions(+), 9 deletions(-) create mode 100644 drizzle/0010_swift_korg.sql create mode 100644 drizzle/meta/0010_snapshot.json diff --git a/drizzle/0010_swift_korg.sql b/drizzle/0010_swift_korg.sql new file mode 100644 index 0000000..fde18cc --- /dev/null +++ b/drizzle/0010_swift_korg.sql @@ -0,0 +1,28 @@ +CREATE TABLE "ai_devlog_review" ( + "id" serial PRIMARY KEY NOT NULL, + "devlogId" integer NOT NULL, + "projectId" integer NOT NULL, + "approved" boolean NOT NULL, + "rationale" text NOT NULL, + "prompt" text NOT NULL, + "model" text NOT NULL, + "createdAt" timestamp DEFAULT now() NOT NULL, + "updatedAt" timestamp DEFAULT now() NOT NULL, + CONSTRAINT "ai_devlog_review_devlogId_unique" UNIQUE("devlogId") +); +--> statement-breakpoint +CREATE TABLE "ai_project_review" ( + "id" serial PRIMARY KEY NOT NULL, + "projectId" integer NOT NULL, + "overallApproved" boolean NOT NULL, + "summary" text NOT NULL, + "prompt" text NOT NULL, + "model" text NOT NULL, + "createdAt" timestamp DEFAULT now() NOT NULL, + "updatedAt" timestamp DEFAULT now() NOT NULL, + CONSTRAINT "ai_project_review_projectId_unique" UNIQUE("projectId") +); +--> statement-breakpoint +ALTER TABLE "ai_devlog_review" ADD CONSTRAINT "ai_devlog_review_devlogId_devlog_id_fk" FOREIGN KEY ("devlogId") REFERENCES "public"."devlog"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "ai_devlog_review" ADD CONSTRAINT "ai_devlog_review_projectId_project_id_fk" FOREIGN KEY ("projectId") REFERENCES "public"."project"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "ai_project_review" ADD CONSTRAINT "ai_project_review_projectId_project_id_fk" FOREIGN KEY ("projectId") REFERENCES "public"."project"("id") ON DELETE no action ON UPDATE no action; \ No newline at end of file diff --git a/drizzle/meta/0010_snapshot.json b/drizzle/meta/0010_snapshot.json new file mode 100644 index 0000000..e87a18b --- /dev/null +++ b/drizzle/meta/0010_snapshot.json @@ -0,0 +1,879 @@ +{ + "id": "ce48574b-8578-465a-b5f5-6b2dae768975", + "prevId": "406e729f-c6c1-4acb-b65f-eca9d9cde379", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.ai_devlog_review": { + "name": "ai_devlog_review", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "devlogId": { + "name": "devlogId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "approved": { + "name": "approved", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "rationale": { + "name": "rationale", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prompt": { + "name": "prompt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "ai_devlog_review_devlogId_devlog_id_fk": { + "name": "ai_devlog_review_devlogId_devlog_id_fk", + "tableFrom": "ai_devlog_review", + "tableTo": "devlog", + "columnsFrom": [ + "devlogId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "ai_devlog_review_projectId_project_id_fk": { + "name": "ai_devlog_review_projectId_project_id_fk", + "tableFrom": "ai_devlog_review", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "ai_devlog_review_devlogId_unique": { + "name": "ai_devlog_review_devlogId_unique", + "nullsNotDistinct": false, + "columns": [ + "devlogId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ai_project_review": { + "name": "ai_project_review", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "overallApproved": { + "name": "overallApproved", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prompt": { + "name": "prompt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "ai_project_review_projectId_project_id_fk": { + "name": "ai_project_review_projectId_project_id_fk", + "tableFrom": "ai_project_review", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "ai_project_review_projectId_unique": { + "name": "ai_project_review_projectId_unique", + "nullsNotDistinct": false, + "columns": [ + "projectId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.devlog": { + "name": "devlog", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timeSpent": { + "name": "timeSpent", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "devlog_userId_user_id_fk": { + "name": "devlog_userId_user_id_fk", + "tableFrom": "devlog", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "devlog_projectId_project_id_fk": { + "name": "devlog_projectId_project_id_fk", + "tableFrom": "devlog", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project": { + "name": "project", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "editorFileType": { + "name": "editorFileType", + "type": "editor_file_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "editorUrl": { + "name": "editorUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uploadedFileUrl": { + "name": "uploadedFileUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "modelFile": { + "name": "modelFile", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'building'" + }, + "printedBy": { + "name": "printedBy", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "project_userId_user_id_fk": { + "name": "project_userId_user_id_fk", + "tableFrom": "project", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "project_printedBy_user_id_fk": { + "name": "project_printedBy_user_id_fk", + "tableFrom": "project", + "tableTo": "user", + "columnsFrom": [ + "printedBy" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_userId_user_id_fk": { + "name": "session_userId_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ship": { + "name": "ship", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "editorFileType": { + "name": "editorFileType", + "type": "editor_file_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "editorUrl": { + "name": "editorUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uploadedFileUrl": { + "name": "uploadedFileUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "modelFile": { + "name": "modelFile", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "ship_userId_user_id_fk": { + "name": "ship_userId_user_id_fk", + "tableFrom": "ship", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "ship_projectId_project_id_fk": { + "name": "ship_projectId_project_id_fk", + "tableFrom": "ship", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.t1_review": { + "name": "t1_review", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "feedback": { + "name": "feedback", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "t1_review_action", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "t1_review_userId_user_id_fk": { + "name": "t1_review_userId_user_id_fk", + "tableFrom": "t1_review", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "t1_review_projectId_project_id_fk": { + "name": "t1_review_projectId_project_id_fk", + "tableFrom": "t1_review", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "idvId": { + "name": "idvId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "profilePicture": { + "name": "profilePicture", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hackatimeTrust": { + "name": "hackatimeTrust", + "type": "hackatime_trust", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "trust": { + "name": "trust", + "type": "trust", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'blue'" + }, + "clay": { + "name": "clay", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "brick": { + "name": "brick", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "shopScore": { + "name": "shopScore", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "hasBasePrinter": { + "name": "hasBasePrinter", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "hasT1Review": { + "name": "hasT1Review", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "hasT2Review": { + "name": "hasT2Review", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "hasAdmin": { + "name": "hasAdmin", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "isPrinter": { + "name": "isPrinter", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "lastLoginAt": { + "name": "lastLoginAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_idvId_unique": { + "name": "user_idvId_unique", + "nullsNotDistinct": false, + "columns": [ + "idvId" + ] + }, + "user_slackId_unique": { + "name": "user_slackId_unique", + "nullsNotDistinct": false, + "columns": [ + "slackId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.editor_file_type": { + "name": "editor_file_type", + "schema": "public", + "values": [ + "url", + "upload" + ] + }, + "public.hackatime_trust": { + "name": "hackatime_trust", + "schema": "public", + "values": [ + "green", + "blue", + "yellow", + "red" + ] + }, + "public.project_audit_log_type": { + "name": "project_audit_log_type", + "schema": "public", + "values": [ + "create", + "update", + "delete" + ] + }, + "public.status": { + "name": "status", + "schema": "public", + "values": [ + "building", + "submitted", + "t1_approved", + "printing", + "printed", + "t2_approved", + "finalized", + "rejected", + "rejected_locked" + ] + }, + "public.t1_review_action": { + "name": "t1_review_action", + "schema": "public", + "values": [ + "approve", + "approve_no_print", + "add_comment", + "reject", + "reject_lock" + ] + }, + "public.trust": { + "name": "trust", + "schema": "public", + "values": [ + "green", + "blue", + "yellow", + "red" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 5a60fe6..c1d5332 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -75,15 +75,8 @@ { "idx": 10, "version": "7", - "when": 1765721547890, - "tag": "0010_curved_puff_adder", - "breakpoints": true - }, - { - "idx": 11, - "version": "7", - "when": 1765721708131, - "tag": "0011_striped_gwen_stacy", + "when": 1765721964860, + "tag": "0010_swift_korg", "breakpoints": true } ] diff --git a/src/lib/server/db/schema.ts b/src/lib/server/db/schema.ts index eef05cd..f4826e6 100644 --- a/src/lib/server/db/schema.ts +++ b/src/lib/server/db/schema.ts @@ -35,6 +35,8 @@ export const user = pgTable('user', { hasAdmin: boolean().notNull().default(false), // Has access to admin section + isPrinter: boolean().notNull().default(false), // Is a printer + createdAt: timestamp().notNull().defaultNow(), // Account creation timestamp lastLoginAt: timestamp().notNull().defaultNow() // Last login timestamp }); @@ -60,6 +62,8 @@ export const projectStatusEnum = pgEnum('status', [ 'rejected_locked' ]); // rejected == can still re-ship, rejected_locked == can't re-ship +export const editorFileEnum = pgEnum('editor_file_type', ['url', 'upload']); + export const project = pgTable('project', { id: serial().primaryKey(), userId: integer() @@ -68,9 +72,17 @@ export const project = pgTable('project', { name: text(), description: text(), + url: text(), + editorFileType: editorFileEnum(), + editorUrl: text(), + uploadedFileUrl: text(), + + modelFile: text(), + status: projectStatusEnum().notNull().default('building'), + printedBy: integer().references(() => user.id), deleted: boolean().notNull().default(false), // Projects aren't actually deleted, just marked as deleted (I cba to deal with foreign key delete issues for audit logs) createdAt: timestamp().notNull().defaultNow(), @@ -83,6 +95,26 @@ export const projectAuditLogTypeEnum = pgEnum('project_audit_log_type', [ 'delete' ]); +export const ship = pgTable('ship', { + id: serial().primaryKey(), + userId: integer() + .notNull() + .references(() => user.id), + projectId: integer() + .notNull() + .references(() => project.id), + + url: text().notNull(), + + editorFileType: editorFileEnum().notNull(), + editorUrl: text(), + uploadedFileUrl: text(), + + modelFile: text().notNull(), + + timestamp: timestamp().notNull().defaultNow() +}); + export const t1ReviewActionEnum = pgEnum('t1_review_action', [ 'approve', 'approve_no_print', From e568b221f5591c71ad74533df4f1f4ba7de37d9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Balogh=20Barnab=C3=A1s?= Date: Mon, 15 Dec 2025 20:14:58 +0100 Subject: [PATCH 5/6] Show message when countdown is over --- src/routes/countdown/+page.svelte | 56 +++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/src/routes/countdown/+page.svelte b/src/routes/countdown/+page.svelte index e912923..6c19106 100644 --- a/src/routes/countdown/+page.svelte +++ b/src/routes/countdown/+page.svelte @@ -3,6 +3,7 @@ import { onMount, onDestroy } from 'svelte'; let timeLeft = $state({ days: 0, hours: 0, minutes: 0, seconds: 0 }); + let isCountdownOver = $state(false); let interval: ReturnType; const targetDate = new Date('2025-12-15T23:59:00-05:00').getTime(); @@ -13,6 +14,7 @@ if (distance < 0) { timeLeft = { days: 0, hours: 0, minutes: 0, seconds: 0 }; + isCountdownOver = true; if (interval) clearInterval(interval); return; } @@ -38,24 +40,42 @@
-

Countdown to Construct!

-
-
- {timeLeft.days} - DAYS + {#if isCountdownOver} +

The countdown is over!

+
-
- {timeLeft.hours} - HOURS + Go back home + {:else} +

Countdown to Construct!

+
+
+ {timeLeft.days} + DAYS +
+
+ {timeLeft.hours} + HOURS +
+
+ {timeLeft.minutes} + MINS +
+
+ {timeLeft.seconds} + SECS +
-
- {timeLeft.minutes} - MINS -
-
- {timeLeft.seconds} - SECS -
-
- Go back home + Go back home + {/if}
From e8df8d3b5f5941b8c9ea626e30599cfd27de74d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Balogh=20Barnab=C3=A1s?= Date: Mon, 15 Dec 2025 20:25:49 +0100 Subject: [PATCH 6/6] Move to new pr "Show message when countdown is over" --- src/routes/countdown/+page.svelte | 56 ++++++++++--------------------- 1 file changed, 18 insertions(+), 38 deletions(-) diff --git a/src/routes/countdown/+page.svelte b/src/routes/countdown/+page.svelte index 6c19106..e912923 100644 --- a/src/routes/countdown/+page.svelte +++ b/src/routes/countdown/+page.svelte @@ -3,7 +3,6 @@ import { onMount, onDestroy } from 'svelte'; let timeLeft = $state({ days: 0, hours: 0, minutes: 0, seconds: 0 }); - let isCountdownOver = $state(false); let interval: ReturnType; const targetDate = new Date('2025-12-15T23:59:00-05:00').getTime(); @@ -14,7 +13,6 @@ if (distance < 0) { timeLeft = { days: 0, hours: 0, minutes: 0, seconds: 0 }; - isCountdownOver = true; if (interval) clearInterval(interval); return; } @@ -40,42 +38,24 @@
- {#if isCountdownOver} -

The countdown is over!

-
-

- Please wait patiently for Construct to start. It will be announced later today in - - #construct-announcements - -

+

Countdown to Construct!

+
+
+ {timeLeft.days} + DAYS
- Go back home - {:else} -

Countdown to Construct!

-
-
- {timeLeft.days} - DAYS -
-
- {timeLeft.hours} - HOURS -
-
- {timeLeft.minutes} - MINS -
-
- {timeLeft.seconds} - SECS -
+
+ {timeLeft.hours} + HOURS
- Go back home - {/if} +
+ {timeLeft.minutes} + MINS +
+
+ {timeLeft.seconds} + SECS +
+
+ Go back home