Implement assessment service and basic endpoints

This commit is contained in:
Dominic Ferrando
2026-08-22 11:42:59 -04:00
parent 7a4a8ee50b
commit dbcfb6900b
2 changed files with 76 additions and 2 deletions
+34 -2
View File
@@ -10,7 +10,7 @@ import {
Printful,
Webflow,
} from "@blade-and-brawn/commerce";
import { DEFAULT_NAME, DUMMY_PASSWORD_HASH, env, log, sha256Sum } from "./util";
import { DEFAULT_NAME, DUMMY_PASSWORD_HASH, env, log } from "./util";
import serverTiming from "@elysia/server-timing";
import jwt from "@elysia/jwt";
import { CommerceService, WOrderStatusSchema } from "./services/commerce";
@@ -22,6 +22,7 @@ import { StandardsService } from "./services/standards";
import { EventsService, EventStatusSchema } from "./services/events";
import { AccountsService, type AccountRole } from "./services/accounts";
import { Value } from "@sinclair/typebox/value";
import { AssessmentsService } from "./services/assessments";
// CONSTANTS
// -----------------------
@@ -37,7 +38,8 @@ const s = (() => {
const Commerce = new CommerceService();
const Accounts = new AccountsService(Calculator);
const Events = new EventsService();
return { Standards, Calculator, Commerce, Accounts, Events };
const Assessments = new AssessmentsService();
return { Standards, Calculator, Commerce, Accounts, Events, Assessments };
})();
// QUEUES
@@ -476,6 +478,36 @@ export const app = new Elysia()
)
)
// ASSESSMENTS
.group("/assessments", (app) => app
.guard({ auth: true }, (app) => app
.post("/me", async ({ body: { player, activityPerformances }, accountId }) => {
await s.Assessments.create(player, activityPerformances, accountId);
}, {
body: t.Object({
player: PlayerSchema,
activityPerformances: t.Array(ActivityPerformanceSchema)
})
})
.get("/me", async ({ accountId }) => {
const assessments = await s.Assessments.list({ filter: { accountId } });
if (!assessments) throw new NotFoundError("Assessments not found");
return assessments;
})
)
.guard({ authAdmin: true }, (app) => app
.post("/", async ({ body: { player, activityPerformances, id } }) => {
await s.Assessments.create(player, activityPerformances, id);
}, {
body: t.Object({
player: PlayerSchema,
activityPerformances: t.Array(ActivityPerformanceSchema),
id: t.Optional(t.String()),
})
})
)
)
// WEBHOOKS
.post("/webhooks/printful", async ({ body, query }) => {
// https://webflow.com/integrations/printful
+42
View File
@@ -0,0 +1,42 @@
import { Activity, type ActivityPerformance, type Player } from "@blade-and-brawn/domain";
import { db } from "../database/db";
export class AssessmentsService {
async create(player: Player, activityPerformances: ActivityPerformance[], accountId?: string) {
const performanceFor = (activity: Activity) =>
activityPerformances.find((p) => p.activity === activity)?.performance ?? null;
return await db.insertInto("assessments")
.values({
account_id: accountId ?? null,
name: player.name ?? "Anonymous",
age: player.metrics.age,
weight: player.metrics.weight,
gender: player.metrics.gender,
perf_back_squat: performanceFor(Activity.BackSquat),
perf_deadlift: performanceFor(Activity.Deadlift),
perf_bench_press: performanceFor(Activity.BenchPress),
perf_broad_jump: performanceFor(Activity.BroadJump),
perf_run: performanceFor(Activity.Run),
perf_cone_drill: performanceFor(Activity.ConeDrill),
})
.returning("id")
.executeTakeFirstOrThrow();
}
async list(opt: {
filter?: { accountId?: string },
limit?: number,
offset?: number,
} = {}) {
return await db.selectFrom("assessments")
.selectAll()
.$if(opt.filter?.accountId !== undefined, (qb) => qb
.where("account_id", "=", opt.filter!.accountId!)
)
.orderBy("created_at", "desc")
.$if(opt.limit !== undefined, (qb) => qb.limit(opt.limit!))
.$if(opt.offset !== undefined, (qb) => qb.offset(opt.offset!))
.execute();
}
}