Merge service REST proxy changes

This commit is contained in:
-Puter
2026-06-04 15:57:23 +05:30
3 changed files with 66 additions and 15 deletions

View File

@@ -2,6 +2,25 @@ import { Hono } from "hono";
import { requireUser, type AuthContext } from "../auth/clerk.js";
import { listServiceCapabilities } from "../workflows/service-capabilities.js";
import { interviewService, resumeService, roleplayService, type JsonObject } from "../services/product-service-clients.js";
import { db } from "../db/client.js";
import { events } from "../db/schema.js";
const DEFAULT_QSCORE = {
q_score: 76,
profession: "Student",
quotients: {
SQ: 76,
XQ: 74,
CQm: 78,
VQ: 72,
DQ: 75,
GQ: 80,
},
};
async function recordServiceEvent(userId: string, type: string, payload: Record<string, unknown>) {
await db.insert(events).values({ userId, actorId: "service-gateway", type, payload });
}
export function serviceRoutes() {
const app = new Hono<AuthContext>();
@@ -17,7 +36,18 @@ export function serviceRoutes() {
return c.json({ error: "unknown_service" }, 404);
});
app.post("/interview/configure", async (c) => c.json(await interviewService.configure(await c.req.json<JsonObject>())));
app.get("/interview/page-state", async (c) => c.json(await interviewService.pageState(c.get("userId"))));
app.post("/interview/configure", async (c) => {
const body = await c.req.json<JsonObject>();
const payload = {
...body,
user_id: String(body.user_id ?? c.get("userId")),
org_id: String(body.org_id ?? "growqr"),
} satisfies JsonObject;
const result = await interviewService.configure(payload);
await recordServiceEvent(c.get("userId"), "interview.configured", { request: payload, result });
return c.json(result);
});
app.post("/interview/preview", async (c) => c.json(await interviewService.preview(await c.req.json<JsonObject>())));
app.post("/interview/questions", async (c) => c.json(await interviewService.editQuestions(await c.req.json())));
app.post("/interview/approve", async (c) => {
@@ -28,13 +58,30 @@ export function serviceRoutes() {
app.get("/interview/assignments", async (c) => c.json(await interviewService.listAssignments(c.req.query("email") ?? "", c.req.query("status") ?? "pending", Number(c.req.query("limit") ?? 20))));
app.post("/interview/assignments/unassign", async (c) => c.json(await interviewService.unassign(await c.req.json())));
app.post("/interview/results:bulk", async (c) => c.json(await interviewService.resultsBulk(await c.req.json<JsonObject>())));
app.get("/interview/review/:sessionId", async (c) => c.json(await interviewService.review(c.req.param("sessionId"))));
app.get("/interview/review/:sessionId", async (c) => {
const sessionId = c.req.param("sessionId");
const result = await interviewService.review(sessionId);
await recordServiceEvent(c.get("userId"), "interview.review_checked", { sessionId, result });
return c.json(result);
});
app.get("/interview/leaderboard", async (c) => c.json(await interviewService.leaderboard()));
app.get("/interview/artifacts/:sessionId/:artifactType", async (c) => c.json(await interviewService.artifact(c.req.param("sessionId"), c.req.param("artifactType"))));
app.post("/interview/sessions/:sessionId/video/upload-url", async (c) => c.json(await interviewService.createVideoUploadUrl(c.req.param("sessionId"), await c.req.json<JsonObject>())));
app.post("/interview/sessions/:sessionId/video/uploaded", async (c) => c.json(await interviewService.markVideoUploaded(c.req.param("sessionId"), await c.req.json<JsonObject>())));
app.post("/roleplay/configure", async (c) => c.json(await roleplayService.configure(await c.req.json<JsonObject>())));
app.get("/roleplay/page-state", async (c) => c.json(await roleplayService.pageState(c.get("userId"))));
app.post("/roleplay/configure", async (c) => {
const body = await c.req.json<JsonObject>();
const payload = {
...body,
user_id: String(body.user_id ?? c.get("userId")),
org_id: String(body.org_id ?? "growqr"),
qscore: (body.qscore as JsonObject | undefined) ?? DEFAULT_QSCORE,
} satisfies JsonObject;
const result = await roleplayService.configure(payload);
await recordServiceEvent(c.get("userId"), "roleplay.configured", { request: payload, result });
return c.json(result);
});
app.post("/roleplay/preview", async (c) => c.json(await roleplayService.preview(await c.req.json<JsonObject>())));
app.post("/roleplay/questions", async (c) => c.json(await roleplayService.editQuestions(await c.req.json())));
app.post("/roleplay/approve", async (c) => {
@@ -45,7 +92,12 @@ export function serviceRoutes() {
app.get("/roleplay/assignments", async (c) => c.json(await roleplayService.listAssignments(c.req.query("email") ?? "", c.req.query("status") ?? "pending", Number(c.req.query("limit") ?? 20))));
app.post("/roleplay/assignments/unassign", async (c) => c.json(await roleplayService.unassign(await c.req.json())));
app.post("/roleplay/results:bulk", async (c) => c.json(await roleplayService.resultsBulk(await c.req.json<JsonObject>())));
app.get("/roleplay/review/:sessionId", async (c) => c.json(await roleplayService.review(c.req.param("sessionId"))));
app.get("/roleplay/review/:sessionId", async (c) => {
const sessionId = c.req.param("sessionId");
const result = await roleplayService.review(sessionId);
await recordServiceEvent(c.get("userId"), "roleplay.review_checked", { sessionId, result });
return c.json(result);
});
app.get("/roleplay/leaderboard", async (c) => c.json(await roleplayService.leaderboard()));
app.get("/roleplay/artifacts/:sessionId/:artifactType", async (c) => c.json(await roleplayService.artifact(c.req.param("sessionId"), c.req.param("artifactType"))));
app.post("/roleplay/sessions/:sessionId/video/upload-url", async (c) => c.json(await roleplayService.createVideoUploadUrl(c.req.param("sessionId"), await c.req.json<JsonObject>())));
@@ -61,7 +113,13 @@ export function serviceRoutes() {
app.post("/resume/resumes", async (c) => c.json(await resumeService.createResume(await c.req.json<JsonObject>())));
app.get("/resume/resumes/:resumeId", async (c) => c.json(await resumeService.getResume(c.req.param("resumeId"))));
app.put("/resume/resumes/:resumeId", async (c) => c.json(await resumeService.updateResume(c.req.param("resumeId"), await c.req.json<JsonObject>())));
app.post("/resume/resumes/:resumeId/analyze", async (c) => c.json(await resumeService.analyzeResume(c.req.param("resumeId"), await c.req.json<JsonObject>().catch(() => ({})))));
app.post("/resume/resumes/:resumeId/analyze", async (c) => {
const resumeId = c.req.param("resumeId");
const payload = await c.req.json<JsonObject>().catch(() => ({}));
const result = await resumeService.analyzeResume(resumeId, payload);
await recordServiceEvent(c.get("userId"), "resume.analysis_completed", { resumeId, request: payload, result });
return c.json(result);
});
app.get("/resume/resumes/:resumeId/suggestions", async (c) => c.json(await resumeService.suggestions(c.req.param("resumeId"))));
app.post("/resume/ai/copilot", async (c) => c.json(await resumeService.copilot(await c.req.json<JsonObject>())));
app.post("/resume/ai/optimize-summary", async (c) => c.json(await resumeService.optimizeSummary(await c.req.json<JsonObject>())));

View File

@@ -5,13 +5,6 @@ import { users, userStacks, type UserStack } from "../db/schema.js";
import { eq } from "drizzle-orm";
import { provisionUserStack } from "../docker/manager.js";
import { log } from "../log.js";
import { createClient, type Client } from "rivetkit/client";
import { config } from "../config.js";
import type { Registry } from "../actors/registry.js";
let _client: Client<Registry> | null = null;
function getClient(): Client<Registry> { return (_client ??= createClient<Registry>(config.rivetClientEndpoint)); }
function publicStack(stack: UserStack | null | undefined) {
if (!stack) return stack;
const { opencodePassword: _opencodePassword, ...safe } = stack;
@@ -42,12 +35,10 @@ export function userRoutes() {
);
}
const grow = await getClient().growActor.getOrCreate([userId]).setup({ userId });
return c.json({
user: userRow,
stack: publicStack(stack) ?? { status: "provisioning" },
grow,
grow: null,
});
});

View File

@@ -29,6 +29,7 @@ async function serviceJson<T = JsonObject>(
export const interviewService = {
health: () => serviceJson(config.interviewServiceUrl, "/health"),
pageState: (userId: string) => serviceJson(config.interviewServiceUrl, `/api/v1/interviews/page-state?${new URLSearchParams({ user_id: userId })}`),
configure: (payload: JsonObject) => serviceJson(config.interviewServiceUrl, "/api/v1/configure", { body: payload }),
preview: (payload: JsonObject) => serviceJson(config.interviewServiceUrl, "/api/v1/configure/preview", { body: payload }),
editQuestions: (payload: { session_id: string; questions: Array<JsonObject | string> }) =>
@@ -55,6 +56,7 @@ export const interviewService = {
export const roleplayService = {
health: () => serviceJson(config.roleplayServiceUrl, "/health"),
pageState: (userId: string) => serviceJson(config.roleplayServiceUrl, `/api/v1/roleplays/page-state?${new URLSearchParams({ user_id: userId })}`),
configure: (payload: JsonObject) => serviceJson(config.roleplayServiceUrl, "/api/v1/roleplays/configure", { body: payload }),
preview: (payload: JsonObject) => serviceJson(config.roleplayServiceUrl, "/api/v1/roleplays/configure/preview", { body: payload }),
editQuestions: (payload: { session_id: string; questions: Array<JsonObject | string> }) =>