diff --git a/electron-builder.json b/electron-builder.json index ad6a717..ee6913d 100644 --- a/electron-builder.json +++ b/electron-builder.json @@ -1,6 +1,7 @@ { "appId": "com.nwaschk.api-tool", "files": ["dist-electron", "dist-svelte"], + "extraResources": ["dist-electron/preload.cjs"], "icon": "./desktopIcon.png", "mac": { "target": "dmg" diff --git a/src/electron/main.ts b/src/electron/main.ts index 599f810..d725c2e 100644 --- a/src/electron/main.ts +++ b/src/electron/main.ts @@ -1,50 +1,38 @@ import {app, BrowserWindow} from 'electron'; import path from 'path'; +import {getPreloadPath, inDevelopment} from './utils.js'; +import {ipcMain} from 'electron/main'; import {ProdressConnection} from './pdr2com/connection.js'; const APPLICATION_PATH = path.join(app.getAppPath(), '/dist-svelte/index.html'); app.on('ready', async () => { - const mainWindow = new BrowserWindow({}); //@todo - if (process.env.NODE_ENV === 'development') { + const mainWindow = new BrowserWindow({ + webPreferences: { + preload: getPreloadPath(), + }, + }); //@todo + if (inDevelopment()) { mainWindow.loadURL('http://localhost:5123/'); } else { mainWindow.loadFile(APPLICATION_PATH); } - const req = new ProdressConnection( - { - port: 4788, - host: 'localhost', - database: 'test', - handler: 'Shopify', - action: 'artikelSync', - }, - [['string', 'HalloWelt']], - [ - ['line1', 'string'], - ['line2', 'string'], - ], - ); - const res = await req.handleRequest(); - console.log(res); - // 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); - // } + ipcMain.handle('ping', () => { + const req = new ProdressConnection( + { + port: 4788, + host: 'localhost', + database: 'test', + handler: 'Shopify', + action: 'artikelSync', + }, + [['string', 'HalloWelt']], + [ + ['line1', 'string'], + ['line2', 'string'], + ], + ); + return req.handleRequest(); + }); }); diff --git a/src/electron/pdr2com/connection.ts b/src/electron/pdr2com/connection.ts index 757a0b4..dfcf83f 100644 --- a/src/electron/pdr2com/connection.ts +++ b/src/electron/pdr2com/connection.ts @@ -1,6 +1,7 @@ import {RequestLeser as ProdressListener, type R2AnfrageBlaupause} from '@prodress/pdr2com'; import {type ResponseTupel, type ResponseObject, ProdressRequest, type RequestTupel} from './wrapper.js'; import {Socket} from 'node:net'; +import {stringifyError} from '../utils.js'; export interface ProdressHeader { port?: number; @@ -10,20 +11,9 @@ export interface ProdressHeader { action: string; } -// @todo move to nyi utils file -function stringifyError(error: Error): string { - const {message, stack} = error; - - return JSON.stringify( - { - message, - stack, - }, - null, - 2, - ); -} - +/** + * Initializes a TCP Socket, sends a ProdressRequest and listens to this Socket for one Response + */ export class ProdressConnection { public static readonly TIMEOUT = 30_000; @@ -35,6 +25,11 @@ export class ProdressConnection { private requestTupelList: RequestTupel[]; 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[]) { this.port = header.port ?? 4788; this.host = header.host ?? 'localhost'; @@ -45,8 +40,12 @@ export class ProdressConnection { this.responseTupelList = responseTupelList; } + /** + * Handels the request- and response-flow + */ public async handleRequest(): Promise { 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((resolve) => { this.socket.once('finished', () => { if (!this.socket.destroyed) { @@ -59,6 +58,7 @@ export class ProdressConnection { this.socket.setTimeout(ProdressConnection.TIMEOUT); this.socket.connect(this.port, this.host); + // Sends the writes and sends the request when the connecting is established this.socket.once('connect', () => { try { this.request.writeEntries(this.requestTupelList); @@ -81,11 +81,14 @@ export class ProdressConnection { this.finished(); }); + // Starts writing data from the buffer to the listener-instance and emits the "dataRecieved" event this.socket.on('data', (data: Buffer) => { this.listener.verarbeiteDaten(data); 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 () => { try { const incomingResponse = await this.listener.leseRequest(); @@ -97,9 +100,13 @@ export class ProdressConnection { this.finished(); } }); + return responseAsPromise; } + /** + * Emits a custom "finished" event + */ private finished(): void { this.socket.emit('finished'); } @@ -112,8 +119,7 @@ export class ProdressConnection { private readResponse(responseInstance: R2AnfrageBlaupause): ResponseObject { const response: ResponseObject = {}; - for (const tupel of this.responseTupelList) { - const [key, type] = tupel; + for (const [key, type] of this.responseTupelList) { switch (type) { case 'string': response[key] = responseInstance.nächsterText(); diff --git a/src/electron/preload.cts b/src/electron/preload.cts new file mode 100644 index 0000000..3b6f766 --- /dev/null +++ b/src/electron/preload.cts @@ -0,0 +1,5 @@ +const electron: typeof Electron = require('electron'); + +electron.contextBridge.exposeInMainWorld('electron', { + ping: () => electron.ipcRenderer.invoke('ping'), +} satisfies Window['electron']); diff --git a/src/electron/tsconfig.json b/src/electron/tsconfig.json index 0c2d639..5c6c7f8 100644 --- a/src/electron/tsconfig.json +++ b/src/electron/tsconfig.json @@ -6,7 +6,7 @@ "moduleResolution": "NodeNext", "esModuleInterop": true, "outDir": "../../dist-electron", - "skipLibCheck": true - //"types": ["../../types"] + "skipLibCheck": true, + "types": ["../../types"] } } diff --git a/src/electron/utils.ts b/src/electron/utils.ts new file mode 100644 index 0000000..3ddacdb --- /dev/null +++ b/src/electron/utils.ts @@ -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); +} diff --git a/src/ui/App.svelte b/src/ui/App.svelte index f2e94b3..b58ed60 100644 --- a/src/ui/App.svelte +++ b/src/ui/App.svelte @@ -5,6 +5,7 @@

Hallo Welt

+
diff --git a/tsconfig.app.json b/tsconfig.app.json index 55a2f9b..ac94fa8 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -1,6 +1,7 @@ { "extends": "@tsconfig/svelte/tsconfig.json", "compilerOptions": { + "types": ["./types"], "target": "ESNext", "useDefineForClassFields": true, "module": "ESNext", diff --git a/types.d.ts b/types.d.ts new file mode 100644 index 0000000..1020cff --- /dev/null +++ b/types.d.ts @@ -0,0 +1,8 @@ +/** + * Extends the Main "Window" Interface + */ +interface Window { + electron: { + ping: () => Promise; + }; +}