Upload via GUI (16 Dateien)

This commit is contained in:
2026-09-14 18:57:05 +00:00
parent 6733901d99
commit 98d23de73f
16 changed files with 1930 additions and 0 deletions
+145
View File
@@ -0,0 +1,145 @@
"use strict";
const { app, BrowserWindow, dialog } = require("electron");
const fs = require("fs");
const path = require("path");
const db = require("./db");
const lock = require("./lock");
const backup = require("./backup");
const reminders = require("./reminders");
const webserver = require("./webserver");
const registerIpc = require("./ipc");
// Works around a blank/unpainted-window glitch seen on some Windows GPU drivers
// (matches the "GPU process exited unexpectedly" warnings this app triggered there) —
// newly inserted DOM content (e.g. the modal) wouldn't repaint until the window was
// resized or moved. Must be called before app is ready.
app.disableHardwareAcceleration();
const settingsPath = path.join(app.getPath("userData"), "settings.json");
const iconPath = path.join(__dirname, "..", "assets", "icon.ico");
function readSettings() {
try {
return JSON.parse(fs.readFileSync(settingsPath, "utf8"));
} catch {
return {};
}
}
function writeSettings(s) {
fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
fs.writeFileSync(settingsPath, JSON.stringify(s, null, 2));
}
let mainWindow = null;
async function chooseDataFolder(isFirstRun) {
const message = isFirstRun
? "Wo soll die Fellakte ihre Daten speichern?\n\nFür zwei PCs in der Praxis: einen Ordner auf einem gemeinsamen Netzlaufwerk oder einem synchronisierten Ordner (z. B. OneDrive/Dropbox) wählen. Für einen einzelnen PC reicht ein normaler Ordner."
: "Neuen Datenordner wählen";
await dialog.showMessageBox({
type: "info",
title: "Fellakte Datenordner",
message,
buttons: ["Weiter"],
});
const result = await dialog.showOpenDialog({
title: "Datenordner für die Fellakte wählen oder anlegen",
properties: ["openDirectory", "createDirectory"],
});
if (result.canceled || !result.filePaths[0]) return null;
return result.filePaths[0];
}
async function ensureDataFolder() {
const settings = readSettings();
let folder = settings.dataFolder;
if (!folder || !fs.existsSync(folder)) {
folder = await chooseDataFolder(true);
if (!folder) {
app.quit();
return null;
}
writeSettings({ ...settings, dataFolder: folder });
}
return folder;
}
async function checkLockAndWarn(folder) {
const { active, info } = lock.checkOther(folder);
if (!active) return true;
const when = new Date(info.updatedAt).toLocaleTimeString("de-DE");
const { response } = await dialog.showMessageBox({
type: "warning",
title: "Fellakte wird eventuell gerade woanders benutzt",
message: `Auf "${info.host}" wurde dieser Datenordner zuletzt um ${when} Uhr verwendet.`,
detail:
"Wenn dort gerade jemand arbeitet und du hier ebenfalls speicherst, überschreibt die zuletzt gespeicherte Version die andere. Am sichersten: kurz nachfragen, ob der andere PC gerade offen ist.",
buttons: ["Trotzdem öffnen", "Abbrechen"],
defaultId: 1,
cancelId: 1,
});
return response === 0;
}
async function createWindow(dataFolder) {
mainWindow = new BrowserWindow({
width: 1360,
height: 860,
minWidth: 980,
minHeight: 640,
backgroundColor: "#f7f0e8",
icon: iconPath,
webPreferences: {
preload: path.join(__dirname, "preload.js"),
backgroundThrottling: false,
contextIsolation: true,
nodeIntegration: false,
sandbox: true,
},
});
mainWindow.setMenuBarVisibility(false);
// Belt-and-suspenders alongside the webPreferences option above: without this,
// Chromium can throttle/skip repainting a window that isn't the active foreground
// one yet, which reads as "newly opened content stays blank until you resize/refocus".
mainWindow.webContents.setBackgroundThrottling(false);
await mainWindow.loadFile(path.join(__dirname, "..", "renderer", "index.html"));
}
app.whenReady().then(async () => {
const dataFolder = await ensureDataFolder();
if (!dataFolder) return;
const proceed = await checkLockAndWarn(dataFolder);
if (!proceed) {
app.quit();
return;
}
lock.acquire(dataFolder);
await db.init(dataFolder);
backup.runDailyBackupIfNeeded(dataFolder);
registerIpc({ dataFolder });
await createWindow(dataFolder);
reminders.start();
webserver.start(dataFolder);
app.on("activate", () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow(dataFolder);
});
});
app.on("window-all-closed", () => {
lock.release();
reminders.stop();
webserver.stop();
if (process.platform !== "darwin") app.quit();
});
app.on("before-quit", () => {
lock.release();
reminders.stop();
webserver.stop();
});