103 lines
3.7 KiB
JavaScript
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();
|