diff --git a/DeskThingServer/src/main/handlers/adbHandler.ts b/DeskThingServer/src/main/handlers/adbHandler.ts index e46f53c..50f957d 100644 --- a/DeskThingServer/src/main/handlers/adbHandler.ts +++ b/DeskThingServer/src/main/handlers/adbHandler.ts @@ -1,9 +1,9 @@ import path from 'path' import { execFile } from 'child_process' import getPlatform from '../utils/get-platform' -import dataListener, { MESSAGE_TYPES } from '../utils/events' +import loggingStore from '../stores/loggingStore' import settingsStore from '../stores/settingsStore' -import { ReplyFn } from '@shared/types' +import { ReplyFn, MESSAGE_TYPES } from '@shared/types' const isDevelopment = process.env.NODE_ENV === 'development' const execPath = isDevelopment @@ -28,17 +28,13 @@ const splitArgs = (str: string): string[] => { export const handleAdbCommands = async (command: string, replyFn?: ReplyFn): Promise => { const settings = await settingsStore.getSettings() const useGlobalADB = settings.globalADB === true - dataListener.asyncEmit( - MESSAGE_TYPES.LOGGING, - useGlobalADB ? 'Using Global ADB' : 'Using Local ADB' - ) + loggingStore.log(MESSAGE_TYPES.LOGGING, useGlobalADB ? 'Using Global ADB' : 'Using Local ADB') return new Promise((resolve, reject) => { execFile( useGlobalADB ? 'adb' : adbPath, splitArgs(command), { cwd: execPath }, (error, stdout, stderr) => { - console.log(error, stdout, stderr) if (error) { replyFn && replyFn('logging', { @@ -47,10 +43,7 @@ export const handleAdbCommands = async (command: string, replyFn?: ReplyFn): Pro final: false, error: stderr }) - dataListener.asyncEmit( - MESSAGE_TYPES.ERROR, - `ADB Error: ${stderr}, ${command}, ${adbPath}` - ) + loggingStore.log(MESSAGE_TYPES.ERROR, `ADB Error: ${stderr}, ${command}, ${adbPath}`) reject(`ADB Error: ${stderr}, ${command}, ${adbPath}`) } else { replyFn && diff --git a/DeskThingServer/src/main/handlers/appHandler.ts b/DeskThingServer/src/main/handlers/appHandler.ts index 6ca5feb..704871d 100644 --- a/DeskThingServer/src/main/handlers/appHandler.ts +++ b/DeskThingServer/src/main/handlers/appHandler.ts @@ -1,10 +1,16 @@ import path from 'path' -import { AppIPCData, ReplyFn } from '@shared/types/ipcTypes' -import dataListener, { MESSAGE_TYPES } from '../utils/events' +import { + App, + AppDataInterface, + AppReturnData, + AppIPCData, + ReplyFn, + MESSAGE_TYPES +} from '@shared/types' +import loggingStore from '../stores/loggingStore' import { getData, setData } from './dataHandler' import { dialog, BrowserWindow } from 'electron' import { sendMessageToApp, AppHandler } from '../services/apps' -import { App, AppDataInterface, AppReturnData } from '@shared/types' const appStore = AppHandler.getInstance() export const appHandler: Record< @@ -109,7 +115,7 @@ export const appHandler: Record< return { path: filePath, name: path.basename(filePath) } }, 'dev-add-app': async (data, replyFn) => { - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.ERROR, 'Developer App Not implemented Yet ', data.payload.appPath @@ -118,7 +124,6 @@ export const appHandler: Record< replyFn('logging', { status: true, data: 'Finished', final: true }) }, 'send-to-app': async (data, replyFn) => { - console.log('sending data to app: ', data.payload.app, data) await sendMessageToApp(data.payload.app, data.payload) replyFn('logging', { status: true, data: 'Finished', final: true }) }, @@ -130,7 +135,6 @@ export const appHandler: Record< const getApps = (replyFn: ReplyFn): App[] => { replyFn('logging', { status: true, data: 'Getting data', final: false }) - console.log('Getting app data') const data = appStore.getAllBase() replyFn('logging', { status: true, data: 'Finished', final: true }) replyFn('app-data', { status: true, data: data, final: true }) @@ -138,8 +142,7 @@ const getApps = (replyFn: ReplyFn): App[] => { } const setAppData = async (replyFn: ReplyFn, id, data: AppDataInterface): Promise => { - console.log('Saving app data: ', data) - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, 'SERVER: Saving ' + id + "'s data " + data) + loggingStore.log(MESSAGE_TYPES.LOGGING, 'SERVER: Saving ' + id + "'s data " + data) await setData(id, data) replyFn('logging', { status: true, data: 'Finished', final: true }) } @@ -150,7 +153,7 @@ const getAppData = async (replyFn, payload): Promise => replyFn('logging', { status: true, data: 'Finished', final: true }) return data } catch (error) { - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, 'SERVER: Error saving manifest' + error) + loggingStore.log(MESSAGE_TYPES.ERROR, 'SERVER: Error saving manifest' + error) console.error('Error setting client manifest:', error) replyFn('logging', { status: false, data: 'Unfinished', error: error, final: true }) return null diff --git a/DeskThingServer/src/main/handlers/authHandler.ts b/DeskThingServer/src/main/handlers/authHandler.ts index ba64065..bbd87df 100644 --- a/DeskThingServer/src/main/handlers/authHandler.ts +++ b/DeskThingServer/src/main/handlers/authHandler.ts @@ -3,8 +3,8 @@ import { sendMessageToApp } from '../services/apps' // Assuming you have an app import http from 'http' import url from 'url' import settingsStore from '../stores/settingsStore' -import dataListener, { MESSAGE_TYPES } from '../utils/events' -import { Settings } from '@shared/types' +import loggingStore from '../stores/loggingStore' +import { Settings, MESSAGE_TYPES } from '@shared/types' const successView = '

Success

You can now close this window.

' @@ -14,7 +14,7 @@ let callBackPort: number function handleCallback(req: http.IncomingMessage, res: http.ServerResponse): void { const parsedUrl = url.parse(req.url || '', true) - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.LOGGING, `AUTH: Received callback request for ${parsedUrl.pathname}` ) @@ -29,7 +29,6 @@ function handleCallback(req: http.IncomingMessage, res: http.ServerResponse): vo const appName = urlParts[1] // The app name should be the third part after '/callback/' const config = getAppData() // Assuming getConfig() returns an object with active apps - console.log('AUTH DATA: ', config) if (!config.apps || !config.apps.some((app) => app.name === appName && app.enabled)) { res.writeHead(404, { 'Content-Type': 'text/html' }) res.end(`

App Not Found

App '${appName}' not found or not active.

`) @@ -37,7 +36,6 @@ function handleCallback(req: http.IncomingMessage, res: http.ServerResponse): vo } const code = parsedUrl.query.code as string - console.log('AUTH CODE: ', code) sendMessageToApp(appName, { type: 'callback-data', payload: code }) res.writeHead(200, { 'Content-Type': 'text/html' }) @@ -47,11 +45,10 @@ function handleCallback(req: http.IncomingMessage, res: http.ServerResponse): vo const startServer = async (): Promise => { if (server) { await server.close(() => { - console.log('CALLBACK: Previous server closed.') + loggingStore.log(MESSAGE_TYPES.LOGGING, 'CALLBACK: Shutting down the server...') }) } - console.log('CALLBACK: Starting server...') server = http.createServer((req, res) => { const parsedUrl = new URL(`http://${req.headers.host}${req.url}`) const pathname = parsedUrl.pathname @@ -63,9 +60,8 @@ const startServer = async (): Promise => { } }) - console.log('CALLBACK: Listening...') server.listen(callBackPort, () => { - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.MESSAGE, `CALLBACK: running at http://localhost:${callBackPort}/` ) @@ -82,16 +78,18 @@ const initializeServer = async (): Promise => { } } -dataListener.on(MESSAGE_TYPES.SETTINGS, (newSettings) => { +settingsStore.addListener((newSettings) => { try { if (newSettings.callbackPort != callBackPort) { callBackPort = newSettings.callbackPort startServer() } else { - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, 'CALLBACK: Not starting - port is not changed') + loggingStore.log(MESSAGE_TYPES.LOGGING, 'CALLBACK: Not starting - port is not changed') } } catch (error) { - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, 'CALLBACK: Error updating with settings', error) + if (error instanceof Error) { + loggingStore.log(MESSAGE_TYPES.ERROR, 'CALLBACK: Error updating with settings' + error) + } } }) diff --git a/DeskThingServer/src/main/handlers/clientHandler.ts b/DeskThingServer/src/main/handlers/clientHandler.ts index 88548ee..8d527b4 100644 --- a/DeskThingServer/src/main/handlers/clientHandler.ts +++ b/DeskThingServer/src/main/handlers/clientHandler.ts @@ -1,5 +1,5 @@ -import { ClientIPCData, ReplyFn } from '@shared/types/ipcTypes' -import dataListener, { MESSAGE_TYPES } from '../utils/events' +import { ClientIPCData, ClientManifest, SocketData, ReplyFn, MESSAGE_TYPES } from '@shared/types' +import loggingStore from '../stores/loggingStore' import { handleAdbCommands } from './adbHandler' import { configureDevice, @@ -9,7 +9,6 @@ import { HandleWebappZipFromUrl, SetupProxy } from './deviceHandler' -import { ClientManifest, SocketData } from '@shared/types' import { sendMessageToClient, sendMessageToClients } from '../services/client/clientCom' export const clientHandler: Record< @@ -28,7 +27,11 @@ export const clientHandler: Record< return `Pinging ${data.payload}...` } catch (error) { console.error('Error pinging client:', error) - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, error) + if (error instanceof Error) { + loggingStore.log(MESSAGE_TYPES.ERROR, error.message) + } else { + loggingStore.log(MESSAGE_TYPES.ERROR, String(error)) + } return 'Error pinging' + data.payload } }, @@ -45,7 +48,6 @@ export const clientHandler: Record< return handleUrl(data, replyFn) }, adb: async (data, replyFn) => { - console.log('Running ADB command:', data.payload) replyFn('logging', { status: true, data: 'Working', final: false }) const response = await handleAdbCommands(data.payload, replyFn) @@ -70,7 +72,7 @@ export const clientHandler: Record< }, 'push-staged': async (data, replyFn) => { try { - console.log('Pushing staged webapp...') + loggingStore.log(MESSAGE_TYPES.LOGGING, 'Pushing staged app...') HandlePushWebApp(data.payload, replyFn) } catch (error) { replyFn('logging', { @@ -79,13 +81,16 @@ export const clientHandler: Record< error: 'Failed to push staged app!', final: true }) - console.error('Error extracting zip file:', error) - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, error) + if (error instanceof Error) { + loggingStore.log(MESSAGE_TYPES.ERROR, error.message) + } else { + loggingStore.log(MESSAGE_TYPES.ERROR, String(error)) + } } }, 'push-proxy-script': async (data, replyFn) => { try { - console.log('Pushing proxy script...') + loggingStore.log(MESSAGE_TYPES.LOGGING, 'Pushing proxy script...') SetupProxy(replyFn, data.payload) replyFn('logging', { status: true, @@ -100,7 +105,11 @@ export const clientHandler: Record< final: true }) console.error('Error pushing proxy script:', error) - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, error) + if (error instanceof Error) { + loggingStore.log(MESSAGE_TYPES.ERROR, error.message) + } else { + loggingStore.log(MESSAGE_TYPES.ERROR, String(error)) + } } }, 'run-device-command': async (data, replyFn) => { @@ -112,7 +121,6 @@ export const clientHandler: Record< request: data.payload.request, payload: !payload.includes('{') ? data.payload.payload : JSON.parse(data.payload.payload) } - console.log('Sending data', data) replyFn('logging', { status: true, data: 'Finished', final: true }) return await sendMessageToClients(message) } diff --git a/DeskThingServer/src/main/handlers/configHandler.ts b/DeskThingServer/src/main/handlers/configHandler.ts index c7ea00f..bbc5480 100644 --- a/DeskThingServer/src/main/handlers/configHandler.ts +++ b/DeskThingServer/src/main/handlers/configHandler.ts @@ -1,6 +1,6 @@ import { sendIpcData } from '..' -import { AppData, App, Manifest, ButtonMapping } from '@shared/types' -import dataListener, { MESSAGE_TYPES } from '../utils/events' +import { AppData, App, MESSAGE_TYPES, Manifest, ButtonMapping } from '@shared/types' +import loggingStore from '../stores/loggingStore' import { readFromFile, writeToFile } from '../utils/fileHandler' const defaultData: AppData = { @@ -17,7 +17,6 @@ const readData = (): AppData => { const data = readFromFile(dataFilePath) if (!data) { // File does not exist, create it with default data - console.log('File does not exist, creating it with default data') writeToFile(defaultData, dataFilePath) return defaultData } @@ -34,11 +33,11 @@ const writeData = (data: AppData): void => { try { const result = writeToFile(data, 'apps.json') if (!result) { - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, 'Error writing data') + loggingStore.log(MESSAGE_TYPES.ERROR, 'Error writing data') } sendIpcData('app-data', data) // Send data to the web UI } catch (err) { - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, 'Error writing data' + err) + loggingStore.log(MESSAGE_TYPES.ERROR, 'Error writing data' + err) console.error('Error writing data:', err) } } @@ -104,12 +103,11 @@ const addConfig = (configName: string, config: string | Array, data = re } else { data.config[configName] = config } - console.log('THIS IS THE FIRST TIME THIS IS BEING EMITTED - TRY AND TRACK IT') - dataListener.asyncEmit(MESSAGE_TYPES.CONFIG, { - app: 'server', - type: 'config', - payload: data.config - }) + // loggingStore.log(MESSAGE_TYPES.CONFIG, { + // app: 'server', + // type: 'config', + // payload: data.config + // }) writeData(data) } const getConfig = ( @@ -150,7 +148,7 @@ const getAppByIndex = (index: number): App | undefined => { } const purgeAppConfig = async (appName: string): Promise => { - console.log('SERVER: Deleting App From Config...', appName) + loggingStore.log(MESSAGE_TYPES.LOGGING, `Purging app: ${appName}`) const data = readData() // Filter out the app to be purged @@ -158,11 +156,11 @@ const purgeAppConfig = async (appName: string): Promise => { data.apps = filteredApps writeData(data) - dataListener.asyncEmit(MESSAGE_TYPES.CONFIG, { - app: 'server', - type: 'config', - payload: data.config - }) + // loggingStore.log(MESSAGE_TYPES.CONFIG, { + // app: 'server', + // type: 'config', + // payload: data.config + // }) } export { diff --git a/DeskThingServer/src/main/handlers/dataHandler.ts b/DeskThingServer/src/main/handlers/dataHandler.ts index ff8fec1..b11a294 100644 --- a/DeskThingServer/src/main/handlers/dataHandler.ts +++ b/DeskThingServer/src/main/handlers/dataHandler.ts @@ -62,7 +62,6 @@ const getData = (app: string): AppDataInterface => { } const purgeAppData = async (appName: string): Promise => { - console.log('SERVER: Deleting app data...') const data = readData() delete data[appName] writeData(data) diff --git a/DeskThingServer/src/main/handlers/deviceHandler.ts b/DeskThingServer/src/main/handlers/deviceHandler.ts index 8155f5c..5442ddf 100644 --- a/DeskThingServer/src/main/handlers/deviceHandler.ts +++ b/DeskThingServer/src/main/handlers/deviceHandler.ts @@ -1,10 +1,10 @@ import { sendIpcData } from '..' -import dataListener, { MESSAGE_TYPES } from '../utils/events' +import loggingStore from '../stores/loggingStore' import { join } from 'path' import * as fs from 'fs' import { app, net } from 'electron' import { handleAdbCommands } from './adbHandler' -import { Client, ClientManifest, ReplyData, ReplyFn } from '@shared/types' +import { Client, ClientManifest, MESSAGE_TYPES, ReplyData, ReplyFn } from '@shared/types' import settingsStore from '../stores/settingsStore' import { getLatestRelease } from './githubHandler' @@ -17,14 +17,11 @@ export const HandleDeviceData = async (data: string): Promise => { sendIpcData('version-status', deviceData) break default: - console.log('Unhandled response', deviceData) + loggingStore.log(MESSAGE_TYPES.ERROR, 'HandleDeviceData Unable to find device version') break } } catch (Exception) { - dataListener.asyncEmit( - MESSAGE_TYPES.ERROR, - 'HandleDeviceData encountered the error ' + Exception - ) + loggingStore.log(MESSAGE_TYPES.ERROR, 'HandleDeviceData encountered the error ' + Exception) } } @@ -34,7 +31,6 @@ export const getDeviceManifestVersion = async (deviceId: string): Promise { @@ -254,7 +247,7 @@ export const HandlePushWebApp = async ( error: 'Client not found!', final: false }) - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.ERROR, '[HandlePushWebApp] Client Not Found! Ensure it is downloaded' ) @@ -262,7 +255,6 @@ export const HandlePushWebApp = async ( } let response - console.log('Remounting...') reply && reply('logging', { status: true, data: 'Remounting...', final: false }) response = await handleAdbCommands(`-s ${deviceId} shell mount -o remount,rw /`) reply && reply('logging', { status: true, data: response || 'Writing to tmp...', final: false }) @@ -344,10 +336,7 @@ export const HandlePushWebApp = async ( error: `${Exception}` }) } - dataListener.asyncEmit( - MESSAGE_TYPES.ERROR, - 'HandlePushWebApp encountered the error ' + Exception - ) + loggingStore.log(MESSAGE_TYPES.ERROR, 'HandlePushWebApp encountered the error ' + Exception) } } @@ -391,9 +380,7 @@ export const HandleWebappZipFromUrl = async ( // Optionally remove the temporary zip file fs.unlinkSync(tempZipPath) - - console.log(`Successfully extracted ${zipFileUrl} to ${extractDir}`) - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.LOGGING, `Successfully extracted ${zipFileUrl} to ${extractDir}` ) @@ -402,7 +389,7 @@ export const HandleWebappZipFromUrl = async ( reply && reply('logging', { status: true, data: 'Success!', final: false }) } catch (error) { console.error('Error extracting zip file:', error) - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, `Error extracting zip file: ${error}`) + loggingStore.log(MESSAGE_TYPES.ERROR, `Error extracting zip file: ${error}`) // Notify failure to the frontend reply && @@ -416,7 +403,7 @@ export const HandleWebappZipFromUrl = async ( }) response.on('error', (error) => { console.error('Error downloading zip file:', error) - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, `Error downloading zip file: ${error}`) + loggingStore.log(MESSAGE_TYPES.ERROR, `Error downloading zip file: ${error}`) // Notify failure to the frontend reply && @@ -430,7 +417,7 @@ export const HandleWebappZipFromUrl = async ( } else { const errorMessage = `Failed to download zip file: ${response.statusCode}` console.error(errorMessage) - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, errorMessage) + loggingStore.log(MESSAGE_TYPES.ERROR, errorMessage) // Notify failure to the frontend reply && @@ -445,7 +432,7 @@ export const HandleWebappZipFromUrl = async ( request.on('error', (error) => { console.error('Error sending request:', error) - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, `Error sending request: ${error}`) + loggingStore.log(MESSAGE_TYPES.ERROR, `Error sending request: ${error}`) // Notify failure to the frontend reply && @@ -487,18 +474,12 @@ export const handleClientManifestUpdate = async ( // Write the updated manifest to the file await fs.promises.writeFile(manifestPath, manifestContent, 'utf8') - console.log('Manifest file updated successfully') + loggingStore.log(MESSAGE_TYPES.LOGGING, 'Manifest file updated successfully') reply && reply('logging', { status: true, data: 'Manifest Updated!', final: false }) - dataListener.asyncEmit( - MESSAGE_TYPES.LOGGING, - 'DEVICE HANDLER: Manifest file updated successfully' - ) + loggingStore.log(MESSAGE_TYPES.LOGGING, 'DEVICE HANDLER: Manifest file updated successfully') } catch (error) { console.error('Error updating manifest file:', error) - dataListener.asyncEmit( - MESSAGE_TYPES.ERROR, - 'DEVICE HANDLER: Error updating manifest file: ' + error - ) + loggingStore.log(MESSAGE_TYPES.ERROR, 'DEVICE HANDLER: Error updating manifest file: ' + error) } } @@ -512,14 +493,14 @@ export const checkForClient = async ( const manifestExists = fs.existsSync(manifestPath) if (!manifestExists) { - console.log('Manifest file not found') + loggingStore.log(MESSAGE_TYPES.LOGGING, 'Manifest file not found') reply && reply('logging', { status: false, data: 'Manifest file not found', final: false }) - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, 'DEVICE HANDLER: Manifest file not found') + loggingStore.log(MESSAGE_TYPES.ERROR, 'DEVICE HANDLER: Manifest file not found') } return manifestExists } @@ -527,12 +508,12 @@ export const checkForClient = async ( export const getClientManifest = async ( reply?: (channel: string, data: ReplyData) => void ): Promise => { - console.log('Getting manifest...') + loggingStore.log(MESSAGE_TYPES.LOGGING, 'Getting manifest...') const userDataPath = app.getPath('userData') const manifestPath = join(userDataPath, 'webapp', 'manifest.js') - console.log('manifestPath: ', manifestPath) + loggingStore.log(MESSAGE_TYPES.LOGGING, 'manifestPath: ' + manifestPath) if (!fs.existsSync(manifestPath)) { - console.log('Manifest file not found') + loggingStore.log(MESSAGE_TYPES.LOGGING, 'Manifest file not found') reply && reply('logging', { status: false, @@ -540,7 +521,7 @@ export const getClientManifest = async ( data: 'Manifest file not found', final: false }) - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.ERROR, 'DEVICE HANDLER: Client is not detected or downloaded! Please download the client! (downloads -> client)' ) @@ -567,12 +548,11 @@ export const getClientManifest = async ( data: 'Manifest loaded!', final: false }) - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, 'DEVICE HANDLER: Manifest file read successfully') - console.log(manifest) + loggingStore.log(MESSAGE_TYPES.LOGGING, 'DEVICE HANDLER: Manifest file read successfully') return manifest } catch (error) { console.error('Error reading or parsing manifest file:', error) - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.ERROR, 'DEVICE HANDLER: Error reading or parsing manifest file: ' + error ) @@ -623,7 +603,7 @@ export const SetupProxy = async ( final: false }) - console.log('Remounting...') + loggingStore.log(MESSAGE_TYPES.LOGGING, 'Remounting...') reply('logging', { status: true, data: 'Remounting...', final: false }) response = await handleAdbCommands(`-s ${deviceId} shell mount -o remount,rw /`) @@ -701,7 +681,7 @@ user=root` final: false, error: `${Exception}` }) - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, 'SetupProxy encountered the error ' + Exception) + loggingStore.log(MESSAGE_TYPES.ERROR, 'SetupProxy encountered the error ' + Exception) throw new Error('SetupProxy encountered the error ' + Exception) } } @@ -815,10 +795,7 @@ export const AppendToSupervisor = async ( final: false, error: `${Exception}` }) - dataListener.asyncEmit( - MESSAGE_TYPES.ERROR, - 'AppendToSupervisor encountered the error ' + Exception - ) + loggingStore.log(MESSAGE_TYPES.ERROR, 'AppendToSupervisor encountered the error ' + Exception) } } @@ -910,7 +887,7 @@ files = /etc/supervisor.d/*.conf\n` final: false, error: `${Exception}` }) - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.ERROR, 'EnsureSupervisorInclude encountered the error ' + Exception ) diff --git a/DeskThingServer/src/main/handlers/firewallHandler.ts b/DeskThingServer/src/main/handlers/firewallHandler.ts index 7924a3e..e97159e 100644 --- a/DeskThingServer/src/main/handlers/firewallHandler.ts +++ b/DeskThingServer/src/main/handlers/firewallHandler.ts @@ -1,10 +1,10 @@ import { exec } from 'child_process' import os from 'os' -import dataListener, { MESSAGE_TYPES } from '../utils/events' +import loggingStore from '../stores/loggingStore' import fs from 'fs' import { join } from 'path' import { app } from 'electron' -import { ReplyFn } from '@shared/types' +import { ReplyFn, MESSAGE_TYPES } from '@shared/types' // Function to execute shell commands function runCommand(command: string): Promise { @@ -45,7 +45,7 @@ async function checkFirewallRuleExists(port: number): Promise { const result = await runCommand(checkCommand) return result.trim() === 'true' } else { - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, `FIREWALL: Unsupported OS!`) + loggingStore.log(MESSAGE_TYPES.ERROR, `FIREWALL: Unsupported OS!`) console.error('Unsupported OS') return false } @@ -65,15 +65,18 @@ async function setupFirewall(port: number, reply?: ReplyFn): Promise { reply && reply('logging', { status: true, data: 'Checking if rules exist', final: false }) const ruleExists = await checkFirewallRuleExists(port) if (ruleExists) { - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.LOGGING, `FIREWALL: Firewall rule for port ${port} verified successfully` ) - console.log(`Firewall rule for port ${port} verified successfully`) + loggingStore.log( + MESSAGE_TYPES.LOGGING, + `Firewall rule for port ${port} verified successfully` + ) reply && reply('logging', { status: true, data: 'Verified that the rule exists!', final: false }) } else { - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.ERROR, `FIREWALL: Failed to verify firewall rule for port ${port}!` ) @@ -112,11 +115,11 @@ async function setupFirewall(port: number, reply?: ReplyFn): Promise { try { await runCommand(`powershell -ExecutionPolicy Bypass -File "${tempScriptPath}"`) - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.LOGGING, `FIREWALL: Firewall rules set up successfully on Windows` ) - console.log('Firewall rules set up successfully on Windows') + loggingStore.log(MESSAGE_TYPES.LOGGING, 'Firewall rules set up successfully on Windows') reply && reply('logging', { status: true, data: 'Firewall ran without error', final: false }) @@ -140,11 +143,11 @@ async function setupFirewall(port: number, reply?: ReplyFn): Promise { ` await runCommand(`echo "${script}" | bash`) - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.LOGGING, `FIREWALL: Firewall rules set up successfully on Linux` ) - console.log('Firewall rules set up successfully on Linux') + loggingStore.log(MESSAGE_TYPES.LOGGING, 'Firewall rules set up successfully on Linux') } else if (platform === 'darwin') { reply && reply('logging', { @@ -165,16 +168,16 @@ async function setupFirewall(port: number, reply?: ReplyFn): Promise { ` await runCommand(`echo "${script}" | bash`) - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.LOGGING, `FIREWALL: Firewall rules set up successfully on macOS` ) - console.log('Firewall rules set up successfully on macOS') + loggingStore.log(MESSAGE_TYPES.LOGGING, 'Firewall rules set up successfully on macOS') } else { console.error('Unsupported OS') } } catch (error) { - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.ERROR, `FIREWALL: Error encountered trying to setup firewall for ${port}! Run administrator and try again` ) diff --git a/DeskThingServer/src/main/handlers/githubHandler.ts b/DeskThingServer/src/main/handlers/githubHandler.ts index 150f2f3..36792a0 100644 --- a/DeskThingServer/src/main/handlers/githubHandler.ts +++ b/DeskThingServer/src/main/handlers/githubHandler.ts @@ -11,12 +11,10 @@ export async function getLatestRelease(repoUrl: string): Promise const owner = repoMatch[1] const repo = repoMatch[2] - console.log('Repo:', owner, repo) const apiUrl = `https://api.github.com/repos/${owner}/${repo}/releases/latest` const response = await fetch(apiUrl) if (!response.ok) { - console.log('HTTP error fetching the latest release! Response: ', response) throw new Error(`HTTP error! status: ${response.status}`) } diff --git a/DeskThingServer/src/main/handlers/musicHandler.ts b/DeskThingServer/src/main/handlers/musicHandler.ts index 5358764..2b4c6cd 100644 --- a/DeskThingServer/src/main/handlers/musicHandler.ts +++ b/DeskThingServer/src/main/handlers/musicHandler.ts @@ -1,8 +1,9 @@ -import dataListener, { MESSAGE_TYPES } from '../utils/events' +import loggingStore from '../stores/loggingStore' import settingsStore from '../stores/settingsStore' -import { Settings, SocketData } from '@shared/types' +import { Settings, SocketData, MESSAGE_TYPES } from '@shared/types' import { sendMessageToApp } from '../services/apps' import { getAppByName } from './configHandler' +import appState from '../services/apps/appState' export class MusicHandler { private static instance: MusicHandler @@ -22,10 +23,13 @@ export class MusicHandler { private async initializeRefreshInterval(): Promise { const settings = await settingsStore.getSettings() // Get from your settings store + this.currentApp = settings.playbackLocation || 'none' + this.updateRefreshInterval(settings.refreshInterval) - dataListener.on(MESSAGE_TYPES.SETTINGS, this.handleSettingsUpdate) + settingsStore.addListener(this.handleSettingsUpdate.bind(this)) setTimeout(() => { + loggingStore.log(MESSAGE_TYPES.DEBUG, '[MusicHandler]: Initialized') this.refreshMusicData() }, 5000) // Delay to ensure settings are loaded } @@ -33,12 +37,12 @@ export class MusicHandler { private handleSettingsUpdate = (settings: Settings): void => { this.updateRefreshInterval(settings.refreshInterval) - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.LOGGING, `[MusicHandler]: Received settings update - checking for changes | Playback location: ${this.currentApp} -> ${settings.playbackLocation}` ) if (settings.playbackLocation) { - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.LOGGING, `[MusicHandler]: Setting restarting to use ${settings.playbackLocation}` ) @@ -53,7 +57,13 @@ export class MusicHandler { } if (refreshRate < 0) { - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, `[MusicHandler]: Cancelling Refresh Interval!`) + loggingStore.log(MESSAGE_TYPES.LOGGING, `[MusicHandler]: Cancelling Refresh Interval!`) + return + } else if (refreshRate < 5) { + loggingStore.log( + MESSAGE_TYPES.WARNING, + `[MusicHandler]: Refresh Interval is ${refreshRate}! Performance may be impacted` + ) return } @@ -62,19 +72,66 @@ export class MusicHandler { }, refreshRate) } - private async refreshMusicData(): Promise { - if (!this.currentApp || this.currentApp.length == 0 || this.currentApp == 'none') { + private async findCurrentPlaybackSource(): Promise { + const Apps = appState.getAllBase() + + const audioSource = Apps.find((app) => app.manifest?.isAudioSource) + + if (audioSource) { + loggingStore.log( + MESSAGE_TYPES.WARNING, + `[MusicHandler]: Found ${audioSource.name} as an audio source automatically. Applying.` + ) + return audioSource.name + } else { + loggingStore.log( + MESSAGE_TYPES.LOGGING, + `[MusicHandler]: Unable to automatically set an audio source. No app found!` + ) + return null + } + } + + private async getPlaybackSource(): Promise { + if (this.currentApp == 'disabled') { + loggingStore.log( + MESSAGE_TYPES.LOGGING, + `[MusicHandler]: Music is disabled! Cancelling refresh` + ) + const settings = await settingsStore.getSettings() + if (settings.refreshInterval > 0) { + settingsStore.updateSetting('refreshInterval', -1) + } + return null + } + + if (this.currentApp == 'none') { + const app = await this.findCurrentPlaybackSource() + if (app) { + this.currentApp = app + settingsStore.updateSetting('playbackLocation', app) + return app + } else { + loggingStore.log( + MESSAGE_TYPES.ERROR, + `[MusicHandler]: No Audiosource Found! Go to Downloads -> Apps and download an audio source! (Spotify, MediaWin, GMP, etc)` + ) + return null + } + } + + if (!this.currentApp || this.currentApp.length == 0) { // Attempt to get audiosource from settings const currentApp = (await settingsStore.getSettings()).playbackLocation - if (!currentApp || currentApp.length == 0 || currentApp == 'none') { - dataListener.asyncEmit( + if (!currentApp || currentApp.length == 0) { + loggingStore.log( MESSAGE_TYPES.ERROR, `[MusicHandler]: No playback location set! Go to settings -> Music to set the playback location!` ) - return + return null } else { - dataListener.asyncEmit( - MESSAGE_TYPES.LOGGING, + loggingStore.log( + MESSAGE_TYPES.WARNING, `[MusicHandler]: Playback location was not set! Setting to ${currentApp}` ) this.currentApp = currentApp @@ -84,29 +141,40 @@ export class MusicHandler { const app = await getAppByName(this.currentApp) if (!app || app.running == false) { - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.ERROR, `[MusicHandler]: App ${this.currentApp} is not found or not running!` ) + return null + } + + return this.currentApp + } + + private async refreshMusicData(): Promise { + const currentApp = await this.getPlaybackSource() + + if (!currentApp) { + return } try { - await sendMessageToApp(this.currentApp, { type: 'get', request: 'refresh', payload: '' }) - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, `[MusicHandler]: Refreshing Music Data!`) + await sendMessageToApp(currentApp, { type: 'get', request: 'refresh', payload: '' }) + loggingStore.log(MESSAGE_TYPES.LOGGING, `[MusicHandler]: Refreshing Music Data!`) } catch (error) { - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, `[MusicHandler]: Music refresh failed: ${error}`) + loggingStore.log(MESSAGE_TYPES.ERROR, `[MusicHandler]: Music refresh failed: ${error}`) } } public async setAudioSource(source: string): Promise { if (source.length == 0) { - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.ERROR, `[MusicHandler]: Unable to update playback location. No playback location passed!` ) return } - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.LOGGING, `[MusicHandler]: Setting Playback Location to ${source}` ) @@ -114,40 +182,24 @@ export class MusicHandler { } public async handleClientRequest(request: SocketData): Promise { - if (!this.currentApp) { - const settings = await settingsStore.getSettings() - if (settings.playbackLocation) { - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, `[MusicHandler]: No playback location set!`) - this.currentApp = settings.playbackLocation - } else { - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, `[MusicHandler]: No playback location set!`) - return - } - } + const currentApp = await this.getPlaybackSource() - if (this.currentApp == 'none') { - dataListener.asyncEmit( - MESSAGE_TYPES.ERROR, - `[MusicHandler]: Playback location is 'none' ! Go to settings -> Music to set the playback location!` - ) + if (!currentApp) { return } if (request.app != 'music' && request.app != 'utility') return if (request.app == 'utility') { - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.LOGGING, `[MusicHandler]: Legacy Name called! Support for this will be dropped in future updates. Migrate your app to use 'music' instead!` ) } - dataListener.asyncEmit( - MESSAGE_TYPES.LOGGING, - `[MusicHandler]: ${request.type} ${request.request}` - ) + loggingStore.log(MESSAGE_TYPES.LOGGING, `[MusicHandler]: ${request.type} ${request.request}`) - sendMessageToApp(this.currentApp, { + sendMessageToApp(currentApp, { type: request.type, request: request.request, payload: request.payload diff --git a/DeskThingServer/src/main/handlers/utilityHandler.ts b/DeskThingServer/src/main/handlers/utilityHandler.ts index 53b64fa..2efb75a 100644 --- a/DeskThingServer/src/main/handlers/utilityHandler.ts +++ b/DeskThingServer/src/main/handlers/utilityHandler.ts @@ -1,13 +1,20 @@ -import { ButtonMapping, Client, GithubRelease, Settings } from '@shared/types' -import { ReplyFn, UtilityIPCData } from '@shared/types/ipcTypes' +import { + ReplyFn, + UtilityIPCData, + ButtonMapping, + Client, + MESSAGE_TYPES, + GithubRelease, + Log, + Settings +} from '@shared/types' import ConnectionStore from '../stores/connectionsStore' import settingsStore from '../stores/settingsStore' import { getReleases } from './githubHandler' -import dataListener, { MESSAGE_TYPES } from '../utils/events' +import loggingStore from '../stores/loggingStore' import path from 'path' import { shell, app, dialog } from 'electron' import keyMapStore from '../stores/keyMapStore' -import logger from '../utils/logger' import { setupFirewall } from './firewallHandler' import { disconnectClient } from '../services/client/clientCom' import { restartServer } from '../services/client/websocket' @@ -18,11 +25,19 @@ export const utilityHandler: Record< data: UtilityIPCData, replyFn: ReplyFn ) => Promise< - void | string | Client[] | boolean | string[] | Settings | GithubRelease[] | ButtonMapping + | void + | string + | Client[] + | boolean + | string[] + | Settings + | GithubRelease[] + | ButtonMapping + | Log[] > > = { ping: async () => { - console.log('Pinged! pong') + loggingStore.log(MESSAGE_TYPES.LOGGING, 'Pinged! pong') return 'pong' }, zip: async (data): Promise => { @@ -63,14 +78,18 @@ export const utilityHandler: Record< try { return await getReleases(data.payload) } catch (error) { - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, error) + if (error instanceof Error) { + loggingStore.log(MESSAGE_TYPES.ERROR, error.message) + } else { + loggingStore.log(MESSAGE_TYPES.ERROR, String(error)) + } return [] } }, logs: async (data) => { switch (data.request) { case 'get': - return await logger.getLogs() + return await loggingStore.getLogs() default: return } @@ -132,14 +151,13 @@ const refreshFirewall = async (replyFn: ReplyFn): Promise => { replyFn('logging', { status: true, data: 'Refreshing Firewall', final: false }) const payload = (await settingsStore.getSettings()) as Settings if (payload) { - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, '[firewall] Setting up firewall') + loggingStore.log(MESSAGE_TYPES.LOGGING, '[firewall] Setting up firewall') try { await setupFirewall(payload.devicePort, replyFn) } catch (firewallError) { - console.log(firewallError) if (!(firewallError instanceof Error)) return - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, `FIREWALL: ${firewallError.message}`) + loggingStore.log(MESSAGE_TYPES.ERROR, `FIREWALL: ${firewallError.message}`) replyFn('logging', { status: false, data: 'Error in firewall', @@ -149,7 +167,7 @@ const refreshFirewall = async (replyFn: ReplyFn): Promise => { return } } else { - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, '[firewall] No settings found!') + loggingStore.log(MESSAGE_TYPES.ERROR, '[firewall] No settings found!') replyFn('logging', { status: false, data: 'Error in firewall', @@ -158,7 +176,7 @@ const refreshFirewall = async (replyFn: ReplyFn): Promise => { }) } } catch (error) { - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, 'SERVER: [firewall] Error saving manifest' + error) + loggingStore.log(MESSAGE_TYPES.ERROR, 'SERVER: [firewall] Error saving manifest' + error) console.error('[Firewall] Error setting client manifest:', error) if (error instanceof Error) { replyFn('logging', { status: false, data: 'Unfinished', error: error.message, final: true }) diff --git a/DeskThingServer/src/main/index.ts b/DeskThingServer/src/main/index.ts index d5c6504..08dfa4c 100644 --- a/DeskThingServer/src/main/index.ts +++ b/DeskThingServer/src/main/index.ts @@ -1,4 +1,4 @@ -import { AppIPCData, AuthScopes, Client, UtilityIPCData } from '@shared/types' +import { AppIPCData, AuthScopes, Client, UtilityIPCData, MESSAGE_TYPES } from '@shared/types' import { app, shell, BrowserWindow, ipcMain, Tray, Menu, nativeImage } from 'electron' import { join, resolve } from 'path' import icon from '../../resources/icon.png?asset' @@ -213,17 +213,16 @@ async function initializeDoc(): Promise { } async function setupIpcHandlers(): Promise { - const dataListener = (await import('./utils/events')).default - const { MESSAGE_TYPES } = await import('./utils/events') + const loggingStore = (await import('./stores/loggingStore')).default const { appHandler } = await import('./handlers/appHandler') const { clientHandler } = await import('./handlers/clientHandler') const { utilityHandler } = await import('./handlers/utilityHandler') - const { ResponseLogger } = await import('./utils/events') + const { ResponseLogger } = await import('./stores/loggingStore') const defaultHandler = async (data: AppIPCData): Promise => { console.error(`No handler implemented for type: ${data.type} ${data}`) - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, `No handler implemented for type: ${data.type}`) + loggingStore.log(MESSAGE_TYPES.ERROR, `No handler implemented for type: ${data.type}`) } ipcMain.handle('APPS', async (event, data: AppIPCData) => { @@ -238,7 +237,7 @@ async function setupIpcHandlers(): Promise { } } catch (error) { console.error('Error in IPC handler:', error) - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, `Error in IPC handler: ${error}`) + loggingStore.log(MESSAGE_TYPES.ERROR, `Error in IPC handler: ${error}`) } }) @@ -254,12 +253,11 @@ async function setupIpcHandlers(): Promise { } } catch (error) { console.error('Error in IPC handler:', error) - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, `Error in IPC handler: ${error}`) + loggingStore.log(MESSAGE_TYPES.ERROR, `Error in IPC handler: ${error}`) } }) ipcMain.handle('UTILITY', async (event, data: UtilityIPCData) => { - console.log('Received IPC data:', data) const handler = utilityHandler[data.type] || defaultHandler const replyFn = ResponseLogger(event.sender.send.bind(event.sender)) @@ -272,19 +270,13 @@ async function setupIpcHandlers(): Promise { } } catch (error) { console.error('Error in IPC handler:', error) - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, `Error in IPC handler: ${error}`) + loggingStore.log(MESSAGE_TYPES.ERROR, `Error in IPC handler: ${error}`) } }) - dataListener.on(MESSAGE_TYPES.ERROR, (errorData) => { - sendIpcData('error', errorData) - }) - dataListener.on(MESSAGE_TYPES.LOGGING, (errorData) => { + loggingStore.addListener((errorData) => { sendIpcData('log', errorData) }) - dataListener.on(MESSAGE_TYPES.MESSAGE, (errorData) => { - sendIpcData('message', errorData) - }) ConnectionStore.on((clients: Client[]) => { sendIpcData('connections', { status: true, data: clients.length, final: true }) sendIpcData('clients', { status: true, data: clients, final: true }) @@ -292,7 +284,7 @@ async function setupIpcHandlers(): Promise { ConnectionStore.onDevice((devices: string[]) => { sendIpcData('adbdevices', { status: true, data: devices, final: true }) }) - dataListener.on(MESSAGE_TYPES.SETTINGS, (newSettings) => { + settingsStore.addListener((newSettings) => { sendIpcData('settings-updated', newSettings) }) } @@ -367,8 +359,6 @@ function handleUrl(url: string | undefined): void { if (url && url.startsWith('deskthing://')) { const path = url.replace('deskthing://', '') - console.log('Handling URL:', url, path) - if (mainWindow) { mainWindow.webContents.send('handle-protocol-url', path) } diff --git a/DeskThingServer/src/main/services/apps/appCommunication.ts b/DeskThingServer/src/main/services/apps/appCommunication.ts index 818e287..fea4749 100644 --- a/DeskThingServer/src/main/services/apps/appCommunication.ts +++ b/DeskThingServer/src/main/services/apps/appCommunication.ts @@ -1,6 +1,6 @@ import { openAuthWindow, sendIpcAuthMessage } from '../..' -import { AuthScopes, IncomingData, Key, Action, ToClientType } from '@shared/types' -import dataListener, { MESSAGE_TYPES } from '../../utils/events' +import { AuthScopes, MESSAGE_TYPES, IncomingData, Key, Action, ToClientType } from '@shared/types' +import loggingStore from '../../stores/loggingStore' import { ipcMain } from 'electron' /** @@ -18,7 +18,7 @@ export async function handleDataFromApp(app: string, appData: IncomingData): Pro switch (appData.type) { case 'message': - dataListener.asyncEmit(MESSAGE_TYPES.MESSAGE, appData.payload) + loggingStore.log(MESSAGE_TYPES.MESSAGE, appData.payload, app.toUpperCase()) break case 'get': switch (appData.request) { @@ -67,7 +67,7 @@ export async function handleDataFromApp(app: string, appData: IncomingData): Pro if (appData.payload && appData.request) { sendMessageToApp(appData.request, appData.payload) } else { - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.ERROR, `${app.toUpperCase()}: App data malformed`, appData.payload @@ -75,10 +75,10 @@ export async function handleDataFromApp(app: string, appData: IncomingData): Pro } break case 'error': - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, `${app.toUpperCase()}: ${appData.payload}`) + loggingStore.log(MESSAGE_TYPES.ERROR, `${appData.payload}`, app.toUpperCase()) break case 'log': - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, `${app.toUpperCase()}: ${appData.payload}`) + loggingStore.log(MESSAGE_TYPES.LOGGING, `${appData.payload}`, app.toUpperCase()) break case 'button': if (appData.request == 'add') { @@ -93,13 +93,13 @@ export async function handleDataFromApp(app: string, appData: IncomingData): Pro Modes: appData.payload.Modes || [] } keyMapStore.addKey(Key) - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.LOGGING, `${app.toUpperCase()}: Added Button Successfully` ) } } catch (Error) { - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, `${app.toUpperCase()}: ${Error}`) + loggingStore.log(MESSAGE_TYPES.ERROR, `${app.toUpperCase()}: ${Error}`) } } else if (appData.request == 'remove') { keyMapStore.removeKey(appData.payload.id) @@ -122,13 +122,13 @@ export async function handleDataFromApp(app: string, appData: IncomingData): Pro source: app } keyMapStore.addAction(Action) - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.LOGGING, `${app.toUpperCase()}: Added Action Successfully` ) } } catch (Error) { - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, `${app.toUpperCase()}: ${Error}`) + loggingStore.log(MESSAGE_TYPES.ERROR, `${app.toUpperCase()}: ${Error}`) } break case 'remove': @@ -172,7 +172,7 @@ export async function requestUserInput(appName: string, scope: AuthScopes): Prom export async function sendMessageToApp(appName: string, data: IncomingData): Promise { const { AppHandler } = await import('./appState') const appHandler = AppHandler.getInstance() - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.LOGGING, `[sendMessageToApp] Sending message to ${appName} with ${data.type}` ) @@ -181,7 +181,7 @@ export async function sendMessageToApp(appName: string, data: IncomingData): Pro if (app && typeof app.func.toClient === 'function') { ;(app.func.toClient as ToClientType)(data) } else { - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.ERROR, `SERVER: App ${appName} not found or does not have toClient function. (is it running?)` ) diff --git a/DeskThingServer/src/main/services/apps/appInstaller.ts b/DeskThingServer/src/main/services/apps/appInstaller.ts index e946330..c10fe03 100644 --- a/DeskThingServer/src/main/services/apps/appInstaller.ts +++ b/DeskThingServer/src/main/services/apps/appInstaller.ts @@ -1,13 +1,14 @@ import { join, resolve } from 'path' import { app } from 'electron' -import dataListener, { MESSAGE_TYPES } from '../../utils/events' +import loggingStore from '../../stores/loggingStore' import { IncomingData, ToClientType, Response, Manifest, DeskThing, - AppReturnData + AppReturnData, + MESSAGE_TYPES } from '@shared/types' import { getAppFilePath, getManifest } from './appUtils' import { mkdirSync, existsSync, rmSync, promises } from 'node:fs' @@ -29,11 +30,14 @@ import { handleDataFromApp } from './appCommunication' export async function handleZip(zipFilePath: string, reply?): Promise { const { getManifest } = await import('./appUtils') try { - console.log(`[handleZip] Extracting ${zipFilePath}...`) + loggingStore.log(MESSAGE_TYPES.LOGGING, `[handleZip] Extracting ${zipFilePath}...`) const appPath = join(app.getPath('userData'), 'apps') // Extract to user data folder // Create the extraction directory if it doesn't exist if (!existsSync(appPath)) { - console.log(`[handleZip] Creating extraction directory at ${appPath}...`) + loggingStore.log( + MESSAGE_TYPES.LOGGING, + `[handleZip] Creating extraction directory at ${appPath}...` + ) mkdirSync(appPath, { recursive: true }) } @@ -42,7 +46,10 @@ export async function handleZip(zipFilePath: string, reply?): Promise((resolve, reject) => { try { - console.log(`[handleZip] Extracting app...`) + loggingStore.log(MESSAGE_TYPES.LOGGING, `[handleZip] Extracting app...`) const zip = new AdmZip.default(zipFilePath) zip.getEntries().forEach((entry) => { if (entry.isDirectory) { - console.log(`[handleZip] Skipping directory ${entry.entryName}`) + loggingStore.log( + MESSAGE_TYPES.LOGGING, + `[handleZip] Skipping directory ${entry.entryName}` + ) } else { - console.log(`[handleZip] Extracting file ${entry.entryName}`) + loggingStore.log( + MESSAGE_TYPES.LOGGING, + `[handleZip] Extracting file ${entry.entryName}` + ) zip.extractEntryTo(entry, tempDir, true, true) } }) zip.extractAllTo(tempDir, true) - console.log(`[handleZip] App extracted to ${tempDir}`) + loggingStore.log(MESSAGE_TYPES.LOGGING, `[handleZip] App extracted to ${tempDir}`) resolve() } catch (error) { - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, `SERVER: Error extracting ${zipFilePath}`) + loggingStore.log(MESSAGE_TYPES.ERROR, `SERVER: Error extracting ${zipFilePath}`) reply && reply('logging', { status: false, data: 'Extraction failed!', final: true }) reject(error) } @@ -80,12 +93,9 @@ export async function handleZip(zipFilePath: string, reply?): Promise { if (app && typeof app.func.start === 'function') { app.func.start() } else { - console.log(`App ${appName} not found.`) + loggingStore.log(MESSAGE_TYPES.LOGGING, `App ${appName} not found.`) } const DeskThing = await getDeskThing(appName) @@ -270,17 +280,17 @@ export async function run(appName: string): Promise { appState.appendManifest(manifest, appName) } - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, `Configuring ${appName}!`) + loggingStore.log(MESSAGE_TYPES.LOGGING, `Configuring ${appName}!`) await setupFunctions(appName, DeskThing) - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, `Running ${appName}!`) + loggingStore.log(MESSAGE_TYPES.LOGGING, `Running ${appName}!`) const result = await start(appName) if (!result) { - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, `App ${appName} failed to start!`) + loggingStore.log(MESSAGE_TYPES.ERROR, `App ${appName} failed to start!`) } } catch (error) { - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, `Error running app ${error}`) + loggingStore.log(MESSAGE_TYPES.ERROR, `Error running app ${error}`) console.error('Error running app:', error) } } @@ -296,17 +306,14 @@ export const start = async (appName: string): Promise => { const appInstance = appState.get(appName) if (!appInstance || !appInstance.func.start || appInstance.func.start === undefined) { - dataListener.asyncEmit( - MESSAGE_TYPES.ERROR, - `App ${appName} not found. or not started correctly` - ) + loggingStore.log(MESSAGE_TYPES.ERROR, `App ${appName} not found. or not started correctly`) return false } // Check if all required apps are running const manifest = appInstance.manifest if (!manifest) { - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, `App ${appName} not found.`) + loggingStore.log(MESSAGE_TYPES.ERROR, `App ${appName} not found.`) return false } @@ -314,7 +321,7 @@ export const start = async (appName: string): Promise => { const requiredApps = manifest.requires || [] for (const requiredApp of requiredApps) { if (!appState.getOrder().includes(requiredApp) && requiredApp.length > 2) { - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.ERROR, `Unable to run ${appName}! This app requires '${requiredApp}' to be enabled and running.` ) @@ -327,20 +334,20 @@ export const start = async (appName: string): Promise => { try { const startResponse: Response = await appInstance.func.start() if (startResponse.status == 200) { - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.MESSAGE, `App ${appName} started successfully with response ${startResponse.data.message}` ) return true } else { - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.ERROR, `App ${appName} failed to start with response ${startResponse.data.message}` ) return false } } catch (error) { - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, `Error starting app ${error}`) + loggingStore.log(MESSAGE_TYPES.ERROR, `Error starting app ${error}`) console.error('Error starting app:', error) } return false @@ -360,7 +367,7 @@ const setupFunctions = async (appName: string, DeskThing: DeskThing): Promise => { return DeskThing.start({ toServer: (data) => handleDataFromApp(appName, data), - SysEvents: (event: string, listener: (...args: string[]) => void) => { - dataListener.on(event, listener) // Add event listener - return () => dataListener.removeListener(event, listener) + // eslint-disable-next-line @typescript-eslint/no-unused-vars + SysEvents: (_event: string, _listener: (...args: string[]) => void) => { + return () => { + /* do something with this to let apps listen for server events like apps being added or settings being changed */ + } } }) } @@ -386,7 +395,7 @@ const setupFunctions = async (appName: string, DeskThing: DeskThing): Promise => { } else if (existsSync(appEntryPointCjs)) { appEntryPoint = appEntryPointCjs } else { - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.ERROR, `Entry point for app ${appName} not found. (Does it have an index.js file?)` ) diff --git a/DeskThingServer/src/main/services/apps/appManager.ts b/DeskThingServer/src/main/services/apps/appManager.ts index 5df36a6..8e1a3ca 100644 --- a/DeskThingServer/src/main/services/apps/appManager.ts +++ b/DeskThingServer/src/main/services/apps/appManager.ts @@ -1,6 +1,6 @@ import { rmSync, readdirSync, statSync, existsSync } from 'node:fs' -import dataListener, { MESSAGE_TYPES } from '../../utils/events' - +import loggingStore from '../../stores/loggingStore' +import { MESSAGE_TYPES } from '@shared/types' export async function clearCache(appName: string): Promise { try { const { join } = await import('path') @@ -21,24 +21,41 @@ export async function clearCache(appName: string): Promise { const resolvedPath = require.resolve(itemPath) if (require.cache[resolvedPath]) { delete require.cache[resolvedPath] - dataListener.asyncEmit( - MESSAGE_TYPES.LOGGING, - `SERVER: Removed ${resolvedPath} from cache` + loggingStore.log(MESSAGE_TYPES.LOGGING, `SERVER: Removed ${resolvedPath} from cache`) + } else { + loggingStore.log(MESSAGE_TYPES.LOGGING, `SERVER: ${resolvedPath} not in cache!`) + } + } catch (error) { + if (error instanceof Error) { + loggingStore.log( + MESSAGE_TYPES.ERROR, + `SERVER: Error clearing cache for ${itemPath}:`, + error.message ) } else { - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, `SERVER: ${resolvedPath} not in cache!`) + loggingStore.log( + MESSAGE_TYPES.ERROR, + `SERVER: Error clearing cache for ${itemPath}:`, + String(error) + ) } - } catch (e) { - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, `SERVER: clearCache Error`, e) } } }) } catch (error) { - dataListener.asyncEmit( - MESSAGE_TYPES.LOGGING, - `SERVER: Error clearing cache for directory ${appName}:`, - error - ) + if (error instanceof Error) { + loggingStore.log( + MESSAGE_TYPES.ERROR, + `SERVER: Error clearing cache for directory ${appName}:`, + error.message + ) + } else { + loggingStore.log( + MESSAGE_TYPES.ERROR, + `SERVER: Error clearing cache for directory ${appName}:`, + String(error) + ) + } } } @@ -49,7 +66,7 @@ export async function clearCache(appName: string): Promise { */ export async function purgeApp(appName: string): Promise { try { - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, `SERVER: Purging App ${appName}`) + loggingStore.log(MESSAGE_TYPES.LOGGING, `SERVER: Purging App ${appName}`) const { purgeAppData } = await import('../../handlers/dataHandler') const { purgeAppConfig } = await import('../../handlers/configHandler') @@ -74,9 +91,9 @@ export async function purgeApp(appName: string): Promise { // Remove the file from filesystem if (existsSync(dir)) { await rmSync(dir, { recursive: true, force: true }) - console.log(`Purged all data for app ${appName}`) + loggingStore.log(MESSAGE_TYPES.LOGGING, `Purged all data for app ${appName}`) } - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, `SERVER: Purged App ${appName}`) + loggingStore.log(MESSAGE_TYPES.LOGGING, `SERVER: Purged App ${appName}`) } catch (error) { console.error(`Error purging app data for ${appName}`, error) } diff --git a/DeskThingServer/src/main/services/apps/appRunner.ts b/DeskThingServer/src/main/services/apps/appRunner.ts index 9d279db..627ce12 100644 --- a/DeskThingServer/src/main/services/apps/appRunner.ts +++ b/DeskThingServer/src/main/services/apps/appRunner.ts @@ -1,4 +1,5 @@ -import dataListener, { MESSAGE_TYPES } from '../../utils/events' +import loggingStore from '../../stores/loggingStore' +import { MESSAGE_TYPES } from '@shared/types' /** * Loads and runs all enabled apps from appData.json @@ -12,12 +13,12 @@ export async function loadAndRunEnabledApps(): Promise { try { const appInstances = appHandler.getAll() - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, 'SERVER: Loaded apps config. Running apps...') + loggingStore.log(MESSAGE_TYPES.LOGGING, 'SERVER: Loaded apps config. Running apps...') const enabledApps = appInstances.filter((appConfig) => appConfig.enabled === true) await Promise.all( enabledApps.map(async (appConfig) => { - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.LOGGING, `SERVER: Automatically running app ${appConfig.name}` ) @@ -29,15 +30,12 @@ export async function loadAndRunEnabledApps(): Promise { await Promise.all( failedApps.map(async (failedApp) => { - dataListener.asyncEmit( - MESSAGE_TYPES.LOGGING, - `SERVER: Attempting to run ${failedApp.name} again` - ) + loggingStore.log(MESSAGE_TYPES.LOGGING, `SERVER: Attempting to run ${failedApp.name} again`) await appHandler.run(failedApp.name) }) ) } catch (error) { - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.ERROR, `SERVER: Error loading and running enabled apps: ${error}` ) diff --git a/DeskThingServer/src/main/services/apps/appState.ts b/DeskThingServer/src/main/services/apps/appState.ts index 8707363..2eeae63 100644 --- a/DeskThingServer/src/main/services/apps/appState.ts +++ b/DeskThingServer/src/main/services/apps/appState.ts @@ -1,7 +1,6 @@ -import { App, AppInstance, Manifest, AppReturnData } from '@shared/types' +import { App, AppInstance, Manifest, AppReturnData, MESSAGE_TYPES } from '@shared/types' import { sendConfigData, sendSettingsData } from '../client/clientCom' -import settingsStore from '../../stores/settingsStore' -import dataListener, { MESSAGE_TYPES } from '../../utils/events' +import loggingStore from '../../stores/loggingStore' /** * TODO: Sync with the file @@ -34,7 +33,7 @@ export class AppHandler { * Loads the apps from file */ async loadApps(): Promise { - console.log('[appState] [loadApps]: Loading apps...') + loggingStore.log(MESSAGE_TYPES.LOGGING, '[appState] [loadApps]: Loading apps...') const { getAppData } = await import('../../handlers/configHandler') const data = await getAppData() @@ -302,20 +301,6 @@ export class AppHandler { // Add the manifest to the config file addAppManifest(manifest, appName) this.saveAppToFile(appName) - - const checkForSettings = async (): Promise => { - const settings = await settingsStore.getSettings() - const playbackLocation = settings.playbackLocation - if (playbackLocation === 'none' || !playbackLocation) { - settingsStore.updateSetting('playbackLocation', appName) - dataListener.asyncEmit( - MESSAGE_TYPES.LOGGING, - '[appState][addAppManifest]: Setting playbackLocation to ' + appName - ) - } - } - - checkForSettings() } } diff --git a/DeskThingServer/src/main/services/apps/appUtils.ts b/DeskThingServer/src/main/services/apps/appUtils.ts index 3999ffa..272249d 100644 --- a/DeskThingServer/src/main/services/apps/appUtils.ts +++ b/DeskThingServer/src/main/services/apps/appUtils.ts @@ -1,7 +1,7 @@ -import { Manifest } from '@shared/types' +import { Manifest, MESSAGE_TYPES } from '@shared/types' import { join } from 'path' import { existsSync, promises } from 'node:fs' -import dataListener, { MESSAGE_TYPES } from '../../utils/events' +import loggingStore from '../../stores/loggingStore' import { app } from 'electron' let devAppPath: string @@ -16,7 +16,7 @@ let devAppPath: string */ export async function getManifest(fileLocation: string): Promise { try { - console.log('[getManifest] Getting manifest for app') + loggingStore.log(MESSAGE_TYPES.LOGGING, '[getManifest] Getting manifest for app') const manifestPath = join(fileLocation, 'manifest.json') if (!existsSync(manifestPath)) { throw new Error('manifest.json not found after extraction') @@ -39,7 +39,7 @@ export async function getManifest(fileLocation: string): Promise => { - console.log(`Sending message to clients: ${JSON.stringify(data)}`) + loggingStore.log(MESSAGE_TYPES.LOGGING, `Sending message to clients: ${JSON.stringify(data)}`) if (server) { server.clients.forEach((client) => { if (client.readyState === 1) { @@ -15,7 +15,7 @@ export const sendMessageToClients = async (data: SocketData): Promise => { } }) } else { - dataListener.emit(MESSAGE_TYPES.LOGGING, 'WSOCKET: No server running - setting one up') + loggingStore.log(MESSAGE_TYPES.LOGGING, 'WSOCKET: No server running - setting one up') } } @@ -25,10 +25,13 @@ export const disconnectClient = (connectionId: string): void => { if (client && server) { client.socket.terminate() Clients.splice(Clients.indexOf(client), 1) - console.log(`Forcibly disconnected client: ${connectionId}`) + loggingStore.log(MESSAGE_TYPES.LOGGING, `Forcibly disconnected client: ${connectionId}`) connectionsStore.removeClient(connectionId) } else { - console.log(`Client not found or server not running: ${connectionId}`) + loggingStore.log( + MESSAGE_TYPES.LOGGING, + `Client not found or server not running: ${connectionId}` + ) } } @@ -58,7 +61,7 @@ export const sendConfigData = async (clientId?: string): Promise => { sendMessageToClient(clientId, { app: 'client', type: 'config', payload: filteredAppData }) - console.log('WSOCKET: Preferences sent!') + loggingStore.log(MESSAGE_TYPES.LOGGING, 'WSOCKET: Preferences sent!') } catch (error) { console.error('WSOCKET: Error getting config data:', error) sendError(clientId, 'WSOCKET: Error getting config data') @@ -89,7 +92,7 @@ export const sendSettingsData = async (clientId?: string): Promise => { } sendMessageToClient(clientId, { app: 'client', type: 'settings', payload: settings }) - console.log('WSOCKET: Preferences sent!') + loggingStore.log(MESSAGE_TYPES.LOGGING, 'WSOCKET: Preferences sent!') } catch (error) { console.error('WSOCKET: Error getting config data:', error) sendError(clientId, 'WSOCKET: Error getting config data') @@ -102,8 +105,8 @@ export const sendMappings = async (clientId?: string): Promise => { sendMessageToClient(clientId, { app: 'client', type: 'button_mappings', payload: mappings }) - console.log('WSOCKET: Button mappings sent!') - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, `WEBSOCKET: Client has been sent button maps!`) + loggingStore.log(MESSAGE_TYPES.LOGGING, 'WSOCKET: Button mappings sent!') + loggingStore.log(MESSAGE_TYPES.LOGGING, `WEBSOCKET: Client has been sent button maps!`) } catch (error) { console.error('WSOCKET: Error getting button mappings:', error) sendError(clientId, 'WSOCKET: Error getting button mappings') diff --git a/DeskThingServer/src/main/services/client/clientUtils.ts b/DeskThingServer/src/main/services/client/clientUtils.ts index 67a4cf1..f84c693 100644 --- a/DeskThingServer/src/main/services/client/clientUtils.ts +++ b/DeskThingServer/src/main/services/client/clientUtils.ts @@ -30,7 +30,6 @@ export const sendTime = async (): Promise => { const formattedMinutes = minutes < 10 ? '0' + minutes : minutes const time = `${formattedHours}:${formattedMinutes} ${ampm}` sendMessageToClients({ app: 'client', type: 'time', payload: time }) - console.log(time) } const initializeTimer = async (): Promise => { diff --git a/DeskThingServer/src/main/services/client/expressServer.ts b/DeskThingServer/src/main/services/client/expressServer.ts index 9cd4cf4..4b4101b 100644 --- a/DeskThingServer/src/main/services/client/expressServer.ts +++ b/DeskThingServer/src/main/services/client/expressServer.ts @@ -1,4 +1,5 @@ -import dataListener, { MESSAGE_TYPES } from '../../utils/events' +import loggingStore from '../../stores/loggingStore' +import { MESSAGE_TYPES } from '@shared/types' import { app as electronApp } from 'electron' import { join } from 'path' import { getAppFilePath } from '../apps' @@ -24,11 +25,14 @@ export const setupExpressServer = async (expressApp: express.Application): Promi ): void => { const userDataPath = electronApp.getPath('userData') const webAppDir = join(userDataPath, 'webapp') - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, `WEBSOCKET: Serving ${appName} from ${webAppDir}`) + loggingStore.log(MESSAGE_TYPES.LOGGING, `WEBSOCKET: Serving ${appName} from ${webAppDir}`) const clientIp = req.hostname - console.log(`WEBSOCKET: Serving ${appName} from ${webAppDir} to ${clientIp}`) + loggingStore.log( + MESSAGE_TYPES.LOGGING, + `WEBSOCKET: Serving ${appName} from ${webAppDir} to ${clientIp}` + ) try { if (req.path.endsWith('manifest.js')) { const manifestPath = join(webAppDir, 'manifest.js') @@ -71,7 +75,7 @@ export const setupExpressServer = async (expressApp: express.Application): Promi handleClientConnection(appName, req, res, next) } else { const appPath = getAppFilePath(appName) - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, `WEBSOCKET: Serving ${appName} from ${appPath}`) + loggingStore.log(MESSAGE_TYPES.LOGGING, `WEBSOCKET: Serving ${appName} from ${appPath}`) if (fs.existsSync(appPath)) { express.static(appPath)(req, res, next) @@ -87,7 +91,7 @@ export const setupExpressServer = async (expressApp: express.Application): Promi const appName = req.params.appName if (iconName != null) { const appPath = getAppFilePath(appName) - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, `WEBSOCKET: Serving ${appName} from ${appPath}`) + loggingStore.log(MESSAGE_TYPES.LOGGING, `WEBSOCKET: Serving ${appName} from ${appPath}`) if (fs.existsSync(join(appPath, 'icons', iconName))) { express.static(join(appPath, 'icons'))(req, res, next) @@ -103,7 +107,7 @@ export const setupExpressServer = async (expressApp: express.Application): Promi const appName = req.params.appName if (imageName != null) { const appPath = getAppFilePath(appName) - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, `WEBSOCKET: Serving ${appName} from ${appPath}`) + loggingStore.log(MESSAGE_TYPES.LOGGING, `WEBSOCKET: Serving ${appName} from ${appPath}`) if (fs.existsSync(join(appPath, 'images', imageName))) { express.static(join(appPath, 'icons'))(req, res, next) @@ -117,10 +121,7 @@ export const setupExpressServer = async (expressApp: express.Application): Promi expressApp.use('/fetch/:url(*)', async (req, res) => { try { const url = decodeURIComponent(req.params.url) - dataListener.asyncEmit( - MESSAGE_TYPES.LOGGING, - `WEBSOCKET: Fetching external resource from ${url}` - ) + loggingStore.log(MESSAGE_TYPES.LOGGING, `WEBSOCKET: Fetching external resource from ${url}`) const response = await fetch(url) const contentType = response.headers.get('content-type') @@ -133,7 +134,7 @@ export const setupExpressServer = async (expressApp: express.Application): Promi } } catch (error) { if (error instanceof Error) { - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.LOGGING, `WEBSOCKET: Error fetching external resource: ${error.message}` ) diff --git a/DeskThingServer/src/main/services/client/websocket.ts b/DeskThingServer/src/main/services/client/websocket.ts index d5b1cca..592ad86 100644 --- a/DeskThingServer/src/main/services/client/websocket.ts +++ b/DeskThingServer/src/main/services/client/websocket.ts @@ -1,7 +1,14 @@ import WebSocket, { WebSocketServer } from 'ws' import { createServer, Server as HttpServer, IncomingMessage } from 'http' -import dataListener, { MESSAGE_TYPES } from '../../utils/events' -import { AppDataInterface, Client, ClientManifest, Settings, SocketData } from '@shared/types' +import loggingStore from '../../stores/loggingStore' +import { + AppDataInterface, + MESSAGE_TYPES, + Client, + ClientManifest, + Settings, + SocketData +} from '@shared/types' import { addData } from '../../handlers/dataHandler' import { HandleDeviceData } from '../../handlers/deviceHandler' import settingsStore from '../../stores/settingsStore' @@ -36,8 +43,8 @@ const THROTTLE_DELAY = 100 // milliseconds export const restartServer = async (): Promise => { try { if (server) { - console.log('WSOCKET: Shutting down the WebSocket server...') - dataListener.emit(MESSAGE_TYPES.LOGGING, 'WSOCKET: Shutting down the WebSocket server...') + loggingStore.log(MESSAGE_TYPES.LOGGING, 'WSOCKET: Shutting down the WebSocket server...') + loggingStore.log(MESSAGE_TYPES.LOGGING, 'WSOCKET: Shutting down the WebSocket server...') ConnectionStore.removeAllClients() server.clients.forEach((client) => { @@ -47,25 +54,28 @@ export const restartServer = async (): Promise => { server.close((err) => { if (err) { console.error('WSOCKET: Error shutting down WebSocket server:', err) - dataListener.emit( + loggingStore.log( MESSAGE_TYPES.ERROR, 'WSOCKET: Error shutting down WebSocket server:' + err ) } else { - console.log('WSOCKET: WebSocket server shut down successfully.') - dataListener.emit( + loggingStore.log( + MESSAGE_TYPES.LOGGING, + 'WSOCKET: WebSocket server shut down successfully.' + ) + loggingStore.log( MESSAGE_TYPES.LOGGING, 'WSOCKET: WebSocket server shut down successfully.' ) } if (httpServer && httpServer.listening) { - console.log('WSOCKET: Stopping HTTP server...') + loggingStore.log(MESSAGE_TYPES.LOGGING, 'WSOCKET: Stopping HTTP server...') httpServer.close((err) => { if (err) { console.error('WSOCKET: Error stopping HTTP server:', err) } else { - console.log('WSOCKET: HTTP server stopped successfully.') + loggingStore.log(MESSAGE_TYPES.LOGGING, 'WSOCKET: HTTP server stopped successfully.') setupServer() } }) @@ -74,14 +84,17 @@ export const restartServer = async (): Promise => { } }) } else { - console.log('WSOCKET: No WebSocket server running - setting one up') + loggingStore.log( + MESSAGE_TYPES.LOGGING, + 'WSOCKET: No WebSocket server running - setting one up' + ) if (httpServer && httpServer.listening) { - console.log('WSOCKET: Stopping HTTP server...') + loggingStore.log(MESSAGE_TYPES.LOGGING, 'WSOCKET: Stopping HTTP server...') httpServer.close((err) => { if (err) { console.error('WSOCKET: Error stopping HTTP server:', err) } else { - console.log('WSOCKET: HTTP server stopped successfully.') + loggingStore.log(MESSAGE_TYPES.LOGGING, 'WSOCKET: HTTP server stopped successfully.') setupServer() } }) @@ -95,7 +108,7 @@ export const restartServer = async (): Promise => { } export const setupServer = async (): Promise => { - dataListener.asyncEmit(MESSAGE_TYPES.MESSAGE, 'WSOCKET: Attempting to setup the server') + loggingStore.log(MESSAGE_TYPES.MESSAGE, 'WSOCKET: Attempting to setup the server') if (!currentPort || !currentAddress) { const settings = await settingsStore.getSettings() @@ -112,11 +125,10 @@ export const setupServer = async (): Promise => { server = new WebSocketServer({ server: httpServer }) - console.log('WSOCKET: WebSocket server is running.') + loggingStore.log(MESSAGE_TYPES.LOGGING, 'WSOCKET: WebSocket server is running.') httpServer.listen(currentPort, currentAddress, () => { - console.log(`CALLBACK: Server listening on ${currentAddress}:${currentPort}`) - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.LOGGING, `WEBSOCKET: Server is listening on ${currentAddress}:${currentPort}` ) @@ -130,7 +142,10 @@ export const setupServer = async (): Promise => { const clientIp = socket._socket.remoteAddress // Setup the initial client data - console.log(`WSOCKET: Client connected! Looking for client with IP ${clientIp}`) + loggingStore.log( + MESSAGE_TYPES.LOGGING, + `WSOCKET: Client connected! Looking for client with IP ${clientIp}` + ) // Local client that is the true source of truth for the device details const client: Client = { @@ -145,13 +160,17 @@ export const setupServer = async (): Promise => { Clients.push({ client, socket }) - console.log( + loggingStore.log( + MESSAGE_TYPES.LOGGING, `WSOCKET: Client with id: ${client.connectionId} connected!\nWSOCKET: Sending preferences...` ) ConnectionStore.addClient(client) - console.log('WSOCKET: Client connected!\nWSOCKET: Sending preferences...') - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, `WEBSOCKET: Sending client preferences...`) + loggingStore.log( + MESSAGE_TYPES.LOGGING, + 'WSOCKET: Client connected!\nWSOCKET: Sending preferences...' + ) + loggingStore.log(MESSAGE_TYPES.LOGGING, `WEBSOCKET: Sending client preferences...`) sendConfigData(client.connectionId) sendSettingsData(client.connectionId) @@ -160,8 +179,7 @@ export const setupServer = async (): Promise => { socket.on('message', async (message) => { const messageData = JSON.parse(message) as SocketData - console.log(`WSOCKET: ${client.connectionId} sent message `, messageData) - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.LOGGING, `WEBSOCKET: Client ${client.connectionId} has sent ${message}` ) @@ -207,7 +225,7 @@ export const setupServer = async (): Promise => { }) socket.on('close', () => { - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.LOGGING, `WSOCKET: Client ${client.connectionId} has disconnected!` ) @@ -223,7 +241,10 @@ export const setupServer = async (): Promise => { const handleServerMessage = (socket, client: Client, messageData: SocketData): void => { try { if (messageData.app === 'server') { - console.log(`WSOCKET: Server message received! ${messageData.type}`) + loggingStore.log( + MESSAGE_TYPES.LOGGING, + `WSOCKET: Server message received! ${messageData.type}` + ) try { switch (messageData.type) { case 'preferences': @@ -241,7 +262,7 @@ const handleServerMessage = (socket, client: Client, messageData: SocketData): v ) break case 'pong': - console.log('Received pong from ', client.connectionId) + loggingStore.log(MESSAGE_TYPES.LOGGING, 'Received pong from ', client.connectionId) sendIpcData(`pong-${client.connectionId}`, messageData.payload) break case 'set': @@ -266,7 +287,7 @@ const handleServerMessage = (socket, client: Client, messageData: SocketData): v sendTime() break case 'message': - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.MESSAGE, `${client.connectionId}: ${messageData.payload}` ) @@ -279,7 +300,10 @@ const handleServerMessage = (socket, client: Client, messageData: SocketData): v const manifest = messageData.payload as ClientManifest if (!manifest) return - console.log('WSOCKET: Received manifest from client', manifest) + loggingStore.log( + MESSAGE_TYPES.LOGGING, + 'WSOCKET: Received manifest from client' + JSON.stringify(manifest) + ) // Update the client to the info received from the client @@ -313,10 +337,10 @@ const handleServerMessage = (socket, client: Client, messageData: SocketData): v } } -dataListener.on(MESSAGE_TYPES.SETTINGS, (newSettings: Settings) => { +settingsStore.addListener((newSettings: Settings) => { if (currentPort !== newSettings.devicePort || currentAddress !== newSettings.address) { restartServer() } else { - dataListener.emit(MESSAGE_TYPES.LOGGING, 'WSOCKET: No settings changed!') + loggingStore.log(MESSAGE_TYPES.LOGGING, 'WSOCKET: No settings changed!') } }) diff --git a/DeskThingServer/src/main/static/defaultMapping.ts b/DeskThingServer/src/main/static/defaultMapping.ts index 9add34c..46e2244 100644 --- a/DeskThingServer/src/main/static/defaultMapping.ts +++ b/DeskThingServer/src/main/static/defaultMapping.ts @@ -103,12 +103,7 @@ const keys: Key[] = [ description: 'Physical Button Scroll', version: '0.9.0', enabled: true, - Modes: [ - EventMode.ScrollUp, - EventMode.ScrollDown, - EventMode.ScrollLeft, - EventMode.ScrollRight - ] + Modes: [EventMode.ScrollUp, EventMode.ScrollDown, EventMode.ScrollLeft, EventMode.ScrollRight] }, { id: 'Enter', @@ -132,12 +127,7 @@ const keys: Key[] = [ description: 'Touchpad Swipe Button', version: '0.9.0', enabled: true, - Modes: [ - EventMode.ScrollUp, - EventMode.ScrollDown, - EventMode.ScrollLeft, - EventMode.ScrollRight - ] + Modes: [EventMode.ScrollUp, EventMode.ScrollDown, EventMode.ScrollLeft, EventMode.ScrollRight] }, { id: 'Pad1', diff --git a/DeskThingServer/src/main/stores/connectionsStore.ts b/DeskThingServer/src/main/stores/connectionsStore.ts index d69aab8..3fcfc19 100644 --- a/DeskThingServer/src/main/stores/connectionsStore.ts +++ b/DeskThingServer/src/main/stores/connectionsStore.ts @@ -1,6 +1,6 @@ -import { Client } from '@shared/types' +import { Client, MESSAGE_TYPES } from '@shared/types' import { handleAdbCommands } from '../handlers/adbHandler' -import dataListener, { MESSAGE_TYPES } from '../utils/events' +import loggingStore from '../stores/loggingStore' import settingsStore from './settingsStore' type ClientListener = (client: Client[]) => void @@ -20,7 +20,7 @@ class ConnectionStore { this.autoDetectADB = settings.autoDetectADB }) - dataListener.on(MESSAGE_TYPES.SETTINGS, (newSettings) => { + settingsStore.addListener((newSettings) => { try { if (newSettings.autoDetectADB !== undefined) { this.autoDetectADB = newSettings.autoDetectADB @@ -31,14 +31,18 @@ class ConnectionStore { if (newSettings.autoDetectADB) { this.checkAutoDetectADB() - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, '[ADB]: Auto-Detect is Enabled') + loggingStore.log(MESSAGE_TYPES.LOGGING, '[ADB]: Auto-Detect is Enabled') } else { - console.log('Auto-detect ADB disabled') - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, '[ADB]: Auto-Detect is Disabled') + loggingStore.log(MESSAGE_TYPES.LOGGING, 'Auto-detect ADB disabled') + loggingStore.log(MESSAGE_TYPES.LOGGING, '[ADB]: Auto-Detect is Disabled') } } } catch (error) { - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, 'ADB: Error updating with settings', error) + if (error instanceof Error) { + loggingStore.log(MESSAGE_TYPES.ERROR, 'ADB: Error updating with settings', error.message) + } else { + loggingStore.log(MESSAGE_TYPES.ERROR, 'ADB: Error updating with settings', String(error)) + } } }) @@ -70,7 +74,7 @@ class ConnectionStore { } pingClient(connectionId: string): boolean { - console.log('Pinging client:', connectionId) + loggingStore.log(MESSAGE_TYPES.LOGGING, 'Pinging client:', connectionId) const clientIndex = this.clients.findIndex((c) => c.connectionId === connectionId) console.error('PINGING CLIENTS NOT IMPLEMENTED YET') if (clientIndex !== -1) { @@ -81,41 +85,38 @@ class ConnectionStore { } getClients(): Client[] { - console.log('Getting clients:', this.clients) return this.clients } getDevices(): string[] { - console.log('Getting devices:', this.devices) return this.devices } addClient(client: Client): void { - console.log('Adding client:', client) this.clients.push(client) this.notifyListeners() } updateClient(connectionId: string, updates: Partial): void { - console.log('Updating client:', connectionId, updates) + loggingStore.log(MESSAGE_TYPES.LOGGING, 'Updating client:' + connectionId + updates) const clientIndex = this.clients.findIndex((c) => c.connectionId === connectionId) if (clientIndex !== -1) { this.clients[clientIndex] = { ...this.clients[clientIndex], ...updates } this.notifyListeners() } else { - console.log('Client not found:', connectionId) + loggingStore.log(MESSAGE_TYPES.LOGGING, 'Client not found:', connectionId) } } removeClient(connectionId: string): void { - console.log('Removing client:', connectionId) + loggingStore.log(MESSAGE_TYPES.LOGGING, 'Removing client:' + connectionId) this.clients = this.clients.filter((c) => c.connectionId !== connectionId) this.notifyListeners() } removeAllClients(): void { - console.log('Removing all clients') + loggingStore.log(MESSAGE_TYPES.LOGGING, 'Removing all clients') this.clients = [] this.notifyListeners() } @@ -142,7 +143,7 @@ class ConnectionStore { const newDevices = parseADBDevices(result) || [] this.devices = newDevices this.notifyDeviceListeners() - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, 'ADB Device found!') + loggingStore.log(MESSAGE_TYPES.LOGGING, 'ADB Device found!') return newDevices }) .catch((error) => { @@ -158,7 +159,7 @@ class ConnectionStore { const checkAndAutoDetect = async (): Promise => { if (this.autoDetectADB === true) { - console.log('Auto-detecting ADB devices...') + loggingStore.log(MESSAGE_TYPES.LOGGING, 'Auto-detecting ADB devices...') await this.getAdbDevices() this.clearTimeout = await setTimeout(checkAndAutoDetect, 7000) } diff --git a/DeskThingServer/src/main/stores/keyMapStore.ts b/DeskThingServer/src/main/stores/keyMapStore.ts index c6e4eb3..b3ccb41 100644 --- a/DeskThingServer/src/main/stores/keyMapStore.ts +++ b/DeskThingServer/src/main/stores/keyMapStore.ts @@ -1,6 +1,13 @@ import { defaultData } from '../static/defaultMapping' -import { Action, ButtonMapping, EventMode, Key, MappingStructure } from '@shared/types' -import dataListener, { MESSAGE_TYPES } from '../utils/events' +import { + Action, + ButtonMapping, + MESSAGE_TYPES, + EventMode, + Key, + MappingStructure +} from '@shared/types' +import loggingStore from '../stores/loggingStore' import { readFromFile, readFromGlobalFile, @@ -50,7 +57,7 @@ export class MappingState { private loadMappings(): MappingStructure { const data = readFromFile('mappings.json') as MappingStructure if (!data || data?.version !== defaultData.version) { - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.ERROR, `MAPHANDLER: Mappings file is corrupt or does not exist, using default` ) @@ -59,7 +66,7 @@ export class MappingState { } const parsedData = data as MappingStructure if (!this.isValidFileStructure(parsedData)) { - dataListener.emit( + loggingStore.log( MESSAGE_TYPES.ERROR, `MAPHANDLER: Mappings file is corrupt, resetting to default` ) @@ -73,7 +80,7 @@ export class MappingState { if (this.isValidFileStructure(mapping)) { writeToFile(mapping, 'mappings.json') } else { - dataListener.emit( + loggingStore.log( MESSAGE_TYPES.ERROR, `MAPHANDLER: New Mappings file is corrupt, resetting to default` ) @@ -192,15 +199,10 @@ export class MappingState { * @param Mode - default is 'onPress' * @param profile - default is 'default' */ - addButton = ( - action: Action, - key: string, - Mode: EventMode, - profile: string = 'default' - ): void => { + addButton = (action: Action, key: string, Mode: EventMode, profile: string = 'default'): void => { const mappings = this.mappings if (!mappings[profile]) { - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.ERROR, `MAPHANDLER: Profile ${profile} does not exist! Create a new profile with the name ${profile} and try again` ) @@ -211,7 +213,7 @@ export class MappingState { } // Ensure that the structure of the button is valid if (!this.isValidAction(action)) { - dataListener.emit( + loggingStore.log( MESSAGE_TYPES.ERROR, `MAPHANDLER: Action ${action.id} is invalid, cannot add to mapping` ) @@ -234,7 +236,7 @@ export class MappingState { removeButton = (key: string, Mode: EventMode | null, profile: string = 'default'): void => { const mappings = this.mappings if (!mappings[profile]) { - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.ERROR, `MAPHANDLER: Profile ${profile} does not exist! Create a new profile with the name ${profile} and try again` ) @@ -242,7 +244,7 @@ export class MappingState { } // Ensuring the key exists in the mapping if (!mappings[profile][key]) { - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.ERROR, `MAPHANDLER: Key ${key} does not exist in profile ${profile}!` ) @@ -252,14 +254,14 @@ export class MappingState { if (Mode === null) { // Remove the entire key delete mappings[profile][key] - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.LOGGING, `MAPHANDLER: Key ${key} removed from profile ${profile}` ) } else { // Ensure that the Mode exists in the mapping if (!mappings[profile][key][Mode]) { - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.ERROR, `MAPHANDLER: Mode ${Mode} does not exist in key ${key} in profile ${profile}!` ) @@ -271,7 +273,7 @@ export class MappingState { // Save the mappings to file this.mappings = mappings - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.LOGGING, `MAPHANDLER: Button ${key} removed from profile ${profile}` ) @@ -281,7 +283,7 @@ export class MappingState { const mappings = this.mappings // Validate key structure if (!this.isValidKey(key)) { - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, `MAPHANDLER: Invalid key structure`) + loggingStore.log(MESSAGE_TYPES.ERROR, `MAPHANDLER: Invalid key structure`) return } // Check if the key already exists @@ -289,11 +291,11 @@ export class MappingState { if (existingKeyIndex !== -1) { // Replace the existing key mappings.keys[existingKeyIndex] = key - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, `MAPHANDLER: Key ${key.id} updated`) + loggingStore.log(MESSAGE_TYPES.LOGGING, `MAPHANDLER: Key ${key.id} updated`) } else { // Add the new key mappings.keys.push(key) - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, `MAPHANDLER: Key ${key.id} added`) + loggingStore.log(MESSAGE_TYPES.LOGGING, `MAPHANDLER: Key ${key.id} added`) } // Save the mappings this.mappings = mappings @@ -306,9 +308,9 @@ export class MappingState { if (keyIndex !== -1) { // Remove the key mappings.keys.splice(keyIndex, 1) - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, `MAPHANDLER: Key ${keyId} removed`) + loggingStore.log(MESSAGE_TYPES.LOGGING, `MAPHANDLER: Key ${keyId} removed`) } else { - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, `MAPHANDLER: Key ${keyId} not found`) + loggingStore.log(MESSAGE_TYPES.ERROR, `MAPHANDLER: Key ${keyId} not found`) } // Save the mappings this.mappings = mappings @@ -323,7 +325,7 @@ export class MappingState { const mappings = this.mappings // Validate action structure if (!this.isValidAction(action)) { - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, `MAPHANDLER: Invalid action structure`) + loggingStore.log(MESSAGE_TYPES.ERROR, `MAPHANDLER: Invalid action structure`) return } // Check if the action already exists @@ -331,11 +333,11 @@ export class MappingState { if (existingActionIndex !== -1) { // Replace the existing action mappings.actions[existingActionIndex] = action - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, `MAPHANDLER: Action ${action.id} updated`) + loggingStore.log(MESSAGE_TYPES.LOGGING, `MAPHANDLER: Action ${action.id} updated`) } else { // Add the new action mappings.actions.push(action) - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, `MAPHANDLER: Action ${action.id} added`) + loggingStore.log(MESSAGE_TYPES.LOGGING, `MAPHANDLER: Action ${action.id} added`) } // Save the mappings this.mappings = mappings @@ -348,9 +350,9 @@ export class MappingState { if (actionIndex !== -1) { // Remove the action mappings.actions.splice(actionIndex, 1) - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, `MAPHANDLER: Action ${actionId} removed`) + loggingStore.log(MESSAGE_TYPES.LOGGING, `MAPHANDLER: Action ${actionId} removed`) } else { - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, `MAPHANDLER: Action ${actionId} not found`) + loggingStore.log(MESSAGE_TYPES.ERROR, `MAPHANDLER: Action ${actionId} not found`) } // Save the mappings this.mappings = mappings @@ -385,7 +387,7 @@ export class MappingState { // Remove keys with the specified source mappings.keys = mappings.keys.filter((key) => key.source !== sourceId) - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.LOGGING, `MAPHANDLER: Actions for source ${sourceId} disabled in all profiles, global actions, and keys` ) @@ -427,7 +429,7 @@ export class MappingState { } }) - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.LOGGING, `MAPHANDLER: Actions for source ${sourceId} disabled in all profiles, global actions, and keys` ) @@ -443,12 +445,9 @@ export class MappingState { if (actionIndex !== -1) { // Update the icon mappings.actions[actionIndex].icon = icon - dataListener.asyncEmit( - MESSAGE_TYPES.LOGGING, - `MAPHANDLER: Icon for action ${actionId} updated` - ) + loggingStore.log(MESSAGE_TYPES.LOGGING, `MAPHANDLER: Icon for action ${actionId} updated`) } else { - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, `MAPHANDLER: Action ${actionId} not found`) + loggingStore.log(MESSAGE_TYPES.ERROR, `MAPHANDLER: Action ${actionId} not found`) } // Update the icon for all actions inside the current profile @@ -476,7 +475,7 @@ export class MappingState { // Update the icon return mappings.actions[actionIndex] } else { - dataListener.asyncEmit(MESSAGE_TYPES.ERROR, `MAPHANDLER: Action ${actionId} not found`) + loggingStore.log(MESSAGE_TYPES.ERROR, `MAPHANDLER: Action ${actionId} not found`) return null } } @@ -492,7 +491,7 @@ export class MappingState { if (this.mappings.profiles[profile]) { this.mappings.selected_profile = profile } else { - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.ERROR, `MAPHANDLER: Profile ${profile} does not exist! Create a new profile with the name ${profile} and try again` ) @@ -513,16 +512,13 @@ export class MappingState { // Check if the profile name already exists if (mappings.profiles[profileName]) { - dataListener.asyncEmit( - MESSAGE_TYPES.ERROR, - `MAPHANDLER: Profile "${profileName}" already exists!` - ) + loggingStore.log(MESSAGE_TYPES.ERROR, `MAPHANDLER: Profile "${profileName}" already exists!`) return } // Ensure the base profile exists if (!mappings.profiles[baseProfile]) { - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.ERROR, `MAPHANDLER: Base profile "${baseProfile}" does not exist!` ) @@ -549,7 +545,7 @@ export class MappingState { // Save the updated mappings this.mappings = mappings - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.LOGGING, `MAPHANDLER: Profile "${profileName}" added successfully.` ) @@ -564,19 +560,13 @@ export class MappingState { // Prevent removal of the default profile if (profileName === 'default') { - dataListener.asyncEmit( - MESSAGE_TYPES.ERROR, - `MAPHANDLER: The "default" profile cannot be removed.` - ) + loggingStore.log(MESSAGE_TYPES.ERROR, `MAPHANDLER: The "default" profile cannot be removed.`) return } // Check if the profile exists if (!mappings.profiles[profileName]) { - dataListener.asyncEmit( - MESSAGE_TYPES.ERROR, - `MAPHANDLER: Profile "${profileName}" does not exist!` - ) + loggingStore.log(MESSAGE_TYPES.ERROR, `MAPHANDLER: Profile "${profileName}" does not exist!`) return } @@ -586,7 +576,7 @@ export class MappingState { // If the removed profile was the selected profile, revert to default if (mappings.selected_profile === profileName) { mappings.selected_profile = 'default' - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.LOGGING, `MAPHANDLER: Selected profile was removed. Reverted to "default" profile.` ) @@ -595,7 +585,7 @@ export class MappingState { // Save the updated mappings this.mappings = mappings - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.LOGGING, `MAPHANDLER: Profile "${profileName}" removed successfully.` ) @@ -610,7 +600,7 @@ export class MappingState { const mappings = this.mappings if (!mappings.profiles[profile]) { - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.ERROR, `MAPHANDLER: Profile ${profile} does not exist! Cannot export.` ) @@ -620,7 +610,7 @@ export class MappingState { const profileData = mappings.profiles[profile] writeToGlobalFile(profileData, filePath) - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.LOGGING, `MAPHANDLER: Profile ${profile} exported to ${filePath}` ) @@ -638,7 +628,7 @@ export class MappingState { const profileData = readFromGlobalFile(filePath) if (!profileData) { - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.ERROR, `MAPHANDLER: Failed to load profile data from ${filePath}` ) @@ -646,10 +636,7 @@ export class MappingState { } if (!this.isValidButtonMapping(profileData)) { - dataListener.asyncEmit( - MESSAGE_TYPES.ERROR, - `MAPHANDLER: Invalid profile data in file ${filePath}` - ) + loggingStore.log(MESSAGE_TYPES.ERROR, `MAPHANDLER: Invalid profile data in file ${filePath}`) return } @@ -657,7 +644,7 @@ export class MappingState { mappings.profiles[profileName] = profileData this.mappings = mappings - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.LOGGING, `MAPHANDLER: Profile ${profileName} imported from ${filePath}` ) @@ -667,16 +654,13 @@ export class MappingState { const mappings = this.mappings const profile = mappings.profiles[profileName] if (!profile) { - dataListener.asyncEmit( - MESSAGE_TYPES.ERROR, - `MAPHANDLER: Profile ${profileName} does not exist!` - ) + loggingStore.log(MESSAGE_TYPES.ERROR, `MAPHANDLER: Profile ${profileName} does not exist!`) return } // Update the profile with the provided data deepMerge(profile, updatedProfile) this.mappings = mappings - dataListener.asyncEmit( + loggingStore.log( MESSAGE_TYPES.LOGGING, `MAPHANDLER: Profile ${profileName} updated successfully.` ) diff --git a/DeskThingServer/src/main/stores/loggingStore.ts b/DeskThingServer/src/main/stores/loggingStore.ts new file mode 100644 index 0000000..70e7f32 --- /dev/null +++ b/DeskThingServer/src/main/stores/loggingStore.ts @@ -0,0 +1,133 @@ +import fs from 'fs' +import { join } from 'path' +import { app } from 'electron' +import { MESSAGE_TYPES, Log, LOGGING_LEVEL, Settings, ReplyData, ReplyFn } from '@shared/types' +import settingsStore from './settingsStore' + +// LoggingStore configuration +const logFile = join(app.getPath('userData'), 'application.log.json') +const readableLogFile = join(app.getPath('userData'), 'readable.log') + +// Ensure log directory exists +const logDir = app.getPath('userData') +if (!fs.existsSync(logDir)) { + fs.mkdirSync(logDir, { recursive: true }) +} + +class LoggingStore { + private static instance: LoggingStore + private listeners: ((data: Log) => void)[] = [] + private logs: Log[] = [] + private logLevel: LOGGING_LEVEL = LOGGING_LEVEL.PRODUCTION + + private constructor() { + fs.writeFileSync(logFile, '[]') + fs.writeFileSync(readableLogFile, '') + + settingsStore.addListener(this.settingsStoreListener.bind(this)) + } + + private settingsStoreListener(settings: Settings): void { + this.logLevel = settings.logLevel + } + + // Singleton instance + public static getInstance(): LoggingStore { + if (!LoggingStore.instance) { + LoggingStore.instance = new LoggingStore() + } + return LoggingStore.instance + } + + // Log a message + async log(level: MESSAGE_TYPES, message: string, source: string = 'server'): Promise { + if ( + level === MESSAGE_TYPES.LOGGING && + source === 'server' && + this.logLevel != LOGGING_LEVEL.SYSTEM + ) { + return + } + + if (level === MESSAGE_TYPES.LOGGING && this.logLevel == LOGGING_LEVEL.PRODUCTION) { + return + } + + const timestamp = new Date().toISOString() + const trace = new Error().stack || '' + + const logData: Log = { + source: source, + type: level, + log: message, + trace: trace, + date: timestamp + } + + this.logs.push(logData) + this.notifyListeners(logData) + + const readableTimestamp = new Date(timestamp).toLocaleString() + const readableMessage = `[${readableTimestamp}] [${source}] ${level.toUpperCase()}: ${message}\n` + + console.log(readableMessage) + + // Write to log file as JSON array + return new Promise((resolve, reject) => { + fs.writeFile(logFile, JSON.stringify(this.logs, null, 2), (err) => { + if (err) { + console.error('Failed to write to log file:', err) + reject(err) + } + resolve() + }) + + fs.appendFile(readableLogFile, readableMessage, (err) => { + if (err) { + console.error('Failed to write to log file:', err) + reject(err) + } + resolve() + }) + }) + } + + notifyListeners(data: Log): void { + this.listeners.forEach((listener) => listener(data)) + } + + addListener(callback: (data: Log) => void): void { + this.listeners.push(callback) + } + + public async getLogs(): Promise { + return new Promise((resolve, reject) => { + if (!fs.existsSync(logFile)) { + resolve([]) + return + } + + fs.readFile(logFile, 'utf8', (err, data) => { + if (err) { + return reject(err) + } + try { + const logs = data ? JSON.parse(data) : [] + resolve(logs) + } catch (error) { + reject(error) + } + }) + }) + } +} + +export const ResponseLogger = (replyFn: ReplyFn): ReplyFn => { + return async (channel: string, reply: ReplyData): Promise => { + LoggingStore.getInstance().log(MESSAGE_TYPES.LOGGING, `[${channel}]: ${JSON.stringify(reply)}`) + + replyFn(channel, reply) + } +} + +export default LoggingStore.getInstance() diff --git a/DeskThingServer/src/main/stores/settingsStore.ts b/DeskThingServer/src/main/stores/settingsStore.ts index 4cabccc..327b884 100644 --- a/DeskThingServer/src/main/stores/settingsStore.ts +++ b/DeskThingServer/src/main/stores/settingsStore.ts @@ -1,15 +1,18 @@ import { readFromFile, writeToFile } from '../utils/fileHandler' -import dataListener, { MESSAGE_TYPES } from '../utils/events' +import loggingStore from './loggingStore' import os from 'os' -import { Settings } from '@shared/types' +import { LOGGING_LEVEL, Settings, MESSAGE_TYPES } from '@shared/types' const settingsVersion = '0.9.2' const version_code = 9.2 +type SettingsStoreListener = (settings: Settings) => void + class SettingsStore { private settings: Settings private settingsFilePath: string = 'settings.json' private static instance: SettingsStore + private listeners: SettingsStoreListener[] = [] constructor() { this.settings = this.getDefaultSettings() @@ -18,7 +21,7 @@ class SettingsStore { if (settings) { this.settings = settings as Settings this.settings.localIp = getLocalIpAddress() - dataListener.asyncEmit(MESSAGE_TYPES.SETTINGS, this.settings) + this.notifyListeners() } }) .catch((err) => { @@ -32,6 +35,16 @@ class SettingsStore { return SettingsStore.instance } + public addListener(listener: SettingsStoreListener): void { + this.listeners.push(listener) + } + + private async notifyListeners(): Promise { + this.listeners.forEach((listener) => { + listener(this.settings) + }) + } + public async getSettings(): Promise { if (this.settings) { return this.settings @@ -51,14 +64,13 @@ class SettingsStore { this.updateAutoLaunch(value) } this.settings[key] = value - dataListener.asyncEmit(MESSAGE_TYPES.SETTINGS, this.settings) this.saveSettings() } public async loadSettings(): Promise { try { const data = await readFromFile(this.settingsFilePath) - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, 'SETTINGS: Loaded settings!') + loggingStore.log(MESSAGE_TYPES.LOGGING, 'SETTINGS: Loaded settings!') if (!data || !data.version_code || data.version_code < version_code) { // File does not exist, create it with default settings @@ -71,6 +83,8 @@ class SettingsStore { await this.updateAutoLaunch(data.autoStart) } + this.notifyListeners() + return data } catch (err) { console.error('Error loading settings:', err) @@ -103,12 +117,13 @@ class SettingsStore { if (settings) { this.settings = settings as Settings await writeToFile(this.settings, this.settingsFilePath) - dataListener.asyncEmit(MESSAGE_TYPES.SETTINGS, this.settings) console.log('SETTINGS: Updated settings!', this.settings) - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, 'SETTINGS: Updated settings!') + loggingStore.log(MESSAGE_TYPES.LOGGING, 'SETTINGS: Updated settings!') } else { - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, 'SETTINGS: Invalid setting format!') + loggingStore.log(MESSAGE_TYPES.LOGGING, 'SETTINGS: Invalid setting format!') } + + this.notifyListeners() } catch (err) { console.error('Error saving settings:', err) } @@ -125,6 +140,7 @@ class SettingsStore { callbackPort: 8888, devicePort: 8891, address: '0.0.0.0', + LogLevel: LOGGING_LEVEL.PRODUCTION, autoStart: false, autoConfig: false, minimizeApp: true, diff --git a/DeskThingServer/src/main/utils/events.ts b/DeskThingServer/src/main/utils/events.ts deleted file mode 100644 index 1d0a204..0000000 --- a/DeskThingServer/src/main/utils/events.ts +++ /dev/null @@ -1,79 +0,0 @@ -import EventEmitter from 'events' -import Logger from './logger' -import { ReplyData, ReplyFn, SocketData } from '@shared/types' - -/** - * The MESSAGE_TYPES object defines a set of constants that represent the different types of messages that can be sent or received in the application. - */ -export enum MESSAGE_TYPES { - ERROR = 'error', - LOGGING = 'log', - MESSAGE = 'message', - WARNING = 'warning', - FATAL = 'fatal', - DEBUG = 'debugging', - CONFIG = 'config', - SETTINGS = 'settings', - MAPPINGS = 'mapping' -} - -/** - * Events is a class that extends the EventEmitter class from the 'events' module. - */ -class Events extends EventEmitter { - constructor() { - super() - } - - /** - * Emits an event with associated data to all connected clients. - * - * @param event - The name of the event to emit. Should be one of the MESSAGE_TYPES defined in this file. - * @param data - The data to be sent along with the event. Can be of any type, but typically an object containing relevant information. - * @returns void - * - * Usage: - * 1. Import the Events instance from this file. - * 2. Call the method with the appropriate event type and data. - * - * @example - * - * import { events } from './events' - * - * events.emit(MESSAGE_TYPES.MESSAGE, { content: 'Hello, world!' }) - */ - async asyncEmit(event: MESSAGE_TYPES, ...data: (string | SocketData | unknown)[]): Promise { - console.log(`[${event}] `, data) - return new Promise((resolve) => { - setImmediate(() => { - // Ensure that two arguments are only emitted at once - if (Array.isArray(data) && data.length > 0 && typeof data[0] === 'string') { - this.emit(event, data.join(' ')) - } else { - this.emit(event, data[0]) - } - - Logger.log(event, `[${event}]: ${JSON.stringify(data, null, 2)}`) - - resolve() - }) - }) - } -} - -const dataListener = new Events() - -/** - * Handles and standardizes the way to reply to ipc handlers - * @param replyFn - * @returns - */ -export const ResponseLogger = (replyFn: ReplyFn): ReplyFn => { - return async (channel: string, reply: ReplyData): Promise => { - dataListener.asyncEmit(MESSAGE_TYPES.LOGGING, `[CHANNEL][${channel}]: ${JSON.stringify(reply)}`) - - replyFn(channel, reply) - } -} - -export default dataListener diff --git a/DeskThingServer/src/main/utils/logger.ts b/DeskThingServer/src/main/utils/logger.ts deleted file mode 100644 index 61cefae..0000000 --- a/DeskThingServer/src/main/utils/logger.ts +++ /dev/null @@ -1,57 +0,0 @@ -import fs from 'fs' -import { join } from 'path' -import { app } from 'electron' -import { MESSAGE_TYPES } from './events' - -// Logger configuration -const logFile = join(app.getPath('userData'), 'application.log') - -// Ensure log directory exists -const logDir = app.getPath('userData') -if (!fs.existsSync(logDir)) { - fs.mkdirSync(logDir, { recursive: true }) -} - -class Logger { - private static instance: Logger - - private constructor() { - fs.writeFileSync(logFile, '') - } - - // Singleton instance - public static getInstance(): Logger { - if (!Logger.instance) { - Logger.instance = new Logger() - } - return Logger.instance - } - - // Log a message - async log(level: MESSAGE_TYPES, message: string): Promise { - const timestamp = new Date().toLocaleTimeString() - const logMessage = `[${timestamp}]: ${level.toUpperCase()} | ${message}` - - // Append to log file - fs.appendFile(logFile, logMessage + '\n', (err) => { - if (err) { - console.error('Failed to write to log file:', err) - } - }) - } - - public async getLogs(): Promise { - console.log('LOGGER: Getting logs') - return new Promise((resolve, reject) => { - fs.readFile(logFile, 'utf8', (err, data) => { - if (err) { - return reject(err) - } - const logs = data.trim().split('\n').filter(Boolean) - resolve(logs) - }) - }) - } -} - -export default Logger.getInstance() diff --git a/DeskThingServer/src/preload/index.d.ts b/DeskThingServer/src/preload/index.d.ts index f3e5bb1..e6cf083 100644 --- a/DeskThingServer/src/preload/index.d.ts +++ b/DeskThingServer/src/preload/index.d.ts @@ -1,5 +1,5 @@ import { ElectronAPI } from '@electron-toolkit/preload' -import { AppDataInterface, AppReturnData, Client, ClientManifest } from '@shared/types' +import { AppDataInterface, AppReturnData, Client, ClientManifest, Log } from '@shared/types' type AppData = { [key: string]: string } @@ -38,7 +38,7 @@ declare global { saveSettings: (settings: Settings) => Promise getSettings: () => Promise fetchGithub: (url: string) => Promise - getLogs: () => Promise + getLogs: () => Promise getMappings: () => Promise addProfile: (profile: string, baseProfile?: string) => Promise deleteProfile: (profile: string) => Promise diff --git a/DeskThingServer/src/preload/index.ts b/DeskThingServer/src/preload/index.ts index 5a38ef4..7195ade 100644 --- a/DeskThingServer/src/preload/index.ts +++ b/DeskThingServer/src/preload/index.ts @@ -11,6 +11,7 @@ import { IncomingData, IPC_HANDLERS, IPCData, + Log, Settings, SocketData } from '@shared/types' @@ -209,7 +210,7 @@ const api = { }) }, - getLogs: (): Promise => { + getLogs: (): Promise => { return sendCommand('UTILITY', { type: 'logs', request: 'get', diff --git a/DeskThingServer/src/renderer/src/assets/icons/icon/IconToggle.tsx b/DeskThingServer/src/renderer/src/assets/icons/icon/IconToggle.tsx index 6ef3df5..afcc11b 100644 --- a/DeskThingServer/src/renderer/src/assets/icons/icon/IconToggle.tsx +++ b/DeskThingServer/src/renderer/src/assets/icons/icon/IconToggle.tsx @@ -3,9 +3,10 @@ import { useEffect, useRef } from 'react' interface ToggleProps extends IconProps { checked: boolean + disabled?: boolean } -function IconToggle({ checked, ...props }: ToggleProps): JSX.Element { +function IconToggle({ disabled = false, checked, ...props }: ToggleProps): JSX.Element { const circleRef = useRef(null) useEffect(() => { @@ -29,9 +30,16 @@ function IconToggle({ checked, ...props }: ToggleProps): JSX.Element { stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" + style={{ opacity: disabled ? 0.5 : 1, cursor: disabled ? 'not-allowed' : 'pointer' }} > - + = ({ client }) => const [showLogging, setShowLogging] = useState(false) const [offline, setOffline] = useState(false) const refreshADbClients = useClientStore((store) => store.requestADBDevices) + const requestClientManifest = useClientStore((store) => store.requestClientManifest) const devicePort = useSettingsStore((store) => store.settings.devicePort) useEffect(() => { @@ -106,6 +107,7 @@ const ConnectionComponent: React.FC = ({ client }) => setLogging(reply) if (reply.final) { unsubscribe() + requestClientManifest() } }) } catch (error) { @@ -219,12 +221,13 @@ const ConnectionComponent: React.FC = ({ client }) => {!client.connected && ( diff --git a/DeskThingServer/src/renderer/src/components/NotificationButton.tsx b/DeskThingServer/src/renderer/src/components/NotificationButton.tsx index 70a3764..a4267d0 100644 --- a/DeskThingServer/src/renderer/src/components/NotificationButton.tsx +++ b/DeskThingServer/src/renderer/src/components/NotificationButton.tsx @@ -9,6 +9,7 @@ const NotificationButton: React.FC = () => { const taskNum = useNotificationStore((state) => state.totalTasks) const logs = useNotificationStore((state) => state.logs) + const requests = useNotificationStore((state) => state.requestQueue) const issues = useNotificationStore((state) => state.issues.length) const [errors, setErrors] = useState(0) @@ -30,8 +31,11 @@ const NotificationButton: React.FC = () => { + + + ) +} diff --git a/DeskThingServer/src/renderer/src/overlays/notifications/EventsPage.tsx b/DeskThingServer/src/renderer/src/overlays/notifications/EventsPage.tsx index 9375690..0c04911 100644 --- a/DeskThingServer/src/renderer/src/overlays/notifications/EventsPage.tsx +++ b/DeskThingServer/src/renderer/src/overlays/notifications/EventsPage.tsx @@ -2,6 +2,7 @@ import React from 'react' import { useNotificationStore } from '@renderer/stores' import { IconTrash } from '@renderer/assets/icons' import Button from '@renderer/components/Button' +import { MESSAGE_TYPES } from '@shared/types' const EvensPage: React.FC = () => { const logs = useNotificationStore((state) => state.logs) @@ -21,21 +22,27 @@ const EvensPage: React.FC = () => {
  • ))} diff --git a/DeskThingServer/src/renderer/src/overlays/notifications/NotificationOverlay.tsx b/DeskThingServer/src/renderer/src/overlays/notifications/NotificationOverlay.tsx index 3fe835c..15aa3eb 100644 --- a/DeskThingServer/src/renderer/src/overlays/notifications/NotificationOverlay.tsx +++ b/DeskThingServer/src/renderer/src/overlays/notifications/NotificationOverlay.tsx @@ -60,7 +60,12 @@ const NotificationOverlay: React.FC = () => { curPage={page} value={notifState.requestQueue.length} Icon={} - /> + className="relative" + > + {notifState.requestQueue.length > 0 && ( +
    + )} + ( ) diff --git a/DeskThingServer/src/renderer/src/overlays/notifications/RequestsPage.tsx b/DeskThingServer/src/renderer/src/overlays/notifications/RequestsPage.tsx index 1fa94c6..a01fd66 100644 --- a/DeskThingServer/src/renderer/src/overlays/notifications/RequestsPage.tsx +++ b/DeskThingServer/src/renderer/src/overlays/notifications/RequestsPage.tsx @@ -43,7 +43,7 @@ interface RequestProps { const RequestComponent = ({ request }: RequestProps): React.ReactElement => { const resolveRequest = useNotificationStore((state) => state.resolveRequest) const [expanded, setIsExpanded] = useState(false) - const [focusedIndex, setFocusedIndex] = useState(-1) + const [focusedIndex, setFocusedIndex] = useState(0) const [formData, setFormData] = useState<{ [key: string]: string }>({}) const [allFieldsFilled, setAllFieldsFilled] = useState(false) @@ -73,7 +73,7 @@ const RequestComponent = ({ request }: RequestProps): React.ReactElement => { const toggleExpanded = (): void => { setIsExpanded(!expanded) - setFocusedIndex(-1) + setFocusedIndex(0) } useEffect(() => { diff --git a/DeskThingServer/src/renderer/src/overlays/settings/MusicSettings.tsx b/DeskThingServer/src/renderer/src/overlays/settings/MusicSettings.tsx index 775e8a5..699c998 100644 --- a/DeskThingServer/src/renderer/src/overlays/settings/MusicSettings.tsx +++ b/DeskThingServer/src/renderer/src/overlays/settings/MusicSettings.tsx @@ -3,13 +3,16 @@ import useSettingsStore from '../../stores/settingsStore' import useAppStore from '../../stores/appStore' import Button from '@renderer/components/Button' import { IconLoading, IconSave, IconToggle } from '@renderer/assets/icons' +import Select from '@renderer/components/Select' +import { SingleValue } from 'react-select' +import { SettingOption, Settings } from '@shared/types' const MusicSettings: React.FC = () => { - const initialSettings = useSettingsStore((settings) => settings.settings) const saveSettings = useSettingsStore((settings) => settings.saveSettings) + const requestSettings = useSettingsStore((settings) => settings.requestSettings) const appsList = useAppStore((state) => state.appsList) const [audioSources, setAudioSources] = useState<{ id: string; name: string }[]>([]) - const [settings, setSettings] = useState(initialSettings) + const [settings, setSettings] = useState(null) const [loading, setLoading] = useState(false) useEffect(() => { @@ -20,14 +23,23 @@ const MusicSettings: React.FC = () => { name: app.manifest?.label || app.name })) setAudioSources(sources) + + const fetchSettings = async (): Promise => { + const settings = await requestSettings() + setSettings(settings) + } + + fetchSettings() }, [appsList]) const handleSettingChange = (key: string, value: string | boolean | number | string[]): void => { + if (!settings) return setSettings({ ...settings, [key]: value }) console.log('Settings Updated:', settings) } const handleSave = async (): Promise => { + if (!settings) return setLoading(true) await saveSettings(settings) setTimeout(() => { @@ -43,55 +55,68 @@ const MusicSettings: React.FC = () => {

    Refresh Interval (seconds)

    handleSettingChange('refreshInterval', Number(e.target.value) * 1000) } className="focus:text-white bg-zinc-900 text-white rounded px-2 py-2" placeholder="Enter A Value" - disabled={settings.refreshInterval === -1} + disabled={!settings || settings.refreshInterval === -1} />
    -

    Playback Sources

    - { - handleSettingChange('playbackLocation', e.target.value) + const value = e as SingleValue + handleSettingChange('playbackLocation', value?.value || '') }} - defaultValue={'Unset'} - className="bg-zinc-900 rounded hover:cursor-pointer text-white px-2 py-2" - > - {audioSources.map((app) => ( - - ))} - - + value={settings ? settings.playbackLocation || '' : 'Disabled'} + className="bg-zinc-900 rounded hover:cursor-pointer text-white px-2 py-2 w-full" + options={[ + ...audioSources.map((app) => ({ + value: app.id, + label: app.name + })), + { + value: 'none', + label: 'None' + }, + { + value: 'disabled', + label: 'Disabled' + } + ]} + />
    +
    +

    Logging Level

    +