@paroicms/site-generator-plugin
Version:
ParoiCMS Site Generator Plugin
204 lines (203 loc) • 7.49 kB
JavaScript
import { parseSqliteDateTime } from "@paroicms/internal-server-lib";
import { type } from "arktype";
import { ensureType, formatActivityCode, formatStepKind, formatStepStatus } from "./formatters.js";
const SessionRowAT = type({
id: "string",
expireAt: "string|number|Date",
successStepCount: "number",
"+": "reject",
});
export async function getInvalidSessionError(ctx, sessionId) {
const { cn } = ctx;
const row = await cn("PgSession as s")
.leftJoin("PgSchemaStep as u", "u.sessionId", "s.id")
.select("s.id", "s.expireAt", cn.raw("count(u.sessionId) as successStepCount"))
.where({ "s.id": sessionId })
.groupBy("s.id")
.first();
if (!row)
return "not found";
const validated = SessionRowAT.assert(row);
if (validated.successStepCount >= 20)
return "too many steps";
const expireAt = parseSqliteDateTime(validated.expireAt).getTime();
if (expireAt < Date.now())
return "expired";
}
const StepSchemaRowAT = type({
siteSchema: "string",
l10n: "string",
localizedValues: "string",
"+": "reject",
});
export async function readStepSchema(ctx, stepNumber) {
const { cn, sessionId } = ctx;
const row = await cn("PgSchemaStep")
.select("siteSchema", "l10n", "localizedValues")
.where({ sessionId, stepNumber })
.first();
if (!row)
throw new Error(`[${sessionId}] Step "${stepNumber}" not found`);
const validated = StepSchemaRowAT.assert(row);
return {
siteSchema: JSON.parse(validated.siteSchema),
l10n: JSON.parse(validated.l10n),
localizedValues: JSON.parse(validated.localizedValues),
};
}
const WorkSessionRowAT = type({
id: "string",
createdAt: "string|number|Date",
expireAt: "string|number|Date",
language: "string",
"+": "reject",
});
export async function fetchWorkSession(ctx) {
const { cn, sessionId } = ctx;
const row = await cn("PgSession")
.select("id", "createdAt", "expireAt", "language")
.where({ id: sessionId })
.first();
if (!row) {
throw new Error(`Session not found: ${sessionId}`);
}
const validated = WorkSessionRowAT.assert(row);
const steps = await loadSessionSteps(ctx);
return {
id: validated.id,
createdAt: parseSqliteDateTime(validated.createdAt).toISOString(),
expireAt: parseSqliteDateTime(validated.expireAt).toISOString(),
language: validated.language,
steps,
};
}
async function loadSessionSteps(ctx) {
const { cn, sessionId } = ctx;
const rows = await buildStepsQuery(cn)
.where({ "s.sessionId": sessionId })
.orderBy("s.stepNumber");
return rows.map((row) => mapRowToStep(row, sessionId));
}
export async function loadStep(ctx, stepNumber) {
const { cn, sessionId } = ctx;
const row = await buildStepsQuery(cn)
.where({ "s.sessionId": sessionId, "s.stepNumber": stepNumber })
.first();
if (!row) {
throw new Error(`[Session "${sessionId}"] Step "${stepNumber}" not found`);
}
return mapRowToStep(row, sessionId);
}
function buildStepsQuery(cn) {
return cn("PgStep as s")
.select("s.stepNumber", "s.kind", "s.status", "s.currentActivity", "s.explanation", "c.promptTitle", "c.siteSchema", "c.l10n", "c.localizedValues as schemaLocalizedValues", "i.siteId", "i.localizedValues as siteLocalizedValues", "i.siteUrl", "i.loginEmail", "i.loginPassword")
.leftJoin("PgSchemaStep as c", {
"c.stepNumber": "s.stepNumber",
"c.sessionId": "s.sessionId",
})
.leftJoin("PgGeneratedSiteStep as i", {
"i.stepNumber": "s.stepNumber",
"i.sessionId": "s.sessionId",
});
}
const StepRowAT = type({
stepNumber: "number",
explanation: "string|null",
status: "string",
kind: "string",
currentActivity: "string|null",
promptTitle: "string|null",
siteSchema: "string|null",
l10n: "string|null",
schemaLocalizedValues: "string|null",
siteId: "string|null",
siteLocalizedValues: "string|null",
siteUrl: "string|null",
loginEmail: "string|null",
loginPassword: "string|null",
"+": "reject",
}).pipe((r) => ({
...r,
explanation: r.explanation ?? undefined,
currentActivity: r.currentActivity ?? undefined,
promptTitle: r.promptTitle ?? undefined,
siteSchema: r.siteSchema ?? undefined,
l10n: r.l10n ?? undefined,
schemaLocalizedValues: r.schemaLocalizedValues ?? undefined,
siteId: r.siteId ?? undefined,
siteLocalizedValues: r.siteLocalizedValues ?? undefined,
siteUrl: r.siteUrl ?? undefined,
loginEmail: r.loginEmail ?? undefined,
loginPassword: r.loginPassword ?? undefined,
}));
function mapRowToStep(row, sessionId) {
const validated = StepRowAT.assert(row);
const baseStep = {
stepNumber: validated.stepNumber,
explanation: validated.explanation,
};
const status = formatStepStatus(validated.status);
const kind = formatStepKind(validated.kind);
if (status === "pending") {
return ensureType({
...baseStep,
status,
kind,
currentActivity: formatActivityCode(validated.currentActivity),
});
}
if (status === "completed") {
if (kind === "initialSchema" || kind === "updateSchema") {
if (!validated.siteSchema) {
throw new Error(`[Session "${sessionId}"] PgSchemaStep row is missing for completed step "${validated.stepNumber}"`);
}
if (!validated.l10n || !validated.schemaLocalizedValues) {
throw new Error(`[Session "${sessionId}"] Missing schema data for completed step "${validated.stepNumber}"`);
}
return ensureType({
...baseStep,
status,
kind,
promptTitle: validated.promptTitle,
siteSchema: JSON.parse(validated.siteSchema),
l10n: JSON.parse(validated.l10n),
localizedValues: JSON.parse(validated.schemaLocalizedValues),
});
}
if (kind === "generateSite") {
if (!validated.siteId) {
throw new Error(`[Session "${sessionId}"] PgGeneratedSiteStep row is missing for completed step "${validated.stepNumber}"`);
}
if (!validated.siteUrl ||
!validated.loginEmail ||
!validated.loginPassword ||
!validated.siteLocalizedValues) {
throw new Error(`[Session "${sessionId}"] Missing generated site data for completed step "${validated.stepNumber}"`);
}
return ensureType({
...baseStep,
status,
kind,
siteId: validated.siteId,
siteUrl: validated.siteUrl,
adminUiUrl: `${validated.siteUrl}/adm`,
loginEmail: validated.loginEmail,
loginPassword: validated.loginPassword,
localizedValues: JSON.parse(validated.siteLocalizedValues),
});
}
throw new Error(`[Session "${sessionId}"] invalid step kind "${kind}"`);
}
if (status === "failed") {
return ensureType({
...baseStep,
status,
kind,
});
}
return ensureType({
...baseStep,
status,
kind,
});
}