WIP: Integrate GraphQL API in React templater

feature/node-rewrite
Sven Slootweg 6 years ago
parent 37daf9a628
commit f8f35c5dcf

@ -4,7 +4,7 @@
"description": "A VPS management panel",
"main": "index.js",
"scripts": {
"dev": "NODE_ENV=development nodemon --ext js,pug --ignore node_modules --ignore src/client bin/server.js"
"dev": "NODE_ENV=development nodemon --ext js,pug,jsx --ignore node_modules --ignore src/client bin/server.js"
},
"repository": {
"type": "git",
@ -13,6 +13,8 @@
"author": "Sven Slootweg",
"license": "WTFPL",
"dependencies": {
"@babel/register": "^7.4.0",
"@joepie91/express-react-views": "^1.0.1",
"@joepie91/gulp-partial-patch-livereload-logger": "^1.0.1",
"JSONStream": "^1.1.4",
"array.prototype.flat": "^1.2.1",
@ -23,13 +25,16 @@
"body-parser": "^1.15.2",
"capitalize": "^2.0.0",
"checkit": "^0.7.0",
"classnames": "^2.2.6",
"create-error": "^0.3.1",
"create-event-emitter": "^1.0.0",
"dataloader": "^1.4.0",
"debounce": "^1.0.0",
"debug": "^4.1.1",
"default-value": "^1.0.0",
"dotty": "^0.1.0",
"end-of-stream": "^1.1.0",
"escape-string-regexp": "^2.0.0",
"execall": "^1.0.0",
"express": "^4.14.0",
"express-promise-router": "^1.1.0",
@ -40,8 +45,10 @@
"joi": "^14.3.0",
"knex": "^0.13.0",
"map-obj": "^3.0.0",
"memoizee": "^0.4.14",
"pg": "^6.1.0",
"pug": "^2.0.0-beta6",
"react-dom": "^16.8.6",
"rfr": "^1.2.3",
"scrypt-for-humans": "^2.0.5",
"snake-case": "^2.1.0",
@ -76,7 +83,7 @@
"json-loader": "^0.5.4",
"listening": "^0.1.0",
"nodemon": "^1.18.11",
"react": "^16.6.3",
"react": "^16.8.6",
"react-hot-loader": "^4.3.12",
"riot": "^3.6.1",
"riotjs-loader": "^4.0.0",

@ -1,11 +1,14 @@
'use strict';
const Promise = require("bluebird");
const express = require("express");
// const expressWs = require("express-ws");
const knex = require("knex");
const path = require("path");
const bodyParser = require("body-parser");
const expressAsyncReact = require("./express-async-react");
function projectPath(targetPath) {
return path.join(__dirname, "..", targetPath);
}
@ -14,14 +17,45 @@ module.exports = function () {
let db = knex(require("../knexfile"));
let imageStore = require("./image-store")(projectPath("./images"));
let taskTracker = require("../lib/tasks/tracker")();
let apiQuery = require("./api")();
let state = {db, imageStore, taskTracker};
let app = express();
// expressWs(app);
app.set("view engine", "pug");
app.set("views", projectPath("views"));
app.engine("jsx", expressAsyncReact.createEngine({
prepare: (template, locals) => {
return Promise.try(() => {
if (template.query != null) {
let queryArguments = (template.queryArguments != null)
? template.queryArguments(locals)
: {};
return apiQuery(template.query, queryArguments);
}
}).then((result) => {
if (result == null) {
return {};
} else {
if (result.errors != null && result.errors.length > 0) {
throw result.errors[0];
} else {
return {
data: result.data
};
}
}
});
}
}));
app.set("view engine", "jsx");
app.set("views", projectPath("src/views"));
app.locals[expressAsyncReact.Settings] = {
componentPath: "template"
};
app.use((req, res, next) => {
res.locals.isUnderPrefix = function isUnderPrefix(path, resultingClass) {
@ -31,7 +65,7 @@ module.exports = function () {
} else {
return "";
}
}
};
next();
});

@ -53,26 +53,27 @@ module.exports = function({db}) {
let router = require("express-promise-router")();
router.get("/", (req, res) => {
return Promise.try(() => {
return getStorageDevices();
}).then((devices) => {
/* FIXME: Auto-formatting of total sizes and units */
let fixedDrives = devices.filter((drive) => drive.removable === false);
let removableDrives = devices.filter((drive) => drive.removable === true);
// return Promise.try(() => {
// return getStorageDevices();
// }).then((devices) => {
// /* FIXME: Auto-formatting of total sizes and units */
// let fixedDrives = devices.filter((drive) => drive.removable === false);
// let removableDrives = devices.filter((drive) => drive.removable === true);
let healthyFixedDrives = fixedDrives.filter((drive) => drive.smartStatus === "healthy");
let deterioratingFixedDrives = fixedDrives.filter((drive) => drive.smartStatus === "deteriorating");
let failingFixedDrives = fixedDrives.filter((drive) => drive.smartStatus === "failing");
// let healthyFixedDrives = fixedDrives.filter((drive) => drive.smartStatus === "healthy");
// let deterioratingFixedDrives = fixedDrives.filter((drive) => drive.smartStatus === "deteriorating");
// let failingFixedDrives = fixedDrives.filter((drive) => drive.smartStatus === "failing");
res.render("hardware/storage-devices/list", {
devices: devices,
totalFixedStorage: roundUnit(B(sumDriveSizes(fixedDrives)).toTiB()),
totalHealthyFixedStorage: roundUnit(B(sumDriveSizes(healthyFixedDrives)).toTiB()),
totalDeterioratingFixedStorage: roundUnit(B(sumDriveSizes(deterioratingFixedDrives)).toTiB()),
totalFailingFixedStorage: roundUnit(B(sumDriveSizes(failingFixedDrives)).toTiB()),
totalRemovableStorage: roundUnit(B(sumDriveSizes(removableDrives)).toGiB())
});
});
// res.render("hardware/storage-devices/list", {
// devices: devices,
// totalFixedStorage: roundUnit(B(sumDriveSizes(fixedDrives)).toTiB()),
// totalHealthyFixedStorage: roundUnit(B(sumDriveSizes(healthyFixedDrives)).toTiB()),
// totalDeterioratingFixedStorage: roundUnit(B(sumDriveSizes(deterioratingFixedDrives)).toTiB()),
// totalFailingFixedStorage: roundUnit(B(sumDriveSizes(failingFixedDrives)).toTiB()),
// totalRemovableStorage: roundUnit(B(sumDriveSizes(removableDrives)).toGiB())
// });
// });
res.render("hardware/storage-devices/list");
});
return router;

@ -0,0 +1,15 @@
"use strict";
const React = require("react");
module.exports = {
template: function ErrorPage({ error }) {
return (
<div className="error">
<h1>An error occurred.</h1>
<h2>{ error.message }</h2>
<pre>{ error.stack }</pre>
</div>
);
}
};

@ -0,0 +1,109 @@
"use strict";
const React = require("react");
const Layout = require("../../layout");
const gql = require("../../../graphql/tag");
module.exports = {
query: gql`
query {
hardware {
drives {
path
model
modelFamily
}
}
}
`,
template: function StorageDeviceList({data}) {
return (
<Layout>
<div className="fancyStuff">
<ul>
{data.hardware.drives.map((drive) => {
return <li>
{drive.path}: {drive.model} ({drive.modelFamily})
</li>;
})}
</ul>
</div>
</Layout>
);
}
};
// extends ../../layout
// block content
// h2 Fixed drives
// //- FIXME: Partitions with mountpoints
// table.drives
// tr
// th SMART
// th Device
// th Total size
// th RPM
// th Serial number
// th Model
// th Family
// th Firmware version
// for device in devices.filter((device) => device.removable === false)
// tr(class=(device.children.length > 0 ? "hasPartitions" : null))
// td(class=`smart ${device.smartStatus}`, rowspan=(1 + device.children.length))
// td= device.name
// td= device.size
// td #{device.information.rpm} RPM
// td= device.information.serialNumber
// td= device.information.model
// td= device.information.modelFamily
// td= device.information.firmwareVersion
// for partition, i in device.children
// tr.partition(class=(i === device.children.length - 1) ? "last" : null)
// td= partition.name
// td= partition.size
// td(colspan=5)
// if partition.mountpoint != null
// = partition.mountpoint
// else
// span.notMounted (not mounted)
// //- tr.partition
// //- td(colspan=8)= JSON.stringify(partition)
// tr
// th(colspan=2) Total
// td= totalFixedStorage
// td(colspan=5).hidden
// tr.smartStatus
// th(colspan=2).healthy Healthy
// td= totalHealthyFixedStorage
// td(colspan=5).hidden
// tr.smartStatus
// th(colspan=2).atRisk At-risk
// td= totalDeterioratingFixedStorage
// td(colspan=5).hidden
// tr.smartStatus
// th(colspan=2).failing Failing
// td= totalFailingFixedStorage
// td(colspan=5).hidden
// h2 Removable drives
// table
// tr
// th Path
// th Total size
// th Mounted at
// for device in devices.filter((device) => device.type === "loopDevice")
// tr
// td= device.path
// td= device.size
// td= device.mountpoint

@ -0,0 +1,45 @@
"use strict";
const React = require("react");
const classnames = require("classnames");
// const {LocalsContext} = require("../express-async-react");
function MenuItem({ path, children }) {
let isActive = false; // FIXME
return (
<div className={classnames("menu-item", {active: isActive})}>
<a href={path}>
{children}
</a>
</div>
);
}
module.exports = function Layout({ children }) {
// let locals = React.useContext(LocalsContext);
return (
<html>
<head>
<title>CVM</title>
<link rel="stylesheet" href="/css/style.css"/>
</head>
<body>
<div className="menu">
<h1>CVM</h1>
<MenuItem path="/disk-images">Disk Images</MenuItem>
<MenuItem path="/instances">Instances</MenuItem>
<MenuItem path="/users">Users</MenuItem>
</div>
<div className="content">
{children}
</div>
<script src="/js/bundle.js" />
<script src="/budo/livereload.js" />
</body>
</html>
);
};

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save