init
This commit is contained in:
Binary file not shown.
+107
@@ -0,0 +1,107 @@
|
||||
import debounce from 'just-debounce-it';
|
||||
import { watch } from 'node:fs';
|
||||
import { stat, readFile, writeFile } from 'node:fs/promises';
|
||||
import { join } from 'node:path';
|
||||
import readline from 'node:readline/promises';
|
||||
import { parse } from 'ini';
|
||||
import MDBReader from 'mdb-reader';
|
||||
import { diff } from 'just-diff';
|
||||
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
});
|
||||
|
||||
async function promptToClose(message: string) {
|
||||
await rl.question(`== ${message}\n== Press enter to quit.`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Check for config.ini file
|
||||
const configPath = join(process.cwd(), 'config.ini');
|
||||
try {
|
||||
const configStat = await stat(configPath);
|
||||
if (configStat.isDirectory()) await promptToClose('The config file must be a file.');
|
||||
} catch (e) {
|
||||
await writeFile(configPath, [
|
||||
'; The database file to upload, defaults to C:\\Users\\<user>\\Documents\\Affirmatech\\N3FJP Software\\ARRL-Field-Day\\LogData.mdb',
|
||||
`; database_file = "${process.env.USERNAME ? `C:\\Users\\${process.env.USERNAME}\\Documents\\Affirmatech\\N3FJP Software\\ARRL-Field-Day\\LogData.mdb` : ''}"`,
|
||||
'',
|
||||
'; The endpoint to upload to',
|
||||
'log_endpoint = "https://example.local/up"'
|
||||
].join('\n'))
|
||||
await promptToClose('Failed to find config file. A new one was created, so put in your variables in there.');
|
||||
}
|
||||
|
||||
let databaseFile: string;
|
||||
let logEndpoint: string;
|
||||
try {
|
||||
const config = parse(await readFile(configPath, { encoding: 'utf-8' }));
|
||||
databaseFile = config.database_file;
|
||||
logEndpoint = config.log_endpoint;
|
||||
if (!logEndpoint) await promptToClose('A log endpoint is required in the config file.');
|
||||
} catch (e) {
|
||||
databaseFile = '';
|
||||
await promptToClose('Failed to parse config file. You can remove the file and re-run this program if more problems occur.');
|
||||
}
|
||||
|
||||
const dbPath = databaseFile ?? process.env.USERNAME ? join('C:/Users', process.env.USERNAME!, 'Documents/Affirmatech/N3FJP Software/ARRL-Field-Day/LogData.mdb') : '';
|
||||
|
||||
// Check for log file
|
||||
try {
|
||||
if (!dbPath) await promptToClose('A log path is needed to use this program.');
|
||||
const fileStat = await stat(dbPath);
|
||||
if (fileStat.isDirectory()) await promptToClose('The log path must point to a file.');
|
||||
console.log(`\n== Following changes for the file "${dbPath}"\n== Ctrl-C to exit.\n`);
|
||||
} catch (e) {
|
||||
await promptToClose(`Failed to find log file at "${dbPath}"`);
|
||||
}
|
||||
|
||||
// Upload logic
|
||||
let lastData: Record<string, any> = {};
|
||||
async function _upload() {
|
||||
console.log('-- Reading log...');
|
||||
const reader = new MDBReader(await readFile(dbPath));
|
||||
if (!reader.getTableNames().includes('tblContacts'))
|
||||
return console.log('!! No "tblContacts" table present.');
|
||||
|
||||
const table = reader.getTable("tblContacts");
|
||||
const tableData = table.getData<Record<string, string>>();
|
||||
|
||||
const data = tableData.reduce((p, r) => ({ ...p, [r.fldPrimaryKey]: r }), {} as Record<string, any>);
|
||||
const differences = diff(lastData, data);
|
||||
const changedIds = [...new Set(differences.filter((r) => r.op !== 'remove').map((r) => r.path[0]))];
|
||||
const changedRows = changedIds.map((id) => data[id]);
|
||||
|
||||
if (changedIds.length === 0) return void console.log('-- No changes found.');
|
||||
|
||||
console.log(`-> Uploading ${changedIds.length.toLocaleString()} row(s)...`);
|
||||
const response = await fetch(logEndpoint, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(changedRows),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
console.log(`<- Recieved ${response.status} (${response.statusText})`);
|
||||
if (response.status === 200) lastData = data;
|
||||
}
|
||||
|
||||
const upload = debounce(_upload, 250);
|
||||
|
||||
const watcher = watch(dbPath, (event, filename) => {
|
||||
if (event === 'rename') return void console.log(`== ${filename} was renamed, something might break`);
|
||||
upload();
|
||||
});
|
||||
|
||||
process.on("SIGINT", () => {
|
||||
// close watcher when Ctrl-C is pressed
|
||||
console.log('\n== Closing watcher...');
|
||||
watcher.close();
|
||||
|
||||
process.exit(0);
|
||||
});
|
||||
rl.close();
|
||||
|
||||
// "Upload first" option
|
||||
if (process.argv[2] === '--upload') _upload();
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "fdlogup-client",
|
||||
"module": "index.ts",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"exe": "bun build --compile --target=bun-windows-x64 ./index.ts --minify --outfile fdlogup-client",
|
||||
"start": "bun run ./index.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
"@types/ini": "^4.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ini": "^4.1.3",
|
||||
"just-debounce-it": "^3.2.0",
|
||||
"just-diff": "^6.0.2",
|
||||
"mdb-reader": "^3.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
// Enable latest features
|
||||
"lib": ["ESNext", "DOM"],
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleDetection": "force",
|
||||
"jsx": "react-jsx",
|
||||
"allowJs": true,
|
||||
|
||||
// Bundler mode
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"noEmit": true,
|
||||
|
||||
// Best practices
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
|
||||
// Some stricter flags (disabled by default)
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noPropertyAccessFromIndexSignature": false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user