Refactoring
This commit is contained in:
34
services/ldap-scheduler.js
Normal file
34
services/ldap-scheduler.js
Normal file
@@ -0,0 +1,34 @@
|
||||
// LDAP-Scheduler Service
|
||||
|
||||
const { db } = require('../database');
|
||||
const LDAPService = require('../ldap-service');
|
||||
|
||||
// Automatische LDAP-Synchronisation einrichten
|
||||
function setupLDAPScheduler() {
|
||||
// Prüfe alle 5 Minuten, ob eine Synchronisation notwendig ist
|
||||
setInterval(() => {
|
||||
db.get('SELECT * FROM ldap_config WHERE id = 1 AND enabled = 1 AND sync_interval > 0', (err, config) => {
|
||||
if (err || !config) {
|
||||
return; // Keine aktive Konfiguration
|
||||
}
|
||||
|
||||
const now = new Date();
|
||||
const lastSync = config.last_sync ? new Date(config.last_sync) : null;
|
||||
const syncIntervalMs = config.sync_interval * 60 * 1000; // Minuten in Millisekunden
|
||||
|
||||
// Prüfe ob Synchronisation fällig ist
|
||||
if (!lastSync || (now - lastSync) >= syncIntervalMs) {
|
||||
console.log('Starte automatische LDAP-Synchronisation...');
|
||||
LDAPService.performSync('scheduled', (err, result) => {
|
||||
if (err) {
|
||||
console.error('Fehler bei automatischer LDAP-Synchronisation:', err.message);
|
||||
} else {
|
||||
console.log(`Automatische LDAP-Synchronisation abgeschlossen: ${result.synced} Benutzer synchronisiert`);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}, 5 * 60 * 1000); // Alle 5 Minuten prüfen
|
||||
}
|
||||
|
||||
module.exports = { setupLDAPScheduler };
|
||||
271
services/pdf-service.js
Normal file
271
services/pdf-service.js
Normal file
@@ -0,0 +1,271 @@
|
||||
// PDF-Generierung Service
|
||||
|
||||
const PDFDocument = require('pdfkit');
|
||||
const { db } = require('../database');
|
||||
const { formatDate, formatDateTime } = require('../helpers/utils');
|
||||
|
||||
// Kalenderwoche berechnen
|
||||
function getCalendarWeek(dateStr) {
|
||||
const date = new Date(dateStr);
|
||||
const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
|
||||
const dayNum = d.getUTCDay() || 7;
|
||||
d.setUTCDate(d.getUTCDate() + 4 - dayNum);
|
||||
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
|
||||
const weekNo = Math.ceil((((d - yearStart) / 86400000) + 1) / 7);
|
||||
return weekNo;
|
||||
}
|
||||
|
||||
// PDF generieren
|
||||
function generatePDF(timesheetId, req, res) {
|
||||
db.get(`SELECT wt.*, u.firstname, u.lastname, u.username, u.wochenstunden
|
||||
FROM weekly_timesheets wt
|
||||
JOIN users u ON wt.user_id = u.id
|
||||
WHERE wt.id = ?`, [timesheetId], (err, timesheet) => {
|
||||
|
||||
if (err || !timesheet) {
|
||||
return res.status(404).send('Stundenzettel nicht gefunden');
|
||||
}
|
||||
|
||||
// Hole Einträge die zum Zeitpunkt der Einreichung existierten
|
||||
// Filtere nach submitted_at der Version, damit jede Version ihre eigenen Daten zeigt
|
||||
// Logik: Wenn updated_at existiert, verwende das, sonst created_at, sonst zeige Eintrag (für alte Daten ohne Timestamps)
|
||||
db.all(`SELECT * FROM timesheet_entries
|
||||
WHERE user_id = ? AND date >= ? AND date <= ?
|
||||
AND (
|
||||
(updated_at IS NOT NULL AND updated_at <= ?) OR
|
||||
(updated_at IS NULL AND created_at IS NOT NULL AND created_at <= ?) OR
|
||||
(updated_at IS NULL AND created_at IS NULL)
|
||||
)
|
||||
ORDER BY date, updated_at DESC, id DESC`,
|
||||
[timesheet.user_id, timesheet.week_start, timesheet.week_end,
|
||||
timesheet.submitted_at, timesheet.submitted_at],
|
||||
(err, allEntries) => {
|
||||
if (err) {
|
||||
return res.status(500).send('Fehler beim Abrufen der Einträge');
|
||||
}
|
||||
|
||||
// Filtere auf neuesten Eintrag pro Tag (basierend auf updated_at oder id)
|
||||
const entriesByDate = {};
|
||||
(allEntries || []).forEach(entry => {
|
||||
const existing = entriesByDate[entry.date];
|
||||
if (!existing) {
|
||||
entriesByDate[entry.date] = entry;
|
||||
} else {
|
||||
// Vergleiche updated_at (falls vorhanden) oder id (höhere ID = neuer)
|
||||
const existingTime = existing.updated_at ? new Date(existing.updated_at).getTime() : 0;
|
||||
const currentTime = entry.updated_at ? new Date(entry.updated_at).getTime() : 0;
|
||||
if (currentTime > existingTime || (currentTime === existingTime && entry.id > existing.id)) {
|
||||
entriesByDate[entry.date] = entry;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Konvertiere zu Array und sortiere nach Datum
|
||||
const entries = Object.values(entriesByDate).sort((a, b) => {
|
||||
return new Date(a.date) - new Date(b.date);
|
||||
});
|
||||
|
||||
const doc = new PDFDocument({ margin: 50 });
|
||||
|
||||
// Prüfe ob inline angezeigt werden soll (für Vorschau)
|
||||
const inline = req.query.inline === 'true';
|
||||
|
||||
// Dateinamen generieren: Stundenzettel_KWxxx_NameMitarbeiter_heutigesDatum.pdf
|
||||
const calendarWeek = getCalendarWeek(timesheet.week_start);
|
||||
const today = new Date();
|
||||
const todayStr = today.getFullYear() + '-' +
|
||||
String(today.getMonth() + 1).padStart(2, '0') + '-' +
|
||||
String(today.getDate()).padStart(2, '0');
|
||||
const employeeName = `${timesheet.firstname}${timesheet.lastname}`.replace(/\s+/g, '');
|
||||
const filename = `Stundenzettel_KW${String(calendarWeek).padStart(2, '0')}_${employeeName}_${todayStr}.pdf`;
|
||||
|
||||
res.setHeader('Content-Type', 'application/pdf');
|
||||
res.setHeader('X-Content-Type-Options', 'nosniff');
|
||||
|
||||
if (inline) {
|
||||
res.setHeader('Content-Disposition', `inline; filename="${filename}"`);
|
||||
// Zusätzliche Header für iframe-Unterstützung
|
||||
res.setHeader('X-Frame-Options', 'SAMEORIGIN');
|
||||
} else {
|
||||
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
|
||||
|
||||
// Marker setzen, dass PDF heruntergeladen wurde (nur bei Download, nicht bei Vorschau)
|
||||
const downloadedBy = req.session.userId; // User der die PDF herunterlädt
|
||||
console.log('PDF Download - User ID:', downloadedBy, 'Timesheet ID:', timesheetId);
|
||||
|
||||
if (downloadedBy) {
|
||||
db.run(`UPDATE weekly_timesheets
|
||||
SET pdf_downloaded_at = CURRENT_TIMESTAMP,
|
||||
pdf_downloaded_by = ?
|
||||
WHERE id = ?`,
|
||||
[downloadedBy, timesheetId],
|
||||
(err) => {
|
||||
if (err) {
|
||||
console.error('Fehler beim Setzen des Download-Markers:', err);
|
||||
} else {
|
||||
console.log('Download-Marker erfolgreich gesetzt für User:', downloadedBy);
|
||||
}
|
||||
// Fehler wird ignoriert, damit PDF trotzdem generiert wird
|
||||
});
|
||||
} else {
|
||||
console.warn('PDF Download - Keine User ID in Session gefunden!');
|
||||
}
|
||||
}
|
||||
|
||||
doc.pipe(res);
|
||||
|
||||
// Header (Kalenderwoche wurde bereits oben berechnet)
|
||||
doc.fontSize(20).text(`Stundenzettel für KW ${calendarWeek}`, { align: 'center' });
|
||||
doc.moveDown();
|
||||
|
||||
// Mitarbeiter-Info
|
||||
doc.fontSize(12);
|
||||
doc.text(`Mitarbeiter: ${timesheet.firstname} ${timesheet.lastname}`);
|
||||
doc.text(`Zeitraum: ${formatDate(timesheet.week_start)} - ${formatDate(timesheet.week_end)}`);
|
||||
doc.text(`Eingereicht am: ${formatDateTime(timesheet.submitted_at)}`);
|
||||
doc.moveDown();
|
||||
|
||||
// Tabelle - Basis-Informationen
|
||||
const tableTop = doc.y;
|
||||
const colWidths = [80, 80, 80, 60, 80];
|
||||
const headers = ['Datum', 'Start', 'Ende', 'Pause', 'Stunden'];
|
||||
|
||||
// Tabellen-Header
|
||||
doc.fontSize(10).font('Helvetica-Bold');
|
||||
let x = 50;
|
||||
headers.forEach((header, i) => {
|
||||
doc.text(header, x, tableTop, { width: colWidths[i], align: 'left' });
|
||||
x += colWidths[i];
|
||||
});
|
||||
|
||||
doc.moveDown();
|
||||
let y = doc.y;
|
||||
doc.moveTo(50, y).lineTo(430, y).stroke();
|
||||
doc.moveDown(0.5);
|
||||
|
||||
// Tabellen-Daten
|
||||
doc.font('Helvetica');
|
||||
let totalHours = 0;
|
||||
let vacationHours = 0; // Urlaubsstunden für Überstunden-Berechnung
|
||||
|
||||
entries.forEach((entry) => {
|
||||
y = doc.y;
|
||||
x = 50;
|
||||
|
||||
// Basis-Zeile
|
||||
const rowData = [
|
||||
formatDate(entry.date),
|
||||
entry.start_time || '-',
|
||||
entry.end_time || '-',
|
||||
entry.break_minutes ? `${entry.break_minutes} min` : '-',
|
||||
entry.total_hours ? entry.total_hours.toFixed(2) + ' h' : '-'
|
||||
];
|
||||
|
||||
rowData.forEach((data, i) => {
|
||||
doc.text(data, x, y, { width: colWidths[i], align: 'left' });
|
||||
x += colWidths[i];
|
||||
});
|
||||
|
||||
// Tätigkeiten sammeln
|
||||
const activities = [];
|
||||
for (let i = 1; i <= 5; i++) {
|
||||
const desc = entry[`activity${i}_desc`];
|
||||
const hours = entry[`activity${i}_hours`];
|
||||
const projectNumber = entry[`activity${i}_project_number`];
|
||||
if (desc && desc.trim() && hours > 0) {
|
||||
activities.push({
|
||||
desc: desc.trim(),
|
||||
hours: parseFloat(hours),
|
||||
projectNumber: projectNumber ? projectNumber.trim() : null
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Tätigkeiten anzeigen
|
||||
if (activities.length > 0) {
|
||||
doc.moveDown(0.3);
|
||||
doc.fontSize(9).font('Helvetica-Oblique');
|
||||
doc.text('Tätigkeiten:', 60, doc.y, { width: 380 });
|
||||
doc.moveDown(0.2);
|
||||
|
||||
activities.forEach((activity, idx) => {
|
||||
let activityText = `${idx + 1}. ${activity.desc}`;
|
||||
if (activity.projectNumber) {
|
||||
activityText += ` (Projekt: ${activity.projectNumber})`;
|
||||
}
|
||||
activityText += ` - ${activity.hours.toFixed(2)} h`;
|
||||
doc.fontSize(9).font('Helvetica');
|
||||
doc.text(activityText, 70, doc.y, { width: 360 });
|
||||
doc.moveDown(0.2);
|
||||
});
|
||||
doc.fontSize(10);
|
||||
}
|
||||
|
||||
// Überstunden und Urlaub anzeigen
|
||||
const overtimeInfo = [];
|
||||
if (entry.overtime_taken_hours && parseFloat(entry.overtime_taken_hours) > 0) {
|
||||
overtimeInfo.push(`Überstunden genommen: ${parseFloat(entry.overtime_taken_hours).toFixed(2)} h`);
|
||||
}
|
||||
if (entry.vacation_type) {
|
||||
const vacationText = entry.vacation_type === 'full' ? 'Ganzer Tag' : 'Halber Tag';
|
||||
overtimeInfo.push(`Urlaub: ${vacationText}`);
|
||||
}
|
||||
|
||||
if (overtimeInfo.length > 0) {
|
||||
doc.moveDown(0.2);
|
||||
doc.fontSize(9).font('Helvetica-Oblique');
|
||||
overtimeInfo.forEach((info, idx) => {
|
||||
doc.text(info, 70, doc.y, { width: 360 });
|
||||
doc.moveDown(0.15);
|
||||
});
|
||||
doc.fontSize(10);
|
||||
}
|
||||
|
||||
if (entry.total_hours) {
|
||||
totalHours += entry.total_hours;
|
||||
}
|
||||
|
||||
// Urlaubsstunden für Überstunden-Berechnung sammeln
|
||||
if (entry.vacation_type === 'full') {
|
||||
vacationHours += 8; // Ganzer Tag = 8 Stunden
|
||||
} else if (entry.vacation_type === 'half') {
|
||||
vacationHours += 4; // Halber Tag = 4 Stunden
|
||||
}
|
||||
|
||||
doc.moveDown(0.5);
|
||||
|
||||
// Trennlinie zwischen Einträgen
|
||||
y = doc.y;
|
||||
doc.moveTo(50, y).lineTo(430, y).stroke();
|
||||
doc.moveDown(0.3);
|
||||
});
|
||||
|
||||
// Summe
|
||||
y = doc.y;
|
||||
doc.moveTo(50, y).lineTo(550, y).stroke();
|
||||
doc.moveDown(0.5);
|
||||
doc.font('Helvetica-Bold');
|
||||
doc.text(`Gesamtstunden: ${totalHours.toFixed(2)} h`, 50, doc.y);
|
||||
|
||||
// Überstunden berechnen und anzeigen
|
||||
const wochenstunden = timesheet.wochenstunden || 0;
|
||||
// Überstunden = Gesamtstunden - Wochenstunden
|
||||
// Urlaub zählt als normale Arbeitszeit, daher sind Urlaubsstunden bereits in totalHours enthalten
|
||||
const overtimeHours = totalHours - wochenstunden;
|
||||
|
||||
doc.moveDown(0.3);
|
||||
doc.font('Helvetica-Bold');
|
||||
if (overtimeHours > 0) {
|
||||
doc.text(`Überstunden: +${overtimeHours.toFixed(2)} h`, 50, doc.y);
|
||||
} else if (overtimeHours < 0) {
|
||||
doc.text(`Überstunden: ${overtimeHours.toFixed(2)} h`, 50, doc.y);
|
||||
} else {
|
||||
doc.text(`Überstunden: 0.00 h`, 50, doc.y);
|
||||
}
|
||||
|
||||
doc.end();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { generatePDF };
|
||||
182
services/ping-service.js
Normal file
182
services/ping-service.js
Normal file
@@ -0,0 +1,182 @@
|
||||
// Ping-Service für IP-basierte automatische Zeiterfassung
|
||||
|
||||
const ping = require('ping');
|
||||
const { db } = require('../database');
|
||||
const { getCurrentDate, getCurrentTime, updateTotalHours } = require('../helpers/utils');
|
||||
|
||||
// Ping-Funktion für einen User
|
||||
async function pingUserIP(userId, ip, currentDate, currentTime) {
|
||||
try {
|
||||
const result = await ping.promise.probe(ip, {
|
||||
timeout: 3,
|
||||
min_reply: 1
|
||||
});
|
||||
|
||||
const isReachable = result.alive;
|
||||
const now = new Date().toISOString();
|
||||
|
||||
// Hole oder erstelle Ping-Status für heute
|
||||
db.get('SELECT * FROM ping_status WHERE user_id = ? AND date = ?',
|
||||
[userId, currentDate], (err, pingStatus) => {
|
||||
if (err) {
|
||||
console.error(`Fehler beim Abrufen des Ping-Status für User ${userId}:`, err);
|
||||
return;
|
||||
}
|
||||
|
||||
// Hole aktuellen Eintrag für heute
|
||||
db.get('SELECT * FROM timesheet_entries WHERE user_id = ? AND date = ? ORDER BY updated_at DESC, id DESC LIMIT 1',
|
||||
[userId, currentDate], (err, entry) => {
|
||||
if (err) {
|
||||
console.error(`Fehler beim Abrufen des Eintrags für User ${userId}:`, err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isReachable) {
|
||||
// IP ist erreichbar
|
||||
if (!pingStatus) {
|
||||
// Erstelle neuen Ping-Status
|
||||
db.run(`INSERT INTO ping_status (user_id, date, last_successful_ping, failed_ping_count, start_time_set)
|
||||
VALUES (?, ?, ?, 0, 0)`,
|
||||
[userId, currentDate, now], (err) => {
|
||||
if (err) console.error(`Fehler beim Erstellen des Ping-Status:`, err);
|
||||
});
|
||||
} else {
|
||||
// Update Ping-Status: Reset failed_ping_count, update last_successful_ping
|
||||
db.run(`UPDATE ping_status
|
||||
SET last_successful_ping = ?, failed_ping_count = 0, first_failed_ping_time = NULL
|
||||
WHERE user_id = ? AND date = ?`,
|
||||
[now, userId, currentDate], (err) => {
|
||||
if (err) console.error(`Fehler beim Aktualisieren des Ping-Status:`, err);
|
||||
});
|
||||
}
|
||||
|
||||
// Start-Zeit setzen wenn noch nicht vorhanden
|
||||
if (entry && !entry.start_time) {
|
||||
db.run('UPDATE timesheet_entries SET start_time = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?',
|
||||
[currentTime, entry.id], (err) => {
|
||||
if (err) {
|
||||
console.error(`Fehler beim Setzen der Start-Zeit für User ${userId}:`, err);
|
||||
} else {
|
||||
console.log(`Start-Zeit gesetzt für User ${userId} um ${currentTime}`);
|
||||
// Markiere dass Start-Zeit gesetzt wurde
|
||||
db.run('UPDATE ping_status SET start_time_set = 1 WHERE user_id = ? AND date = ?',
|
||||
[userId, currentDate], (err) => {
|
||||
if (err) console.error(`Fehler beim Aktualisieren von start_time_set:`, err);
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (!entry) {
|
||||
// Kein Eintrag existiert → Erstelle neuen mit start_time
|
||||
db.run(`INSERT INTO timesheet_entries (user_id, date, start_time, updated_at)
|
||||
VALUES (?, ?, ?, CURRENT_TIMESTAMP)`,
|
||||
[userId, currentDate, currentTime], (err) => {
|
||||
if (err) {
|
||||
console.error(`Fehler beim Erstellen des Eintrags für User ${userId}:`, err);
|
||||
} else {
|
||||
console.log(`Eintrag erstellt und Start-Zeit gesetzt für User ${userId} um ${currentTime}`);
|
||||
// Markiere dass Start-Zeit gesetzt wurde
|
||||
db.run('UPDATE ping_status SET start_time_set = 1 WHERE user_id = ? AND date = ?',
|
||||
[userId, currentDate], (err) => {
|
||||
if (err) {
|
||||
// Falls kein Ping-Status existiert, erstelle einen
|
||||
db.run(`INSERT INTO ping_status (user_id, date, last_successful_ping, failed_ping_count, start_time_set)
|
||||
VALUES (?, ?, ?, 0, 1)`,
|
||||
[userId, currentDate, now], (err) => {
|
||||
if (err) console.error(`Fehler beim Erstellen des Ping-Status:`, err);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// IP ist nicht erreichbar
|
||||
if (!pingStatus) {
|
||||
// Erstelle neuen Ping-Status mit failed_ping_count = 1
|
||||
db.run(`INSERT INTO ping_status (user_id, date, failed_ping_count, first_failed_ping_time)
|
||||
VALUES (?, ?, 1, ?)`,
|
||||
[userId, currentDate, now], (err) => {
|
||||
if (err) console.error(`Fehler beim Erstellen des Ping-Status:`, err);
|
||||
});
|
||||
} else {
|
||||
// Erhöhe failed_ping_count
|
||||
const newFailedCount = (pingStatus.failed_ping_count || 0) + 1;
|
||||
const firstFailedTime = pingStatus.first_failed_ping_time || now;
|
||||
|
||||
db.run(`UPDATE ping_status
|
||||
SET failed_ping_count = ?, first_failed_ping_time = ?
|
||||
WHERE user_id = ? AND date = ?`,
|
||||
[newFailedCount, firstFailedTime, userId, currentDate], (err) => {
|
||||
if (err) console.error(`Fehler beim Aktualisieren des Ping-Status:`, err);
|
||||
});
|
||||
|
||||
// Wenn 3 oder mehr fehlgeschlagene Pings UND Start-Zeit existiert UND keine End-Zeit
|
||||
if (newFailedCount >= 3 && entry && entry.start_time && !entry.end_time) {
|
||||
// Setze End-Zeit auf Zeit des ersten fehlgeschlagenen Pings
|
||||
const firstFailedDate = new Date(firstFailedTime);
|
||||
const endTime = `${String(firstFailedDate.getHours()).padStart(2, '0')}:${String(firstFailedDate.getMinutes()).padStart(2, '0')}`;
|
||||
|
||||
// Berechne total_hours
|
||||
const breakMinutes = entry.break_minutes || 0;
|
||||
const totalHours = updateTotalHours(entry.start_time, endTime, breakMinutes);
|
||||
|
||||
db.run('UPDATE timesheet_entries SET end_time = ?, total_hours = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?',
|
||||
[endTime, totalHours, entry.id], (err) => {
|
||||
if (err) {
|
||||
console.error(`Fehler beim Setzen der End-Zeit für User ${userId}:`, err);
|
||||
} else {
|
||||
console.log(`End-Zeit gesetzt für User ${userId} um ${endTime} (nach ${newFailedCount} fehlgeschlagenen Pings)`);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`Fehler beim Ping für User ${userId} (IP: ${ip}):`, error);
|
||||
// Behandle als nicht erreichbar
|
||||
const now = new Date().toISOString();
|
||||
db.get('SELECT * FROM ping_status WHERE user_id = ? AND date = ?',
|
||||
[userId, currentDate], (err, pingStatus) => {
|
||||
if (!err && pingStatus) {
|
||||
const newFailedCount = (pingStatus.failed_ping_count || 0) + 1;
|
||||
const firstFailedTime = pingStatus.first_failed_ping_time || now;
|
||||
|
||||
db.run(`UPDATE ping_status
|
||||
SET failed_ping_count = ?, first_failed_ping_time = ?
|
||||
WHERE user_id = ? AND date = ?`,
|
||||
[newFailedCount, firstFailedTime, userId, currentDate], (err) => {
|
||||
if (err) console.error(`Fehler beim Aktualisieren des Ping-Status:`, err);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Ping-Service Setup
|
||||
function setupPingService() {
|
||||
setInterval(async () => {
|
||||
const currentDate = getCurrentDate();
|
||||
const currentTime = getCurrentTime();
|
||||
|
||||
// Hole alle User mit IP-Adresse
|
||||
db.all('SELECT id, ping_ip FROM users WHERE ping_ip IS NOT NULL AND ping_ip != ""', (err, users) => {
|
||||
if (err) {
|
||||
console.error('Fehler beim Abrufen der User mit IP-Adressen:', err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!users || users.length === 0) {
|
||||
return; // Keine User mit IP-Adressen
|
||||
}
|
||||
|
||||
// Ping alle User parallel
|
||||
users.forEach(user => {
|
||||
pingUserIP(user.id, user.ping_ip, currentDate, currentTime);
|
||||
});
|
||||
});
|
||||
}, 60000); // Jede Minute
|
||||
}
|
||||
|
||||
module.exports = { setupPingService };
|
||||
Reference in New Issue
Block a user