frontend and backend to follow db.js strictly

This commit is contained in:
Edison
2025-11-03 14:23:35 +08:00
parent 7e37230894
commit 1d89d47c53
2 changed files with 35 additions and 11 deletions
+4 -3
View File
@@ -280,12 +280,13 @@ const dayNameFromYMD = (yyyyMmDd) => {
return res.status(409).json({ message: `Worker is already clocked ${status}.` })
}
// --- THIS IS THE FIX ---
const sanitizedTimestamp = timestamp.replace('T', ' ')
const dt = parseNaiveAsTZ(timestamp, req.tz); // Date at the correct instant
const utcSql = dt.toISOString().slice(0, 19).replace('T', ' ');
await db.execute(
'INSERT INTO clock_records (worker_id, event_type, timestamp, notes, qr_code_id, latitude, longitude) VALUES (?, ?, ?, ?, NULL, NULL, NULL)',
[workerId, eventType, sanitizedTimestamp, notes],
)
[workerId, eventType, utcSql, notes],
);
res.status(201).json({ message: 'Manual record added successfully.' })
} catch (error) {
+31 -8
View File
@@ -2,7 +2,7 @@ import express from 'express';
import { point, polygon, booleanPointInPolygon, pointToLineDistance } from '@turf/turf';
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
// Removed unused import
import { APP_TIMEZONE } from './config/db.js';
async function validateDeviceForUser(userId, deviceUuid, db) {
const [userRows] = await db.execute('SELECT device_uuid FROM workers WHERE id = ?', [userId]);
@@ -15,9 +15,13 @@ async function validateDeviceForUser(userId, deviceUuid, db) {
return { valid: device_uuid === deviceUuid, message: 'Device validation failed' };
}
async function isClockingEnabled(db) {
const today = new Date().toISOString().slice(0, 10); // YYYY-MM-DD format
const [rows] = await db.execute('SELECT 1 FROM enabled_dates WHERE enabled_date = ? LIMIT 1', [today]);
async function isClockingEnabled(db, at) {
const today = new Intl.DateTimeFormat('en-CA', {
timeZone: APP_TIMEZONE, year: 'numeric', month: '2-digit', day: '2-digit'
}).format(at); // use the proposed event time
const [rows] = await db.execute(
'SELECT 1 FROM enabled_dates WHERE enabled_date = ? LIMIT 1', [today]
);
return rows.length > 0;
}
@@ -99,12 +103,18 @@ router.post('/clock', async (req, res) => {
try {
const { userId, eventType, qrCodeValue, latitude, longitude, device_epoch_ms } = req.body;
// Use the device's recorded instant if provided; fall back to server time.
const ts = Number.isFinite(+device_epoch_ms) ? new Date(Number(device_epoch_ms)) : new Date();
const serverNow = new Date();
const hasDeviceEpoch = Number.isFinite(+device_epoch_ms);
const proposed = hasDeviceEpoch ? new Date(Number(device_epoch_ms)) : serverNow;
const skewMs = Math.abs(serverNow - proposed);
let ts = skewMs > 10 * 60 * 1000 ? serverNow : proposed; // clamp at ±10 min
if (ts > serverNow) ts = serverNow; // never store “future” time
const currentTimestamp = ts.toISOString().slice(0, 19).replace('T', ' ');
// 1. Kill Switch Enforcement
const clockingAllowed = await isClockingEnabled(db);
const clockingAllowed = await isClockingEnabled(db, ts);
if (!clockingAllowed) {
const note = 'Clock-in/out function is not enabled for today.';
await db.execute(
@@ -202,13 +212,26 @@ router.post('/clock', async (req, res) => {
router.get('/worker/clock-history/:userId', async (req, res) => {
const { userId } = req.params;
const parseAsUTC = (s) => new Date((s.includes('T') ? s : s.replace(' ', 'T')) + 'Z');
const fmtDate = (d, tz) => new Intl.DateTimeFormat('en-CA', { timeZone: tz, year:'numeric', month:'2-digit', day:'2-digit' }).format(d);
const fmtTime = (d, tz) => new Intl.DateTimeFormat('en-GB', { timeZone: tz, hour:'2-digit', minute:'2-digit', second:'2-digit' }).format(d);
const [rows] = await db.execute(`
SELECT cr.id, cr.event_type, cr.timestamp, COALESCE(qc.name, 'Manual Entry') as qrCodeUsedName
FROM clock_records cr
LEFT JOIN qr_codes qc ON cr.qr_code_id = qc.id
WHERE cr.worker_id = ? ORDER BY cr.timestamp DESC
`, [userId]);
res.json(rows);
const enriched = rows.map(r => {
const d = parseAsUTC(r.timestamp);
return { ...r,
tz: APP_TIMEZONE,
local_date: fmtDate(d, APP_TIMEZONE),
local_time: fmtTime(d, APP_TIMEZONE),
};
});
res.json(enriched);
});
router.put('/worker/change-password', async (req, res) => {