diff --git a/package.json b/package.json index be2e07dac..27af4afab 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "test:puterjs-api": "vitest run tests/puterJsApiTests", "start=gui": "nodemon --exec \"node dev-server.js\" ", "start": "npm run build:ts && node ./tools/run-selfhosted.js", + "dev": "npm run build:ts && DEVCONSOLE=1 node ./tools/run-selfhosted.js", "build": "cd src/gui; node ./build.js", "check-translations": "node tools/check-translations.js", "prepare": "husky", diff --git a/src/backend/src/CoreModule.js b/src/backend/src/CoreModule.js index 3b84a2a53..76c6e961e 100644 --- a/src/backend/src/CoreModule.js +++ b/src/backend/src/CoreModule.js @@ -269,7 +269,11 @@ const install = async ({ context, services, app, useapi, modapi }) => { }); services.registerService('__refresh-assocs', RefreshAssociationsService); services.registerService('__prod-debugging', MakeProdDebuggingLessAwfulService); - if ( config.env == 'dev' && ! config.no_devconsole ) { + if ( config.env == 'dev' && ! config.no_devsocket ) { + const { DevSocketService } = require('./services/DevSocketService.js'); + services.registerService('dev-socket', DevSocketService); + } + if ( (config.env == 'dev' && ! config.no_devconsole && process.env.DEVCONSOLE) || config.devconsole ) { const { DevConsoleService } = require('./services/DevConsoleService'); services.registerService('dev-console', DevConsoleService); } else { diff --git a/src/backend/src/modules/core/LogService.js b/src/backend/src/modules/core/LogService.js index ddd323f3e..bea09b850 100644 --- a/src/backend/src/modules/core/LogService.js +++ b/src/backend/src/modules/core/LogService.js @@ -41,7 +41,17 @@ const WINSTON_LEVELS = { http: 30, verbose: 40, debug: 50, - silly: 60 + silly: 60, +}; + +let display_log_level = process.env.DEBUG ? 100 : 3; +const display_log_level_label = { + 0: 'ERRO', + 1: 'WARN', + 2: 'INFO', + 3: 'SYSTEM', + 4: 'DEBUG', + 100: 'ALL', }; @@ -193,7 +203,7 @@ class DevLogger { if ( this.off ) return; - if ( ! process.env.DEBUG && log_lvl.ordinal >= 4 ) return; + if ( ! process.env.DEBUG && log_lvl.ordinal > display_log_level ) return; const ld = Context.get('logdent', { allow_fallback: true }) const prefix = globalThis.dev_console_indent_on @@ -430,7 +440,22 @@ class LogService extends BaseService { globalThis.dev_console_indent_on = ! globalThis.dev_console_indent_on; } - } + }, + { + id: 'get-level', + description: 'get the current log level for displayed logs', + handler: async (args, log) => { + log.log(`${display_log_level} (${display_log_level_label[display_log_level] ?? '?'})`); + }, + }, + { + id: 'set-level', + description: 'set the new log level for displayed logs', + handler: async (args, log) => { + display_log_level = Number(args[0]); + log.log(`${display_log_level} (${display_log_level_label[display_log_level] ?? '?'})`); + }, + }, ]); } /** diff --git a/src/backend/src/services/DevSocketService.js b/src/backend/src/services/DevSocketService.js new file mode 100644 index 000000000..bf87b2b43 --- /dev/null +++ b/src/backend/src/services/DevSocketService.js @@ -0,0 +1,76 @@ +const BaseService = require("./BaseService"); + +const path_ = require('node:path'); +const net = require('node:net'); +const fs = require('node:fs'); + +const SOCKET_INTRO = `Nice, you've found the dev socket! +This is a convenient debugging/diagnosis interface to Puter's backend +that only runs in development mode. Any line of text sent over the socket +stream beginning with the character \`{\` of \`[\` MUST be a valid JSON +value; that goes for both incoming and outgoing messages. + +Try entering the 'help' command. +`; + +class DevSocketService extends BaseService { + async ['__on_boot.consolidation'] () { + this.log.noticeme('dev socket service'); + this.sock = process.env.DEV_SOCKET_PATH ?? + path_.join(process.cwd(), 'dev.sock'); + + try { + fs.unlinkSync(this.sock); + } catch ( _err ) { + // NOOP + } + + const svc_command = this.services.get('commands'); + + const server = net.createServer(conn => { + + conn.setEncoding("utf8"); + conn.write(SOCKET_INTRO + "\n"); + + let buf = ""; + conn.on("data", (chunk) => { + buf += chunk; + let nl; + while ((nl = buf.indexOf("\n")) >= 0) { + let line = buf.slice(0, nl); buf = buf.slice(nl + 1); + line = line.trim(); + + const logoutputs = []; + const logfn = (...a) => { + logoutputs.push(a.join(' ')); + conn.write(JSON.stringify(a) + "\n"); + }; + const log = { + log: logfn, + info: logfn, + warn: logfn, + error: logfn, + }; + svc_command.executeRawCommand(line, log); + } + }); + }); + + server.listen(this.sock, () => { + fs.chmodSync(this.sock, 0o600); + }); + process.on('beforeExit', this.cleanup_.bind(this)); + } + + cleanup_ () { + if ( this.cleaned_up ) return; + this.cleaned_up = true; + + fs.unlinkSync(this.sock); + } +} + + +module.exports = { + DevSocketService, +};