More api and database cleanup

This commit is contained in:
Dominic Ferrando
2026-09-26 18:41:59 -04:00
parent 930bd61e62
commit 91f682ec71
5 changed files with 11 additions and 6 deletions
@@ -8,6 +8,7 @@ export async function up(db: Kysely<any>): Promise<void> {
.$call(addDefaultColumns) .$call(addDefaultColumns)
.addColumn("assessment_id", "bigint", (cb) => cb.notNull().unique()) .addColumn("assessment_id", "bigint", (cb) => cb.notNull().unique())
.addColumn("status", "text", (cb) => cb.notNull().defaultTo("draft")) // draft | submitted | in_review | action_requested | completed .addColumn("status", "text", (cb) => cb.notNull().defaultTo("draft")) // draft | submitted | in_review | action_requested | completed
.addColumn("submitted_at", "timestamptz")
.addColumn("completed_at", "timestamptz") .addColumn("completed_at", "timestamptz")
.addColumn("completed_by", "uuid") .addColumn("completed_by", "uuid")
.addColumn("media_key_back_squat", "text") .addColumn("media_key_back_squat", "text")
+3 -3
View File
@@ -12,7 +12,7 @@ import {
Printful, Printful,
Webflow, Webflow,
} from "@blade-and-brawn/commerce"; } from "@blade-and-brawn/commerce";
import { BigIntIdSchema, DEFAULT_NAME, DUMMY_PASSWORD_HASH, env, log } from "./util"; import { AccountIdSchema, BigIntIdSchema, DEFAULT_NAME, DUMMY_PASSWORD_HASH, env, log } from "./util";
import serverTiming from "@elysia/server-timing"; import serverTiming from "@elysia/server-timing";
import jwt from "@elysia/jwt"; import jwt from "@elysia/jwt";
import { CommerceService, WOrderStatusSchema } from "./services/commerce"; import { CommerceService, WOrderStatusSchema } from "./services/commerce";
@@ -471,7 +471,7 @@ export const app = new Elysia()
if (!stats) return status(404, { error: "Account stats not found" }); if (!stats) return status(404, { error: "Account stats not found" });
return stats; return stats;
}, { }, {
params: t.Object({ id: t.String() }) params: t.Object({ id: AccountIdSchema })
}) })
.guard({ authAdmin: true }, (app) => app .guard({ authAdmin: true }, (app) => app
.get("/:id", async ({ params: { id } }) => { .get("/:id", async ({ params: { id } }) => {
@@ -479,7 +479,7 @@ export const app = new Elysia()
if (!account) return status(404, { error: "Account not found" }); if (!account) return status(404, { error: "Account not found" });
return account; return account;
}, { }, {
params: t.Object({ id: t.String() }) params: t.Object({ id: AccountIdSchema })
}) })
) )
.group("/me", { auth: true }, (app) => app .group("/me", { auth: true }, (app) => app
+4 -1
View File
@@ -62,6 +62,7 @@ export class VerificationsService {
.where("status", "in", ALLOWED_FROM.update) .where("status", "in", ALLOWED_FROM.update)
.set({ .set({
status: "draft" satisfies VerificationStatus, status: "draft" satisfies VerificationStatus,
submitted_at: null,
assessment_id: assessmentId, assessment_id: assessmentId,
media_key_back_squat: activityMediaKeys[Activity.BackSquat], media_key_back_squat: activityMediaKeys[Activity.BackSquat],
media_key_deadlift: activityMediaKeys[Activity.Deadlift], media_key_deadlift: activityMediaKeys[Activity.Deadlift],
@@ -96,6 +97,7 @@ export class VerificationsService {
.$if(opt.filter?.status !== undefined, (qb) => qb .$if(opt.filter?.status !== undefined, (qb) => qb
.where("verifications.status", "=", opt.filter!.status!) .where("verifications.status", "=", opt.filter!.status!)
) )
.orderBy("verifications.submitted_at", (ob) => ob.desc().nullsLast())
.orderBy("verifications.created_at", "desc") .orderBy("verifications.created_at", "desc")
.$if(opt.limit !== undefined, (qb) => qb.limit(opt.limit!)) .$if(opt.limit !== undefined, (qb) => qb.limit(opt.limit!))
.$if(opt.offset !== undefined, (qb) => qb.offset(opt.offset!)) .$if(opt.offset !== undefined, (qb) => qb.offset(opt.offset!))
@@ -109,7 +111,7 @@ export class VerificationsService {
.where((eb) => eb.and( .where((eb) => eb.and(
REQUIRED_MEDIA_ACTIVITIES.map((activity) => eb(MEDIA_KEY_COLUMN[activity], "is not", null)) REQUIRED_MEDIA_ACTIVITIES.map((activity) => eb(MEDIA_KEY_COLUMN[activity], "is not", null))
)) ))
.set({ status: "submitted" satisfies VerificationStatus }) .set({ status: "submitted" satisfies VerificationStatus, submitted_at: new Date() })
.returning(["id", "status"]) .returning(["id", "status"])
.executeTakeFirst(); .executeTakeFirst();
} }
@@ -159,6 +161,7 @@ export class VerificationsService {
"verifications.assessment_id", "verifications.assessment_id",
"verifications.status", "verifications.status",
"verifications.created_at", "verifications.created_at",
"verifications.submitted_at",
"verifications.completed_at", "verifications.completed_at",
"verifications.completed_by", "verifications.completed_by",
"verifications.reviewer_notes", "verifications.reviewer_notes",
+1
View File
@@ -39,6 +39,7 @@ export const DEFAULT_NAME = "Default";
export const DUMMY_PASSWORD_HASH = await Bun.password.hash("Dummy"); export const DUMMY_PASSWORD_HASH = await Bun.password.hash("Dummy");
export const BigIntIdSchema = t.String({ pattern: "^\\d+$" }); export const BigIntIdSchema = t.String({ pattern: "^\\d+$" });
export const AccountIdSchema = t.String({ pattern: "^(@.+|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$" });
function requireEnv(key: string): string { function requireEnv(key: string): string {
const val = Bun.env[key]; const val = Bun.env[key];
@@ -137,7 +137,7 @@
<th>Player</th> <th>Player</th>
<th>Status</th> <th>Status</th>
<th>Videos</th> <th>Videos</th>
<th>Requested</th> <th>Submitted</th>
<th></th> <th></th>
</tr> </tr>
</thead> </thead>
@@ -151,7 +151,7 @@
</div> </div>
</td> </td>
<td>{videoCount(row)} / {Object.values(Activity).length}</td> <td>{videoCount(row)} / {Object.values(Activity).length}</td>
<td class="whitespace-nowrap">{formatDate(row.created_at)}</td> <td class="whitespace-nowrap">{formatDate(row.submitted_at)}</td>
<td> <td>
<a <a
class="btn btn-primary btn-xs" class="btn btn-primary btn-xs"