Erweiterung um preload und typen

This commit is contained in:
Nils 2025-06-01 19:45:01 +02:00
parent b1f264424c
commit 56c16a6ab8
9 changed files with 101 additions and 55 deletions

View File

@ -1,6 +1,7 @@
{ {
"appId": "com.nwaschk.api-tool", "appId": "com.nwaschk.api-tool",
"files": ["dist-electron", "dist-svelte"], "files": ["dist-electron", "dist-svelte"],
"extraResources": ["dist-electron/preload.cjs"],
"icon": "./desktopIcon.png", "icon": "./desktopIcon.png",
"mac": { "mac": {
"target": "dmg" "target": "dmg"

View File

@ -1,50 +1,38 @@
import {app, BrowserWindow} from 'electron'; import {app, BrowserWindow} from 'electron';
import path from 'path'; import path from 'path';
import {getPreloadPath, inDevelopment} from './utils.js';
import {ipcMain} from 'electron/main';
import {ProdressConnection} from './pdr2com/connection.js'; import {ProdressConnection} from './pdr2com/connection.js';
const APPLICATION_PATH = path.join(app.getAppPath(), '/dist-svelte/index.html'); const APPLICATION_PATH = path.join(app.getAppPath(), '/dist-svelte/index.html');
app.on('ready', async () => { app.on('ready', async () => {
const mainWindow = new BrowserWindow({}); //@todo const mainWindow = new BrowserWindow({
if (process.env.NODE_ENV === 'development') { webPreferences: {
preload: getPreloadPath(),
},
}); //@todo
if (inDevelopment()) {
mainWindow.loadURL('http://localhost:5123/'); mainWindow.loadURL('http://localhost:5123/');
} else { } else {
mainWindow.loadFile(APPLICATION_PATH); mainWindow.loadFile(APPLICATION_PATH);
} }
const req = new ProdressConnection( ipcMain.handle('ping', () => {
{ const req = new ProdressConnection(
port: 4788, {
host: 'localhost', port: 4788,
database: 'test', host: 'localhost',
handler: 'Shopify', database: 'test',
action: 'artikelSync', handler: 'Shopify',
}, action: 'artikelSync',
[['string', 'HalloWelt']], },
[ [['string', 'HalloWelt']],
['line1', 'string'], [
['line2', 'string'], ['line1', 'string'],
], ['line2', 'string'],
); ],
const res = await req.handleRequest(); );
console.log(res); return req.handleRequest();
// try { });
// const socket = new Socket();
// socket.connect(4788, 'localhost');
//
// const test = new ProdressRequest('db', 'handler', 'aktion');
// test.schreibeText('HalloWelt 2355435');
// socket.on('connect', () => {
// console.log('verbunden');
// test.sende(socket);
// });
// const r2reader = new RequestLeser();
// socket.on('data', (data: Buffer) => {
// r2reader.verarbeiteDaten(data);
// });
// const r2request = await r2reader.leseRequest();
// console.log(r2request.nächsterText());
// } catch (error) {
// console.error(error);
// }
}); });

View File

@ -1,6 +1,7 @@
import {RequestLeser as ProdressListener, type R2AnfrageBlaupause} from '@prodress/pdr2com'; import {RequestLeser as ProdressListener, type R2AnfrageBlaupause} from '@prodress/pdr2com';
import {type ResponseTupel, type ResponseObject, ProdressRequest, type RequestTupel} from './wrapper.js'; import {type ResponseTupel, type ResponseObject, ProdressRequest, type RequestTupel} from './wrapper.js';
import {Socket} from 'node:net'; import {Socket} from 'node:net';
import {stringifyError} from '../utils.js';
export interface ProdressHeader { export interface ProdressHeader {
port?: number; port?: number;
@ -10,20 +11,9 @@ export interface ProdressHeader {
action: string; action: string;
} }
// @todo move to nyi utils file /**
function stringifyError(error: Error): string { * Initializes a TCP Socket, sends a ProdressRequest and listens to this Socket for one Response
const {message, stack} = error; */
return JSON.stringify(
{
message,
stack,
},
null,
2,
);
}
export class ProdressConnection { export class ProdressConnection {
public static readonly TIMEOUT = 30_000; public static readonly TIMEOUT = 30_000;
@ -35,6 +25,11 @@ export class ProdressConnection {
private requestTupelList: RequestTupel[]; private requestTupelList: RequestTupel[];
private responseTupelList: ResponseTupel[]; private responseTupelList: ResponseTupel[];
/**
* @param header Headerdata to initialize the the TCP Socket and the ProdressRequest
* @param requestTupelList Requestdata as list of tupel
* @param responseTupelList Expected Responsedata as list of tupel
*/
public constructor(header: ProdressHeader, requestTupelList: RequestTupel[], responseTupelList: ResponseTupel[]) { public constructor(header: ProdressHeader, requestTupelList: RequestTupel[], responseTupelList: ResponseTupel[]) {
this.port = header.port ?? 4788; this.port = header.port ?? 4788;
this.host = header.host ?? 'localhost'; this.host = header.host ?? 'localhost';
@ -45,8 +40,12 @@ export class ProdressConnection {
this.responseTupelList = responseTupelList; this.responseTupelList = responseTupelList;
} }
/**
* Handels the request- and response-flow
*/
public async handleRequest(): Promise<string> { public async handleRequest(): Promise<string> {
let response: string = ''; let response: string = '';
// Wraps a Promise around the reponse which resolves when the "finished" event is emitted and destroys the socekt
const responseAsPromise = new Promise<string>((resolve) => { const responseAsPromise = new Promise<string>((resolve) => {
this.socket.once('finished', () => { this.socket.once('finished', () => {
if (!this.socket.destroyed) { if (!this.socket.destroyed) {
@ -59,6 +58,7 @@ export class ProdressConnection {
this.socket.setTimeout(ProdressConnection.TIMEOUT); this.socket.setTimeout(ProdressConnection.TIMEOUT);
this.socket.connect(this.port, this.host); this.socket.connect(this.port, this.host);
// Sends the writes and sends the request when the connecting is established
this.socket.once('connect', () => { this.socket.once('connect', () => {
try { try {
this.request.writeEntries(this.requestTupelList); this.request.writeEntries(this.requestTupelList);
@ -81,11 +81,14 @@ export class ProdressConnection {
this.finished(); this.finished();
}); });
// Starts writing data from the buffer to the listener-instance and emits the "dataRecieved" event
this.socket.on('data', (data: Buffer) => { this.socket.on('data', (data: Buffer) => {
this.listener.verarbeiteDaten(data); this.listener.verarbeiteDaten(data);
this.socket.emit('dataRecieved'); this.socket.emit('dataRecieved');
}); });
// Since we only expect one response the "dataRecieved" event triggers only once
// Reads the data until finished and writes the response afterwards
this.socket.once('dataRecieved', async () => { this.socket.once('dataRecieved', async () => {
try { try {
const incomingResponse = await this.listener.leseRequest(); const incomingResponse = await this.listener.leseRequest();
@ -97,9 +100,13 @@ export class ProdressConnection {
this.finished(); this.finished();
} }
}); });
return responseAsPromise; return responseAsPromise;
} }
/**
* Emits a custom "finished" event
*/
private finished(): void { private finished(): void {
this.socket.emit('finished'); this.socket.emit('finished');
} }
@ -112,8 +119,7 @@ export class ProdressConnection {
private readResponse(responseInstance: R2AnfrageBlaupause): ResponseObject { private readResponse(responseInstance: R2AnfrageBlaupause): ResponseObject {
const response: ResponseObject = {}; const response: ResponseObject = {};
for (const tupel of this.responseTupelList) { for (const [key, type] of this.responseTupelList) {
const [key, type] = tupel;
switch (type) { switch (type) {
case 'string': case 'string':
response[key] = responseInstance.nächsterText(); response[key] = responseInstance.nächsterText();

5
src/electron/preload.cts Normal file
View File

@ -0,0 +1,5 @@
const electron: typeof Electron = require('electron');
electron.contextBridge.exposeInMainWorld('electron', {
ping: () => electron.ipcRenderer.invoke('ping'),
} satisfies Window['electron']);

View File

@ -6,7 +6,7 @@
"moduleResolution": "NodeNext", "moduleResolution": "NodeNext",
"esModuleInterop": true, "esModuleInterop": true,
"outDir": "../../dist-electron", "outDir": "../../dist-electron",
"skipLibCheck": true "skipLibCheck": true,
//"types": ["../../types"] "types": ["../../types"]
} }
} }

36
src/electron/utils.ts Normal file
View File

@ -0,0 +1,36 @@
import path from 'path';
import {app} from 'electron';
/**
* @returns true, when the application is starded in develpoment mode
*/
export function inDevelopment(): boolean {
return process.env.NODE_ENV === 'development';
}
/**
* Converts an error into a stringified object
* @param error
* @returns an indented JSON srting
*/
export function stringifyError(error: Error): string {
const {message, stack} = error;
return JSON.stringify(
{
message,
stack,
},
null,
2,
);
}
/**
* resolves the path of the preload script, based on in which state the application is starded
* @returns the path to the preload script
*/
export function getPreloadPath(): string {
const preloadScript = '/dist-electron/preload.cjs';
return path.join(app.getAppPath(), inDevelopment() ? '.' : '..', preloadScript);
}

View File

@ -5,6 +5,7 @@
<main> <main>
<p>Hallo Welt</p> <p>Hallo Welt</p>
<div> <div>
<a href="https://svelte.dev" target="_blank" rel="noreferrer"> <a href="https://svelte.dev" target="_blank" rel="noreferrer">
<img src={svelteLogo} class="logo svelte" alt="Svelte Logo" /> <img src={svelteLogo} class="logo svelte" alt="Svelte Logo" />

View File

@ -1,6 +1,7 @@
{ {
"extends": "@tsconfig/svelte/tsconfig.json", "extends": "@tsconfig/svelte/tsconfig.json",
"compilerOptions": { "compilerOptions": {
"types": ["./types"],
"target": "ESNext", "target": "ESNext",
"useDefineForClassFields": true, "useDefineForClassFields": true,
"module": "ESNext", "module": "ESNext",

8
types.d.ts vendored Normal file
View File

@ -0,0 +1,8 @@
/**
* Extends the Main "Window" Interface
*/
interface Window {
electron: {
ping: () => Promise<string>;
};
}