feat(admin): move integration URLs and credentials to GUI settings

This commit is contained in:
Matthias Hochmeister
2026-04-20 16:29:12 +02:00
parent 65820805b0
commit c55ec55e1b
15 changed files with 860 additions and 93 deletions

View File

@@ -1,7 +1,7 @@
import 'dotenv/config';
import * as http from 'http';
import { Pool } from 'pg';
import { scrapeAll } from './scraper';
import { scrapeAll, FdiskConfig } from './scraper';
import { syncToDatabase, syncLehrgangToAtemschutz, syncUntersuchungenToAtemschutz } from './db';
// In-memory log ring buffer — last 500 lines captured from all modules
@@ -52,8 +52,6 @@ async function runSync(force = false): Promise<void> {
return;
}
syncRunning = true;
const username = requireEnv('FDISK_USERNAME');
const password = requireEnv('FDISK_PASSWORD');
const pool = new Pool({
host: requireEnv('DB_HOST'),
@@ -64,10 +62,32 @@ async function runSync(force = false): Promise<void> {
});
try {
// Load FDISK config from app_settings, fall back to env vars
const settingKeys = ['fdisk_base_url', 'fdisk_id_feuerwehren', 'fdisk_id_instanzen', 'fdisk_username', 'fdisk_password'];
const rows = await pool.query(`SELECT key, value FROM app_settings WHERE key = ANY($1)`, [settingKeys]);
const dbSettings: Record<string, string> = {};
for (const r of rows.rows) { dbSettings[r.key] = r.value as string; }
const getOrEnv = (key: string, envFallback: string) =>
(dbSettings[key] && dbSettings[key] !== '' && dbSettings[key] !== '""') ? dbSettings[key] : envFallback;
const fdiskConfig: FdiskConfig = {
baseUrl: getOrEnv('fdisk_base_url', process.env.FDISK_BASE_URL || 'https://app.fdisk.at'),
idFeuerwehren: getOrEnv('fdisk_id_feuerwehren', process.env.FDISK_ID_FEUERWEHREN || '164'),
idInstanzen: getOrEnv('fdisk_id_instanzen', process.env.FDISK_ID_INSTANZEN || '2853'),
username: getOrEnv('fdisk_username', process.env.FDISK_USERNAME || ''),
password: getOrEnv('fdisk_password', process.env.FDISK_PASSWORD || ''),
};
if (!fdiskConfig.username || !fdiskConfig.password) {
log('WARN: FDISK username/password not configured in DB or env vars — skipping sync');
return;
}
if (force) log('Force mode: ON');
log('Starting FDISK sync');
const { members, ausbildungen, befoerderungen, untersuchungen, fahrgenehmigungen } = await scrapeAll(username, password);
const { members, ausbildungen, befoerderungen, untersuchungen, fahrgenehmigungen } = await scrapeAll(fdiskConfig);
await syncToDatabase(pool, members, ausbildungen, befoerderungen, untersuchungen, fahrgenehmigungen, force);
log(`Sync complete — ${members.length} members, ${ausbildungen.length} Ausbildungen, ${befoerderungen.length} Beförderungen, ${untersuchungen.length} Untersuchungen, ${fahrgenehmigungen.length} Fahrgenehmigungen`);
await syncLehrgangToAtemschutz(pool);

View File

@@ -7,12 +7,20 @@ import {
FdiskFahrgenehmigung,
} from './types';
const BASE_URL = process.env.FDISK_BASE_URL ?? 'https://app.fdisk.at';
const ID_FEUERWEHREN = process.env.FDISK_ID_FEUERWEHREN ?? '164';
const ID_INSTANZEN = process.env.FDISK_ID_INSTANZEN ?? '2853';
export interface FdiskConfig {
baseUrl: string;
idFeuerwehren: string;
idInstanzen: string;
username: string;
password: string;
}
const LOGIN_URL = `${BASE_URL}/fdisk/module/vws/logins/logins.aspx`;
const MEMBERS_URL = `${BASE_URL}/fdisk/module/mgvw/mitgliedschaften/meine_Mitglieder.aspx`;
let BASE_URL = process.env.FDISK_BASE_URL ?? 'https://app.fdisk.at';
let ID_FEUERWEHREN = process.env.FDISK_ID_FEUERWEHREN ?? '164';
let ID_INSTANZEN = process.env.FDISK_ID_INSTANZEN ?? '2853';
let LOGIN_URL = `${BASE_URL}/fdisk/module/vws/logins/logins.aspx`;
let MEMBERS_URL = `${BASE_URL}/fdisk/module/mgvw/mitgliedschaften/meine_Mitglieder.aspx`;
/**
* Maps a raw FDISK status string to a dashboard status value.
@@ -173,13 +181,20 @@ async function scrapeKnownMembers(
return members;
}
export async function scrapeAll(username: string, password: string): Promise<{
export async function scrapeAll(config: FdiskConfig): Promise<{
members: FdiskMember[];
ausbildungen: FdiskAusbildung[];
befoerderungen: FdiskBefoerderung[];
untersuchungen: FdiskUntersuchung[];
fahrgenehmigungen: FdiskFahrgenehmigung[];
}> {
// Apply config to module-level variables used by helper functions
BASE_URL = config.baseUrl;
ID_FEUERWEHREN = config.idFeuerwehren;
ID_INSTANZEN = config.idInstanzen;
LOGIN_URL = `${BASE_URL}/fdisk/module/vws/logins/logins.aspx`;
MEMBERS_URL = `${BASE_URL}/fdisk/module/mgvw/mitgliedschaften/meine_Mitglieder.aspx`;
const browser = await chromium.launch({
headless: true,
args: ['--disable-gpu', '--disable-software-rasterizer'],
@@ -190,7 +205,7 @@ export async function scrapeAll(username: string, password: string): Promise<{
const page = await context.newPage();
try {
await login(page, username, password);
await login(page, config.username, config.password);
// After login, page is on Start.aspx (frameset).
// Direct navigation to MitgliedschaftenList.aspx causes a server BLError because