Files
zopu-code/packages/backend/convex/projectSetupQueries.ts
2026-08-04 17:37:50 +05:30

92 lines
3.1 KiB
TypeScript

import { v } from "convex/values";
import { internalQuery } from "./_generated/server";
/**
* Query the project source metadata the coordinator needs (clone URL, default
* branch, org). Internal so the Node action can read it without touching the DB.
*
* Lives outside the `"use node"` projectSetup module because Convex forbids
* query/mutation definitions in Node-runtime modules.
*/
export const getProjectSource = internalQuery({
args: { projectId: v.id("projects") },
handler: async (ctx, args) => {
const project = await ctx.db.get(args.projectId);
if (!project) {
return null;
}
return {
defaultBranch: project.defaultBranch ?? null,
name: project.name,
organizationId: project.organizationId,
sourceUrl: project.sourceUrl,
};
},
});
/** Authorize a retry and read its runtime within the query context. Actions do
* not have direct database access, so the authenticated user ID is derived by
* the action and verified against the project's organization here. */
export const getRetryableRuntime = internalQuery({
args: { projectId: v.id("projects"), userId: v.string() },
handler: async (ctx, args) => {
const project = await ctx.db.get(args.projectId);
if (!project) {
throw new Error("Project not found");
}
const membership = await ctx.db
.query("organizationMembers")
.withIndex("by_organizationId_and_userId", (q) =>
q.eq("organizationId", project.organizationId).eq("userId", args.userId)
)
.unique();
if (!membership) {
throw new Error("Organization membership required");
}
const runtime = await ctx.db
.query("projectRuntimes")
.withIndex("by_projectId", (q) => q.eq("projectId", args.projectId))
.unique();
return { organizationId: project.organizationId, runtime };
},
});
/**
* Read-only project setup status for the setup screen: latest runtime row plus
* the setup-scoped events. Auth-gated to project members.
*/
export const getStatus = internalQuery({
args: { projectId: v.id("projects") },
handler: async (ctx, args) => {
const project = await ctx.db.get(args.projectId);
if (!project) {
return { events: [], runtime: null };
}
const runtime = await ctx.db
.query("projectRuntimes")
.withIndex("by_projectId", (q) => q.eq("projectId", args.projectId))
.unique();
const events = await ctx.db
.query("events")
.withIndex("by_organizationId_and_projectId_and_recordedAt", (q) =>
q
.eq("organizationId", project.organizationId)
.eq("projectId", args.projectId)
)
.order("desc")
.filter((q) =>
q.or(
q.eq(q.field("type"), "project.setup.creating_vm"),
q.eq(q.field("type"), "project.setup.cloning"),
q.eq(q.field("type"), "project.setup.checking_repository"),
q.eq(q.field("type"), "project.ready"),
q.eq(q.field("type"), "project.setup.failed"),
q.eq(q.field("type"), "project.setup.started")
)
)
.take(50);
return { events, runtime };
},
});