Finish adding config endpoints
This commit is contained in:
@@ -31,7 +31,7 @@ export async function up(db: Kysely<any>): Promise<void> {
|
|||||||
await db.schema.createTable("calculator_configs")
|
await db.schema.createTable("calculator_configs")
|
||||||
.$call(addDefaultColumns)
|
.$call(addDefaultColumns)
|
||||||
.addColumn("standards_config", "jsonb", (cb) => cb.notNull())
|
.addColumn("standards_config", "jsonb", (cb) => cb.notNull())
|
||||||
.addColumn("is_selected", "boolean", (cb) => cb.notNull())
|
.addColumn("is_selected", "boolean", (cb) => cb.notNull().defaultTo(false))
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
// INDEX: ONE_SELECTED_CALCULATOR_CONFIG
|
// INDEX: ONE_SELECTED_CALCULATOR_CONFIG
|
||||||
|
|||||||
+22
-5
@@ -18,6 +18,7 @@ import { CommerceService } from "./services/commerce";
|
|||||||
import cluster from "node:cluster";
|
import cluster from "node:cluster";
|
||||||
import { randomUUIDv7 } from "bun";
|
import { randomUUIDv7 } from "bun";
|
||||||
import { CalculatorService } from "./services/calculator";
|
import { CalculatorService } from "./services/calculator";
|
||||||
|
import { StandardsConfigSchema } from "@blade-and-brawn/calculator";
|
||||||
|
|
||||||
// SERVICES
|
// SERVICES
|
||||||
// -----------
|
// -----------
|
||||||
@@ -100,6 +101,7 @@ export const app = new Elysia()
|
|||||||
// CALCULATOR
|
// CALCULATOR
|
||||||
.group("/calculator",
|
.group("/calculator",
|
||||||
(app) => app
|
(app) => app
|
||||||
|
// Non-authenticated
|
||||||
.post("/calculate", async ({ body }) => {
|
.post("/calculate", async ({ body }) => {
|
||||||
return { levels: calculatorService.calculate(body.player, body.activityPerformances) };
|
return { levels: calculatorService.calculate(body.player, body.activityPerformances) };
|
||||||
}, {
|
}, {
|
||||||
@@ -108,15 +110,30 @@ export const app = new Elysia()
|
|||||||
activityPerformances: t.Array(ActivityPerformanceSchema)
|
activityPerformances: t.Array(ActivityPerformanceSchema)
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
// Authenticated
|
||||||
.resolve(async ({ jwt, cookie: { auth } }) => {
|
.resolve(async ({ jwt, cookie: { auth } }) => {
|
||||||
const token = auth.value && await jwt.verify(auth.value);
|
const token = auth.value && await jwt.verify(auth.value);
|
||||||
if (!token || !token.sessionId) throw status(401, "Unauthorized");
|
if (!token || !token.sessionId) throw status(401, "Unauthorized");
|
||||||
return { sessionId: token.sessionId.toString() };
|
return { sessionId: token.sessionId.toString() };
|
||||||
})
|
})
|
||||||
.post("/config", () => { })
|
.put("/config/:id", async ({ params: { id }, body: { standardsConfig } }) => {
|
||||||
|
await calculatorService.setConfig(id, standardsConfig);
|
||||||
|
}, {
|
||||||
|
params: t.Object({ id: t.String() }),
|
||||||
|
body: t.Object({ standardsConfig: StandardsConfigSchema })
|
||||||
|
})
|
||||||
|
.post("/config", async ({ body: { standardsConfig } }) => {
|
||||||
|
await calculatorService.createConfig(standardsConfig);
|
||||||
|
}, {
|
||||||
|
body: t.Object({ standardsConfig: StandardsConfigSchema })
|
||||||
|
})
|
||||||
|
.post("/config/:id/select", async ({ params: { id } }) => {
|
||||||
|
await calculatorService.selectConfig(id);
|
||||||
|
}, {
|
||||||
|
params: t.Object({ id: t.String() })
|
||||||
|
})
|
||||||
.get("/config", async () => {
|
.get("/config", async () => {
|
||||||
const config = await calculatorService.getConfig();
|
return await calculatorService.getSelectedConfig();
|
||||||
return config;
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -150,8 +167,8 @@ export const app = new Elysia()
|
|||||||
});
|
});
|
||||||
}, { params: t.Object({ printfulProductId: t.Optional(t.Numeric()) }) }),
|
}, { params: t.Object({ printfulProductId: t.Optional(t.Numeric()) }) }),
|
||||||
)
|
)
|
||||||
.get("/:printfulProductId", async ({ params }) => {
|
.get("/:printfulProductId", async ({ params: { printfulProductId } }) => {
|
||||||
const printfulProduct = await commerceService.printful.Products.get(params.printfulProductId);
|
const printfulProduct = await commerceService.printful.Products.get(printfulProductId);
|
||||||
if (!printfulProduct) throw new NotFoundError("Missing printful product");
|
if (!printfulProduct) throw new NotFoundError("Missing printful product");
|
||||||
|
|
||||||
const webflowProductId = printfulProduct.sync_product.external_id.split("-")[0];
|
const webflowProductId = printfulProduct.sync_product.external_id.split("-")[0];
|
||||||
|
|||||||
@@ -21,13 +21,41 @@ export class CalculatorService {
|
|||||||
return this.levelCalculator.calculate(player, activityPerformances);
|
return this.levelCalculator.calculate(player, activityPerformances);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getConfig(opt: { skipCache?: boolean } = {}): Promise<StandardsConfig> {
|
async getSelectedConfig(opt: { skipCache?: boolean } = {}): Promise<StandardsConfig> {
|
||||||
if (!opt.skipCache) return this.levelCalculator.standards.cfg;
|
if (!opt.skipCache) return this.levelCalculator.standards.cfg;
|
||||||
|
|
||||||
await this.refreshConfig();
|
await this.refreshConfig();
|
||||||
return this.levelCalculator.standards.cfg;
|
return this.levelCalculator.standards.cfg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async selectConfig(id: string) {
|
||||||
|
await db.transaction().execute(async (trx) => {
|
||||||
|
await trx.updateTable("calculator_configs")
|
||||||
|
.set({ is_selected: false })
|
||||||
|
.where("is_selected", "=", true)
|
||||||
|
.execute();
|
||||||
|
const result = await trx.updateTable("calculator_configs")
|
||||||
|
.set({ is_selected: true })
|
||||||
|
.where("id", "=", id)
|
||||||
|
.execute();
|
||||||
|
if (result[0]?.numUpdatedRows === 0n) throw new Error(`No config with id "${id}"`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async setConfig(id: string, standardsConfig: StandardsConfig) {
|
||||||
|
const result = await db.updateTable("calculator_configs")
|
||||||
|
.set({ standards_config: standardsConfig })
|
||||||
|
.where("id", "=", id)
|
||||||
|
.execute();
|
||||||
|
if (result[0]?.numUpdatedRows === 0n) throw new Error(`No config with id "${id}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async createConfig(standardsConfig: StandardsConfig) {
|
||||||
|
await db.insertInto("calculator_configs")
|
||||||
|
.values({ standards_config: standardsConfig })
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
|
||||||
async refreshConfig(opt: { onlyIfStale?: boolean } = {}) {
|
async refreshConfig(opt: { onlyIfStale?: boolean } = {}) {
|
||||||
if (opt.onlyIfStale)
|
if (opt.onlyIfStale)
|
||||||
if (Date.now() - this.lastRefreshedAt < CalculatorService.CONFIG_REFRESH_INTERVAL_MS) return;
|
if (Date.now() - this.lastRefreshedAt < CalculatorService.CONFIG_REFRESH_INTERVAL_MS) return;
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
{
|
||||||
|
"global": {
|
||||||
|
"maxLevel": 100
|
||||||
|
},
|
||||||
|
"activity": {
|
||||||
|
"BackSquat": {
|
||||||
|
"enableGeneration": true,
|
||||||
|
"weightModifier": 0,
|
||||||
|
"weightSkew": 0,
|
||||||
|
"ageModifier": 0,
|
||||||
|
"difficultyModifier": 0.3,
|
||||||
|
"peakAge": 0,
|
||||||
|
"stretch": {
|
||||||
|
"upper": 0,
|
||||||
|
"lower": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"BenchPress": {
|
||||||
|
"enableGeneration": true,
|
||||||
|
"weightModifier": 0,
|
||||||
|
"weightSkew": 0,
|
||||||
|
"ageModifier": 0,
|
||||||
|
"difficultyModifier": 0.05,
|
||||||
|
"peakAge": 0,
|
||||||
|
"stretch": {
|
||||||
|
"upper": 1,
|
||||||
|
"lower": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Deadlift": {
|
||||||
|
"enableGeneration": true,
|
||||||
|
"weightModifier": 0,
|
||||||
|
"weightSkew": 0,
|
||||||
|
"ageModifier": 0,
|
||||||
|
"difficultyModifier": -0.05,
|
||||||
|
"peakAge": 0,
|
||||||
|
"stretch": {
|
||||||
|
"upper": 1,
|
||||||
|
"lower": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Run": {
|
||||||
|
"enableGeneration": true,
|
||||||
|
"weightModifier": 0.1,
|
||||||
|
"weightSkew": 0,
|
||||||
|
"ageModifier": 0,
|
||||||
|
"difficultyModifier": 0.35,
|
||||||
|
"peakAge": 0,
|
||||||
|
"stretch": {
|
||||||
|
"upper": 1,
|
||||||
|
"lower": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"BroadJump": {
|
||||||
|
"enableGeneration": true,
|
||||||
|
"weightModifier": -0.1,
|
||||||
|
"weightSkew": 0,
|
||||||
|
"ageModifier": -0.25,
|
||||||
|
"difficultyModifier": -0.15,
|
||||||
|
"peakAge": 23,
|
||||||
|
"stretch": {
|
||||||
|
"upper": 3,
|
||||||
|
"lower": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ConeDrill": {
|
||||||
|
"enableGeneration": true,
|
||||||
|
"weightModifier": -0.1,
|
||||||
|
"weightSkew": 0,
|
||||||
|
"ageModifier": -0.25,
|
||||||
|
"difficultyModifier": 0.15,
|
||||||
|
"peakAge": 23,
|
||||||
|
"stretch": {
|
||||||
|
"upper": 0,
|
||||||
|
"lower": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user