SpaceCache/app.js

152 lines
3.9 KiB
JavaScript
Raw Normal View History

2023-01-18 20:01:58 +01:00
// load configs and spaces
2023-01-18 17:28:38 +01:00
const config = require("./config.json");
const spaces = require("./spaces.json");
2023-01-18 12:15:52 +01:00
2023-01-18 20:01:58 +01:00
// load modules
2023-01-18 17:28:38 +01:00
const { setInterval } = require("node:timers/promises");
const fetch = require("node-fetch");
2023-01-18 20:01:58 +01:00
const express = require("express");
const { graphqlHTTP } = require('express-graphql');
const { buildSchema } = require('graphql');
2023-01-18 17:28:38 +01:00
const NodeCache = require("node-cache");
2023-01-18 12:15:52 +01:00
const jp = require('jsonpath');
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
2023-01-19 23:28:37 +01:00
const cors = require('cors');
2023-01-18 12:15:52 +01:00
2023-01-18 20:01:58 +01:00
// set up things
2023-01-18 17:28:38 +01:00
const cache = new NodeCache({ stdTTL: config.checkperiod * 3 });
2023-01-18 12:15:52 +01:00
2023-01-18 20:01:58 +01:00
let schema = buildSchema(`
2023-01-19 08:51:59 +01:00
type Space {
name: String
id: String!
open: Boolean!
updatedAt: String!
2023-01-24 10:57:37 +01:00
lastChange: String
area: String
2023-01-19 08:51:59 +01:00
}
2023-01-18 20:01:58 +01:00
type Query {
isOpen(id: String): Boolean
2023-01-19 08:51:59 +01:00
spaces: [Space!]!
2023-01-24 10:57:37 +01:00
inArea(area: String): [Space!]!
}
2023-01-18 20:01:58 +01:00
`);
let root = {
2023-01-19 10:00:08 +01:00
isOpen: async ({ id }) => {
let data = await prisma.space.findUnique({ where: { id: id } });
return data.open;
2023-01-19 08:51:59 +01:00
},
2023-01-19 23:28:37 +01:00
spaces: async () => {
let data = await prisma.space.findMany();
return data;
2023-01-24 10:57:37 +01:00
},
inArea: async ({ area }) => {
// try to find the exact area first, if that fails, try to find at least a partial match
let data = await prisma.space.findMany({ where: { area: area } });
if (data.length == 0) {
data = await prisma.space.findMany({
where: {
area: {
contains: area
}
}
});
}
return data;
}
2023-01-18 20:01:58 +01:00
};
let app = express();
app.use(cors());
2023-01-18 20:01:58 +01:00
app.use('/graphql', graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true,
2023-01-18 20:01:58 +01:00
}));
2023-01-19 10:08:35 +01:00
app.listen(config.port || 4000);
2023-01-20 09:15:19 +01:00
console.log(`Running a GraphQL API server at localhost:${config.port || 4000}/graphql`);
2023-01-18 20:01:58 +01:00
// CHECK LOOP
2023-01-18 17:28:38 +01:00
(async function () {
2023-01-19 08:39:44 +01:00
await loop();
for await (const time of setInterval(config.checkperiod * 1000)) {
await loop();
2023-01-18 12:15:52 +01:00
}
2023-01-18 17:28:38 +01:00
})();
2023-01-18 12:15:52 +01:00
async function loop() {
2023-01-20 09:15:19 +01:00
console.log(new Date(), "Checking for spaces...");
let changecount = 0;
for (const space of spaces) {
2023-01-20 09:15:19 +01:00
let response = await checkSpace(space);
if (typeof response.open === "undefined") {
console.error(`The space ${space.id} might not be reachable. Please check the endpoint.`);
continue;
}
if (response.open != cache.get(space.id)) {
2023-01-23 12:43:59 +01:00
// update or create the space in the database
let update = await prisma.space.upsert({
where: { id: space.id },
2023-01-23 12:43:59 +01:00
update: {
open: response.open,
lastChange: response.lastchange
},
create: {
id: space.id,
open: response.open,
lastChange: response.lastchange,
name: space.name,
2023-01-24 10:57:37 +01:00
area: space.area,
2023-01-23 12:43:59 +01:00
}
});
2023-01-23 12:43:59 +01:00
2023-01-20 09:15:19 +01:00
changecount++;
console.debug(`Space ${space.id} changed to ${response.open ? "open" : "closed"}.`);
}
cache.set(space.id, response.open);
}
2023-01-20 09:15:19 +01:00
console.log(new Date(), "Check complete, updated", changecount, "spaces.");
}
2023-01-18 20:01:58 +01:00
// HELPER FUNCTIONS
2023-01-18 12:15:52 +01:00
async function checkSpace(space) {
2023-01-18 17:36:19 +01:00
let response, data, open;
2023-01-20 09:15:19 +01:00
let lastchange = null;
2023-01-23 12:43:59 +01:00
2023-01-18 17:36:19 +01:00
try {
response = await fetch(space.endpoint);
data = await response.json();
2023-01-23 12:43:59 +01:00
}
catch (e) {
console.error(`The space ${space.id} might not be reachable. Please check the endpoint. Error: ${e}`);
}
2023-01-18 12:15:52 +01:00
2023-01-23 12:43:59 +01:00
// Check if the space is using the SpaceAPI standard
2023-01-18 12:15:52 +01:00
if (!space.path) {
2023-01-20 09:15:19 +01:00
try {
open = data.state.open;
lastchange = new Date(data.state.lastchange*1000);
}
2023-01-23 12:43:59 +01:00
catch {
console.error(`The space ${space.id} is not using the SpaceAPI standard. Please specify a path.`);
}
2023-01-18 12:15:52 +01:00
}
2023-01-23 12:43:59 +01:00
else {
try {
// Query the JSONPath. If the expected value is not set, assume true or a similar value like 1.
open = (jp.query(data, space.path) == (space.expected ? space.expected : true));
}
catch {
console.error(`The space ${space.id} has an invalid JSONPath to the target value. Please use https://jsonpath.com/ to evaluate the path.`);
}
}
2023-01-20 09:15:19 +01:00
return { open, lastchange };
2023-01-18 12:15:52 +01:00
}