116 lines
3.8 KiB
JavaScript
116 lines
3.8 KiB
JavaScript
'use strict';
|
|
|
|
const { app, BrowserWindow, clipboard, dialog, ipcMain, net, protocol, shell } = require('electron');
|
|
const fs = require('node:fs/promises');
|
|
const path = require('node:path');
|
|
const { pathToFileURL } = require('node:url');
|
|
const { buildCsv } = require('./static/export-utils');
|
|
const { APP_RENDERER_URL, resolveAppAssetPath } = require('./src/app-protocol');
|
|
const { KEY_PATTERN } = require('./src/discovery');
|
|
const { createScanSession } = require('./src/scan-session');
|
|
|
|
protocol.registerSchemesAsPrivileged([{
|
|
scheme: 'app',
|
|
privileges: { standard: true, secure: true, supportFetchAPI: true },
|
|
}]);
|
|
|
|
let mainWindow = null;
|
|
const scanSession = createScanSession();
|
|
|
|
function isTrustedSender(event) {
|
|
if (!mainWindow || mainWindow.isDestroyed() || !event || !event.senderFrame) return false;
|
|
return event.sender === mainWindow.webContents &&
|
|
event.senderFrame === mainWindow.webContents.mainFrame &&
|
|
event.senderFrame.url === APP_RENDERER_URL;
|
|
}
|
|
|
|
function registerIpc(channel, handler) {
|
|
ipcMain.handle(channel, async (event, ...args) => {
|
|
if (!isTrustedSender(event)) throw new Error('Forbidden IPC sender');
|
|
return handler(...args);
|
|
});
|
|
}
|
|
|
|
async function openCameraUrl(value) {
|
|
const url = scanSession.allowedCameraUrl(value);
|
|
if (!url) return;
|
|
await shell.openExternal(url);
|
|
}
|
|
|
|
function registerIpcHandlers() {
|
|
registerIpc('scan-cameras', scanSession.scanOnce);
|
|
registerIpc('copy-text', (value) => {
|
|
const text = String(value || '');
|
|
if (!KEY_PATTERN.test(text)) throw new Error('Invalid Alta Key.');
|
|
clipboard.writeText(text);
|
|
return { ok: true };
|
|
});
|
|
registerIpc('export-csv', async () => {
|
|
const csv = buildCsv(scanSession.getLatestCameras());
|
|
if (Buffer.byteLength(csv, 'utf8') > 1024 * 1024) throw new Error('CSV export is too large.');
|
|
const result = await dialog.showSaveDialog(mainWindow, {
|
|
title: 'Export discovered Alta cameras',
|
|
defaultPath: 'alta-camera-keys.csv',
|
|
filters: [{ name: 'CSV file', extensions: ['csv'] }],
|
|
});
|
|
if (result.canceled || !result.filePath) return { ok: false, canceled: true };
|
|
await fs.writeFile(result.filePath, csv, 'utf8');
|
|
return { ok: true, canceled: false };
|
|
});
|
|
}
|
|
|
|
function registerAppProtocol() {
|
|
const staticDir = path.join(__dirname, 'static');
|
|
protocol.handle('app', (request) => {
|
|
const assetPath = resolveAppAssetPath(request.url, staticDir);
|
|
if (!assetPath) return new Response('Not found', { status: 404 });
|
|
return net.fetch(pathToFileURL(assetPath).href);
|
|
});
|
|
}
|
|
|
|
function denyRendererPermissions(targetSession) {
|
|
targetSession.setPermissionCheckHandler(() => false);
|
|
targetSession.setPermissionRequestHandler((_webContents, _permission, callback) => callback(false));
|
|
}
|
|
|
|
function createWindow() {
|
|
mainWindow = new BrowserWindow({
|
|
width: 1180,
|
|
height: 760,
|
|
minWidth: 820,
|
|
minHeight: 620,
|
|
backgroundColor: '#f5f6f8',
|
|
icon: path.join(__dirname, 'assets', 'icon.png'),
|
|
title: 'Alta Local Camera Discovery',
|
|
autoHideMenuBar: true,
|
|
webPreferences: {
|
|
nodeIntegration: false,
|
|
contextIsolation: true,
|
|
sandbox: true,
|
|
preload: path.join(__dirname, 'preload.js'),
|
|
},
|
|
});
|
|
denyRendererPermissions(mainWindow.webContents.session);
|
|
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
|
|
openCameraUrl(url).catch(() => {});
|
|
return { action: 'deny' };
|
|
});
|
|
mainWindow.webContents.on('will-navigate', (event) => event.preventDefault());
|
|
mainWindow.loadURL(APP_RENDERER_URL);
|
|
if (!app.isPackaged && process.argv.includes('--dev')) mainWindow.webContents.openDevTools();
|
|
}
|
|
|
|
app.whenReady().then(() => {
|
|
registerAppProtocol();
|
|
registerIpcHandlers();
|
|
createWindow();
|
|
});
|
|
|
|
app.on('activate', () => {
|
|
if (BrowserWindow.getAllWindows().length === 0) createWindow();
|
|
});
|
|
|
|
app.on('window-all-closed', () => {
|
|
if (process.platform !== 'darwin') app.quit();
|
|
});
|