big update

This commit is contained in:
2023-07-30 17:52:18 +02:00
parent 4ec3c5ab2f
commit 65f8d8567e
12 changed files with 2631 additions and 3115 deletions

View File

@@ -2,5 +2,5 @@
"development": false, "development": false,
"defaultMinuteSpan": 60, "defaultMinuteSpan": 60,
"reloadIntervalSec": 30, "reloadIntervalSec": 30,
"dataEndpoint": "http://home.djledda.de:4040/climate/api" "dataEndpoint": "http://home.djledda.de/climate/api"
} }

View File

@@ -1,7 +1,7 @@
import {defineConfig} from "vite"; import {defineConfig} from "vite";
export default defineConfig({ export default defineConfig({
base: "/climate/static", base: "/climate",
build: { build: {
outDir: "../dist", outDir: "../dist",
}, },

3074
package-lock.json generated

File diff suppressed because it is too large Load Diff

2591
server/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,7 @@
{ {
"name": "climate-server", "name": "climate-server",
"version": "1.0.0", "version": "1.0.0",
"type": "module",
"dependencies": { "dependencies": {
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"child-process-promise": "^2.2.1", "child-process-promise": "^2.2.1",

View File

@@ -12,7 +12,7 @@ try:
humidity = dhtDevice.humidity humidity = dhtDevice.humidity
co2 = None co2 = None
try: try:
mhz19_reading = mh_z19.read() mhz19_reading = mh_z19.read(serial_console_untouched=True)
if mhz19_reading is not None and mhz19_reading['co2'] is not None: if mhz19_reading is not None and mhz19_reading['co2'] is not None:
co2 = mhz19_reading['co2'] co2 = mhz19_reading['co2']
except Exception as error: except Exception as error:

View File

@@ -1,16 +1,36 @@
import { URL } from 'url';
import dotenv from "dotenv"; import dotenv from "dotenv";
import express from "express"; import express from "express";
import {newMainRouter} from "./mainRouter"; import {newMainRouter} from "./mainRouter";
import {setupCollections} from "./Collections"; import {setupCollections} from "./Collections";
import path from "path"; import path from "path";
import {startSensorPinger} from "./pingSensors"; import {startSensorPinger} from "./pingSensors";
import { GenericPersistenceError, ClayPIError } from './errors';
const __dirname = new URL('.', import.meta.url).pathname;
const DOTENV_PATH = path.resolve(__dirname, "..", "./.env"); const DOTENV_PATH = path.resolve(__dirname, "..", "./.env");
dotenv.config({ path: DOTENV_PATH }); dotenv.config({ path: DOTENV_PATH });
const SERVER_ROOT = process.env.SERVER_ROOT ?? "/"; const SERVER_ROOT = process.env.SERVER_ROOT ?? "/";
console.log(DOTENV_PATH); const topLevelErrorHandler: express.ErrorRequestHandler = (err, req, res, next) => {
console.log(process.env.SERVER_ROOT); const errOutput = {
error: true,
message: "",
};
if (err instanceof GenericPersistenceError) {
errOutput.message = `An error occurred accessing the database: ${err.displayMessage}`;
}
else if (err instanceof ClayPIError) {
errOutput.message = `An error occurred: ${err.displayMessage}`;
}
else {
errOutput.message = "An unknown error occurred!";
}
console.log({...errOutput, internalMessage: err.message});
res.status(500).send(errOutput);
};
async function main() { async function main() {
try { try {
const collections = await setupCollections(); const collections = await setupCollections();
@@ -22,8 +42,12 @@ async function main() {
app.locals = { app.locals = {
rootUrl: SERVER_ROOT, rootUrl: SERVER_ROOT,
}; };
app.use(SERVER_ROOT, express.static(path.resolve(__dirname + "/../dist/public"))); app.use(SERVER_ROOT + '/api', mainRouter);
app.use(SERVER_ROOT, mainRouter); app.get(SERVER_ROOT + '/dashboard', (req, res) => {
res.sendFile(path.resolve(__dirname, '../../dist/index.html'));
});
app.use(SERVER_ROOT, express.static(path.resolve(__dirname, "../../dist")));
app.use(topLevelErrorHandler);
app.listen(app.get("port"), () => { app.listen(app.get("port"), () => {
console.log("ClayPI running on http://localhost:%d", app.get("port")); console.log("ClayPI running on http://localhost:%d", app.get("port"));
}); });

View File

@@ -7,27 +7,9 @@ import newByteSeriesRouter from "./byteSeriesRouter";
export function newMainRouter(collections: CollectionRegistry) { export function newMainRouter(collections: CollectionRegistry) {
const router = express.Router(); const router = express.Router();
router.use("/api/snapshots", newSnapshotRouter(collections)); router.use("/snapshots", newSnapshotRouter(collections));
router.use("/api/timeseries", newByteSeriesRouter(collections)); router.use("/timeseries", newByteSeriesRouter(collections));
router.use(topLevelErrorHandler);
return router; return router;
} }
const topLevelErrorHandler: express.ErrorRequestHandler = (err, req, res, next) => {
const errOutput = {
error: true,
message: "",
};
if (err instanceof GenericPersistenceError) {
errOutput.message = `An error occurred accessing the database: ${err.displayMessage}`;
}
else if (err instanceof ClayPIError) {
errOutput.message = `An error occurred: ${err.displayMessage}`;
}
else {
errOutput.message = "An unknown error occurred!";
}
console.log({...errOutput, internalMessage: err.message});
res.status(500).send(errOutput);
};

View File

@@ -1,8 +1,11 @@
import { URL } from 'url';
import {ISOSnapshot} from "./Snapshot"; import {ISOSnapshot} from "./Snapshot";
import {exec} from "child-process-promise"; import {exec} from "child-process-promise";
import path from "path"; import path from "path";
import {ClayPIError} from "./errors"; import {ClayPIError} from "./errors";
const __dirname = new URL('.', import.meta.url).pathname;
async function pingSensors(): Promise<Omit<ISOSnapshot, "id">> { async function pingSensors(): Promise<Omit<ISOSnapshot, "id">> {
try { try {
const process = await exec(`python3 ${path.resolve(__dirname + "/../scripts/climate-pinger.py")}`); const process = await exec(`python3 ${path.resolve(__dirname + "/../scripts/climate-pinger.py")}`);

View File

@@ -10,7 +10,7 @@ function newSnapshotRouter(collections: CollectionRegistry) {
router.use(unixTimeParamMiddleware); router.use(unixTimeParamMiddleware);
router.get("", async (req, res) => { router.get("/", async (req, res) => {
const query = req.query as Record<string, string>; const query = req.query as Record<string, string>;
const isMinutesQuery = typeof query["last-minutes"] !== "undefined" && !query.from && !query.to; const isMinutesQuery = typeof query["last-minutes"] !== "undefined" && !query.from && !query.to;
const isFromToQuery = typeof query.from !== "undefined"; const isFromToQuery = typeof query.from !== "undefined";
@@ -66,6 +66,7 @@ function newSnapshotRouter(collections: CollectionRegistry) {
}); });
router.post("/", async (req, res) => { router.post("/", async (req, res) => {
console.log("New snapshot:", req.body);
const goodRequest = req.body.snapshots const goodRequest = req.body.snapshots
&& req.body.snapshots.length === 1 && req.body.snapshots.length === 1
&& SnapshotCollection.isSubmissibleSnapshot(req.body.snapshots[0]); && SnapshotCollection.isSubmissibleSnapshot(req.body.snapshots[0]);

View File

@@ -1,15 +1,15 @@
{ {
"compilerOptions": { "compilerOptions": {
"noImplicitAny": true, "noImplicitAny": true,
"module": "commonjs", "module": "ES2022",
"target": "ES6", "target": "ES2022",
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
"esModuleInterop": true, "esModuleInterop": true,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"strict": true, "strict": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"moduleResolution": "node", "moduleResolution": "Node",
"resolveJsonModule": true, "resolveJsonModule": true,
"experimentalDecorators": true "experimentalDecorators": true
}, },
@@ -17,6 +17,6 @@
"lib": [ "lib": [
"dom", "dom",
"dom.iterable", "dom.iterable",
"esnext" "ES2022"
] ]
} }