SpaceCache/app.js

118 lines
3.4 KiB
JavaScript

// load configs and spaces
const config = require("./config.json");
const spaces = require("./spaces.json");
// load modules
const { setInterval } = require("node:timers/promises");
const fetch = require("node-fetch");
const express = require("express");
const { graphqlHTTP } = require('express-graphql');
const { buildSchema } = require('graphql');
const NodeCache = require("node-cache");
const jp = require('jsonpath');
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
const cors = require('cors');
// set up things
const cache = new NodeCache({ stdTTL: config.checkperiod * 3 });
let schema = buildSchema(`
type Space {
name: String
id: String!
open: Boolean!
updatedAt: String!
}
type Query {
isOpen(id: String): Boolean
spaces: [Space!]!
}
`);
let root = {
isOpen: async ({ id }) => {
let data = await prisma.space.findUnique({ where: { id: id } });
return data.open;
},
spaces: async () => {
let data = await prisma.space.findMany();
return data;
}
};
let app = express();
/* leaving this here for reference
app.options('/graphql', function (req, res) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET,POST,OPTIONS');
res.send(200);
});
*/
app.use(cors());
app.use('/graphql', graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true,
}));
app.listen(config.port || 4000);
console.log(`Running a GraphQL API server at localhost:${config.port || 4000}/graphql`);
// CHECK LOOP
(async function () {
await loop();
for await (const time of setInterval(config.checkperiod * 1000)) {
await loop();
}
})();
async function loop() {
console.log(new Date(), "Checking for spaces...");
let changecount = 0;
for (const space of spaces) {
//console.log(`Checking ${space.id}...`);
let response = await checkSpace(space);
//console.log(`Space ${space.id} is ${o ? "open" : "closed"}`);
if (typeof response.open === "undefined") {
console.error(`The space ${space.id} might not be reachable. Please check the endpoint.`);
continue;
}
if (JSON.stringify(response) != JSON.stringify(cache.get(space.id))) {
cache.set(space.id, response);
let update = await prisma.space.upsert({
where: { id: space.id },
update: { open: response.open, lastChange: response.lastchange },
create: { id: space.id, open: response.open, lastChange: response.lastchange, name: space.name }
});
changecount++;
}
}
console.log(new Date(), "Check complete, updated", changecount, "spaces.");
}
// HELPER FUNCTIONS
async function checkSpace(space) {
let response, data, open;
let lastchange = null;
try {
response = await fetch(space.endpoint);
data = await response.json();
} catch (e) { console.error(`The space ${space.id} might not be reachable. Please check the endpoint. Error: ${e}`); }
if (!space.path) {
try {
open = data.state.open;
lastchange = new Date(data.state.lastchange*1000);
}
catch { console.error(`The space ${space.id} is not using the SpaceAPI standard. Please specify a path.`); }
} else {
try { 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.`); }
}
return { open, lastchange };
}