Add command for copying static assets to a folder

This is useful in case the static assets are hosted by a separate web
server like Nginx, or for testing that the static_url option works as
intended.
This commit is contained in:
Hornwitser committed 2026-08-31 20:14:13 +02:00
1 parent 7c0d7faa24
commit 71f78c14a3
3 files changed
+72

No files matched your search

+10
View File
@@ -104,6 +104,16 @@ This can be used both in the web interface and as the `controller_token` to conn
Creates a clusterioctl config for the given user with url and token set up for connecting to the cluster.
### `copy-static [target]`
Copies the static web assets to the given target folder (which defaults to `./static/`).
Useful in case you want to host the assets on a CDN or separate web server and want provide the files directly instead of fetching them from the controller.
Note that mod pack exports will create additional static files that also have to be hosted, these are written to the `./static/` folder in the clusterio installation directory.
See also the `controller.public_url` config option for setting where static assets are loaded from.
### `run`
Runs the controller.
+43
View File
@@ -211,6 +211,40 @@ async function handleBootstrapCommand(
}
}
async function handleCopyStaticCommand(
args: any,
pluginInfos: lib.PluginNodeEnvInfo[],
) {
await fs.cp(
path.join(__dirname, "..", "web", "static"),
args.target,
{
recursive: true,
mode: fs.constants.COPYFILE_FICLONE,
},
);
logger.info("Copied main web interface files");
for (const pluginInfo of pluginInfos) {
try {
await fs.cp(
pluginInfo.webStaticPath,
args.target,
{
recursive: true,
mode: fs.constants.COPYFILE_FICLONE,
},
);
} catch (err: any) {
if (err.code === "ENOENT") {
logger.info(`Skipped web interface files for ${pluginInfo.name}: ${err.message}`);
} else {
logger.error(`Unexpected error copying web interface files for ${pluginInfo.name}:\n${err.stack}`);
process.exitCode = 1;
}
}
logger.info(`Copied web interface files for ${pluginInfo.name}`);
}
}
export interface ControllerArgs {
[x: string]: unknown;
@@ -284,6 +318,13 @@ async function initialize(): Promise<InitializeParameters> {
})
.demandCommand(1, "You need to specify a command to run");
})
.command("copy-static [target]", "Copy static Web UI files to a folder", yargs => {
yargs.positional("target", {
describe: "Target directory to copy files to",
type: "string",
default: "static",
});
})
.command("run", "Run controller", yargs => {
yargs.option("can-restart", {
type: "boolean", nargs: 0, default: false,
@@ -399,6 +440,8 @@ async function initialize(): Promise<InitializeParameters> {
await lib.handleConfigCommand(args, controllerConfig, controllerConfigLock);
} else if (command === "bootstrap") {
await handleBootstrapCommand(args, controllerConfig, controllerConfigLock);
} else if (command === "copy-static") {
await handleCopyStaticCommand(args, pluginInfos);
} else if (shouldRun) {
await controllerConfigLock.acquire(); // Hold the lock until process exit
}
+19
View File
@@ -140,6 +140,25 @@ describe("Integration of Clusterio", function() {
});
});
describe("copy-static", function() {
it("should copy the files from dist/web/static", async function() {
const dir = "temp/test/copy-static";
await fs.rm(dir, { force: true, recursive: true, maxRetries: 10 });
await execController("copy-static copy-static");
const controllerFiles = await fs.readdir("packages/controller/dist/web/static");
const globalChatFiles = await fs.readdir("plugins/global_chat/dist/web/static");
const targetFiles = await fs.readdir(dir);
assert(
controllerFiles.every(name => targetFiles.includes(name)),
"Missing files from controller static folder",
);
assert(
globalChatFiles.every(name => targetFiles.includes(name)),
"Missing files from global_chat's static folder",
);
});
});
describe("config", function() {
it("can read the config file", async function() {
await execController("config list");