inital
This commit is contained in:
140
node/watch.ts
Normal file
140
node/watch.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
import { watch, readFileSync } from 'fs';
|
||||
import { readdir, readFile, stat } from 'fs/promises';
|
||||
import { resolve, basename, extname, join, dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
// --- Configuration ---
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
function loadEnv() {
|
||||
const envPath = resolve(__dirname, '.env');
|
||||
try {
|
||||
const envFileContent = readFileSync(envPath, 'utf-8');
|
||||
envFileContent.split(/\r?\n/).forEach(line => {
|
||||
const trimmedLine = line.trim();
|
||||
if (trimmedLine && !trimmedLine.startsWith('#')) {
|
||||
const parts = trimmedLine.split('=', 2);
|
||||
if (parts.length === 2) {
|
||||
const [key, value] = parts;
|
||||
process.env[key.trim()] = value.trim().replace(/^"|"$/g, '');
|
||||
}
|
||||
}
|
||||
});
|
||||
console.log('Successfully loaded configuration from .env file.');
|
||||
} catch (error) {
|
||||
console.error('FATAL: Could not find or read the .env file. Please ensure it exists in the same directory as this script.');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
loadEnv();
|
||||
|
||||
const { WEBHOOK_BASE_URL, BASIC_AUTH_USERNAME, BASIC_AUTH_PASSWORD } = process.env;
|
||||
|
||||
if (!WEBHOOK_BASE_URL || !BASIC_AUTH_USERNAME || !BASIC_AUTH_PASSWORD) {
|
||||
console.error('FATAL: Please define WEBHOOK_BASE_URL, BASIC_AUTH_USERNAME, and BASIC_AUTH_PASSWORD in your .env file.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const WATCH_PATH = resolve(__dirname, '..', '..', '..', 'script-output', 'signal_export');
|
||||
const DEBOUNCE_DELAY_MS = 250;
|
||||
const debounceTimers = new Map<string, NodeJS.Timeout>();
|
||||
|
||||
// --- Core Logic ---
|
||||
|
||||
async function sendToWebhook(filePath: string) {
|
||||
try {
|
||||
const combinatorName = basename(filePath, extname(filePath));
|
||||
const url = `${WEBHOOK_BASE_URL}/${encodeURIComponent(combinatorName)}`;
|
||||
const content = await readFile(filePath, 'utf-8');
|
||||
|
||||
// Silently ignore empty files, which can occur during file writing.
|
||||
if (!content.trim()) return;
|
||||
JSON.parse(content);
|
||||
|
||||
const authHeader = 'Basic ' + Buffer.from(`${BASIC_AUTH_USERNAME}:${BASIC_AUTH_PASSWORD}`).toString('base64');
|
||||
|
||||
console.log(`Sending data for '${combinatorName}'...`);
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'POST', // Using POST to send a request body, as is standard for webhooks.
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': authHeader,
|
||||
},
|
||||
body: content,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP Error: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
console.log(`Successfully sent data for '${combinatorName}'. Server responded with ${response.status}.`);
|
||||
} catch (err: any) {
|
||||
const fileName = basename(filePath);
|
||||
if (err instanceof SyntaxError) {
|
||||
console.error(`Skipping ${fileName}: Invalid JSON content. The file might be incomplete.`);
|
||||
} else {
|
||||
console.error(`Error sending webhook for ${fileName}:`, err.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleFileChange(filename: string | null) {
|
||||
if (!filename || !filename.endsWith('.json')) return;
|
||||
|
||||
if (debounceTimers.has(filename)) {
|
||||
clearTimeout(debounceTimers.get(filename)!);
|
||||
}
|
||||
|
||||
const newTimer = setTimeout(async () => {
|
||||
const fullPath = join(WATCH_PATH, filename);
|
||||
try {
|
||||
const stats = await stat(fullPath);
|
||||
if (stats.isFile()) {
|
||||
await sendToWebhook(fullPath);
|
||||
}
|
||||
} catch {
|
||||
// Error (e.g., file deleted before processing), ignore.
|
||||
} finally {
|
||||
debounceTimers.delete(filename);
|
||||
}
|
||||
}, DEBOUNCE_DELAY_MS);
|
||||
|
||||
debounceTimers.set(filename, newTimer);
|
||||
}
|
||||
|
||||
async function initialScanAndProcess() {
|
||||
console.log(`Performing initial scan of directory: ${WATCH_PATH}`);
|
||||
try {
|
||||
const files = await readdir(WATCH_PATH);
|
||||
const jsonFiles = files.filter(f => f.endsWith('.json'));
|
||||
console.log(`Found ${jsonFiles.length} existing JSON file(s). Processing...`);
|
||||
for (const file of jsonFiles) {
|
||||
await sendToWebhook(join(WATCH_PATH, file));
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error(`Error during initial scan: ${err.message}. Ensure the directory exists.`);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Script Execution ---
|
||||
|
||||
(async () => {
|
||||
await initialScanAndProcess();
|
||||
|
||||
console.log(`\nWatching for file changes in: ${WATCH_PATH}`);
|
||||
try {
|
||||
watch(WATCH_PATH, (eventType, filename) => {
|
||||
if (filename) {
|
||||
console.log(`[${new Date().toLocaleTimeString()}] Event '${eventType}' on '${filename}'`);
|
||||
handleFileChange(filename);
|
||||
}
|
||||
});
|
||||
console.log("Watcher is now running. Press Ctrl+C to stop.");
|
||||
} catch (err: any) {
|
||||
console.error(`FATAL: Failed to start watcher. Please check if the path is correct. Error: ${err.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user