Fail fast on missing terrain assets and recover empty renders
This commit is contained in:
138
scripts/reprocess.mjs
Normal file
138
scripts/reprocess.mjs
Normal file
@@ -0,0 +1,138 @@
|
||||
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.`,
|
||||
);
|
||||
Reference in New Issue
Block a user