Automatically upload export to master server

Upload the created export data zip file to a new /api/upload-export
endpoint on the master server which unpacks it into the appropriate
directory on the master.
This commit is contained in:
Hornwitser committed 2020-05-26 15:31:42 +02:00
1 parent f3d4cfdcf5
commit 36cf14074e
5 files changed
+62 -4

No files matched your search

+2 -2
View File
@@ -306,6 +306,7 @@ async function exportLocale(server, modVersions, modOrder, languageCode) {
*
* @param {module:lib/factorio.FactorioServer} server -
* The server to export the data from.
* @returns {JSZip} zip file with exported data.
* @memberof module:lib/factorio
*/
async function exportData(server) {
@@ -347,8 +348,7 @@ async function exportData(server) {
zip.file("export/item-spritesheet.png", await iconSheet.getBufferAsync(Jimp.MIME_PNG));
zip.file("export/item-metadata.json", JSON.stringify([...itemData.entries()]));
zip.file("export/locale.json", JSON.stringify([...locale.entries()]));
let content = await zip.generateAsync({ type: "nodebuffer" });
await fs.outputFile("temp/export.zip", content);
return zip;
}
+40
View File
@@ -27,6 +27,7 @@ const setBlocking = require("set-blocking");
const events = require("events");
const yargs = require("yargs");
const WebSocket = require("ws");
const JSZip = require("jszip");
const version = require("./package").version;
// ugly globals
@@ -174,6 +175,45 @@ async function getMetrics(req, res, next) {
}
app.get('/metrics', (req, res, next) => getMetrics(req, res, next).catch(next));
// Handle an uploaded export package.
async function uploadExport(req, res, next) {
endpointHitCounter.labels(req.route.path).inc();
if (req.get("Content-Type") !== "application/zip") {
res.sendStatus(415);
return;
}
let data = [];
for await (let chunk of req) {
data.push(chunk);
}
data = Buffer.concat(data);
let zip = await JSZip.loadAsync(data);
delete data;
// This is hardcoded to prevent path expansion attacks
let exportFiles = [
"export/item-spritesheet.png",
"export/item-metadata.json",
"export/locale.json",
];
for (let filePath of exportFiles) {
let file = zip.file(filePath);
if (!file) {
continue;
}
let name = path.posix.basename(filePath);
await fs.outputFile(path.join("static", "export", name), await file.async("nodebuffer"));
}
res.sendStatus(200);
}
app.put("/api/upload-export",
authenticate.middleware,
(req, res, next) => uploadExport(req, res, next).catch(next)
);
const masterConnectedClientsCount = new prometheus.Gauge(
'clusterio_master_connected_clients_count', "How many clients are currently connected to this master server",
+1
View File
@@ -55,6 +55,7 @@
"needle": "^2.4.0",
"node-factorio-api": "^0.3.8",
"node-forge": "^0.7.1",
"phin": "^3.4.1",
"pidusage": "^2.0.18",
"rcon-client": "^4.2.0",
"request": "^2.83.0",
+18 -1
View File
@@ -16,6 +16,7 @@ const yargs = require("yargs");
const events = require("events");
const pidusage = require("pidusage");
const setBlocking = require("set-blocking");
const phin = require("phin");
const version = require("./package").version;
// internal libraries
@@ -446,7 +447,23 @@ class Instance extends link.Link{
console.log("Exporting data .....");
await symlinkMods(this, "sharedMods", console);
await factorio.exportData(this.server);
let zip = await factorio.exportData(this.server);
let content = await zip.generateAsync({ type: "nodebuffer" });
let url = new URL(this._slave.config.get("slave.master_url"));
url.pathname += "api/upload-export";
let response = await phin({
url, method: "PUT",
data: content,
core: { rejectUnauthorized: false },
headers: {
"Content-Type": "application/zip",
"x-access-token": this._slave.config.get("slave.master_token"),
},
});
if (response.statusCode !== 200) {
throw Error(`Upload failed: ${response.statusCode} ${response.statusMessage}: ${response.body}`)
}
} finally {
this.notifyExit();
+1 -1
View File
@@ -62,7 +62,7 @@ describe("Integration of Clusterio", function() {
it("exports the data", async function() {
slowTest(this);
await exec(`node clusterctl --config ${controlConfigPath} export-data --instance test`);
assert(await fs.exists(path.join("temp", "export.zip")), "Export was not created");
assert(await fs.exists(path.join("static", "export", "locale.json")), "Export was not created");
});
});