Serve interface on all routes for the interface

Add all routes the web interface is served on to the express app and
serve the index.html file on.  Due to the index html not knowing which
path it's hosted under, a path to the root of the web interface is
replaced using string substitution from the master server.
This commit is contained in:
Hornwitser committed 2020-09-28 05:51:11 +02:00
1 parent 392a7655c4
commit 94200433cc
9 files changed
+173 -162

No files matched your search

+31
View File
@@ -40,6 +40,7 @@ let devMiddleware;
// homebrew modules
const generateSSLcert = require("./src/generate_ssl_cert");
const routes = require("./src/routes");
const database = require("@clusterio/lib/database");
const schema = require("@clusterio/lib/schema");
const link = require("@clusterio/lib/link");
@@ -71,6 +72,26 @@ app.use(bodyParser.urlencoded({
app.use(fileUpload());
app.use(compression());
// Servers the web interface with the root path set apropriately.
function serveWeb(route) {
// The depth is is the number of slashes in the route minus one, but due
// to lenient matching on trailing slashes in the route we need to
// compensate if the request path contains a slash but not the route,
// and vice versa.
let routeDepth = (route.match(/\//g) || []).length - 1 - (route.slice(-1) === "/");
return function(req, res, next) {
let depth = routeDepth + (req.path.slice(-1) === "/");
let webRoot = "../".repeat(depth) || "./";
fs.readFile(path.join(__dirname, "web", "index.html"), "utf8").then((content) => {
res.type("text/html");
res.send(content.replace(/__WEB_ROOT__/g, webRoot));
}).catch(err => {
next(err);
});
};
}
// Set folder to serve static content from (the website)
app.use(express.static(path.join(__dirname, "static")));
app.use(express.static("static")); // Used for data export files
@@ -1698,6 +1719,16 @@ async function startServer() {
});
}
// Add routes for the web interface
for (let route of routes) {
app.get(route, serveWeb(route));
}
for (let pluginInfo of pluginInfos) {
for (let route of pluginInfo.routes || []) {
app.get(route, serveWeb(route));
}
}
// Load plugins
await pluginManagement(pluginInfos);
-1
View File
@@ -43,7 +43,6 @@
"clean-webpack-plugin": "^3.0.0",
"css-loader": "^4.3.0",
"fast-deep-equal": "^3.1.3",
"html-webpack-plugin": "^4.5.0",
"null-loader": "^4.0.0",
"react": "^16.13.1",
"react-dom": "^16.13.1",
+16
View File
@@ -0,0 +1,16 @@
"use strict";
// Routes used in the web interface and served by the master server
module.exports = [
"/",
"/instances",
"/instances/:id/view",
"/slaves",
"/slaves/:id/view",
"/users",
"/users/:id/view",
"/roles",
"/roles/:id/view",
"/permissions",
"/permissions/:id/view",
"/settings",
];
+6
View File
@@ -0,0 +1,6 @@
// Calculate basedir of application based on current location and
// the provided web_root from master server.
const location = new URL(window.web_root, document.location)
const basename = location.pathname !== "/" ? location.pathname : "";
export default basename;
+13
View File
@@ -0,0 +1,13 @@
import React from "react";
import { BrowserRouter } from "react-router-dom";
import basename from "../basename";
import Layout from "../layout";
export default function App() {
return (
<BrowserRouter basename={basename}>
<Layout />
</BrowserRouter>
);
}
+4
View File
@@ -4,9 +4,13 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Clusterio</title>
<script>
window.web_root = "__WEB_ROOT__";
</script>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script src="__WEB_ROOT__bundle.js"></script>
</body>
</html>
+4 -9
View File
@@ -1,13 +1,8 @@
import "./index.css"
import React from "react"
import ReactDOM from "react-dom"
import { BrowserRouter } from "react-router-dom"
import "./index.css"
import Layout from "./layout"
import App from "./components/App";
ReactDOM.render(
<BrowserRouter>
<Layout />
</BrowserRouter>,
document.getElementById('root')
);
ReactDOM.render(<App />, document.getElementById("root"));
+99 -148
View File
@@ -1,7 +1,6 @@
import { Layout, Menu } from "antd";
import { withRouter } from "react-router-dom";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import { Route, Link, withRouter } from "react-router-dom";
import React, { Component } from "react";
import { Dashboard } from "../pages/dashboard";
@@ -24,8 +23,6 @@ class SiteLayout extends Component {
super(props);
const { location } = props;
// this.history = useHistory();
this.state = {
collapsed: false,
setcollapsed: false,
@@ -58,156 +55,110 @@ class SiteLayout extends Component {
render() {
return (
<Router>
<Layout style={{ minHeight: "100vh" }}>
{document.location.search !== "?hideExtras" ? (
<Sider
collapsible
collapsed={this.state.collapsed}
onCollapse={this.onCollapse.bind(this)}
width={250}
breakpoint="lg"
>
<p></p>
<div style={{ width: "100%", textAlign: "center" }}>
{" "}
{this.state.collapsed ? (
<Layout style={{ minHeight: "100vh" }}>
{document.location.search !== "?hideExtras" ? (
<Sider
collapsible
collapsed={this.state.collapsed}
onCollapse={this.onCollapse.bind(this)}
width={250}
breakpoint="lg"
>
<p></p>
<div style={{ width: "100%", textAlign: "center" }}>
{" "}
{this.state.collapsed ? (
<img
alt=""
src="/smal_logo.png"
style={{ height: "48px", margin: 8 }}
/>
) : (
<img
alt=""
src="/smal_logo.png"
style={{ height: "48px", margin: 8 }}
src="/logo.png"
style={{ width: "200px", margin: 8 }}
/>
) : (
<img
alt=""
src="/logo.png"
style={{ width: "200px", margin: 8 }}
/>
)}
</div>
<p></p>
<p></p>
)}
</div>
<p></p>
<p></p>
<Menu
theme="dark"
defaultSelectedKeys={[
this.state.location.pathname.split("/")[
this.state.location.pathname.split("/").length - 1
],
]}
defaultOpenKeys={(() => {
let x = this.state.location.pathname.split("/");
x.shift();
return x;
})()}
mode="inline"
>
<Menu.Item key="slaves">
<span>Slaves</span>
<Link to="/slaves" />
</Menu.Item>
<Menu.Item key="instances">
<span>Instances</span>
<Link to="/instances" />
</Menu.Item>
<Menu.Item key="users">
<span>Users</span>
<Link to="/users" />
</Menu.Item>
<Menu.Item key="roles">
<span>Roles</span>
<Link to="/roles" />
</Menu.Item>
<Menu.Item key="permissions">
<span>Permissions</span>
<Link to="/permissions" />
</Menu.Item>
<Menu.Item key="settings">
<span>Settings</span>
<Link to="/settings" />
</Menu.Item>
</Menu>
</Sider>
) : (
""
)}
<Layout style={{
// background: "#F4F4F4"
}}>
<Content
style={{
margin: "24px 16px",
padding: 24,
// background: "#fff",
minHeight: 280,
}}
<Menu
theme="dark"
defaultSelectedKeys={[
this.state.location.pathname.split("/")[
this.state.location.pathname.split("/").length - 1
],
]}
defaultOpenKeys={(() => {
let x = this.state.location.pathname.split("/");
x.shift();
return x;
})()}
mode="inline"
>
<Route exact path="/" component={Dashboard} />
<Route
exact
path="/instances"
component={InstancesTable}
/>
<Route
exact
path="/instances/:id/view"
component={InstanceView}
/>
<Route
exact
path="/slaves"
component={SlavesTable}
/>
<Route
exact
path="/slaves/:id/view"
component={SlavesTable}
/>
<Route
exact
path="/users"
component={UsersTable}
/>
<Route
exact
path="/users/:id/view"
component={UserView}
/>
<Route
exact
path="/roles"
component={RolesTable}
/>
<Route
exact
path="/roles/:id/view"
component={RoleView}
/>
<Route
exact
path="/permissions"
component={PermissionsTable}
/>
<Route
exact
path="/permissions/:id/view"
component={PermissionsTable}
/>
<Route
exact
path="/settings"
component={Settings}
/>
</Content>
<Footer style={{
// background: "#F4F4F4",
textAlign: "center"
}}>
Clusterio 2.0 web interface https://github.com/clusterio
</Footer>
</Layout>
<Menu.Item key="slaves">
<span>Slaves</span>
<Link to="/slaves" />
</Menu.Item>
<Menu.Item key="instances">
<span>Instances</span>
<Link to="/instances" />
</Menu.Item>
<Menu.Item key="users">
<span>Users</span>
<Link to="/users" />
</Menu.Item>
<Menu.Item key="roles">
<span>Roles</span>
<Link to="/roles" />
</Menu.Item>
<Menu.Item key="permissions">
<span>Permissions</span>
<Link to="/permissions" />
</Menu.Item>
<Menu.Item key="settings">
<span>Settings</span>
<Link to="/settings" />
</Menu.Item>
</Menu>
</Sider>
) : (
""
)}
<Layout style={{
// background: "#F4F4F4"
}}>
<Content
style={{
margin: "24px 16px",
padding: 24,
// background: "#fff",
minHeight: 280,
}}
>
<Route exact path="/" component={Dashboard} />
<Route exact path="/instances" component={InstancesTable} />
<Route exact path="/instances/:id/view" component={InstanceView} />
<Route exact path="/slaves" component={SlavesTable} />
<Route exact path="/slaves/:id/view" component={SlavesTable} />
<Route exact path="/users" component={UsersTable} />
<Route exact path="/users/:id/view" component={UserView} />
<Route exact path="/roles" component={RolesTable} />
<Route exact path="/roles/:id/view" component={RoleView} />
<Route exact path="/permissions" component={PermissionsTable} />
<Route exact path="/permissions/:id/view" component={PermissionsTable} />
<Route exact path="/settings" component={Settings} />
</Content>
<Footer style={{
// background: "#F4F4F4",
textAlign: "center"
}}>
Clusterio 2.0 web interface https://github.com/clusterio
</Footer>
</Layout>
</Router>
</Layout>
);
}
}
-4
View File
@@ -1,7 +1,6 @@
"use strict";
const path = require("path");
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = (env = {}) => ({
@@ -20,9 +19,6 @@ module.exports = (env = {}) => ({
new webpack.DefinePlugin({
"process.env.APP_ENV": JSON.stringify("browser"),
}),
new HtmlWebpackPlugin({
template: "web/index.html",
}),
// Make sure ant-design icons use the ES variant
new webpack.NormalModuleReplacementPlugin(