mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-11 15:04:52 +09:00
Merge pull request #981 from bbassie/fix/static-root-web-root
Make staticRoot include the static/ prefix and use it for plugin bundles
This commit is contained in:
9 files changed
+102
-7
No files matched your search
@@ -1217,7 +1217,7 @@ export default class Controller {
|
||||
}
|
||||
|
||||
/**
|
||||
* Servers the web interface with the root path set apropriately
|
||||
* Serves the web interface with the root path set appropriately
|
||||
*
|
||||
* @param route - route the interface is served under.
|
||||
* @returns Experess.js route handler.
|
||||
@@ -1231,7 +1231,7 @@ export default class Controller {
|
||||
return function(req: Request, res: Response, next: NextFunction) {
|
||||
let depth = routeDepth + Number(req.path.slice(-1) === "/");
|
||||
let webRoot = "../".repeat(depth) || "./";
|
||||
let staticRoot = webRoot;
|
||||
let staticRoot = `${webRoot}static/`;
|
||||
let mainBundle: string = "";
|
||||
if (res.app.locals.mainBundle) {
|
||||
mainBundle = res.app.locals.mainBundle;
|
||||
@@ -1239,6 +1239,7 @@ export default class Controller {
|
||||
let stats = res.locals.webpack.devMiddleware.stats.stats[0];
|
||||
mainBundle = stats.toJson().assetsByChunkName["main"];
|
||||
}
|
||||
mainBundle = routes.stripStaticPrefix(mainBundle);
|
||||
|
||||
fs.readFile(path.join(__dirname, "..", "..", "..", "web", "index.html"), "utf8").then((content) => {
|
||||
res.type("text/html");
|
||||
|
||||
@@ -104,6 +104,14 @@ async function getMetrics(req: Request, res: Response, next: any) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes the static/ prefix webpack adds to bundle paths so they can be
|
||||
* resolved relative to staticRoot.
|
||||
*/
|
||||
export function stripStaticPrefix(bundlePath: string) {
|
||||
return bundlePath.replace(/^static\//, "");
|
||||
}
|
||||
|
||||
function getPlugins(req: Request, res: Response) {
|
||||
let plugins: lib.PluginWebApi[] = [];
|
||||
for (let pluginInfo of req.app.locals.controller.pluginInfos) {
|
||||
@@ -129,6 +137,9 @@ function getPlugins(req: Request, res: Response) {
|
||||
if (web.main === "remoteEntry.js") {
|
||||
web.error = "Incompatible old remoteEntry.js entrypoint.";
|
||||
}
|
||||
if (web.main) {
|
||||
web.main = stripStaticPrefix(web.main);
|
||||
}
|
||||
plugins.push({ name, version: pluginInfo.version, enabled, loaded, web, npmPackage: pluginInfo.npmPackage });
|
||||
}
|
||||
res.send(plugins);
|
||||
|
||||
Vendored
+2
@@ -13,7 +13,9 @@ declare module "*.md" {
|
||||
export default value;
|
||||
}
|
||||
|
||||
/** Path the web interface is served under, ends with a slash. */
|
||||
declare const webRoot: string;
|
||||
/** Path static assets are served under, ends with a slash. */
|
||||
declare const staticRoot: string;
|
||||
|
||||
declare const __webpack_init_sharing__: (shareScope: string) => Promise<void>;
|
||||
|
||||
@@ -50,7 +50,7 @@ async function loadPluginInfos(): Promise<[lib.PluginWebpackEnvInfo[], string]>
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
await loadScript(`${webRoot}${meta.web.main}`);
|
||||
await loadScript(`${staticRoot}${meta.web.main}`);
|
||||
let container: any = (window as { [key: string]: any })[`plugin_${meta.name}`];
|
||||
if (!container) {
|
||||
throw new Error(`Plugin did not expose its container via plugin_${meta.name}`);
|
||||
|
||||
@@ -1102,7 +1102,7 @@ function useExportedAsset(modPack: lib.ModPack | undefined, asset: "settings"|"l
|
||||
return;
|
||||
}
|
||||
|
||||
let response = await fetch(`${staticRoot}static/${assetFilename}`);
|
||||
let response = await fetch(`${staticRoot}${assetFilename}`);
|
||||
if (response.ok) {
|
||||
let data = await response.json();
|
||||
setAssetData(asset === "locale" ? new Map(data) : data);
|
||||
|
||||
@@ -21,7 +21,7 @@ class ExportLocaleStore {
|
||||
return;
|
||||
}
|
||||
|
||||
let response = await fetch(`${staticRoot}static/${cacheEntry.localePath}`);
|
||||
let response = await fetch(`${staticRoot}${cacheEntry.localePath}`);
|
||||
if (response.ok) {
|
||||
cacheEntry.data = new Map(await response.json() as ExportLocale);
|
||||
for (const callback of cacheEntry.callbacks) {
|
||||
|
||||
@@ -26,7 +26,7 @@ class ExportPrototypeMetadataStore {
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await fetch(`${staticRoot}static/${cacheEntry.metadataPath}`);
|
||||
const response = await fetch(`${staticRoot}${cacheEntry.metadataPath}`);
|
||||
if (!response.ok) {
|
||||
notify(
|
||||
`Failed to fetch prototype metadata for mod pack ${modPackId}, server returned: ` +
|
||||
@@ -43,7 +43,7 @@ class ExportPrototypeMetadataStore {
|
||||
|
||||
const metadata = await response.json() as ExportMetadata;
|
||||
|
||||
const spriteUrl = `${staticRoot}static/${cacheEntry.spritesheetPath}`;
|
||||
const spriteUrl = `${staticRoot}${cacheEntry.spritesheetPath}`;
|
||||
const styleContent: string[] = [];
|
||||
styleContent.push(`\
|
||||
.factorio-icon\
|
||||
|
||||
@@ -3,6 +3,9 @@ const path = require("node:path");
|
||||
const assert = require("assert").strict;
|
||||
const { Controller, HostRecord, InstanceRecord, UserRecord } = require("@clusterio/controller");
|
||||
const { EventEmitter } = require("stream");
|
||||
const events = require("node:events");
|
||||
const http = require("node:http");
|
||||
const express = require("express");
|
||||
|
||||
const {
|
||||
ControllerConfig, Address, RequestError,
|
||||
@@ -48,6 +51,40 @@ describe("controller/src/Controller", function() {
|
||||
assert(list.check(ip), `${ip} missing from list`);
|
||||
}
|
||||
}
|
||||
describe(".serveWeb()", function() {
|
||||
let server, port;
|
||||
before(async function() {
|
||||
const app = express();
|
||||
app.locals.controller = { config: { get: () => "Test Cluster" } };
|
||||
app.locals.mainBundle = "static/main.abc.js";
|
||||
for (let route of ["/", "/instances/:id/view"]) {
|
||||
app.get(route, Controller.serveWeb(route));
|
||||
}
|
||||
server = http.createServer(app);
|
||||
server.listen(0, "localhost");
|
||||
await events.once(server, "listening");
|
||||
port = server.address().port;
|
||||
});
|
||||
after(function() {
|
||||
server.close();
|
||||
});
|
||||
it("should serve the root with static/ in staticRoot", async function() {
|
||||
const html = await (await fetch(`http://localhost:${port}/`)).text();
|
||||
assert(html.includes("<title>Test Cluster</title>"));
|
||||
assert(html.includes("src=\"./static/main.abc.js\""));
|
||||
assert(html.includes("new URL(\"./\", document.location)"));
|
||||
assert(html.includes("new URL(\"./static/\", document.location)"));
|
||||
});
|
||||
it("should use relative roots for nested routes", async function() {
|
||||
let html = await (await fetch(`http://localhost:${port}/instances/1/view`)).text();
|
||||
assert(html.includes("src=\"../../static/main.abc.js\""));
|
||||
assert(html.includes("new URL(\"../../\", document.location)"));
|
||||
assert(html.includes("new URL(\"../../static/\", document.location)"));
|
||||
html = await (await fetch(`http://localhost:${port}/instances/1/view/`)).text();
|
||||
assert(html.includes("src=\"../../../static/main.abc.js\""));
|
||||
assert(html.includes("new URL(\"../../../static/\", document.location)"));
|
||||
});
|
||||
});
|
||||
describe(".parseTrustedProxies()", function() {
|
||||
it("should parse addresses", function() {
|
||||
controller.config.set("controller.trusted_proxies", "10.0.0.1");
|
||||
|
||||
@@ -73,6 +73,50 @@ describe("controller/src/routes", function() {
|
||||
assert.equal(await responses[1].text(), "test content");
|
||||
});
|
||||
});
|
||||
describe("stripStaticPrefix()", function() {
|
||||
it("should remove a leading static/", function() {
|
||||
assert.equal(routes.stripStaticPrefix("static/main.abc.js"), "main.abc.js");
|
||||
});
|
||||
it("should leave other paths alone", function() {
|
||||
assert.equal(routes.stripStaticPrefix("main.abc.js"), "main.abc.js");
|
||||
assert.equal(routes.stripStaticPrefix("other/static/main.js"), "other/static/main.js");
|
||||
});
|
||||
});
|
||||
describe("/api/plugins", function() {
|
||||
let endpoint;
|
||||
beforeEach(function() {
|
||||
endpoint = `http://localhost:${port}/api/plugins`;
|
||||
controller.plugins = new Map([["foo", {}]]);
|
||||
controller.mockConfigEntries.set("foo.load_plugin", true);
|
||||
});
|
||||
it("should strip static/ from the plugin bundle path", async function() {
|
||||
controller.pluginInfos = [{
|
||||
name: "foo", version: "1.0.0", npmPackage: "foo",
|
||||
manifest: { "foo.js": "static/foo.abc.js" },
|
||||
}];
|
||||
let response = await fetch(endpoint);
|
||||
assert.equal(response.status, 200);
|
||||
assert.deepEqual(await response.json(), [{
|
||||
name: "foo", version: "1.0.0", enabled: true, loaded: true, npmPackage: "foo",
|
||||
web: { main: "foo.abc.js" },
|
||||
}]);
|
||||
});
|
||||
it("should report missing manifest and entrypoint", async function() {
|
||||
controller.pluginInfos = [
|
||||
{ name: "foo", version: "1.0.0", npmPackage: "foo" },
|
||||
{ name: "bar", version: "1.0.0", npmPackage: "bar", manifest: {} },
|
||||
{ name: "baz", version: "1.0.0", npmPackage: "baz", manifest: { "baz.js": "remoteEntry.js" } },
|
||||
];
|
||||
let response = await fetch(endpoint);
|
||||
assert.equal(response.status, 200);
|
||||
let data = await response.json();
|
||||
assert.equal(data[0].web.error, "Missing dist/web/manifest.json");
|
||||
assert.equal(data[1].web.error, "Missing bar.js entry in manifest.json");
|
||||
assert.equal(data[2].web.error, "Incompatible old remoteEntry.js entrypoint.");
|
||||
assert.equal(data[1].loaded, false);
|
||||
assert.equal(data[1].enabled, false);
|
||||
});
|
||||
});
|
||||
describe("/api/cluster-name", function() {
|
||||
let endpoint;
|
||||
beforeEach(function() {
|
||||
|
||||
Reference in new issue
Block a user