-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
127 lines (111 loc) · 4.12 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import auth from './auth.cjs';
import Logger from './logging.cjs';
import VictronApi from './vrm.js';
import InfluxApi from './influx.js';
import express from 'express';
import cookieParser from 'cookie-parser';
import session from 'express-session';
import 'dotenv/config';
import config from './config.json' assert { type: 'json' };
const log = new Logger('app');
// validate config
// create api connections
const apis = [];
config.apis.forEach((api, i) => {
const apiType = api.type || 'vrm';
apis.push(
apiType === 'vrm'
? new VictronApi(api, process.env.VRM_ID_USER, process.env.VRM_ACCESS_TOKEN)
: new InfluxApi(api)
);
});
const port = process.env.PORT || 8080;
const app = express();
const contextPath = process.env.CONTEXT_PATH || '';
log.debug('context path is set to:', contextPath);
let credentials = process.env.USER_CONFIG;
let users = {};
if (credentials) {
const config = JSON.parse(credentials);
for (let k of Object.keys(config)) {
users[k] = config[k];
}
}
const globalState = {};
const updateInterval = 10 * 60 * 1000;
function shutDown() {
log.debug('shutdown called ... bye');
process.exit(0);
}
process.on('SIGTERM', shutDown);
process.on('SIGINT', shutDown);
app.set('view engine', 'ejs');
app.disable('x-powered-by');
app.use(cookieParser(process.env.SESSION_SECRET));
app.use(contextPath, express.static('public'));
app.use(express.raw({ type: '*/*' }));
app.use(
session({
secret: process.env.SESSION_SECRET,
cookie: { path: contextPath, httpOnly: true },
resave: false,
saveUninitialized: true
})
);
if (credentials) {
app.use(auth({ users, contextPath }));
}
// default entry point - rendered via ejs template
app.get([contextPath, contextPath + '/'], (req, res) => {
res.render('app', {
title: config.title
});
});
app.get(contextPath + '/state', (req, res) => {
const interval = req.query.i;
log.debug('state request for interval:', interval);
if (globalState[interval] === undefined) globalState[interval] = { lastUpdate: null };
let state = globalState[interval];
log.debug('lastUpdate of stats was:', state.lastUpdate === null ? 'null' : new Date(state.lastUpdate));
const nextUpdate = state.lastUpdate === null ? 0 : state.lastUpdate + updateInterval;
log.debug('next update after:', state.lastUpdate === null ? 'now' : new Date(nextUpdate));
if (state.lastUpdate === null || nextUpdate < new Date().getTime()) {
if (state.rows === undefined) state.rows = {};
let promises = [];
// collect API calls as promise, so we can process them all at once
for (const api of apis) {
promises.push(api.fetchStats(interval));
}
let timeDataAdded = false;
Promise.all(promises)
.then((responses) => {
state.rows = {}; // clear data for update
for (let data of responses) {
for (let day of Object.keys(data.rows)) {
if (state.rows[day] === undefined) {
state.rows[day] = data.rows[day];
} else {
for (let id of Object.keys(data.rows[day])) {
if (state.rows[day][id] === undefined) state.rows[day][id] = data.rows[day][id];
else {
data.rows[day][id].forEach((o) => state.rows[day][id].push(o));
}
}
}
}
if (!timeDataAdded) {
state.timeframe = data.timeframe;
state.lastUpdate = new Date().getTime();
timeDataAdded = true;
}
}
res.status(200).type('application/json').send(JSON.stringify(state));
})
.catch((error) => log.error(error));
} else {
res.status(200).type('application/json').send(JSON.stringify(state));
}
});
app.listen(port, () => {
log.debug(`app listening on port ${port}`);
});