frontend and backend to follow db.js strictly
This commit is contained in:
@@ -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
@@ -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) => {
|
||||
|
||||
Reference in New Issue
Block a user