diff --git a/apps/api/src/database/migrations/2026-06-03.ts b/apps/api/src/database/migrations/2026-06-03.ts index a3ec329..ea69ead 100644 --- a/apps/api/src/database/migrations/2026-06-03.ts +++ b/apps/api/src/database/migrations/2026-06-03.ts @@ -20,12 +20,27 @@ export async function up(db: Kysely): Promise { // INDEX: ONE_ACTIVE_PRODUCT_SYNC await db.schema.createIndex("idx_one_active_product_sync") .on("product_syncs") - .unique() .column("ended_at") + .unique() .where(sql.ref("started_at"), "is not", null) .where("ended_at", "is", null) .nullsNotDistinct() .execute(); + + // TABLE: CALCULATOR_CONFIGS + await db.schema.createTable("calculator_configs") + .$call(addDefaultColumns) + .addColumn("parameters", "jsonb", (cb) => cb.notNull()) + .addColumn("is_selected", "boolean", (cb) => cb.notNull()) + .execute(); + + // INDEX: ONE_SELECTED_CALCULATOR_CONFIG + await db.schema.createIndex("idx_one_selected_calculator_config") + .on("calculator_configs") + .column("is_selected") + .unique() + .where("is_selected", "=", true) + .execute(); } export async function down(db: Kysely): Promise { @@ -34,4 +49,10 @@ export async function down(db: Kysely): Promise { // INDEX: ONE_ACTIVE_PRODUCT_SYNC await db.schema.dropIndex("idx_one_active_product_sync").ifExists().execute(); + + // TABLE: CALCULATOR_CONFIGS + await db.schema.dropTable("calculator_configs").ifExists().execute() + + // INDEX: ONE_SELECTED_CALCULATOR_CONFIG + await db.schema.dropIndex("idx_one_selected_calculator_config").ifExists().execute(); } diff --git a/apps/api/src/server.ts b/apps/api/src/server.ts index bc1486e..ba3a04a 100644 --- a/apps/api/src/server.ts +++ b/apps/api/src/server.ts @@ -96,6 +96,7 @@ export const app = new Elysia() status }, "request"); }) + .guard({ cookie: t.Cookie({ auth: t.Optional(t.String()) }) }) .get("/", () => ({ status: "ok" })) .get("/health", () => ({ status: "ok" })) @@ -115,24 +116,30 @@ export const app = new Elysia() ".bladeandbrawn.com" : undefined }); - }, { - body: t.Object({ password: t.String() }), - cookie: t.Cookie({ auth: t.Optional(t.String()) }) - }) + }, { body: t.Object({ password: t.String() }) }) // CALCULATOR - .post("/calculate", async ({ body }) => { - return { levels: levelCalculator.calculate(body.player, body.activityPerformances) }; - }, { - body: t.Object({ - player: PlayerSchema, - activityPerformances: t.Array(ActivityPerformanceSchema) - }), - }) + .group("/calculator", + (app) => app + .post("/calculate", async ({ body }) => { + return { levels: levelCalculator.calculate(body.player, body.activityPerformances) }; + }, { + body: t.Object({ + player: PlayerSchema, + activityPerformances: t.Array(ActivityPerformanceSchema) + }), + }) + .resolve(async ({ jwt, cookie: { auth } }) => { + const token = auth.value && await jwt.verify(auth.value); + if (!token || !token.sessionId) throw status(401, "Unauthorized"); + return { sessionId: token.sessionId.toString() }; + }) + .post("/config", () => { }) + .get("/config", () => { }) + ) // COMMERCE .group("/products", - { cookie: t.Cookie({ auth: t.Optional(t.String()) }) }, (app) => app .resolve(async ({ jwt, cookie: { auth } }) => { const token = auth.value && await jwt.verify(auth.value); diff --git a/apps/api/src/services/calculator.ts b/apps/api/src/services/calculator.ts new file mode 100644 index 0000000..398290e --- /dev/null +++ b/apps/api/src/services/calculator.ts @@ -0,0 +1,3 @@ +export class CalculatorService { + +}