mirror of
https://github.com/Dvorinka/Primora.git
synced 2026-10-28 07:41:23 +00:00
CI / Backend Tests (push) Canceled after 0s
CI / Frontend Tests (push) Canceled after 0s
CI / Build All Services (push) Canceled after 0s
CI / Docker Build (auth) (push) Canceled after 0s
CI / Docker Build (backend) (push) Canceled after 0s
CI / Docker Build (frontend) (push) Canceled after 0s
CI / Publish edge images (auth) (push) Canceled after 0s
CI / Publish edge images (backend) (push) Canceled after 0s
CI / Publish edge images (frontend) (push) Canceled after 0s
CI / Migration Up/Down (push) Canceled after 0s
CI / Compose E2E Smoke (push) Canceled after 0s
CI / Lint (push) Canceled after 0s
- desktop: probe-then-persist server URL — scheme-less input tries https then http; a failed probe no longer saves a dead URL that would trap the next launch in a redirect to a broken address - frontend: close the create-collection modal on successful submit (backdrop stayed mounted and intercepted navigation) - frontend: reload once on service-worker controllerchange so returning users don't run a stale cached shell after a deploy - frontend: gate dashboard empty states on resource loading to stop the "No project selected" / "Create your workspace" flash - cli: resolve scheduled-job refs by name or id for jobs:run, jobs:runs, and jobs:rm, matching bucket/org resolution - cli: correct stale pk_live_/pk_test_ help text to the real prm_ format; align README command spellings and demo seed prefixes - setup: rewrite all public URL vars (auth, vite, backend) and cookie domain for the entered host — https for domains, http for IPs — with anchored full-line substitutions so stale ports/paths can't survive - docs: VITE_* vars never reach the frontend image build; the app resolves API/auth URLs same-origin at runtime, so host changes need no rebuild — noted HTTPS requirement for PWA/secure-context features Generated with [Devin](https://devin.ai) Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
153 lines
4.2 KiB
TypeScript
153 lines
4.2 KiB
TypeScript
import type { ScheduledJob, ScheduledJobRun } from "@primora/api-client";
|
|
import { AutomationService } from "@primora/api-client";
|
|
|
|
import { loadConfig } from "../config.js";
|
|
import { configureClient, requireAuth, requireProject, resolveJobId } from "../http.js";
|
|
import { formatDate, isJson, printJson, printTable, success } from "../out.js";
|
|
|
|
interface JobOptions {
|
|
project?: string;
|
|
json?: boolean;
|
|
}
|
|
|
|
function jobRow(job: ScheduledJob): string[] {
|
|
return [
|
|
job.name,
|
|
job.schedule,
|
|
job.url,
|
|
job.enabled ? "on" : "off",
|
|
job.last_status ?? "—",
|
|
job.last_run_at ? formatDate(job.last_run_at) : "never",
|
|
job.enabled && job.next_run_at ? formatDate(job.next_run_at) : "—",
|
|
job.id,
|
|
];
|
|
}
|
|
|
|
export async function cmdJobsList(options: JobOptions): Promise<void> {
|
|
const cfg = loadConfig();
|
|
requireAuth(cfg);
|
|
configureClient(cfg);
|
|
|
|
const projectId = requireProject(cfg, options.project);
|
|
const page = await AutomationService.listScheduledJobs({ projectId });
|
|
if (isJson(options)) {
|
|
printJson(page);
|
|
} else {
|
|
printTable(
|
|
["NAME", "SCHEDULE", "URL", "ENABLED", "LAST", "LAST RUN", "NEXT RUN", "ID"],
|
|
page.items.map(jobRow),
|
|
);
|
|
}
|
|
}
|
|
|
|
export async function cmdJobsCreate(
|
|
name: string,
|
|
options: JobOptions & {
|
|
schedule?: string;
|
|
url?: string;
|
|
payload?: string;
|
|
secret?: string;
|
|
disabled?: boolean;
|
|
},
|
|
): Promise<void> {
|
|
const cfg = loadConfig();
|
|
requireAuth(cfg);
|
|
configureClient(cfg);
|
|
|
|
if (!options.schedule) throw new Error("pass --schedule, e.g. '*/15 * * * *' or '@every 1h'");
|
|
if (!options.url) throw new Error("pass --url <https endpoint>");
|
|
|
|
let payload: Record<string, unknown> | undefined;
|
|
if (options.payload) {
|
|
try {
|
|
payload = JSON.parse(options.payload) as Record<string, unknown>;
|
|
} catch {
|
|
throw new Error("--payload must be valid JSON");
|
|
}
|
|
}
|
|
|
|
const projectId = requireProject(cfg, options.project);
|
|
const job = await AutomationService.createScheduledJob({
|
|
projectId,
|
|
requestBody: {
|
|
name,
|
|
schedule: options.schedule,
|
|
url: options.url,
|
|
secret: options.secret || undefined,
|
|
payload,
|
|
enabled: !options.disabled,
|
|
},
|
|
});
|
|
if (isJson(options)) {
|
|
printJson(job);
|
|
return;
|
|
}
|
|
success(`created ${job.name} (${job.id})`);
|
|
if (job.secret) {
|
|
process.stderr.write(`signing secret (shown once): ${job.secret}\n`);
|
|
}
|
|
}
|
|
|
|
export async function cmdJobsRemove(jobRef: string, options: JobOptions & { yes?: boolean }): Promise<void> {
|
|
const cfg = loadConfig();
|
|
requireAuth(cfg);
|
|
configureClient(cfg);
|
|
|
|
const projectId = requireProject(cfg, options.project);
|
|
const jobId = await resolveJobId(projectId, jobRef);
|
|
await AutomationService.deleteScheduledJob({ projectId, jobId });
|
|
if (isJson(options)) {
|
|
printJson({ deleted: jobId });
|
|
} else {
|
|
success(`deleted ${jobId}`);
|
|
}
|
|
}
|
|
|
|
export async function cmdJobsRun(jobRef: string, options: JobOptions): Promise<void> {
|
|
const cfg = loadConfig();
|
|
requireAuth(cfg);
|
|
configureClient(cfg);
|
|
|
|
const projectId = requireProject(cfg, options.project);
|
|
const jobId = await resolveJobId(projectId, jobRef);
|
|
const run = await AutomationService.runScheduledJob({ projectId, jobId });
|
|
if (isJson(options)) {
|
|
printJson(run);
|
|
} else {
|
|
success(`run queued (${run.id}) — status ${run.status}`);
|
|
}
|
|
}
|
|
|
|
function runRow(run: ScheduledJobRun): string[] {
|
|
return [
|
|
formatDate(run.started_at),
|
|
run.status,
|
|
run.triggered_by,
|
|
run.status_code != null ? String(run.status_code) : "—",
|
|
run.duration_ms != null ? `${run.duration_ms}ms` : "—",
|
|
run.error || "—",
|
|
];
|
|
}
|
|
|
|
export async function cmdJobsRuns(
|
|
jobRef: string,
|
|
options: JobOptions & { limit?: string },
|
|
): Promise<void> {
|
|
const cfg = loadConfig();
|
|
requireAuth(cfg);
|
|
configureClient(cfg);
|
|
|
|
const projectId = requireProject(cfg, options.project);
|
|
const jobId = await resolveJobId(projectId, jobRef);
|
|
const page = await AutomationService.listScheduledJobRuns({
|
|
projectId,
|
|
jobId,
|
|
limit: options.limit ? Number(options.limit) : 50,
|
|
});
|
|
if (isJson(options)) {
|
|
printJson(page);
|
|
} else {
|
|
printTable(["STARTED", "STATUS", "TRIGGER", "HTTP", "DURATION", "ERROR"], page.items.map(runRow));
|
|
}
|
|
}
|