v0.2.2 balance peek endpoint
This commit is contained in:
@@ -21,6 +21,7 @@ import {
|
|||||||
import { transcribeRouter } from "./routes/transcribe.js";
|
import { transcribeRouter } from "./routes/transcribe.js";
|
||||||
import { analyzeRouter } from "./routes/analyze.js";
|
import { analyzeRouter } from "./routes/analyze.js";
|
||||||
import { healthRouter } from "./routes/health.js";
|
import { healthRouter } from "./routes/health.js";
|
||||||
|
import { balanceRouter } from "./routes/balance.js";
|
||||||
import { adminRouter } from "./routes/admin.js";
|
import { adminRouter } from "./routes/admin.js";
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
@@ -46,6 +47,7 @@ setupAdminAuthRoutes(app);
|
|||||||
// authenticates per-call via headers (X-Recap-Install-Id required,
|
// authenticates per-call via headers (X-Recap-Install-Id required,
|
||||||
// Authorization optional).
|
// Authorization optional).
|
||||||
app.use("/relay", healthRouter());
|
app.use("/relay", healthRouter());
|
||||||
|
app.use("/relay", balanceRouter());
|
||||||
app.use("/relay", transcribeRouter());
|
app.use("/relay", transcribeRouter());
|
||||||
app.use("/relay", analyzeRouter());
|
app.use("/relay", analyzeRouter());
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
// GET /relay/balance — peek at the current install's credit balance +
|
||||||
|
// tier WITHOUT charging anything. Recap clients call this to populate
|
||||||
|
// the "N credits remaining · Tier: X" banner before the user runs any
|
||||||
|
// transcribe/analyze, so the display is accurate on first load instead
|
||||||
|
// of saying "balance unknown — no relay calls yet".
|
||||||
|
//
|
||||||
|
// Same auth surface as the metered endpoints:
|
||||||
|
// X-Recap-Install-Id (required)
|
||||||
|
// Authorization (optional Bearer LIC1-... — absent = Core)
|
||||||
|
//
|
||||||
|
// Returns the standard envelope shape with result=null and
|
||||||
|
// credit_charged=0. The license-resolution path is identical to
|
||||||
|
// /relay/transcribe and /relay/analyze, so the cached online check
|
||||||
|
// against keysat happens here too — but no row mutation, no job-id
|
||||||
|
// reservation, no upstream backend call.
|
||||||
|
|
||||||
|
import express from "express";
|
||||||
|
import { resolveLicense } from "../keysat-client.js";
|
||||||
|
import { getOrCreateRow } from "../credits.js";
|
||||||
|
import { envelope, errorEnvelope } from "./envelope.js";
|
||||||
|
|
||||||
|
export function balanceRouter() {
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.get("/balance", async (req, res) => {
|
||||||
|
const installId = req.header("X-Recap-Install-Id");
|
||||||
|
const auth = req.header("Authorization");
|
||||||
|
if (!installId) {
|
||||||
|
const e = await errorEnvelope({
|
||||||
|
error: "missing X-Recap-Install-Id header",
|
||||||
|
statusHint: 400,
|
||||||
|
});
|
||||||
|
return res.status(400).json(e.body);
|
||||||
|
}
|
||||||
|
const license = await resolveLicense(auth);
|
||||||
|
const tier = license.tier;
|
||||||
|
// Touch the row so tier_snapshot reflects the most recently seen
|
||||||
|
// license tier — same as the metered endpoints do — but commit
|
||||||
|
// nothing.
|
||||||
|
const row = await getOrCreateRow(installId);
|
||||||
|
row.tier_snapshot = tier;
|
||||||
|
const body = await envelope({
|
||||||
|
result: null,
|
||||||
|
installId,
|
||||||
|
tier,
|
||||||
|
creditCharged: 0,
|
||||||
|
});
|
||||||
|
res.json(body);
|
||||||
|
});
|
||||||
|
|
||||||
|
return router;
|
||||||
|
}
|
||||||
@@ -2,8 +2,9 @@ import { VersionGraph } from '@start9labs/start-sdk'
|
|||||||
import { v_0_1_0 } from './v0.1.0'
|
import { v_0_1_0 } from './v0.1.0'
|
||||||
import { v_0_2_0 } from './v0.2.0'
|
import { v_0_2_0 } from './v0.2.0'
|
||||||
import { v_0_2_1 } from './v0.2.1'
|
import { v_0_2_1 } from './v0.2.1'
|
||||||
|
import { v_0_2_2 } from './v0.2.2'
|
||||||
|
|
||||||
export const versionGraph = VersionGraph.of({
|
export const versionGraph = VersionGraph.of({
|
||||||
current: v_0_2_1,
|
current: v_0_2_2,
|
||||||
other: [v_0_2_0, v_0_1_0],
|
other: [v_0_2_1, v_0_2_0, v_0_1_0],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { VersionInfo } from '@start9labs/start-sdk'
|
||||||
|
|
||||||
|
export const v_0_2_2 = VersionInfo.of({
|
||||||
|
version: '0.2.2:0',
|
||||||
|
releaseNotes: {
|
||||||
|
en_US:
|
||||||
|
'New GET /relay/balance endpoint returns credit balance + tier without charging. Recap clients call this on boot so the "N credits remaining" banner is populated before the user runs any transcribe/analyze.',
|
||||||
|
},
|
||||||
|
migrations: {
|
||||||
|
up: async ({ effects }) => {},
|
||||||
|
down: async ({ effects }) => {},
|
||||||
|
},
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user