mirror of
https://github.com/HeyPuter/puter.git
synced 2026-08-25 15:37:12 +00:00
Remove DevConsoleService and related dev console widgets (#2167)
* Remove DevConsoleService and related dev console widgets Eliminates `DevConsoleService` and `NullDevConsoleService`, along with all code that registers or manages dev console widgets across the backend. Developer messages and notifications are now logged directly to the console. Removes related commands and test mocks, simplifying the developer experience and reducing code complexity. * Remove MinLogService and related registration code Deleted MinLogService.js and removed its registration from SelfHostedModule.js. This cleans up unused or deprecated logging functionality. * Remove `DevSocketService` and related registration * Increase test job timeouts in CI workflow * Remove dev environment widget ops counter Eliminated increment and decrement of the dev_batch-widget ops counter in BatchExecutor. This simplifies the code and removes dev-only UI tracking logic from the batch operation flow. * Remove console Puter logo feature and related code Deleted the src/fun/logos.js file and removed all references to the Puter logo display in WebServerService and documentation. This streamlines the server startup output and eliminates unused or unnecessary code. * Update WebServerService.js * Standardize console banner formatting in services
This commit is contained in:
@@ -82,7 +82,7 @@ jobs:
|
||||
api-test:
|
||||
name: API tests (node env, api-test)
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
timeout-minutes: 10
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -111,7 +111,7 @@ jobs:
|
||||
vitest:
|
||||
name: puterjs (node env, vitest)
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
timeout-minutes: 10
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
|
||||
@@ -234,18 +234,6 @@ const install = async ({ context, services, app, useapi, modapi }) => {
|
||||
});
|
||||
services.registerService('__refresh-assocs', RefreshAssociationsService);
|
||||
services.registerService('__prod-debugging', MakeProdDebuggingLessAwfulService);
|
||||
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 {
|
||||
const { NullDevConsoleService } = require('./services/NullDevConsoleService');
|
||||
services.registerService('dev-console', NullDevConsoleService);
|
||||
}
|
||||
|
||||
const { EventService } = require('./services/EventService');
|
||||
services.registerService('event', EventService);
|
||||
|
||||
|
||||
@@ -63,10 +63,6 @@ class BatchExecutor extends AdvancedBase {
|
||||
}
|
||||
|
||||
this.concurrent_ops++;
|
||||
if ( config.env == 'dev' ) {
|
||||
const wid = this.x.get('dev_batch-widget');
|
||||
wid.ops++;
|
||||
}
|
||||
|
||||
const { expectations } = this;
|
||||
const command_cls = commands[op.op];
|
||||
@@ -153,10 +149,6 @@ class BatchExecutor extends AdvancedBase {
|
||||
const serialized_error = e.serialize();
|
||||
return serialized_error;
|
||||
} finally {
|
||||
if ( config.env == 'dev' ) {
|
||||
const wid = x.get('dev_batch-widget');
|
||||
wid.ops--;
|
||||
}
|
||||
this.concurrent_ops--;
|
||||
if ( this.ops_promise && this.concurrent_ops < this.max_concurrent_ops ) {
|
||||
this.ops_promise.resolve();
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024-present Puter Technologies Inc.
|
||||
*
|
||||
* This file is part of Puter.
|
||||
*
|
||||
* Puter is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
module.exports = [
|
||||
{
|
||||
sz: 40,
|
||||
txt: `&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
|
||||
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
|
||||
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
|
||||
&&&&&&&&&&&&&&&. ,& &&&&&&&&&&&
|
||||
&&&&&&&& &&&&&&&&&( &&&&&&&&& &&&&&&&&&
|
||||
&&&&&&& &&&&&&&&&&& &&&&&&&&&&& &&&&&&&&
|
||||
&&&&&&. &&&&&&&, &&&&&&&&&&&&&&&& &&&&&&
|
||||
&&&&&&& /&&&&&&&&&&&&&&&&&&&&&&&& %&&&&&
|
||||
&&&&&&&&& ***&&/***&&&***&&&* &&&&&&&
|
||||
&&&&&&&&&&&&&& &&&&& &&&&& &&&&&&&&&&&&&
|
||||
&&&&&&&&&&&&& &&&&& &&&&& /&&&&&&&&&&&&
|
||||
&&&&&&&&( & &&&&&&& &&&&&&& ,& &&&&&&&&
|
||||
&&&&&&&&& .&&&&& & &&&&& &&&&&&&&
|
||||
&&&&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&&&
|
||||
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
|
||||
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&`,
|
||||
},
|
||||
{
|
||||
sz: 72,
|
||||
txt: `&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
|
||||
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
|
||||
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
|
||||
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
|
||||
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
|
||||
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&. ,&&&&&&/ .&&&&&&&&&&&&&&&&&&&&&&&
|
||||
&&&&&&&&&&&&&&&&&&& &&&&&& &&&&&&&&/ &&&&&&&&&&&&&&&&&&&
|
||||
&&&&&&&&&&&&&&& (&&&&&&&&&&&&&&&& .&&&&&&&&&&&&&&& #&&&&&&&&&&&&&&&&
|
||||
&&&&&&&&&&&&& (&&&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&&& ,&&&&&&&&&&&&&&&
|
||||
&&&&&&&&&&&& &&&&&&&&&&&&&&&&&&& #&&&&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&
|
||||
&&&&&&&&&&& %&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&
|
||||
&&&&&&&&&&& %&&&&&&&&&&&&& &&&&&&&&&&&&&&&&&&&&&&&&&&&&% &&&&&&&&&&&
|
||||
&&&&&&&&&&&& &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&( ,&&&&&&&&&&
|
||||
&&&&&&&&&&&&& #&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& /&&&&&&&&&&
|
||||
&&&&&&&&&&&&&&& %&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&% #&&&&&&&&&&&
|
||||
&&&&&&&&&&&&&&&&&&# /&&&& /&&&& /&&&& .&&&&&&&&&&&&&&
|
||||
&&&&&&&&&&&&&&&&&&&&&&&& /&&&&&&&& /&&&&&&&& /&&&&&&&&&&&&&&&&&&&&&&&
|
||||
&&&&&&&&&&&&&&&&&&&&&&&& /&&&&&&&& /&&&&&&&& /&&&&&&&&&&&&&&&&&&&&&&&
|
||||
&&&&&&&&&&&&&&&&&&&&&&&& /&&&&&&&& /&&&&&&&& .&&&&&&&&&&&&&&&&&&&&&&&
|
||||
&&&&&&&&&&&&&&&&&#.,&# ,&&&&&&&&&& /&&&&&&&&&& &&.,%&&&&&&&&&&&&&&&&
|
||||
&&&&&&&&&&&&&&& (&& &&&&&&&&&&&& /&&&&&&&&&&&& ,&&. &&&&&&&&&&&&&&
|
||||
&&&&&&&&&&&&&&& .&&&& &&&&&&&&&&& .&&&&&&&&&&& &&&& &&&&&&&&&&&&&&
|
||||
&&&&&&&&&&&&&&&& .&&&&&&&&&& *&&& ,&&&&&&&&&& &&&&&&&&&&&&&&&
|
||||
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& ,&&& *&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
|
||||
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
|
||||
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
|
||||
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
|
||||
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
|
||||
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&`,
|
||||
},
|
||||
];
|
||||
@@ -58,27 +58,6 @@ class AlarmService extends BaseService {
|
||||
// TODO:[self-hosted] fix this properly
|
||||
this.known_errors = [];
|
||||
|
||||
if ( this.global_config.env === 'dev' ) {
|
||||
/**
|
||||
* This method initializes the AlarmService instance by registering commands, setting up the pager, and initializing the known errors.
|
||||
* It also sets up the widget to display alarms in the dev environment.
|
||||
*
|
||||
* @param {BaseService} services - The BaseService instance that provides access to other services.
|
||||
* @returns {void}
|
||||
*/
|
||||
this.alarm_widget = () => {
|
||||
const lines = [];
|
||||
for ( const alarm of Object.values(this.alarms) ) {
|
||||
const line =
|
||||
'\x1B[31;1m [alarm]\x1B[0m ' +
|
||||
`${alarm.id_string}: ${alarm.message} (${alarm.count})`;
|
||||
const line_lines = this.stdioutil.split_lines(line);
|
||||
lines.push(...line_lines);
|
||||
}
|
||||
|
||||
return lines;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -288,12 +267,10 @@ class AlarmService extends BaseService {
|
||||
|
||||
this.apply_known_errors_(alarm);
|
||||
|
||||
// dev console
|
||||
if ( this.global_config.env === 'dev' && !this.attached_dev ) {
|
||||
this.attached_dev = true;
|
||||
const svc_devConsole = this.services.get('dev-console');
|
||||
svc_devConsole.turn_on_the_warning_lights();
|
||||
svc_devConsole.add_widget(this.alarm_widget);
|
||||
const realConsole = globalThis.original_console_object ?? console;
|
||||
realConsole.error('\x1B[33;1m[alarm]\x1B[0m Active alarms detected; see logs for details.');
|
||||
}
|
||||
|
||||
const args = this.Context.get('args') ?? {};
|
||||
|
||||
@@ -21,12 +21,10 @@ const { HLWrite } = require('../../filesystem/hl_operations/hl_write');
|
||||
const { NodePathSelector } = require('../../filesystem/node/selectors');
|
||||
const { get_user, invalidate_cached_user } = require('../../helpers');
|
||||
const { Context } = require('../../util/context');
|
||||
const { asyncSafeSetInterval } = require('@heyputer/putility').libs.promise;
|
||||
const { buffer_to_stream } = require('../../util/streamutil');
|
||||
const BaseService = require('../../services/BaseService');
|
||||
const { Actor, UserActorType } = require('../../services/auth/Actor');
|
||||
const { DB_WRITE } = require('../../services/database/consts');
|
||||
const { TEAL } = require('../../services/NullDevConsoleService');
|
||||
const { quot } = require('@heyputer/putility').libs.string;
|
||||
const bcrypt = require('bcrypt');
|
||||
const uuidv4 = require('uuid').v4;
|
||||
@@ -89,66 +87,16 @@ class DefaultUserService extends BaseService {
|
||||
if ( ! is_default_password ) return;
|
||||
|
||||
// console.log(`password for admin is: ${tmp_password}`);
|
||||
const svc_devConsole = this.services.get('dev-console');
|
||||
|
||||
// NB: this is needed for the CI to extract the password
|
||||
console.log(`password for admin is: ${tmp_password}`);
|
||||
|
||||
const realConsole = globalThis.original_console_object ?? console;
|
||||
realConsole.log('\n');
|
||||
svc_devConsole.notice({
|
||||
colors: TEAL,
|
||||
style: 'stars',
|
||||
title: 'Your default login credentials are',
|
||||
lines: [
|
||||
'Username: \x1b[1madmin\x1b[0m',
|
||||
`Password: \x1b[1m${tmp_password}\x1b[0m`,
|
||||
],
|
||||
});
|
||||
realConsole.log('\n');
|
||||
|
||||
// show console widget
|
||||
this.default_user_widget = ({ is_docker }) => {
|
||||
if ( is_docker ) {
|
||||
// In Docker we keep the output as simple as possible because
|
||||
// we're unable to determine the size of the terminal
|
||||
return [
|
||||
`Password for \`admin\`: ${ tmp_password}`,
|
||||
// TODO: possible bug
|
||||
// These blank lines are necessary for it to render and
|
||||
// I'm not entirely sure why anymore.
|
||||
'', '',
|
||||
];
|
||||
}
|
||||
const lines = [
|
||||
'Your admin user has been created!',
|
||||
`\x1B[31;1musername:\x1B[0m ${USERNAME}`,
|
||||
`\x1B[32;1mpassword:\x1B[0m ${tmp_password}`,
|
||||
'(change the password to remove this message)',
|
||||
];
|
||||
if ( ! this.global_config.minimal_console ) {
|
||||
lines.unshift('\x1B[31;1m==========================\x1B[0m');
|
||||
lines.push('\x1B[31;1m==========================\x1B[0m');
|
||||
}
|
||||
return lines;
|
||||
};
|
||||
this.default_user_widget.critical = true;
|
||||
this.start_poll_({ tmp_password, user });
|
||||
svc_devConsole.add_widget(this.default_user_widget);
|
||||
}
|
||||
start_poll_ ({ tmp_password }) {
|
||||
const interval = 1000 * 3; // 3 seconds
|
||||
const poll_interval = asyncSafeSetInterval(async () => {
|
||||
const user = await get_user({ username: USERNAME });
|
||||
const is_default_password = await bcrypt.compare(tmp_password,
|
||||
user.password);
|
||||
if ( ! is_default_password ) {
|
||||
const svc_devConsole = this.services.get('dev-console');
|
||||
svc_devConsole.remove_widget(this.default_user_widget);
|
||||
clearInterval(poll_interval);
|
||||
return;
|
||||
}
|
||||
}, interval);
|
||||
realConsole.log('\n************************************************************');
|
||||
realConsole.log('* Your default login credentials are:');
|
||||
realConsole.log(`* Username: admin`);
|
||||
realConsole.log(`* Password: ${tmp_password}`);
|
||||
realConsole.log('* (change the password to remove this message)');
|
||||
realConsole.log('************************************************************\n');
|
||||
}
|
||||
async create_default_user_ () {
|
||||
const db = this.services.get('database').get(DB_WRITE, USERNAME);
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
const BaseService = require('../../services/BaseService');
|
||||
|
||||
class MinLogService extends BaseService {
|
||||
static DESCRIPTION = `
|
||||
MinLogService hides any log messages which specify an area of concern.
|
||||
A developer can enable particular areas of concern through the console.
|
||||
`;
|
||||
|
||||
_construct () {
|
||||
this.on = false;
|
||||
this.visible = new Set();
|
||||
|
||||
this.widget_ = null;
|
||||
}
|
||||
|
||||
_init () {
|
||||
// On operating systems where low-level config (high customization) is
|
||||
// expected, we can turn off minlog by default.
|
||||
if ( this.global_config.os.refined ) this.on = false;
|
||||
|
||||
// Show console widget so developer knows logs are hidden
|
||||
this.add_dev_console_widget_();
|
||||
|
||||
// Register log middleware to hide logs
|
||||
const svc_log = this.services.get('log-service');
|
||||
svc_log.register_log_middleware(async log_details => {
|
||||
if ( ! this.on ) return;
|
||||
|
||||
const { fields } = log_details;
|
||||
if ( fields.hasOwnProperty('concern') ) {
|
||||
if ( ! this.visible.has(fields.concern) ) {
|
||||
return { skip: true };
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
});
|
||||
|
||||
this._register_commands(this.services.get('commands'));
|
||||
}
|
||||
|
||||
add_dev_console_widget_ () {
|
||||
const svc_devConsole = this.services.get('dev-console', { optional: true });
|
||||
if ( ! svc_devConsole ) return;
|
||||
|
||||
this.widget_ = () => {
|
||||
if ( ! this.on ) return ['minlog is off'];
|
||||
const lines = [
|
||||
'\x1B[31;1mSome logs hidden! Type minlog:off to see all logs.\x1B[0m',
|
||||
];
|
||||
return lines;
|
||||
};
|
||||
svc_devConsole.add_widget(this.widget_);
|
||||
}
|
||||
|
||||
rm_dev_console_widget_ () {
|
||||
const svc_devConsole = this.services.get('dev-console', { optional: true });
|
||||
if ( ! svc_devConsole ) return;
|
||||
|
||||
const lines = this.widget_();
|
||||
this.log.info(lines[0]);
|
||||
|
||||
svc_devConsole.remove_widget(this.widget_);
|
||||
this.widget_ = null;
|
||||
}
|
||||
|
||||
_register_commands (commands) {
|
||||
commands.registerCommands('minlog', [
|
||||
{
|
||||
id: 'on',
|
||||
handler: async (args, log) => {
|
||||
this.on = true;
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'off',
|
||||
handler: async (args, log) => {
|
||||
this.rm_dev_console_widget_();
|
||||
this.on = false;
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'show',
|
||||
handler: async (args, log) => {
|
||||
const [ name ] = args;
|
||||
|
||||
this.visible.add(name);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'hide',
|
||||
handler: async (args, log) => {
|
||||
const [ name ] = args;
|
||||
|
||||
this.visible.delete(name);
|
||||
},
|
||||
},
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MinLogService;
|
||||
@@ -41,9 +41,6 @@ class SelfHostedModule extends AdvancedBase {
|
||||
const { DBKVServiceWrapper } = require('../../services/repositories/DBKVStore/index.mjs');
|
||||
services.registerService('puter-kvstore', DBKVServiceWrapper);
|
||||
|
||||
// const MinLogService = require('./MinLogService');
|
||||
// services.registerService('min-log', MinLogService);
|
||||
|
||||
// TODO: sucks
|
||||
const RELATIVE_PATH = '../../../../../';
|
||||
|
||||
|
||||
@@ -63,4 +63,3 @@ extension.
|
||||
- `../../middleware/auth.js`
|
||||
- `../../util/strutil.js`
|
||||
- `../../helpers.js`
|
||||
- `../../fun/logos.js`
|
||||
|
||||
@@ -207,32 +207,16 @@ class WebServerService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! config.disable_fun ) this.print_puter_logo_();
|
||||
|
||||
const link = `\x1B[34;1m${strutil.osclink(url)}\x1B[0m`;
|
||||
const lines = [
|
||||
`Puter is now live at: ${link}`,
|
||||
];
|
||||
const svc_devConsole = this.services.get('dev-console', { optional: true });
|
||||
if ( this.config.old_widget_behavior ) {
|
||||
this.startup_widget = () => {
|
||||
const output = [...lines];
|
||||
if ( ! this.global_config.minimal_console ) {
|
||||
output.unshift('\x1B[34;1mPuter is live!\x1B[0m');
|
||||
output.push('Type web:dismiss to un-stick this message');
|
||||
}
|
||||
return output;
|
||||
};
|
||||
this.startup_widget.unimportant = true;
|
||||
if ( svc_devConsole ) svc_devConsole.add_widget(this.startup_widget);
|
||||
} else if ( svc_devConsole ) {
|
||||
svc_devConsole.notice({
|
||||
colors: { bg: '38;2;0;0;0;48;2;0;202;252;1', bginv: '38;2;0;202;252' },
|
||||
style: 'stars',
|
||||
title: 'Puter is live!',
|
||||
lines,
|
||||
});
|
||||
}
|
||||
const realConsole = globalThis.original_console_object ?? console;
|
||||
lines.forEach(line => realConsole.log(line));
|
||||
|
||||
realConsole.log('\n************************************************************');
|
||||
realConsole.log(`* Puter is now live at: ${url}`);
|
||||
realConsole.log('************************************************************');
|
||||
|
||||
server.timeout = 1000 * 60 * 60 * 2; // 2 hours
|
||||
server.requestTimeout = 1000 * 60 * 60 * 2; // 2 hours
|
||||
@@ -317,7 +301,6 @@ class WebServerService extends BaseService {
|
||||
this.app = app;
|
||||
|
||||
app.set('services', this.services);
|
||||
this._register_commands(this.services.get('commands'));
|
||||
|
||||
this.middlewares = { auth };
|
||||
|
||||
@@ -671,60 +654,6 @@ class WebServerService extends BaseService {
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
_register_commands (commands) {
|
||||
commands.registerCommands('web', [
|
||||
{
|
||||
id: 'dismiss',
|
||||
description: 'Dismiss the startup message',
|
||||
handler: async (_, log) => {
|
||||
if ( ! this.startup_widget ) return;
|
||||
const svc_devConsole = this.services.get('dev-console', { optional: true });
|
||||
if ( svc_devConsole ) svc_devConsole.remove_widget(this.startup_widget);
|
||||
const lines = this.startup_widget();
|
||||
for ( const line of lines ) log.log(line);
|
||||
this.startup_widget = null;
|
||||
},
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the Puter logo seen in the console after the server is started.
|
||||
*
|
||||
* Depending on the size of the terminal, a different version of the
|
||||
* logo is displayed. The logo is displayed in blue text.
|
||||
*
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
// comment above line 497
|
||||
print_puter_logo_ () {
|
||||
const realConsole = globalThis.original_console_object;
|
||||
if ( this.global_config.env !== 'dev' ) return;
|
||||
const logos = require('../../fun/logos.js');
|
||||
let last_logo = undefined;
|
||||
for ( const logo of logos ) {
|
||||
if ( logo.sz <= (process.stdout.columns ?? 0) ) {
|
||||
last_logo = logo;
|
||||
} else break;
|
||||
}
|
||||
if ( last_logo ) {
|
||||
const lines = last_logo.txt.split('\n');
|
||||
const width = process.stdout.columns;
|
||||
const pad = (width - last_logo.sz) / 2;
|
||||
const pad_left = Math.floor(pad);
|
||||
const pad_right = Math.ceil(pad);
|
||||
for ( let i = 0 ; i < lines.length ; i++ ) {
|
||||
lines[i] = ' '.repeat(pad_left) + lines[i] + ' '.repeat(pad_right);
|
||||
}
|
||||
const txt = lines.join('\n');
|
||||
realConsole.log(`\n\x1B[34;1m${ txt }\x1B[0m\n`);
|
||||
}
|
||||
if ( config.os.archbtw ) {
|
||||
realConsole.log('\x1B[34;1mPuter is running on Arch btw\x1B[0m');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = WebServerService;
|
||||
|
||||
@@ -93,29 +93,6 @@ module.exports = eggspress('/batch', {
|
||||
initial: 1,
|
||||
});
|
||||
|
||||
const batch_widget = {
|
||||
ic: 0,
|
||||
ops: 0,
|
||||
sc: 0,
|
||||
ec: 0,
|
||||
wc: 0,
|
||||
output () {
|
||||
let s = `Batch Operation: ${this.ic}`;
|
||||
s += `; oc = ${this.ops}`;
|
||||
s += `; sc = ${this.sc}`;
|
||||
s += `; ec = ${this.ec}`;
|
||||
s += `; wc = ${this.wc}`;
|
||||
s += `; cz = ${globalThis.average_chunk_size.get()}`;
|
||||
return s;
|
||||
},
|
||||
};
|
||||
if ( config.env == 'dev' ) {
|
||||
const svc_devConsole = x.get('services').get('dev-console');
|
||||
svc_devConsole.remove_widget('batch');
|
||||
svc_devConsole.add_widget(batch_widget.output.bind(batch_widget), 'batch');
|
||||
x.set('dev_batch-widget', batch_widget);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------
|
||||
// Variables used by busboy callbacks
|
||||
//-------------------------------------------------------------
|
||||
@@ -221,7 +198,6 @@ module.exports = eggspress('/batch', {
|
||||
busboy.on('file', async (fieldname, stream ) => {
|
||||
if ( batch_exe.total_tbd ) {
|
||||
batch_exe.total_tbd = false;
|
||||
batch_widget.ic = pending_operations.length;
|
||||
on_nonfile_data_end();
|
||||
}
|
||||
|
||||
@@ -298,4 +274,4 @@ module.exports = eggspress('/batch', {
|
||||
}
|
||||
|
||||
res.status(batch_exe.hasError ? 218 : 200).send({ results });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,367 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024-present Puter Technologies Inc.
|
||||
*
|
||||
* This file is part of Puter.
|
||||
*
|
||||
* Puter is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
const { ansi_visible_length } = require('@heyputer/putility/src/libs/string');
|
||||
const { consoleLogManager } = require('../util/consolelog');
|
||||
const BaseService = require('./BaseService');
|
||||
const { NullDevConsoleService } = require('./NullDevConsoleService');
|
||||
|
||||
/**
|
||||
* DevConsoleService - A service for managing the developer console interface,
|
||||
* providing functionalities such as adding and removing widgets, updating the display,
|
||||
* and handling command input/output in a terminal environment.
|
||||
*/
|
||||
class DevConsoleService extends BaseService {
|
||||
static MODULES = {
|
||||
fs: require('fs'),
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes the DevConsoleService instance, setting up required properties
|
||||
* and determining if the application is running in a Docker environment.
|
||||
*
|
||||
* @returns {void} This method does not return a value.
|
||||
*/
|
||||
_construct () {
|
||||
this.static_lines = [];
|
||||
this.widgets = [];
|
||||
this.identifiers = {};
|
||||
this.has_updates = false;
|
||||
|
||||
try {
|
||||
const require = this.require;
|
||||
const fs = require('fs');
|
||||
this.is_docker = fs.existsSync('/.dockerenv');
|
||||
} catch (e) {
|
||||
// if this fails, we assume is_docker should
|
||||
// be false.
|
||||
this.log.error(e.message);
|
||||
this.is_docker = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates the warning lights by adding a widget that outputs a warning message.
|
||||
* The widget will display a flashing warning message when it is turned on.
|
||||
*/
|
||||
turn_on_the_warning_lights () {
|
||||
/**
|
||||
* Turns on the warning lights by adding a warning widget to the console.
|
||||
* This function calls the `add_widget` method to display a formatted
|
||||
* warning message. The widget will be shown in red and blinking,
|
||||
* indicating a warning state.
|
||||
*/
|
||||
this.add_widget(() => {
|
||||
return `\x1B[31;1m\x1B[5m *** ${
|
||||
Array(3).fill('WARNING').join(' ** ')
|
||||
} ***\x1B[0m`;
|
||||
});
|
||||
}
|
||||
|
||||
add_widget (outputter, opt_id) {
|
||||
this.widgets.push(outputter);
|
||||
if ( opt_id ) {
|
||||
this.identifiers[opt_id] = outputter;
|
||||
}
|
||||
this.mark_updated();
|
||||
}
|
||||
|
||||
remove_widget (id_or_outputter) {
|
||||
if ( typeof id_or_outputter === 'string' ) {
|
||||
id_or_outputter = this.identifiers[id_or_outputter];
|
||||
}
|
||||
this.widgets = this.widgets.filter(w => w !== id_or_outputter);
|
||||
this.mark_updated();
|
||||
}
|
||||
|
||||
notice = NullDevConsoleService.notice;
|
||||
|
||||
/**
|
||||
* Updates the displayed output based on the current state of widgets.
|
||||
* This method collects output from all active widgets, handles any errors,
|
||||
* and maintains the integrity of displayed information.
|
||||
*
|
||||
* It modifies the static_lines array to reflect the latest outputs and
|
||||
* removes widgets that produce errors. The display is updated only if there
|
||||
* are changes in the output.
|
||||
*/
|
||||
update_ () {
|
||||
const initialOutput = [...this.static_lines];
|
||||
this.static_lines = [];
|
||||
// if a widget throws an error we MUST remove it;
|
||||
// it's probably a stack overflow because it's printing.
|
||||
const to_remove = [];
|
||||
let positions = [];
|
||||
for ( const w of this.widgets ) {
|
||||
let output; try {
|
||||
output = w({ is_docker: this.is_docker });
|
||||
} catch ( e ) {
|
||||
consoleLogManager.log_raw('error', e);
|
||||
to_remove.push(w);
|
||||
continue;
|
||||
}
|
||||
output = Array.isArray(output) ? output : [output];
|
||||
positions.push([this.static_lines.length, output.length]);
|
||||
this.static_lines.push(...output);
|
||||
}
|
||||
|
||||
// The desired minimum output lines for display; used to ensure there's enough space for content.
|
||||
const DESIRED_MIN_OUT = 10;
|
||||
const size_ok = () =>
|
||||
process.stdout.rows - DESIRED_MIN_OUT > this.static_lines.length;
|
||||
let n_hidden = 0;
|
||||
for ( let i = this.widgets.length - 1 ; i >= 0 ; i-- ) {
|
||||
if ( size_ok() ) break;
|
||||
const w = this.widgets[i];
|
||||
if ( w.critical ) continue;
|
||||
if ( ! w.unimportant ) continue;
|
||||
n_hidden++;
|
||||
const [start, length] = positions[i];
|
||||
this.static_lines.splice(start, length);
|
||||
// update positions
|
||||
for ( let j = i ; j < positions.length ; j++ ) {
|
||||
positions[j][0] -= length;
|
||||
}
|
||||
}
|
||||
for ( let i = this.widgets.length - 1 ; i >= 0 ; i-- ) {
|
||||
if ( size_ok() ) break;
|
||||
const w = this.widgets[i];
|
||||
if ( w.critical ) continue;
|
||||
n_hidden++;
|
||||
const [start, length] = positions[i];
|
||||
this.static_lines.splice(start, length);
|
||||
}
|
||||
if ( n_hidden && size_ok() ) {
|
||||
this.static_lines.push(`\x1B[33m${
|
||||
this.generateEnd(`[ ${n_hidden} widget${n_hidden === 1 ? '' : 's'} hidden ]`)
|
||||
}\x1B[0m`);
|
||||
}
|
||||
|
||||
if ( ! this.arrays_equal(initialOutput, this.static_lines) ) {
|
||||
this.mark_updated(); // Update only if outputs have changed
|
||||
}
|
||||
for ( const w of to_remove ) {
|
||||
this.remove_widget(w);
|
||||
}
|
||||
}
|
||||
|
||||
arrays_equal (a, b) {
|
||||
return a.length === b.length && a.every((val, index) => val === b[index]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks that an update has occurred in the DevConsoleService.
|
||||
* This method sets the has_updates flag to true, indicating that
|
||||
* the service should refresh the display during the next render cycle.
|
||||
*/
|
||||
mark_updated () {
|
||||
this.has_updates = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the DevConsoleService, setting up necessary interfaces
|
||||
* and handlers for command execution. This method manages input and
|
||||
* output streams, integrates command completion, and refreshes console
|
||||
* output based on widget updates.
|
||||
*
|
||||
* @async
|
||||
* @returns {Promise<void>} Resolves when the initialization completes.
|
||||
*/
|
||||
async _init () {
|
||||
const services = this.services;
|
||||
const commands = services.get('commands');
|
||||
|
||||
const readline = require('readline');
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
prompt: 'puter> ',
|
||||
terminal: true,
|
||||
completer: line => {
|
||||
if ( line.includes(' ') ) {
|
||||
const [ commandName, ...args ] = line.split(/\s+/);
|
||||
const command = commands.getCommand(commandName);
|
||||
if ( ! command )
|
||||
{
|
||||
return;
|
||||
}
|
||||
return [ command.completeArgument(args), args[args.length - 1] ];
|
||||
}
|
||||
|
||||
const results = commands.commandNames
|
||||
.filter(name => name.startsWith(line))
|
||||
.map(name => `${name} `); // Add a space after to make typing arguments more convenient
|
||||
return [ results, line ];
|
||||
},
|
||||
});
|
||||
rl.on('line', async (input) => {
|
||||
this._before_cmd();
|
||||
if ( input.startsWith('ev') ) {
|
||||
eval(input.slice(3));
|
||||
} else {
|
||||
await commands.executeRawCommand(input, console);
|
||||
}
|
||||
this._after_cmd();
|
||||
});
|
||||
|
||||
/**
|
||||
* _before_cmd - Handles operations needed before a command is executed.
|
||||
*
|
||||
* (resets cursor to correct position, if I recall correctly)
|
||||
*/
|
||||
this._before_cmd = () => {
|
||||
rl.pause();
|
||||
rl.output.write('\x1b[1A\r');
|
||||
rl.output.write('\x1b[2K\r');
|
||||
console.log(`\x1B[33m${
|
||||
this.generateSeparator('[ Command Output ]')
|
||||
}\x1B[0m`);
|
||||
};
|
||||
|
||||
/**
|
||||
* _after_cmd - Handles operations needed after a command is executed.
|
||||
*
|
||||
* This method is called to clean up the output after a command has been processed.
|
||||
* It logs a formatted message indicating the end of the command output.
|
||||
*/
|
||||
this._after_cmd = () => {
|
||||
console.log(`\x1B[33m${
|
||||
this.generateEnd()
|
||||
}\x1B[0m`);
|
||||
};
|
||||
|
||||
/**
|
||||
* Prepares the output console by pausing input, clearing the console lines,
|
||||
* and writing the static lines to be displayed. This method interacts directly
|
||||
* with the terminal output handling the cursor movements and line clearances.
|
||||
*/
|
||||
this._pre_write = () => {
|
||||
rl.pause();
|
||||
process.stdout.write('\x1b[0m');
|
||||
rl.output.write('\x1b[2K\r');
|
||||
for ( let i = 0; i < this.static_lines.length + 1; i++ ) {
|
||||
process.stdout.write('\x1b[1A'); // Move cursor up one line
|
||||
process.stdout.write('\x1b[2K'); // Clear the line
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Re-draws static lines and the input prompt (everything at the bottom of
|
||||
* the dev console) when something is written.
|
||||
*/
|
||||
this._post_write = () => {
|
||||
this.update_();
|
||||
// Draw separator bar
|
||||
process.stdout.write(`\x1B[36m${
|
||||
this.generateSeparator()
|
||||
}\x1B[0m\n`);
|
||||
|
||||
// Input background disabled on Mac OS because it
|
||||
// has a - brace yourself - light-theme terminal 😱
|
||||
const drawInputBackground =
|
||||
process.platform !== 'darwin';
|
||||
|
||||
// Redraw the static lines
|
||||
this.static_lines.forEach(line => {
|
||||
process.stdout.write(`${line }\n`);
|
||||
});
|
||||
if ( drawInputBackground ) {
|
||||
// input background
|
||||
process.stdout.write('\x1b[48;5;234m');
|
||||
}
|
||||
rl.resume();
|
||||
rl._refreshLine();
|
||||
if ( drawInputBackground ) {
|
||||
// input background
|
||||
process.stdout.write('\x1b[48;5;237m');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Triggers the pre-write and post-write processes to refresh the console output.
|
||||
* This method ensures that the latest changes are reflected in the console view.
|
||||
*/
|
||||
this._redraw = () => {
|
||||
this._pre_write();
|
||||
this._post_write();
|
||||
};
|
||||
|
||||
/**
|
||||
* Starts an interval that periodically checks for updates and redraws the console output if needed.
|
||||
* The interval runs every 2000 milliseconds (2 seconds) and invokes the `_redraw` method if
|
||||
* any updates have occurred since the last check.
|
||||
*/
|
||||
setInterval(() => {
|
||||
if ( this.has_updates ) {
|
||||
this._redraw();
|
||||
this.has_updates = false;
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
/**
|
||||
* Decorates all console log messages with the specified pre-write actions.
|
||||
*
|
||||
* This method is invoked before each log message is printed to the console,
|
||||
* ensuring that any necessary updates or redrawing of the console UI
|
||||
* can occur before the message is displayed.
|
||||
*
|
||||
* It does not accept any parameters and does not return any value.
|
||||
*/
|
||||
consoleLogManager.decorate_all(({ replace }, ...args) => {
|
||||
this._pre_write();
|
||||
});
|
||||
|
||||
consoleLogManager.post_all(() => {
|
||||
this._post_write();
|
||||
});
|
||||
|
||||
// This prevents the promptline background from staying
|
||||
// when Ctrl+C is used to terminate the server
|
||||
rl.on('SIGINT', () => {
|
||||
process.stdout.write('\x1b[0m\r');
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
|
||||
generateSeparator (text) {
|
||||
text = text || '[ Dev Console ]';
|
||||
const totalWidth = process.stdout.columns;
|
||||
|
||||
if ( totalWidth <= text.length + 1 ) {
|
||||
return '═'.repeat(totalWidth < 0 ? 0 : totalWidth);
|
||||
}
|
||||
|
||||
const paddingSize = (totalWidth - text.length) / 2;
|
||||
|
||||
// Construct the separator
|
||||
return '═'.repeat(Math.floor(paddingSize)) + text + '═'.repeat(Math.ceil(paddingSize));
|
||||
}
|
||||
|
||||
generateEnd (text) {
|
||||
text = text || '';
|
||||
const totalWidth = process.stdout.columns;
|
||||
const paddingSize = (totalWidth - text.length) / 2;
|
||||
|
||||
// Construct the separator
|
||||
return '─'.repeat(Math.floor(paddingSize)) + text + '─'.repeat(Math.ceil(paddingSize));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
DevConsoleService,
|
||||
};
|
||||
@@ -1,74 +0,0 @@
|
||||
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.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,
|
||||
};
|
||||
@@ -1,78 +0,0 @@
|
||||
const { ansi_visible_length } = require('@heyputer/putility/src/libs/string');
|
||||
const BaseService = require('./BaseService');
|
||||
|
||||
const TEAL = {
|
||||
bg: '38;2;0;0;0;48;2;0;252;202;1',
|
||||
bginv: '38;2;0;252;202;1',
|
||||
};
|
||||
|
||||
/**
|
||||
* When DevConsoleService is not enabled, it is a more robust approach
|
||||
* to replace it with a null implementation rather than not have
|
||||
* 'dev-console' present in the services registry. This is because any
|
||||
* errors caused by accessing methods on 'dev-console' without ensuring
|
||||
* it exists are likely only to be caught in the production environment.
|
||||
*/
|
||||
class NullDevConsoleService extends BaseService {
|
||||
notice ({ colors, title, lines, style }) {
|
||||
colors = colors ?? {
|
||||
bg: '46',
|
||||
bginv: '36',
|
||||
};
|
||||
|
||||
// line length
|
||||
let longest = 0;
|
||||
for ( const line of lines ) {
|
||||
const this_lines_length = ansi_visible_length(line);
|
||||
if ( this_lines_length > longest ) {
|
||||
longest = this_lines_length;
|
||||
}
|
||||
}
|
||||
|
||||
const longestWithTitle = Math.max(longest, ansi_visible_length(title));
|
||||
|
||||
const realConsole = globalThis.original_console_object ?? console;
|
||||
|
||||
({
|
||||
highlighter: () => {
|
||||
realConsole.log(`\x1B[${colors.bginv}m▐\x1B[0m\x1B[${colors.bg}m ${title} \x1B[0m`);
|
||||
for ( const line of lines ) {
|
||||
realConsole.log(`\x1B[${colors.bginv}m▐▌\x1B[0m${line}\x1B[0m`);
|
||||
}
|
||||
},
|
||||
highlighter2: () => {
|
||||
let top = '';
|
||||
for ( let i = title.length + 2; i < longest + 3; i++ ) top += `\x1B[${colors.bginv}m▁\x1B[0m`;
|
||||
realConsole.log(`\x1B[${colors.bginv}m▐\x1B[0m\x1B[${colors.bg}m ${title}${top || ' '}\x1B[0m`);
|
||||
for ( const line of lines ) {
|
||||
const diff = line.length - ansi_visible_length(line);
|
||||
realConsole.log(`\x1B[${colors.bginv}m▐▌\x1B[0m${line.padEnd(longest + diff)}` +
|
||||
`\x1B[${colors.bginv}m▐\x1B[0m`);
|
||||
}
|
||||
realConsole.log(` \x1B[${colors.bginv}m${Array(longest + 2).fill('▔').join('')}\x1B[0m`);
|
||||
},
|
||||
stars: () => {
|
||||
const len = longestWithTitle + 1;
|
||||
const horiz = Array(len).fill('*').join('');
|
||||
realConsole.log(`\x1B[${colors.bginv}m**${horiz}**\x1B[0m`);
|
||||
realConsole.log(`\x1B[${colors.bginv}m*\x1B[0m ${(`${title }:`).padEnd(len)} \x1B[${colors.bginv}m*\x1B[0m`);
|
||||
for ( const line of lines ) {
|
||||
const diff = line.length - ansi_visible_length(line);
|
||||
realConsole.log(`\x1B[${colors.bginv}m*\x1B[0m ${line.padEnd(len + diff)} \x1B[${colors.bginv}m*\x1B[0m`);
|
||||
}
|
||||
realConsole.log(`\x1B[${colors.bginv}m**${horiz}**\x1B[0m`);
|
||||
},
|
||||
})[style ?? 'highlighter2']();
|
||||
}
|
||||
turn_on_the_warning_lights () {
|
||||
}
|
||||
add_widget () {
|
||||
}
|
||||
remove_widget () {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
NullDevConsoleService,
|
||||
TEAL,
|
||||
};
|
||||
@@ -62,20 +62,10 @@ class SystemValidationService extends BaseService {
|
||||
|
||||
// If we're in dev mode...
|
||||
if ( this.global_config.env === 'dev' ) {
|
||||
// Display a permanent message in the console
|
||||
const svc_devConsole = this.services.get('dev-console');
|
||||
svc_devConsole.turn_on_the_warning_lights();
|
||||
/**
|
||||
* Turns on the warning lights in the developer console and adds a widget indicating that the system is in an invalid state.
|
||||
* This is used in development mode to provide a visual indicator of the invalid state without shutting down the server.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
svc_devConsole.add_widget(() => {
|
||||
return '\x1B[33;1m *** SYSTEM IS IN AN INVALID STATE *** \x1B[0m';
|
||||
});
|
||||
|
||||
// Don't shut down
|
||||
const realConsole = globalThis.original_console_object ?? console;
|
||||
realConsole.error('\n*** SYSTEM IS IN AN INVALID STATE ***');
|
||||
realConsole.error(message);
|
||||
realConsole.error('Resolve the error above to clear this state.\n');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,20 +34,6 @@ describe('SystemValidationService', async () => {
|
||||
report: mockReport,
|
||||
};
|
||||
|
||||
// Mock dev-console service
|
||||
const mockTurnOn = vi.fn();
|
||||
const mockAddWidget = vi.fn();
|
||||
const mockDevConsole = {
|
||||
turn_on_the_warning_lights: mockTurnOn,
|
||||
add_widget: mockAddWidget,
|
||||
};
|
||||
|
||||
const originalGet = testKernel.services.get.bind(testKernel.services);
|
||||
testKernel.services.get = vi.fn((name: string) => {
|
||||
if (name === 'dev-console') return mockDevConsole;
|
||||
return originalGet(name);
|
||||
}) as any;
|
||||
|
||||
try {
|
||||
await systemValidationService.mark_invalid('test message', new Error('test error'));
|
||||
|
||||
@@ -57,16 +43,11 @@ describe('SystemValidationService', async () => {
|
||||
trace: true,
|
||||
alarm: true,
|
||||
}));
|
||||
|
||||
// Verify dev console was called
|
||||
expect(mockTurnOn).toHaveBeenCalled();
|
||||
expect(mockAddWidget).toHaveBeenCalled();
|
||||
} finally {
|
||||
// Restore original environment
|
||||
if (systemValidationService.global_config) {
|
||||
systemValidationService.global_config.env = originalEnv;
|
||||
}
|
||||
testKernel.services.get = originalGet as any;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -81,17 +62,6 @@ describe('SystemValidationService', async () => {
|
||||
report: mockReport,
|
||||
};
|
||||
|
||||
const mockDevConsole = {
|
||||
turn_on_the_warning_lights: vi.fn(),
|
||||
add_widget: vi.fn(),
|
||||
};
|
||||
|
||||
const originalGet = testKernel.services.get.bind(testKernel.services);
|
||||
testKernel.services.get = vi.fn((name: string) => {
|
||||
if (name === 'dev-console') return mockDevConsole;
|
||||
return originalGet(name);
|
||||
}) as any;
|
||||
|
||||
try {
|
||||
await systemValidationService.mark_invalid('test without source');
|
||||
|
||||
@@ -102,7 +72,6 @@ describe('SystemValidationService', async () => {
|
||||
if (systemValidationService.global_config) {
|
||||
systemValidationService.global_config.env = originalEnv;
|
||||
}
|
||||
testKernel.services.get = originalGet as any;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -117,17 +86,6 @@ describe('SystemValidationService', async () => {
|
||||
report: mockReport,
|
||||
};
|
||||
|
||||
const mockDevConsole = {
|
||||
turn_on_the_warning_lights: vi.fn(),
|
||||
add_widget: vi.fn(),
|
||||
};
|
||||
|
||||
const originalGet = testKernel.services.get.bind(testKernel.services);
|
||||
testKernel.services.get = vi.fn((name: string) => {
|
||||
if (name === 'dev-console') return mockDevConsole;
|
||||
return originalGet(name);
|
||||
}) as any;
|
||||
|
||||
try {
|
||||
const testError = new Error('specific error');
|
||||
await systemValidationService.mark_invalid('specific message', testError);
|
||||
@@ -142,8 +100,6 @@ describe('SystemValidationService', async () => {
|
||||
if (systemValidationService.global_config) {
|
||||
systemValidationService.global_config.env = originalEnv;
|
||||
}
|
||||
testKernel.services.get = originalGet as any;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -237,40 +237,13 @@ class SqliteDatabaseAccessService extends BaseDatabaseAccessService {
|
||||
// Update version number
|
||||
await this.db.exec(`PRAGMA user_version = ${TARGET_VERSION};`);
|
||||
|
||||
// Add sticky notification
|
||||
/**
|
||||
* This method is responsible for applying database migrations. It checks the current version of the database against the available migrations, and if the database is out of date, it applies the necessary SQL files to bring it up to date.
|
||||
*
|
||||
* @param {void}
|
||||
* @returns {void}
|
||||
*/
|
||||
// Add this comment above line 222
|
||||
// It describes the purpose of the method and its behavior
|
||||
// It does not include any parameters or return values since the method does not take any inputs and does not return any output.
|
||||
this.database_update_notice = () => {
|
||||
const lines = [
|
||||
'Database has been updated!',
|
||||
`Current version: ${TARGET_VERSION}`,
|
||||
'Type sqlite:dismiss to dismiss this message',
|
||||
];
|
||||
if ( ! this.global_config.minimal_console ) {
|
||||
lines.unshift('\x1B[33;1m-----------------------------\x1B[0m');
|
||||
lines.push('\x1B[33;1m-----------------------------\x1B[0m');
|
||||
}
|
||||
return lines;
|
||||
};
|
||||
|
||||
const svc_devConsole = this.services.get('dev-console', { optional: true });
|
||||
if ( svc_devConsole ) {
|
||||
svc_devConsole.add_widget(this.database_update_notice);
|
||||
}
|
||||
this.log.info(`Database has been updated to version ${TARGET_VERSION}`);
|
||||
}
|
||||
|
||||
const svc_serverHealth = this.services.get('server-health');
|
||||
|
||||
/**
|
||||
* @description This method is used to register SQLite database-related commands with the dev-console service.
|
||||
* @param {object} commands - The dev-console service commands object.
|
||||
* Register a health check to ensure the SQLite schema matches the expected version.
|
||||
*/
|
||||
svc_serverHealth.add_check('sqlite', async () => {
|
||||
const [{ user_version }] = await this.requireRead('PRAGMA user_version');
|
||||
@@ -413,19 +386,6 @@ class SqliteDatabaseAccessService extends BaseDatabaseAccessService {
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'dismiss',
|
||||
description: 'dismiss the database update notice',
|
||||
handler: async (_, log) => {
|
||||
const svc_devConsole = this.services.get('dev-console');
|
||||
if ( ! svc_devConsole ) return;
|
||||
if ( ! this.database_update_notice ) return;
|
||||
svc_devConsole.remove_widget(this.database_update_notice);
|
||||
const lines = this.database_update_notice();
|
||||
for ( const line of lines ) log.log(line);
|
||||
this.database_update_notice = null;
|
||||
},
|
||||
},
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user