mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-13 15:14:52 +09:00
Merge pull request #1154 from bbassie/fix/deleted-exports
Handle deleted export files
This commit is contained in:
1 parent
d7d1859aa0
commit
1b87497e15
7 files changed
+178
-25
No files matched your search
@@ -226,6 +226,8 @@ export default class Controller {
|
||||
await fs.mkdir(modsDirectory, { recursive: true });
|
||||
const modStore = await lib.ModStore.fromDirectory(modsDirectory);
|
||||
|
||||
await this.validateExports(modPacks, "static");
|
||||
|
||||
// Add default mod packs
|
||||
if (modPacks.size === 0) {
|
||||
modPacks.setMany(lib.ModPack.defaultModPacks);
|
||||
@@ -876,6 +878,37 @@ export default class Controller {
|
||||
});
|
||||
}
|
||||
|
||||
/** Remove export manifests that reference files missing from staticDirectory */
|
||||
static async validateExports(modPacks: lib.SubscribableDatastore<lib.ModPack>, staticDirectory: string) {
|
||||
const invalid: lib.ModPack[] = [];
|
||||
for (const modPack of modPacks.valuesMutable()) {
|
||||
if (!modPack.exportManifest) {
|
||||
continue;
|
||||
}
|
||||
const missing: string[] = [];
|
||||
for (const file of Object.values(modPack.exportManifest.assets)) {
|
||||
const valid = path.basename(file) === file
|
||||
&& await fs.stat(path.join(staticDirectory, file)).then(s => s.isFile(), () => false);
|
||||
if (!valid) {
|
||||
missing.push(file);
|
||||
}
|
||||
}
|
||||
if (missing.length) {
|
||||
Controller.dropExport(modPack, `missing ${missing.join(", ")}`);
|
||||
invalid.push(modPack);
|
||||
}
|
||||
}
|
||||
if (invalid.length) {
|
||||
modPacks.setMany(invalid);
|
||||
}
|
||||
}
|
||||
|
||||
/** Clear the export manifest of a mutable mod pack, caller must save it */
|
||||
static dropExport(modPack: lib.ModPack, reason: string) {
|
||||
logger.warn(`Removing export data of mod pack ${modPack.name} (id ${modPack.id}): ${reason}`);
|
||||
modPack.exportManifest = undefined;
|
||||
}
|
||||
|
||||
static migrateRoles(rawJson: unknown[]): Static<typeof lib.Role.jsonSchema>[] {
|
||||
const serialized = rawJson as Static<typeof lib.Role.jsonSchema>[];
|
||||
// migrate: host token permissions were removed with host pairing in 2.1
|
||||
@@ -984,6 +1017,7 @@ export default class Controller {
|
||||
express.static(path.join(import.meta.dirname, "..", "..", "web", "static"), staticOptions)
|
||||
);
|
||||
app.use("/static", express.static("static", staticOptions)); // Used for data export files
|
||||
app.use("/static", routes.missingExportAsset);
|
||||
|
||||
// Add API routes
|
||||
routes.addRouteHandlers(app);
|
||||
|
||||
@@ -394,6 +394,30 @@ async function uploadExport(req: Request, res: Response) {
|
||||
res.sendStatus(200);
|
||||
}
|
||||
|
||||
// Reached when no static file matched, drops exports whose files were removed
|
||||
export function missingExportAsset(req: Request, res: Response, next: any) {
|
||||
const file = req.path.slice(1);
|
||||
if (!["GET", "HEAD"].includes(req.method) || !file) {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
const modPacks = res.app.locals.controller.modPacks;
|
||||
const affected = [...modPacks.valuesMutable()].filter(
|
||||
modPack => modPack.exportManifest && Object.values(modPack.exportManifest.assets).includes(file)
|
||||
);
|
||||
if (!affected.length) {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
for (const modPack of affected) {
|
||||
Controller.dropExport(modPack, `${file} was removed from disk`);
|
||||
}
|
||||
modPacks.setMany(affected);
|
||||
res.status(404).type("text/plain").send(
|
||||
`Export data file ${file} is missing, run export-data on an instance to regenerate it`
|
||||
);
|
||||
}
|
||||
|
||||
type ProxyStreamEvents = {
|
||||
"error": [ err: any ],
|
||||
"close": [],
|
||||
|
||||
Reference in new issue
Block a user