Files
MineGeo/scripts/reprocess.mjs

139 lines
4.0 KiB
JavaScript

import { existsSync } from "node:fs";
import { createClient } from "@supabase/supabase-js";
import { prepareAssets } from "./assets.mjs";
if (existsSync(".env.local")) process.loadEnvFile(".env.local");
const id = process.argv[2];
if (
!id ||
!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id)
)
throw new Error(
"Usage: npm run reprocess -- <dataset UUID>. Stop the MineGeo worker first.",
);
if (!process.env.NEXT_PUBLIC_SUPABASE_URL || !process.env.SUPABASE_SECRET_KEY)
throw new Error("Configure Supabase in .env.local before reprocessing.");
const client = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.SUPABASE_SECRET_KEY,
{
auth: { persistSession: false, autoRefreshToken: false },
},
);
function check(result) {
if (result.error) throw new Error(result.error.message);
return result.data;
}
const dataset = check(
await client.from("datasets").select("*").eq("id", id).single(),
);
if (
dataset.revision_id ||
!["draft", "review", "rejected", "failed"].includes(dataset.state)
)
throw new Error(
"Only an unpublished, inactive draft can be reprocessed. Published revisions cannot be changed.",
);
const job = check(
await client.from("upload_jobs").select("*").eq("dataset_id", id).single(),
);
if (!["complete", "failed", "cancelled"].includes(job.state))
throw new Error(
"This job is already queued or running. Stop the worker and wait for it to finish before retrying.",
);
const input = check(
await client
.from("dataset_inputs")
.select("validated,manifest")
.eq("dataset_id", id)
.single(),
);
if (dataset.source === "plugin") await prepareAssets();
// This maintenance command runs with the worker stopped. Compare-and-set
// guards reject intervening publication, ownership of a job, or another repair.
const changed = check(
await client
.from("datasets")
.update({
state: "processing",
rejection_reason: null,
map_path: null,
cover_path: null,
panorama_count: 0,
updated_at: new Date().toISOString(),
})
.eq("id", id)
.eq("state", dataset.state)
.is("revision_id", null)
.select("id"),
);
if (changed.length !== 1)
throw new Error(
"Dataset state changed; nothing was queued. Run the command again.",
);
try {
check(
await client
.from("dataset_inputs")
.update({ validated: false })
.eq("dataset_id", id),
);
const queued = check(
await client
.from("upload_jobs")
.update({
state: "queued",
attempts: 0,
progress: 0,
error: null,
lease_owner: null,
lease_until: null,
updated_at: new Date().toISOString(),
})
.eq("id", job.id)
.eq("state", job.state)
.eq("updated_at", job.updated_at)
.select("id"),
);
if (queued.length !== 1)
throw new Error(
"Job state changed while queuing; stop the worker before retrying.",
);
} catch (error) {
// A transport error can occur after an update committed. If the job is
// queued/running, leave its dataset processing; otherwise restore its draft.
const current = await client
.from("upload_jobs")
.select("state")
.eq("id", job.id)
.single();
if (!current.error && !["queued", "running"].includes(current.data.state)) {
check(
await client
.from("datasets")
.update({
state: dataset.state,
rejection_reason: dataset.rejection_reason,
map_path: dataset.map_path,
cover_path: dataset.cover_path,
panorama_count: dataset.panorama_count,
updated_at: new Date().toISOString(),
})
.eq("id", id)
.eq("state", "processing")
.is("revision_id", null),
);
check(
await client
.from("dataset_inputs")
.update({ validated: input.validated })
.eq("dataset_id", id),
);
}
throw error;
}
console.log(
`Queued dataset ${id} for a fresh render. Start MineGeo; no new export or upload is needed.`,
);