Files
HRM-System/branch-agent/sync.js

103 lines
3.7 KiB
JavaScript

const path = require('path');
const fs = require('fs');
// For pkg-bundled .exe: read .env from same folder as the executable
const envPath = path.join(path.dirname(process.execPath), '.env');
require('dotenv').config({ path: envPath });
const ZKLib = require('zklib-js');
const axios = require('axios');
const ZKTECO_IP = process.env.ZKTECO_IP || '192.168.1.201';
const ZKTECO_PORT = process.env.ZKTECO_PORT || 4370;
const SERVER_API_URL = process.env.SERVER_API_URL || 'https://scxsoftware.com/api/zkteco/sync';
const API_KEY = process.env.API_KEY || 'default_secure_sync_key_123';
const BRANCH_ID = process.env.BRANCH_ID || 1;
const SYNC_DAYS = parseInt(process.env.SYNC_DAYS || 7, 10);
const logFilePath = path.join(path.dirname(process.execPath), 'sync.log');
// Enhanced Logger Helper
function log(message, type = 'INFO') {
const timestamp = new Date().toISOString().replace('T', ' ').substring(0, 19);
const formattedMessage = `[${timestamp}] [${type}] ${message}\n`;
// Output to console
if (type === 'ERROR') {
console.error(formattedMessage.trim());
} else {
console.log(formattedMessage.trim());
}
// Append to file
try {
fs.appendFileSync(logFilePath, formattedMessage, 'utf8');
} catch (err) {
console.error(`[${timestamp}] [ERROR] Failed to write to local sync.log: ${err.message}`);
}
}
async function syncAttendance() {
log('Starting ZKTeco Sync...');
log(`Target Device: ${ZKTECO_IP}:${ZKTECO_PORT}`);
log(`Sync Window: Last ${SYNC_DAYS} days`);
const zkInstance = new ZKLib(ZKTECO_IP, ZKTECO_PORT, 10000, 4000);
try {
await zkInstance.createSocket();
log('Connected to ZKTeco device.');
log('Fetching attendance records...');
const attendances = await zkInstance.getAttendances();
if (attendances && attendances.data && attendances.data.length > 0) {
log(`Found total ${attendances.data.length} records on device.`);
// Filter to last X days
const targetDate = new Date();
targetDate.setDate(targetDate.getDate() - SYNC_DAYS);
const recentLogs = attendances.data.filter(log => {
const logDate = new Date(log.recordTime);
return logDate >= targetDate;
});
log(`Filtered to ${recentLogs.length} records from the last ${SYNC_DAYS} days.`);
if (recentLogs.length > 0) {
log(`Pushing to Cloud API: ${SERVER_API_URL}`);
const response = await axios.post(SERVER_API_URL, {
api_key: API_KEY,
branch_id: BRANCH_ID,
zkteco_ip: ZKTECO_IP,
attendances: recentLogs
});
log(`Sync Successful: ${JSON.stringify(response.data)}`);
} else {
log('No records found within the sync window.');
}
} else {
log('No attendance records found on device.');
}
} catch (e) {
log(`Error during sync: ${e.message}`, 'ERROR');
if (e.response && e.response.data) {
log(`API Response Error: ${JSON.stringify(e.response.data)}`, 'ERROR');
}
} finally {
try {
await zkInstance.disconnect();
log('Disconnected from ZKTeco device.');
} catch (err) {}
// Keep window open for user to read logs
console.log('\nPress any key to exit...');
process.stdin.setRawMode(true);
process.stdin.resume();
process.stdin.on('data', process.exit.bind(process, 0));
}
}
// Execute the sync
syncAttendance();