refactor: move WebServerService and socketio to module

This commit is contained in:
KernelDeimos 2024-12-03 14:58:35 -05:00
parent 3887ce05da
commit 10f6e5458e
20 changed files with 137 additions and 126 deletions

View File

@ -28,6 +28,7 @@ const { Context } = require("./src/util/context.js");
const { TestDriversModule } = require("./src/modules/test-drivers/TestDriversModule.js");
const { PuterAIModule } = require("./src/modules/puterai/PuterAIModule.js");
const { BroadcastModule } = require("./src/modules/broadcast/BroadcastModule.js");
const { WebModule } = require("./src/modules/web/WebModule.js");
module.exports = {
@ -42,9 +43,15 @@ module.exports = {
Context,
Kernel,
EssentialModules: [
CoreModule,
WebModule,
],
// Pre-built modules
CoreModule,
WebModule,
DatabaseModule,
PuterDriversModule,
LocalDiskStorageModule,

View File

@ -129,7 +129,6 @@ const install = async ({ services, app, useapi, modapi }) => {
const { ConfigurableCountingService } = require('./services/ConfigurableCountingService');
const { FSLockService } = require('./services/fs/FSLockService');
const { StrategizedService } = require('./services/StrategizedService');
const WebServerService = require('./services/WebServerService');
const FilesystemAPIService = require('./services/FilesystemAPIService');
const ServeGUIService = require('./services/ServeGUIService');
const PuterAPIService = require('./services/PuterAPIService');
@ -143,7 +142,6 @@ const install = async ({ services, app, useapi, modapi }) => {
services.registerService('server-health', ServerHealthService);
services.registerService('log-service', LogService);
services.registerService('commands', CommandService);
services.registerService('web-server', WebServerService, { app });
services.registerService('__api-filesystem', FilesystemAPIService);
services.registerService('__api', PuterAPIService);
services.registerService('__gui', ServeGUIService);
@ -354,6 +352,9 @@ const install = async ({ services, app, useapi, modapi }) => {
const { UserService } = require('./services/UserService');
services.registerService('user', UserService);
const { WSPushService } = require('./services/WSPushService');
services.registerService('__event-push-ws', WSPushService);
}
const install_legacy = async ({ services }) => {
@ -361,7 +362,6 @@ const install_legacy = async ({ services }) => {
// const { FilesystemService } = require('./filesystem/FilesystemService');
const PerformanceMonitor = require('./monitor/PerformanceMonitor');
const { OperationTraceService } = require('./services/OperationTraceService');
const { WSPushService } = require('./services/WSPushService');
const { ClientOperationService } = require('./services/ClientOperationService');
const { EngPortalService } = require('./services/EngPortalService');
const { AppInformationService } = require('./services/AppInformationService');
@ -371,7 +371,6 @@ const install_legacy = async ({ services }) => {
services.registerService('process-event', ProcessEventService);
// services.registerService('filesystem', FilesystemService);
services.registerService('operationTrace', OperationTraceService);
services.registerService('__event-push-ws', WSPushService);
services.registerService('file-cache', FileCacheService);
services.registerService('client-operation', ClientOperationService);
services.registerService('app-information', AppInformationService);

View File

@ -44,7 +44,6 @@ class FilesystemService extends BaseService {
static MODULES = {
_path: require('path'),
uuidv4: require('uuid').v4,
socketio: require('../socketio.js'),
config: require('../config.js'),
}

View File

@ -242,7 +242,6 @@ class HLMkdir extends HLFilesystemOperation {
static MODULES = {
_path: require('path'),
socketio: require('../../socketio.js'),
}
static PROPERTIES = {
@ -258,7 +257,7 @@ class HLMkdir extends HLFilesystemOperation {
async _run () {
const { context, values } = this;
const { _path, socketio } = this.modules;
const { _path } = this.modules;
const fs = context.get('services').get('filesystem');
if ( ! is_valid_path(values.path, {

View File

@ -116,7 +116,6 @@ class HLWrite extends HLFilesystemOperation {
static MODULES = {
_path: require('path'),
socketio: require('../../socketio.js'),
mime: require('mime-types'),
}

View File

@ -0,0 +1,51 @@
const BaseService = require('../../services/BaseService');
class SocketioService extends BaseService {
static MODULES = {
socketio: require('socket.io'),
};
['__on_install.socketio'] (_, { server }) {
const require = this.require;
const socketio = require('socket.io');
/**
* @type {import('socket.io').Server}
*/
this.io = socketio(server, {
cors: {
origin: '*',
}
});
}
async send (socket_specifiers, key, data) {
const svc_getUser = this.services.get('get-user');
if ( ! Array.isArray(socket_specifiers) ) {
socket_specifiers = [socket_specifiers];
}
for ( const socket_specifier of socket_specifiers ) {
if ( socket_specifier.room ) {
this.io.to(socket_specifier.room).emit(key, data);
} else if ( socket_specifier.socket ) {
const io = this.io.sockets.sockets.get(socket_specifier.socket)
if ( ! io ) continue;
io.emit(key, data);
}
}
}
has (socket_specifier) {
if ( socket_specifier.room ) {
const room = this.io.sockets.adapter.rooms.get(socket_specifier.room);
return (!!room) && room.size > 0;
}
if ( socket_specifier.socket ) {
return this.io.sockets.sockets.has(socket_specifier.socket);
}
}
}
module.exports = SocketioService;

View File

@ -0,0 +1,17 @@
const { AdvancedBase } = require("@heyputer/putility");
class WebModule extends AdvancedBase {
async install (context) {
const services = context.get('services');
const SocketioService = require("./SocketioService");
services.registerService('socketio', SocketioService);
const WebServerService = require("./WebServerService");
services.registerService('web-server', WebServerService);
}
}
module.exports = {
WebModule,
};

View File

@ -18,18 +18,19 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
const express = require('express');
const eggspress = require("../api/eggspress");
const { Context, ContextExpressMiddleware } = require("../util/context");
const BaseService = require("./BaseService");
const eggspress = require("../../api/eggspress.js");
const { Context, ContextExpressMiddleware } = require("../../util/context.js");
const BaseService = require("../../services/BaseService.js");
const config = require('../config');
const config = require('../../config.js');
const https = require('https')
var http = require('http');
const fs = require('fs');
const auth = require('../middleware/auth');
const { osclink } = require('../util/strutil');
const { surrounding_box, es_import_promise } = require('../fun/dev-console-ui-utils');
const auth = require('../../middleware/auth.js');
const { osclink } = require('../../util/strutil.js');
const { surrounding_box, es_import_promise } = require('../../fun/dev-console-ui-utils.js');
const relative_require = require;
/**
* This class, WebServerService, is responsible for starting and managing the Puter web server.
@ -103,9 +104,9 @@ class WebServerService extends BaseService {
// error handling middleware goes last, as per the
// expressjs documentation:
// https://expressjs.com/en/guide/error-handling.html
this.app.use(require('../api/api_error_handler'));
this.app.use(require('../../api/api_error_handler.js'));
const { jwt_auth } = require('../helpers');
const { jwt_auth } = require('../../helpers.js');
config.http_port = process.env.PORT ?? config.http_port;
@ -224,7 +225,11 @@ class WebServerService extends BaseService {
// server.keepAliveTimeout = 1000 * 60 * 60 * 2; // 2 hours
// Socket.io server instance
const socketio = require('../socketio.js').init(server);
// const socketio = require('../../socketio.js').init(server);
// TODO: ^ Replace above line with the following code:
await this.services.emit('install.socketio', { server });
const socketio = this.services.get('socketio').io;
// Socket.io middleware for authentication
socketio.use(async (socket, next) => {
@ -305,13 +310,13 @@ class WebServerService extends BaseService {
const require = this.require;
const config = this.global_config;
new ContextExpressMiddleware({
parent: globalThis.root_context.sub({
puter_environment: Context.create({
env: config.env,
version: require('../../package.json').version,
version: relative_require('../../../package.json').version,
}),
}, 'mw')
}).install(app);
@ -580,6 +585,8 @@ class WebServerService extends BaseService {
app.options('/*', (_, res) => {
return res.sendStatus(200);
});
console.log('WEB SERVER INIT DONE');
}
_register_commands (commands) {
@ -611,7 +618,7 @@ class WebServerService extends BaseService {
// comment above line 497
print_puter_logo_() {
if ( this.global_config.env !== 'dev' ) return;
const logos = require('../fun/logos.js');
const logos = require('../../fun/logos.js');
let last_logo = undefined;
for ( const logo of logos ) {
if ( logo.sz <= (process.stdout.columns ?? 0) ) {

View File

@ -289,10 +289,8 @@ router.all('*', async function(req, res, next) {
invalidate_cached_user(user);
// send realtime success msg to client
let socketio = require('../socketio.js').getio();
if(socketio){
socketio.to(user.id).emit('user.email_confirmed', {})
}
const svc_socketio = req.services.get('socketio');
svc_socketio.send({ room: user.id }, 'user.email_confirmed', {});
// return results
h += `<p style="text-align:center; color:green;">Your email has been successfully confirmed.</p>`;

View File

@ -83,10 +83,8 @@ const CHANGE_EMAIL_CONFIRM = eggspress('/change_email/confirm', {
});
invalidate_cached_user_by_id(user_id);
let socketio = require('../socketio.js').getio();
if(socketio){
socketio.to(user_id).emit('user.email_changed', {})
}
const svc_socketio = req.services.get('socketio');
svc_socketio.send({ room: user_id }, 'user.email_changed', {});
const h = `<p style="text-align:center; color:green;">Your email has been successfully confirmed.</p>`;
return res.send(h);

View File

@ -87,14 +87,14 @@ router.post('/confirm-email', auth, express.json(), async (req, res, next)=>{
// Send realtime success msg to client
if(req.body.code === req.user.email_confirm_code){
let socketio = require('../socketio.js').getio();
if(socketio){
socketio.to(req.user.id).emit('user.email_confirmed', {original_client_socket_id: req.body.original_client_socket_id})
}
const svc_socketio = req.services.get('socketio');
svc_socketio.send({ room: req.user.id }, 'user.email_confirmed', {
original_client_socket_id: req.body.original_client_socket_id
});
}
// return results
return res.send(res_obj)
})
module.exports = router
module.exports = router

View File

@ -48,8 +48,6 @@ module.exports = eggspress('/delete', {
else if(paths.length === 0)
return res.status(400).send('paths cannot be empty')
const socketio = require('../../socketio.js').getio();
// try to delete each path in the array one by one (if glob, resolve first)
// TODO: remove this pseudo-batch
for(let j=0; j < paths.length; j++){
@ -67,9 +65,11 @@ module.exports = eggspress('/delete', {
});
// send realtime success msg to client
if(socketio){
socketio.to(req.user.id).emit('item.removed', {path: item_path, descendants_only: descendants_only})
}
const svc_socketio = req.services.get('socketio');
svc_socketio.send({ room: req.user.id }, 'item.removed', {
path: item_path,
descendants_only: descendants_only,
});
}
res.send({});

View File

@ -174,10 +174,8 @@ module.exports = eggspress('/rename', {
};
// send realtime success msg to client
let socketio = require('../../socketio.js').getio();
if(socketio){
socketio.to(req.user.id).emit('item.renamed', return_obj)
}
const svc_socketio = req.services.get('socketio');
svc_socketio.send({ room: req.user.id }, 'item.renamed', return_obj);
return res.send(return_obj);
});

View File

@ -90,8 +90,8 @@ router.post('/rao', auth, express.json(), async (req, res, next)=>{
}
// Update clients
const socketio = require('../socketio.js').getio();
socketio.to(req.user.id).emit('app.opened', {
const svc_socketio = req.services.get('socketio');
svc_socketio.send({ room: req.user.id }, 'app.opened', {
uuid: opened_app.uid,
uid: opened_app.uid,
name: opened_app.name,

View File

@ -457,10 +457,8 @@ module.exports = eggspress('/writeFile', {
};
// send realtime success msg to client
let socketio = require('../socketio.js').getio();
if(socketio){
socketio.to(fsentry.user_id).emit('item.renamed', return_obj)
}
const svc_socketio = req.services.get('socketio');
svc_socketio.send({ room: req.user.id }, 'item.renamed', return_obj);
return res.send(return_obj);
}

View File

@ -32,7 +32,6 @@ const { AdvancedBase } = require("@heyputer/putility");
*/
class EngPortalService extends AdvancedBase {
static MODULES = {
socketio: require('../socketio.js'),
uuidv4: require('uuid').v4,
};

View File

@ -17,17 +17,10 @@
* 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 { AdvancedBase } = require("@heyputer/putility");
class WSPushService extends AdvancedBase {
static MODULES = {
socketio: require('../socketio.js'),
}
constructor ({ services }) {
super();
this.log = services.get('log-service').create('WSPushService');
this.svc_event = services.get('event');
const BaseService = require("./BaseService");
class WSPushService extends BaseService {
async _init () {
this.svc_event = this.services.get('event');
this.svc_event.on('fs.create.*', this._on_fs_create.bind(this));
this.svc_event.on('fs.write.*', this._on_fs_update.bind(this));
@ -50,7 +43,6 @@ class WSPushService extends AdvancedBase {
*/
async _on_fs_create (key, data) {
const { node, context } = data;
const { socketio } = this.modules;
const metadata = {
from_new_service: true,
@ -111,7 +103,6 @@ class WSPushService extends AdvancedBase {
*/
async _on_fs_update (key, data) {
const { node, context } = data;
const { socketio } = this.modules;
const metadata = {
from_new_service: true,
@ -177,7 +168,6 @@ class WSPushService extends AdvancedBase {
*/
async _on_fs_move (key, data) {
const { moved, old_path, context } = data;
const { socketio } = this.modules;
const metadata = {
from_new_service: true,
@ -235,7 +225,6 @@ class WSPushService extends AdvancedBase {
*/
async _on_fs_pending (key, data) {
const { fsentry, context } = data;
const { socketio } = this.modules;
const metadata = {
from_new_service: true,
@ -292,7 +281,6 @@ class WSPushService extends AdvancedBase {
*/
async _on_upload_progress (key, data) {
this.log.info('got upload progress event');
const { socketio } = this.modules;
const { upload_tracker, context, meta } = data;
const metadata = {
@ -320,25 +308,23 @@ class WSPushService extends AdvancedBase {
}
this.log.info('socket id: ' + socket_id);
const io = socketio.getio()
.sockets.sockets
.get(socket_id);
// socket disconnected; that's allowed
if ( ! io ) return;
const svc_socketio = context.get('services').get('socketio');
if ( ! svc_socketio.has({ socket: socket_id }) ) {
return;
}
const ws_event_name = metadata.call_it_download
? 'download.progress' : 'upload.progress' ;
upload_tracker.sub(delta => {
this.log.info('emitting progress event');
io.emit(ws_event_name, {
svc_socketio.send({ socket: socket_id }, ws_event_name, {
...metadata,
total: upload_tracker.total_,
loaded: upload_tracker.progress_,
loaded_diff: delta,
})
});
})
}
@ -357,16 +343,13 @@ class WSPushService extends AdvancedBase {
async _on_outer_gui (key, { user_id_list, response }, meta) {
key = key.slice('outer.gui.'.length);
const { socketio } = this.modules;
const io = socketio.getio();
const svc_socketio = this.services.get('socketio');
for ( const user_id of user_id_list ) {
const room = io.sockets.adapter.rooms.get(user_id);
if ( ! room || room.size <= 0 ) {
if ( ! svc_socketio.has({ room: user_id }) ) {
continue;
}
io.to(user_id).emit(key, response);
svc_socketio.send({ room: user_id }, key, response);
this.svc_event.emit(`sent-to-user.${key}`, {
user_id,
response,

View File

@ -25,11 +25,6 @@ const { UserActorType } = require("../auth/Actor");
const { Endpoint } = require("../../util/expressutil");
const APIError = require("../../api/APIError.js");
/**
* This service registers endpoints that are protected by password authentication,
* excluding login. These endpoints are typically for actions that affect
* security settings on the user's account.
*/
/**
* @class UserProtectedEndpointsService
* @extends BaseService

View File

@ -1,38 +0,0 @@
/*
* Copyright (C) 2024 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/>.
*/
let io;
module.exports = {
init: function(server) {
// start socket.io server and cache io value
io = require('socket.io')(server, {
cors: {
origin: '*',
}
});
// io.origins('*:*');
return io;
},
getio: function() {
// return previously cached value
if (!io) {
throw new Error("must call .init(server) before you can call .getio()");
}
return io;
}
}

View File

@ -80,7 +80,7 @@ if ( ! import.meta.filename ) {
const main = async () => {
const {
Kernel,
CoreModule,
EssentialModules,
DatabaseModule,
LocalDiskStorageModule,
SelfHostedModule,
@ -92,7 +92,9 @@ const main = async () => {
const k = new Kernel({
entry_path: import.meta.filename
});
k.add_module(new CoreModule());
for ( const mod of EssentialModules ) {
k.add_module(new mod());
}
k.add_module(new DatabaseModule());
k.add_module(new LocalDiskStorageModule());
k.add_module(new SelfHostedModule());