feat: implement PocketBase integration for timetable synchronization

This commit is contained in:
2026-02-27 23:59:39 +01:00
parent a2a8932cd1
commit a29bceda4e
3 changed files with 126 additions and 29 deletions

147
index.js
View File

@@ -1,17 +1,13 @@
require('dotenv').config() require('dotenv').config()
const express = require('express')
const luxon = require('luxon'); const luxon = require('luxon');
const { WebUntis } = require('webuntis'); const { WebUntis } = require('webuntis');
const PocketBase = require('pocketbase/cjs');
const app = express() const pb = new PocketBase('https://fsae41.de');
const port = process.env.PORT;
app.get('/', (req, res) => { async function getTimetabe() {
res.send('server is running')
})
app.get('/timetable', async (req, res) => {
const untis = new WebUntis(process.env.WEBUNTIS_SCHOOL, process.env.WEBUNTIS_USER, process.env.WEBUNTIS_PASS, process.env.WEBUNTIS_URL); const untis = new WebUntis(process.env.WEBUNTIS_SCHOOL, process.env.WEBUNTIS_USER, process.env.WEBUNTIS_PASS, process.env.WEBUNTIS_URL);
let timetable;
try { try {
await untis.login(); await untis.login();
// Start und Ende der aktuellen Woche bestimmen // Start und Ende der aktuellen Woche bestimmen
@@ -21,33 +17,126 @@ app.get('/timetable', async (req, res) => {
const endOfWeek = now.endOf('week').plus({ days: 7 * 4 * 4 }).toJSDate(); // Sonntag const endOfWeek = now.endOf('week').plus({ days: 7 * 4 * 4 }).toJSDate(); // Sonntag
// Stundenplan für diese Woche abrufen // Stundenplan für diese Woche abrufen
const timetable = await untis.getOwnTimetableForRange(startOfWeek, endOfWeek); timetable = await untis.getOwnTimetableForRange(startOfWeek, endOfWeek);
await untis.logout(); await untis.logout();
res.json({ status: 'success', data: timetable });
} catch (error) { } catch (error) {
console.log(error); console.log(error);
res.statusCode = 500;
res.json({ status: 'error', message: error.message || error.toString() });
}
})
const os = require('os');
const nets = os.networkInterfaces();
const results = {};
for (const name of Object.keys(nets)) {
for (const net of nets[name]) {
if (!net.internal) {
results[name] = results[name] || [];
results[name].push(net.address);
}
} }
return timetable;
} }
console.log('Local IPs:', results);
setInterval(async () => {
const timetable = await getTimetabe();
if (!timetable || timetable.length === 0) {
console.log("No timetable data available.");
return;
}
app.listen(port, () => { let created = 0;
console.log(`Example app listening on port ${port}`) let updated = 0;
}) let skipped = 0;
const sorted = timetable.sort((a, b) => {
// Zuerst nach Datum
if (a.date !== b.date) {
return a.date - b.date;
}
// Falls Datum gleich, nach Startzeit
if (a.startTime !== b.startTime) {
return a.startTime - b.startTime;
}
// Falls Startzeit gleich, nach Endzeit
return a.endTime - b.endTime;
});
let final = []
for (let i = 0; i < sorted.length - 1; i++) {
let now = sorted[i];
let next = sorted[i + 1];
//gleicher tag
if (now.date == next.date) {
if (now.endTime == next.startTime) {
now.endTime = next.endTime;
final.push(now);
}
}
}
pb.autoCancellation(false);
const records = await pb.collection('classes').getFullList();
//console.log(records);
for (let i = 0; i < final.length; i++) {
let existingRecords;
try {
existingRecords = await pb.collection('classes').getList(1, 0, {
filter: 'untis_id = "' + final[i].id + '"',
});
existingRecords = existingRecords.items[0];
} catch (error) {
}
//console.log(typeof existingRecords);
if (!existingRecords) {
// Neu erstellen
const data = {
"untis_id": final[i].id,
"date": final[i].date,
"startTime": final[i].startTime,
"endTime": final[i].endTime,
"ro": JSON.stringify(final[i].ro),
"su": JSON.stringify(final[i].su),
"kl": JSON.stringify(final[i].kl),
"raw": JSON.stringify(final[i]),
"old_version": "JSON"
};
const record = await pb.collection('classes').create(data);
created++;
continue; // Überspringe die Erstellung, wenn der Datensatz bereits existiert
}
//console.log("Record with untis_id " + final[i].id + " already exists. Skipping creation.");
// Prüfe, ob etwas geändert wurde
const isChanged =
existingRecords.date != final[i].date ||
existingRecords.startTime != final[i].startTime ||
existingRecords.endTime != final[i].endTime ||
(existingRecords.kl[0].id != final[i].kl[0].id) ||
(existingRecords.ro[0].id != final[i].ro[0].id) ||
(existingRecords.su[0].id != final[i].su[0].id);
if (!isChanged) {
skipped++;
continue;
}
// Aktualisiere den bestehenden Datensatz
existingRecords.old_version = existingRecords.raw;
existingRecords.raw = JSON.stringify(final[i]);
existingRecords.date = final[i].date;
existingRecords.startTime = final[i].startTime;
existingRecords.endTime = final[i].endTime;
existingRecords.kl = final[i].kl;
existingRecords.ro = final[i].ro;
existingRecords.su = final[i].su;
existingRecords.last_update = new Date().toISOString();
const record = await pb.collection('classes').update(existingRecords.id, existingRecords);
updated++;
}
console.log(`✅ Sync done — Created: ${created}, Updated: ${updated}, Skipped: ${skipped}`);
}, 1000 * 60 * 30); // Alle 30 Minuten aktualisieren

7
package-lock.json generated
View File

@@ -12,6 +12,7 @@
"dotenv": "^17.3.1", "dotenv": "^17.3.1",
"express": "^5.2.1", "express": "^5.2.1",
"luxon": "^3.7.2", "luxon": "^3.7.2",
"pocketbase": "^0.26.8",
"webuntis": "^2.2.1" "webuntis": "^2.2.1"
} }
}, },
@@ -796,6 +797,12 @@
"url": "https://opencollective.com/express" "url": "https://opencollective.com/express"
} }
}, },
"node_modules/pocketbase": {
"version": "0.26.8",
"resolved": "https://registry.npmjs.org/pocketbase/-/pocketbase-0.26.8.tgz",
"integrity": "sha512-aQ/ewvS7ncvAE8wxoW10iAZu6ElgbeFpBhKPnCfvRovNzm2gW8u/sQNPGN6vNgVEagz44kK//C61oKjfa+7Low==",
"license": "MIT"
},
"node_modules/proxy-addr": { "node_modules/proxy-addr": {
"version": "2.0.7", "version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",

View File

@@ -18,6 +18,7 @@
"dotenv": "^17.3.1", "dotenv": "^17.3.1",
"express": "^5.2.1", "express": "^5.2.1",
"luxon": "^3.7.2", "luxon": "^3.7.2",
"pocketbase": "^0.26.8",
"webuntis": "^2.2.1" "webuntis": "^2.2.1"
} }
} }