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');
|
2023-01-19 08:08:53 +01:00
|
|
|
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-18 20:01:58 +01:00
|
|
|
type Query {
|
2023-01-19 08:08:53 +01:00
|
|
|
isOpen(id: String): Boolean
|
2023-01-19 08:51:59 +01:00
|
|
|
spaces: [Space!]!
|
2023-01-19 08:08:53 +01:00
|
|
|
}
|
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-19 08:08:53 +01:00
|
|
|
}
|
2023-01-18 20:01:58 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
let app = express();
|
2023-01-20 08:45:56 +01:00
|
|
|
|
|
|
|
app.use(cors());
|
2023-01-18 20:01:58 +01:00
|
|
|
app.use('/graphql', graphqlHTTP({
|
2023-01-19 08:08:53 +01:00
|
|
|
schema: schema,
|
|
|
|
rootValue: root,
|
|
|
|
graphiql: true,
|
2023-01-18 20:01:58 +01:00
|
|
|
}));
|
2023-01-19 08:08:53 +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)) {
|
2023-01-19 08:30:37 +01:00
|
|
|
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
|
|
|
|
2023-01-19 08:30:37 +01:00
|
|
|
async function loop() {
|
2023-01-20 09:15:19 +01:00
|
|
|
console.log(new Date(), "Checking for spaces...");
|
|
|
|
let changecount = 0;
|
2023-01-19 08:30:37 +01:00
|
|
|
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 (JSON.stringify(response) != JSON.stringify(cache.get(space.id))) {
|
|
|
|
cache.set(space.id, response);
|
2023-01-23 12:43:59 +01:00
|
|
|
|
|
|
|
// update or create the space in the database
|
2023-01-19 08:30:37 +01:00
|
|
|
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-19 08:30:37 +01:00
|
|
|
});
|
2023-01-23 12:43:59 +01:00
|
|
|
|
2023-01-20 09:15:19 +01:00
|
|
|
changecount++;
|
2023-01-19 08:30:37 +01:00
|
|
|
}
|
|
|
|
}
|
2023-01-20 09:15:19 +01:00
|
|
|
console.log(new Date(), "Check complete, updated", changecount, "spaces.");
|
2023-01-19 08:30:37 +01:00
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|