1.1.0 #29
@@ -11,18 +11,24 @@ export async function up(db: Kysely<any>): Promise<void> {
|
||||
)
|
||||
.addColumn("discord_id", "text", (cb) => cb.unique())
|
||||
.addColumn("email", "text", (cb) => cb.unique())
|
||||
.addColumn("name", "text", (cb) => cb.notNull().defaultTo(""))
|
||||
.addColumn("gender", "text", (cb) => cb.notNull())
|
||||
.addColumn("name", "text", (cb) => cb.notNull().defaultTo("Anonymous"))
|
||||
.addColumn("gender", "text")
|
||||
.execute();
|
||||
|
||||
// TABLE: ASSESSMENTS
|
||||
await db.schema.createTable("assessments")
|
||||
.$call(addDefaultColumns)
|
||||
.addColumn("account_id", "uuid")
|
||||
.addColumn("name", "text", (cb) => cb.notNull().defaultTo(""))
|
||||
.addColumn("name", "text", (cb) => cb.notNull().defaultTo("Anonymous"))
|
||||
.addColumn("gender", "text", (cb) => cb.notNull())
|
||||
.addColumn("age", "numeric", (cb) => cb.notNull())
|
||||
.addColumn("weight", "numeric", (cb) => cb.notNull())
|
||||
.addColumn("age", "integer", (cb) => cb.notNull())
|
||||
.addColumn("weight", "float8", (cb) => cb.notNull())
|
||||
.addColumn("perf_back_squat", "float8") // kg
|
||||
.addColumn("perf_deadlift", "float8") // kg
|
||||
.addColumn("perf_bench_press", "float8") // kg
|
||||
.addColumn("perf_run", "float8") // ms
|
||||
.addColumn("perf_broad_jump", "float8") // cm
|
||||
.addColumn("perf_cone_drill", "float8") // ms
|
||||
.addForeignKeyConstraint(
|
||||
"fk_assessments_account_id",
|
||||
["account_id"],
|
||||
|
||||
+25
-2
@@ -20,10 +20,11 @@ import { CalculatorService, CalculatorUnavailableError } from "./services/calcul
|
||||
import { StandardsParamsSchema } from "@blade-and-brawn/calculator";
|
||||
import { StandardsService } from "./services/standards";
|
||||
import { EventsService, EventStatusSchema } from "./services/events";
|
||||
import { AccountsService } from "./services/accounts";
|
||||
|
||||
// CONSTANTS
|
||||
// -----------------------
|
||||
const EVENT_QUEUE_MANAGE_DELAY_MS = 1000 // 1 sec
|
||||
const EVENT_QUEUE_MANAGE_DELAY_MS = 1000 // 1 sec
|
||||
const JWT_EXP = "1d";
|
||||
|
||||
// SERVICES
|
||||
@@ -32,8 +33,9 @@ const s = (() => {
|
||||
const Standards = new StandardsService();
|
||||
const Calculator = new CalculatorService(DEFAULT_NAME);
|
||||
const Commerce = new CommerceService();
|
||||
const Accounts = new AccountsService(Calculator);
|
||||
const Events = new EventsService();
|
||||
return { Standards, Calculator, Commerce, Events };
|
||||
return { Standards, Calculator, Commerce, Accounts, Events };
|
||||
})();
|
||||
|
||||
// QUEUES
|
||||
@@ -337,6 +339,27 @@ export const app = new Elysia()
|
||||
.get("/groups", async ({ }) => queues.map((q) => q.group))
|
||||
)
|
||||
|
||||
// ACCOUNTS
|
||||
.group("/accounts", (app) => app
|
||||
// Non-authenticated
|
||||
.get("/:id/stats", async ({ params: { id } }) => {
|
||||
const stats = await s.Accounts.stats(id);
|
||||
if (!stats) throw new NotFoundError("Account stats not found");
|
||||
return stats;
|
||||
}, {
|
||||
params: t.Object({ id: t.String() })
|
||||
})
|
||||
// Authenticated
|
||||
.guard({ auth: true })
|
||||
.get("/:id", async ({ params: { id } }) => {
|
||||
const account = await s.Accounts.get(id);
|
||||
if (!account) throw new NotFoundError("Account not found");
|
||||
return account;
|
||||
}, {
|
||||
params: t.Object({ id: t.String() })
|
||||
})
|
||||
)
|
||||
|
||||
// WEBHOOKS
|
||||
.post("/webhooks/printful", async ({ body, query }) => {
|
||||
// https://webflow.com/integrations/printful
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
import { Activity, Gender, type Player } from "@blade-and-brawn/domain";
|
||||
import { db } from "../database/db";
|
||||
import type { CalculatorService } from "./calculator";
|
||||
import { Value } from "@sinclair/typebox/value";
|
||||
import { t } from "elysia";
|
||||
|
||||
export class AccountsService {
|
||||
constructor(private Calculator: CalculatorService) { }
|
||||
|
||||
async get(id: string) {
|
||||
return await (db).selectFrom("accounts")
|
||||
.selectAll()
|
||||
.where("id", "=", id)
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
async stats(id: string) {
|
||||
const latestAssessment = await db.selectFrom("accounts")
|
||||
.innerJoin("assessments", "assessments.account_id", "accounts.id")
|
||||
.select([
|
||||
"assessments.age", "assessments.gender", "assessments.weight", "assessments.name",
|
||||
"assessments.perf_back_squat", "assessments.perf_bench_press", "assessments.perf_broad_jump",
|
||||
"assessments.perf_cone_drill", "assessments.perf_deadlift", "assessments.perf_run"
|
||||
])
|
||||
.where("accounts.id", "=", id)
|
||||
.orderBy("assessments.created_at", "desc")
|
||||
.limit(1)
|
||||
.executeTakeFirst();
|
||||
if (!latestAssessment) return;
|
||||
Value.Assert(t.Enum(Gender), latestAssessment.gender);
|
||||
|
||||
return this.Calculator.calculate(
|
||||
{
|
||||
name: latestAssessment.name,
|
||||
metrics: {
|
||||
age: latestAssessment.age,
|
||||
weight: latestAssessment.weight,
|
||||
gender: latestAssessment.gender
|
||||
}
|
||||
},
|
||||
[
|
||||
// Strength
|
||||
{
|
||||
activity: Activity.BenchPress,
|
||||
performance: latestAssessment.perf_bench_press ?? 0
|
||||
},
|
||||
{
|
||||
activity: Activity.Deadlift,
|
||||
performance: latestAssessment.perf_deadlift ?? 0
|
||||
},
|
||||
{
|
||||
activity: Activity.BackSquat,
|
||||
performance: latestAssessment.perf_back_squat ?? 0
|
||||
},
|
||||
// Power
|
||||
{
|
||||
activity: Activity.BroadJump,
|
||||
performance: latestAssessment.perf_broad_jump ?? 0
|
||||
},
|
||||
// Endurance
|
||||
{
|
||||
activity: Activity.Run,
|
||||
performance: latestAssessment.perf_run ?? 0
|
||||
},
|
||||
// Agility
|
||||
{
|
||||
activity: Activity.ConeDrill,
|
||||
performance: latestAssessment.perf_cone_drill ?? 0
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user