diff --git a/src/backend/src/services/DetailProviderService.js b/src/backend/src/services/DetailProviderService.js index e03dba1d..fafcbcad 100644 --- a/src/backend/src/services/DetailProviderService.js +++ b/src/backend/src/services/DetailProviderService.js @@ -1,3 +1,4 @@ +// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o"}} /* * Copyright (C) 2024 Puter Technologies Inc. * @@ -23,7 +24,23 @@ const BaseService = require("./BaseService") * detail providers. A detail provider is a function that takes an * input object and uses its values to populate another object. */ +/** +* @class DetailProviderService +* @extends BaseService +* @description This class manages a collection of detail providers, +* which are functions that accept an input object to populate another object +* with relevant details. It provides methods to register new providers and +* retrieve details using all registered providers in sequence. +*/ class DetailProviderService extends BaseService { + /** + * Retrieves detailed information by invoking all registered detail providers with the given context. + * Each provider is expected to modify the out object directly. + * + * @param {Object} context - The input context for the providers. + * @param {Object} [out={}] - An object to store the combined results from each provider. + * @returns {Object} The combined results populated by the detail providers. + */ _construct () { this.providers_ = []; } @@ -32,6 +49,20 @@ class DetailProviderService extends BaseService { this.providers_.push(fn); } + + /** + * Asynchronously retrieves details by invoking registered detail providers + * in sequence. Populates the provided output object with the results of + * each provider. If no output object is provided, a new one is created + * by default. + * + * @param {Object} context - The context object containing input data for + * the providers. + * @param {Object} [out={}] - An optional output object to populate with + * the details. + * @returns {Promise} The populated output object after all + * providers have been processed. + */ async get_details (context, out) { out = out || {}; diff --git a/src/backend/src/services/DevConsoleService.js b/src/backend/src/services/DevConsoleService.js index 93910ac6..9f24045b 100644 --- a/src/backend/src/services/DevConsoleService.js +++ b/src/backend/src/services/DevConsoleService.js @@ -1,3 +1,4 @@ +// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o-mini"}} /* * Copyright (C) 2024 Puter Technologies Inc. * @@ -19,11 +20,24 @@ const { consoleLogManager } = require('../util/consolelog'); const BaseService = require('./BaseService'); + +/** +* 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 = []; @@ -42,7 +56,18 @@ class DevConsoleService extends BaseService { } } + + /** + * 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(' ** ') @@ -66,6 +91,16 @@ class DevConsoleService extends BaseService { this.mark_updated(); } + + /** + * 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 = []; @@ -86,6 +121,7 @@ class DevConsoleService extends BaseService { 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; @@ -133,10 +169,26 @@ class DevConsoleService extends BaseService { 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} Resolves when the initialization completes. + */ async _init () { const services = this.services; // await services.ready; @@ -174,6 +226,17 @@ class DevConsoleService extends BaseService { // rl.prompt(); }); + + /** + * Handles the initialization of the DevConsole service, setting up + * command line interface and managing input/output operations. + * + * This method creates a readline interface for user input, processes + * commands, and manages the display of command output in the console. + * + * @async + * @returns {Promise} A promise that resolves when the initialization is complete. + */ this._before_cmd = () => { rl.pause(); rl.output.write('\x1b[1A\r'); @@ -185,6 +248,13 @@ class DevConsoleService extends BaseService { ); } + + /** + * _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` + @@ -193,6 +263,12 @@ class DevConsoleService extends BaseService { ); } + + /** + * 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'); @@ -203,6 +279,13 @@ class DevConsoleService extends BaseService { } } + + /** + * Prepares the console for output by performing necessary actions + * such as clearing previous lines and setting up the environment + * for rendering new output. This method is called before new + * data is written to the console. + */ this._post_write = () => { this.update_(); // Draw separator bar @@ -233,11 +316,22 @@ class DevConsoleService extends BaseService { } }; + + /** + * 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(); @@ -248,6 +342,15 @@ class DevConsoleService extends BaseService { consoleLogManager.decorate_all(({ replace }, ...args) => { this._pre_write(); }); + /** + * 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.post_all(() => { this._post_write(); }) @@ -266,6 +369,11 @@ class DevConsoleService extends BaseService { // This prevents the promptline background from staying // when Ctrl+C is used to terminate the server + /** + * Handles the SIGINT signal to gracefully terminate the server. + * This method ensures that the console output is reset and the process exits cleanly. + * It is triggered when the user presses Ctrl+C in the terminal. + */ rl.on('SIGINT', () => { process.stdout.write(`\x1b[0m\r`); process.exit(0); diff --git a/src/backend/src/services/DevTODService.js b/src/backend/src/services/DevTODService.js index 661cf198..d3a4f54a 100644 --- a/src/backend/src/services/DevTODService.js +++ b/src/backend/src/services/DevTODService.js @@ -1,3 +1,4 @@ +// METADATA // {"ai-commented":{"service":"claude"}} /* * Copyright (C) 2024 Puter Technologies Inc. * @@ -50,6 +51,13 @@ const tips = ( .filter((line) => line.length) ; + +/** +* Wraps text to specified width by breaking it into lines +* @param {string} text - The text to wrap +* @param {number} width - Maximum width of each line +* @returns {string[]} Array of wrapped text lines +*/ const wordwrap = (text, width) => { const lines = []; while ( text.length ) { @@ -59,11 +67,32 @@ const wordwrap = (text, width) => { return lines; }; + +/** +* @class DevTODService +* @extends BaseService +* @description Service that manages the "Tip of the Day" functionality in the development console. +* Displays random helpful tips about the system during startup and provides commands to manage +* the tip display. Inherits from BaseService and integrates with the dev-console and commands +* services to provide an interactive tip system for developers. +*/ class DevTODService extends BaseService { + /** + * DevTODService class - Manages "Tip of the Day" functionality for the developer console + * @extends BaseService + * @description Provides random development tips and console commands for managing tip display + * Integrates with the dev console to show helpful tips about source code and CLI usage + */ async _init () { const svc_commands = this.services.get('commands'); this._register_commands(svc_commands); } + /** + * Initializes the DevTODService by registering commands with the command service + * @private + * @async + * @returns {Promise} + */ async ['__on_boot.consolidation'] () { let random_tip = tips[Math.floor(Math.random() * tips.length)]; random_tip = wordwrap( @@ -71,6 +100,13 @@ class DevTODService extends BaseService { process.stdout.columns ? process.stdout.columns - 6 : 50 ); + /** + * Handles the boot consolidation phase for the Tip of the Day service + * Selects a random tip, wraps it to fit the console width, and creates + * a widget function to display the formatted tip with optional header/footer + * + * @returns {Promise} + */ this.tod_widget = () => { const lines = [ ...random_tip, diff --git a/src/backend/src/services/EmailService.js b/src/backend/src/services/EmailService.js index a11bd18e..b31fe63a 100644 --- a/src/backend/src/services/EmailService.js +++ b/src/backend/src/services/EmailService.js @@ -1,3 +1,4 @@ +// METADATA // {"ai-commented":{"service":"mistral","model":"mistral-large-latest"}} /* * Copyright (C) 2024 Puter Technologies Inc. * @@ -162,6 +163,15 @@ If this was not you, please contact support@puter.com immediately. }, } + +/** +* @class EmailService +* @extends BaseService +* @description The EmailService class handles the sending of emails using predefined templates. +* It utilizes the nodemailer library for sending emails and Handlebars for template rendering. +* The class includes methods for constructing and initializing the service, getting the email transport, +* and sending emails with provided templates and values. +*/ class Emailservice extends BaseService { static MODULES = { nodemailer: require('nodemailer'), @@ -169,6 +179,16 @@ class Emailservice extends BaseService { dedent: require('dedent'), }; + + /** + * Initializes the EmailService by compiling email templates. + * + * This method compiles the email templates using Handlebars and dedent + * to ensure that they are ready for use. It stores the compiled templates + * in an object for quick access. + * + * @returns {void} + */ _construct () { this.templates = TEMPLATES; @@ -189,9 +209,26 @@ class Emailservice extends BaseService { } } + + /** + * Initializes the email service. + * This method is called during the initialization phase of the service. + * It sets up any necessary configurations or resources needed for the service to function correctly. + * + * @returns {void} + */ _init () { } + + /** + * Configures and initializes the email transport using Nodemailer. + * + * This method sets up the email transport configuration based on the provided settings and + * returns a configured Nodemailer transport object. + * + * @returns {Object} The configured Nodemailer transport object. + */ get_transport_ () { const nodemailer = this.modules.nodemailer; @@ -203,6 +240,18 @@ class Emailservice extends BaseService { return transport; } + + /** + * Sends an email using the configured transport and template. + * + * This method constructs an email message by applying the provided values to the specified template, + * then sends the email using the configured transport. + * + * @param {Object} user - The user object containing the email address. + * @param {string} template - The template key to use for constructing the email. + * @param {Object} values - The values to apply to the template. + * @returns {Promise} - A promise that resolves when the email is sent. + */ async send_email (user, template, values) { const email = user.email; diff --git a/src/backend/src/services/EngPortalService.js b/src/backend/src/services/EngPortalService.js index ce507568..478e83c1 100644 --- a/src/backend/src/services/EngPortalService.js +++ b/src/backend/src/services/EngPortalService.js @@ -1,3 +1,4 @@ +// METADATA // {"ai-commented":{"service":"mistral","model":"mistral-large-latest"}} /* * Copyright (C) 2024 Puter Technologies Inc. * @@ -18,6 +19,17 @@ */ const { AdvancedBase } = require("@heyputer/putility"); + +/** +* @class EngPortalService +* @extends {AdvancedBase} +* +* EngPortalService is a class that provides services for managing and accessing various operations, alarms, and statistics +* within a system. It inherits from the AdvancedBase class and utilizes multiple dependencies such as socket.io for communication +* and uuidv4 for generating unique identifiers. The class includes methods for listing operations, serializing frames, listing alarms, +* fetching server statistics, and registering command handlers. This class is integral to maintaining and monitoring system health +* and operations efficiently. +*/ class EngPortalService extends AdvancedBase { static MODULES = { socketio: require('../socketio.js'), @@ -31,6 +43,15 @@ class EngPortalService extends AdvancedBase { this._registerCommands(this.commands); } + + /** + * Lists all ongoing operations. + * This method retrieves all ongoing operations from the 'operationTrace' service, + * serializes them, and returns the serialized list. + * + * @async + * @returns {Promise} A list of serialized operation frames. + */ async list_operations () { const svc_operationTrace = this.services.get('operationTrace'); const ls = []; @@ -68,6 +89,14 @@ class EngPortalService extends AdvancedBase { return out; } + + /** + * Retrieves a list of alarms. + * + * This method fetches all active alarms from the 'alarm' service and returns a serialized array of alarm objects. + * + * @returns {Promise} A promise that resolves to an array of serialized alarm objects. + */ async list_alarms () { const svc_alarm = this.services.get('alarm'); const ls = []; @@ -79,6 +108,15 @@ class EngPortalService extends AdvancedBase { return ls; } + + /** + * Gets the system statistics. + * + * This method retrieves the system statistics from the server-health service and returns them. + * + * @async + * @returns {Promise} A promise that resolves to the system statistics. + */ async get_stats () { const svc_health = this.services.get('server-health'); return await svc_health.get_stats(); diff --git a/src/backend/src/services/EntityStoreService.js b/src/backend/src/services/EntityStoreService.js index af732778..bb2720c4 100644 --- a/src/backend/src/services/EntityStoreService.js +++ b/src/backend/src/services/EntityStoreService.js @@ -1,3 +1,4 @@ +// METADATA // {"ai-commented":{"service":"xai"}} /* * Copyright (C) 2024 Puter Technologies Inc. * @@ -22,7 +23,23 @@ const { Null } = require("../om/query/query"); const { Context } = require("../util/context"); const BaseService = require("./BaseService"); + +/** +* EntityStoreService - A service class that manages entity-related operations in the backend of Puter. +* This class extends BaseService to provide methods for creating, reading, updating, selecting, +* upserting, and deleting entities. It interacts with an upstream data provider to perform these +* operations, ensuring consistency and providing context-aware functionality for entity management. +*/ class EntityStoreService extends BaseService { + /** + * Initializes the EntityStoreService with necessary entity and upstream configurations. + * + * @param {Object} args - The initialization arguments. + * @param {string} args.entity - The name of the entity to operate on. Required. + * @param {Object} args.upstream - The upstream service to handle operations. + * + * @throws {Error} If `args.entity` is not provided. + */ async _init (args) { if ( ! args.entity ) { throw new Error('EntityStoreService requires an entity name'); @@ -30,6 +47,21 @@ class EntityStoreService extends BaseService { this.upstream = args.upstream; + + /** + * Initializes the EntityStoreService with the provided arguments. + * + * @param {Object} args - Initialization arguments. + * @param {string} args.entity - The name of the entity this service will manage. + * If not provided, an error is thrown. + * @param {Object} args.upstream - The upstream service or data source. + * + * @throws {Error} If the entity name is not provided in the arguments. + * + * @returns {Promise} A promise that resolves when initialization is complete. + * + * @note This method sets up the context for the entity operations and provides it to the upstream service. + */ const context = Context.get().sub({ services: this.services }); const om = this.services.get('registry').get('om:mapping').get(args.entity); this.om = om; @@ -41,12 +73,33 @@ class EntityStoreService extends BaseService { } // TODO: can replace these with MethodProxyFeature + /** + * Retrieves an entity by its unique identifier. + * + * @param {string} uid - The unique identifier of the entity to read. + * @returns {Promise} The entity object if found, otherwise null or throws an error. + * @throws {APIError} If the entity with the given uid does not exist. + */ async create (entity, options) { return await this.upstream.upsert(entity, { old_entity: null, options }); } + /** + * Reads an entity from the upstream data store using its unique identifier. + * + * @param {string} uid - The unique identifier of the entity to read. + * @returns {Promise} A promise that resolves to the entity object if found. + * @throws {APIError} If the entity with the given `uid` does not exist. + */ async read (uid) { return await this.upstream.read(uid); } + /** + * Retrieves an entity by its unique identifier (UID). + * + * @param {string} uid - The unique identifier of the entity to retrieve. + * @returns {Promise} The entity associated with the given UID. + * @throws {Error} If the entity cannot be found or an error occurs during retrieval. + */ async select ({ predicate, ...rest }) { if ( ! predicate ) predicate = []; if ( Array.isArray(predicate) ) { @@ -56,6 +109,19 @@ class EntityStoreService extends BaseService { if ( ! predicate) predicate = new Null(); return await this.upstream.select({ predicate, ...rest }); } + /** + * Retrieves entities matching a given predicate. + * + * This method performs a selection query on the upstream data source. + * If no predicate is provided, it defaults to selecting all entities. + * + * @param {Object} options - The selection options. + * @param {Array|Function} options.predicate - The predicate for filtering entities. + * If an array, it's expected to be in the format [operator, ...args]. + * If not provided, it defaults to a Null predicate, effectively selecting all entities. + * @param {Object} [options.rest] - Additional options for the selection query. + * @returns {Promise} A promise that resolves to an array of entities matching the predicate. + */ async update (entity, id, options) { let old_entity = await this.read( await entity.get(this.om.primary_identifier)); @@ -86,6 +152,21 @@ class EntityStoreService extends BaseService { return await this.upstream.upsert(entity, { old_entity, options }); } + /** + * Updates an existing entity in the store. + * + * @param {Object} entity - The entity to update with new values. + * @param {string|number} id - The identifier of the entity to update. Can be a string or number. + * @param {Object} options - Additional options for the update operation. + * @returns {Promise} The updated entity after the operation. + * @throws {APIError} If the entity to be updated is not found. + * + * @note This method first attempts to fetch the entity by its primary identifier. If not found, + * it uses `IdentifierUtil` to detect and fetch by other identifiers if provided. + * If the entity still isn't found, an error is thrown. The method ensures that the + * entity's primary identifier is updated to match the existing entity before performing + * the actual update through `this.upstream.upsert`. + */ async upsert (entity, id, options) { let old_entity = await this.read( await entity.get(this.om.primary_identifier)); @@ -112,6 +193,18 @@ class EntityStoreService extends BaseService { return await this.upstream.upsert(entity, { old_entity, options }); } + /** + * Deletes an entity from the store. + * + * @param {string} uid - The unique identifier of the entity to delete. + * @returns {Promise} A promise that resolves when the entity is deleted. + * @throws {APIError} If the entity with the given `uid` is not found. + * + * This method first attempts to read the entity with the given `uid`. If the entity + * does not exist, it throws an `APIError` with the message 'entity_not_found'. + * If the entity exists, it calls the upstream service to delete the entity, + * passing along the old entity data for reference. + */ async delete (uid) { const old_entity = await this.read(uid); if ( ! old_entity ) { diff --git a/src/backend/src/services/EventService.js b/src/backend/src/services/EventService.js index 89417ed5..bf705bf1 100644 --- a/src/backend/src/services/EventService.js +++ b/src/backend/src/services/EventService.js @@ -1,3 +1,4 @@ +// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o-mini"}} /* * Copyright (C) 2024 Puter Technologies Inc. * @@ -19,6 +20,13 @@ const { Context } = require("../util/context"); const BaseService = require("./BaseService"); + +/** +* EventService class extends BaseService to provide a mechanism for +* emitting and listening to events within the application. It manages +* event listeners scoped to specific keys and allows global listeners +* for broader event handling. +*/ class ScopedEventBus { constructor (event_bus, scope) { this.event_bus = event_bus; @@ -34,7 +42,22 @@ class ScopedEventBus { } } + +/** +* Class representing the EventService, which extends the BaseService. +* This service is responsible for managing event listeners and emitting +* events within a scoped context, allowing for flexible event handling +* and decoupled communication between different parts of the application. +*/ class EventService extends BaseService { + /** + * Initializes listeners and global listeners for the EventService. + * This method is called to set up the internal data structures needed + * for managing event listeners upon construction of the service. + * + * @async + * @returns {Promise} A promise that resolves when the initialization is complete. + */ async _construct () { this.listeners_ = {}; this.global_listeners_ = []; @@ -56,6 +79,17 @@ class EventService extends BaseService { // IIAFE wrapper to catch errors without blocking // event dispatch. + /** + * IIAFE wrapper to handle emitting events asynchronously while catching errors. + * This method ensures that any errors thrown in the event listeners do not block + * the dispatching of other events. + * + * @param {string} key - The event key to emit. + * @param {any} data - The data to be sent with the event. + * @param {Object} [meta={}] - Additional metadata for the event. + * + * @returns {void} + */ Context.arun(async () => { try { await callback(key, data, meta); @@ -73,6 +107,16 @@ class EventService extends BaseService { for ( const callback of this.global_listeners_ ) { // IIAFE wrapper to catch errors without blocking // event dispatch. + /** + * Invokes all registered global listeners for an event with the provided key, data, and meta + * information. Each callback is executed within a context that handles errors gracefully, + * ensuring that one failing listener does not disrupt subsequent invocations. + * + * @param {string} key - The event key to emit. + * @param {*} data - The data to be passed to the listeners. + * @param {Object} [meta={}] - Optional metadata related to the event. + * @returns {void} + */ Context.arun(async () => { try { await callback(key, data, meta); @@ -95,6 +139,17 @@ class EventService extends BaseService { listeners.push(callback); const det = { + /** + * Registers a callback function for the specified event selector. + * + * This method will push the provided callback onto the list of listeners + * for the event specified by the selector. It returns an object containing + * a detach method, which can be used to remove the listener. + * + * @param {string} selector - The event selector to listen for. + * @param {Function} callback - The function to be invoked when the event is emitted. + * @returns {Object} An object with a detach method to unsubscribe the listener. + */ detach: () => { const idx = listeners.indexOf(callback); if ( idx !== -1 ) { diff --git a/src/backend/src/services/FeatureFlagService.js b/src/backend/src/services/FeatureFlagService.js index 65337fab..7b9a797f 100644 --- a/src/backend/src/services/FeatureFlagService.js +++ b/src/backend/src/services/FeatureFlagService.js @@ -1,3 +1,4 @@ +// METADATA // {"ai-commented":{"service":"claude"}} const { Context } = require("../util/context"); const { whatis } = require("../util/langutil"); const { PermissionUtil } = require("./auth/PermissionService"); @@ -7,13 +8,31 @@ const BaseService = require("./BaseService"); * FeatureFlagService is a way to let the client (frontend) know what features * are enabled or disabled for the current user. */ +/** +* @class FeatureFlagService +* @extends BaseService +* @description A service that manages feature flags to control feature availability across the application. +* Provides methods to register, check, and retrieve feature flags based on user permissions and configurations. +* Integrates with the permission system to determine feature access for different users. +* Supports both static configuration flags and dynamic function-based feature flags. +*/ class FeatureFlagService extends BaseService { + /** + * Initializes the FeatureFlagService instance by setting up an empty Map for known flags + * @private + * @method + */ _construct () { this.known_flags = new Map(); } register (name, spec) { this.known_flags.set(name, spec); } + /** + * Registers a new feature flag with the service + * @param {string} name - The name/identifier of the feature flag + * @param {Object|boolean} spec - The specification for the flag. Can be a boolean value or an object with $ property indicating flag type + */ async _init () { const svc_detailProvider = this.services.get('whoami'); svc_detailProvider.register_provider(async (context, out) => { @@ -21,9 +40,25 @@ class FeatureFlagService extends BaseService { out.feature_flags = await this.get_summary(context.actor); }); } + /** + * Initializes the feature flag service by registering a provider with the whoami service. + * This provider adds feature flag information to user details when requested. + * + * @async + * @private + * @returns {Promise} + */ async check (...a) { // allows binding call with multiple options objects; // the last argument is the permission to check + /** + * Checks if a feature flag is enabled for the given context + * @param {...Object} options - Configuration options objects that will be merged + * @param {string} permission - The feature flag name to check + * @returns {Promise} Whether the feature flag is enabled + * @description Processes multiple option objects and a permission string to determine + * if a feature flag is enabled. Handles config flags, function flags, and permission-based flags. + */ const { options, value: permission } = (() => { let value; const options = {}; @@ -63,6 +98,15 @@ class FeatureFlagService extends BaseService { return true; } + + /** + * Gets a summary of all feature flags for a given actor + * @param {Object} actor - The actor to check feature flags for + * @returns {Promise} Object mapping feature flag names to their values: + * - For config flags: returns the configured value + * - For function flags: returns result of calling the flag function + * - For permission flags: returns true if actor has any matching permissions, false otherwise + */ async get_summary (actor) { const summary = {}; for ( const [key, value] of this.known_flags.entries() ) { diff --git a/src/backend/src/services/FilesystemAPIService.js b/src/backend/src/services/FilesystemAPIService.js index b8872e52..703df36c 100644 --- a/src/backend/src/services/FilesystemAPIService.js +++ b/src/backend/src/services/FilesystemAPIService.js @@ -1,3 +1,4 @@ +// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o-mini"}} /* * Copyright (C) 2024 Puter Technologies Inc. * @@ -18,7 +19,27 @@ */ const BaseService = require("./BaseService"); + +/** +* @class FilesystemAPIService +* @extends BaseService +* @description This service handles all filesystem-related API routes, +* allowing for operations like file creation, deletion, +* reading, and searching through a structured set of +* endpoints. It integrates with the web server to expose +* these functionalities for client use. +*/ class FilesystemAPIService extends BaseService { + /** + * Sets up the route handlers for the Filesystem API. + * This method registers various endpoints related to filesystem operations + * such as creating, deleting, reading, and updating files. It uses the + * web server's app instance to attach the corresponding routers. + * + * @async + * @function __on_install.routes + * @returns {Promise} A promise that resolves when the routes are set up. + */ async ['__on_install.routes'] () { const { app } = this.services.get('web-server'); diff --git a/src/backend/src/services/GetUserService.js b/src/backend/src/services/GetUserService.js index 9c3d2510..d748f2f7 100644 --- a/src/backend/src/services/GetUserService.js +++ b/src/backend/src/services/GetUserService.js @@ -1,3 +1,4 @@ +// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o"}} /* * Copyright (C) 2024 Puter Technologies Inc. * @@ -31,7 +32,17 @@ const { DB_READ } = require("./database/consts"); * * The original `get_user` function now uses this service. */ +/** +* Class representing a service to retrieve user information by various identifying properties. +* The GetUserService provides an interface for accessing user data while facilitating caching +* mechanisms to optimize database interactions, allowing for read operations against different +* identifiers such as username, email, UUID, and referral codes. +*/ class GetUserService extends BaseService { + /** + * Constructor for GetUserService. + * Initializes the set of identifying properties used to retrieve user data. + */ _construct () { this.id_properties = new Set(); @@ -41,8 +52,19 @@ class GetUserService extends BaseService { this.id_properties.add('email'); this.id_properties.add('referral_code'); } + /** + * Initializes the GetUserService instance, setting up the + * identifying properties used for user retrieval. + */ async _init () { } + /** + * Initializes the GetUserService instance. + * This method prepares any necessary internal structures or states. + * It is called automatically upon instantiation of the service. + * + * @returns {Promise} A promise that resolves when the initialization is complete. + */ async get_user (options) { const user = await this.get_user_(options); if ( ! user ) return null; @@ -51,6 +73,19 @@ class GetUserService extends BaseService { await svc_whoami.get_details({ user }, user); return user; } + /** + * Retrieves a user object based on the provided options. + * + * This method queries the user from cache or database, + * depending on the caching options provided. If the user + * is found, it also calls the 'whoami' service to enrich + * the user details before returning. + * + * @param {Object} options - The options for retrieving the user. + * @param {boolean} [options.cached=true] - Indicates if caching should be used. + * @param {boolean} [options.force=false] - Forces a read from the database regardless of cache. + * @returns {Promise} The user object if found, else null. + */ async get_user_ (options) { const services = this.services; diff --git a/src/backend/src/services/HelloWorldService.js b/src/backend/src/services/HelloWorldService.js index aa233ca0..b4e1f4d0 100644 --- a/src/backend/src/services/HelloWorldService.js +++ b/src/backend/src/services/HelloWorldService.js @@ -1,13 +1,43 @@ +// METADATA // {"ai-commented":{"service":"mistral","model":"mistral-large-latest"}} const BaseService = require("./BaseService"); + +/** +* @class HelloWorldService +* @extends BaseService +* @description This class extends the BaseService and provides methods to get the version +* of the service and to generate a greeting message. The greeting message can be personalized +* based on the input subject. +*/ class HelloWorldService extends BaseService { static IMPLEMENTS = { ['version']: { + /** + * Returns the current version of the service. + * + * @returns {string} The version string. + */ get_version () { return 'v1.0.0'; } }, ['hello-world']: { + /** + * Greets the user with a customizable message. + * + * @param {Object} options - The options object. + * @param {string} [options.subject] - The subject of the greeting. If not provided, defaults to "World". + * @returns {string} The greeting message. + */ + + ```javascript + 11: async greet ({ subject }) { + 12: if ( subject ) { + 13: return `Hello, ${subject}!`; + 14: } + 15: return `Hello, World!`; + 16: } + 17: }, async greet ({ subject }) { if ( subject ) { return `Hello, ${subject}!`; diff --git a/src/backend/src/services/HostDiskUsageService.js b/src/backend/src/services/HostDiskUsageService.js index fa2c50e8..a564efa0 100644 --- a/src/backend/src/services/HostDiskUsageService.js +++ b/src/backend/src/services/HostDiskUsageService.js @@ -1,3 +1,4 @@ +// METADATA // {"ai-commented":{"service":"xai"}} /* * Copyright (C) 2024 Puter Technologies Inc. * @@ -20,6 +21,16 @@ const { BaseService } = require("../../exports"); const { execSync } = require('child_process'); const config = require("../config"); + +/** +* The HostDiskUsageService class extends BaseService to provide functionality for monitoring +* and reporting disk usage on the host system. This service identifies the mount point or drive +* where the current process is running, and performs disk usage checks for that specific location. +* It supports different operating systems like macOS and Linux, with placeholders for future +* Windows support. +* +* @extends BaseService +*/ class HostDiskUsageService extends BaseService { static DESCRIPTION = ` This service is responsible for identifying the mountpoint/drive @@ -27,6 +38,18 @@ class HostDiskUsageService extends BaseService { disk usage of that mountpoint/drive. `; + + /** + * Initializes the service by determining the disk usage of the mountpoint/drive + * where the current working directory resides. + * + * @async + * @function + * @memberof HostDiskUsageService + * @instance + * @returns {Promise} A promise that resolves when initialization is complete. + * @throws {Error} If unable to determine disk usage for the platform. + */ async _init() { const current_platform = process.platform; @@ -49,6 +72,17 @@ class HostDiskUsageService extends BaseService { } // TODO: TTL cache this value + /** + * Retrieves the current disk usage for the host system. + * + * This method checks the disk usage of the mountpoint or drive + * where the current process is running, based on the operating system. + * + * @returns {number} The amount of disk space used in bytes. + * + * @note This method does not cache its results and should be optimized + * with a TTL cache to prevent excessive system calls. + */ get_host_usage () { const current_platform = process.platform; @@ -67,6 +101,13 @@ class HostDiskUsageService extends BaseService { } // Called by the /df endpoint + /** + * Retrieves extra disk usage information for the host. + * This method is used by the /df endpoint to gather + * additional statistics on host disk usage. + * + * @returns {Object} An object containing the host's disk usage data. + */ get_extra () { return { host_used: this.get_host_usage(), diff --git a/src/backend/src/services/KernelInfoService.js b/src/backend/src/services/KernelInfoService.js index e8807f21..67b3fcd1 100644 --- a/src/backend/src/services/KernelInfoService.js +++ b/src/backend/src/services/KernelInfoService.js @@ -1,18 +1,42 @@ +// METADATA // {"ai-commented":{"service":"claude"}} const configurable_auth = require("../middleware/configurable_auth"); const { Context } = require("../util/context"); const { Endpoint } = require("../util/expressutil"); const BaseService = require("./BaseService"); const { Interface } = require("./drivers/meta/Construct"); +// Permission flag that grants access to view all services in the kernel info system const PERM_SEE_ALL = 'kernel-info:see-all-services'; +// Permission flag that grants access to view all services in the kernel info system const PERM_SEE_DRIVERS = 'kernel-info:see-all-drivers'; + +/** +* KernelInfoService class provides information about the kernel's services, modules, and interfaces. +* It handles listing available modules, services, and their implementations based on user permissions. +* The service exposes endpoints for querying kernel module information and manages access control +* through permission checks for viewing all services and drivers. +* @extends BaseService +*/ class KernelInfoService extends BaseService { + /** + * Service for providing kernel and service information + * Extends BaseService to provide system-level information about services, interfaces and drivers + * Handles permissions and access control for viewing service information + * Exposes endpoints for listing modules and service information + */ async _init () { // } ['__on_install.routes'] (_, { app }) { + /** + * Installs routes for the kernel info service + * @param {*} _ Unused parameter + * @param {Object} param1 Object containing Express app instance + * @param {Express} param1.app Express application instance + * @private + */ const router = (() => { const require = this.require; const express = require('express'); diff --git a/src/backend/src/services/LocalDiskStorageService.js b/src/backend/src/services/LocalDiskStorageService.js index 5b921383..464b64fa 100644 --- a/src/backend/src/services/LocalDiskStorageService.js +++ b/src/backend/src/services/LocalDiskStorageService.js @@ -1,3 +1,4 @@ +// METADATA // {"ai-commented":{"service":"mistral","model":"mistral-large-latest"}} /* * Copyright (C) 2024 Puter Technologies Inc. * @@ -21,12 +22,30 @@ const { TeePromise } = require("../util/promise"); const { progress_stream, size_limit_stream } = require("../util/streamutil"); const BaseService = require("./BaseService"); + +/** +* @class LocalDiskStorageService +* @extends BaseService +* +* The LocalDiskStorageService class is responsible for managing local disk storage. +* It provides methods for storing, retrieving, and managing files on the local disk. +* This service extends the BaseService class to inherit common service functionalities. +*/ class LocalDiskStorageService extends BaseService { static MODULES = { fs: require('fs'), path: require('path'), } + + /** + * Initializes the context for the storage service. + * + * This method registers the LocalDiskStorageStrategy with the context + * initialization service and sets the storage for the mountpoint service. + * + * @returns {Promise} A promise that resolves when the context is initialized. + */ async ['__on_install.context-initializers'] () { const svc_contextInit = this.services.get('context-init'); const storage = new LocalDiskStorageStrategy({ services: this.services }); @@ -36,6 +55,14 @@ class LocalDiskStorageService extends BaseService { svc_mountpoint.set_storage(storage); } + + /** + * Initializes the local disk storage service. + * + * This method sets up the storage directory and ensures it exists. + * + * @returns {Promise} A promise that resolves when the initialization is complete. + */ async _init () { const require = this.require; const path_ = require('path'); @@ -53,6 +80,22 @@ class LocalDiskStorageService extends BaseService { return path.join(this.path, key); } + + /** + * Stores a stream to local disk storage. + * + * This method takes a stream and stores it on the local disk under the specified key. + * It also supports progress tracking and size limiting. + * + * @async + * @function store_stream + * @param {Object} options - The options object. + * @param {string} options.key - The key under which the stream will be stored. + * @param {number} options.size - The size of the stream. + * @param {stream.Readable} options.stream - The readable stream to be stored. + * @param {Function} [options.on_progress] - The callback function to track progress. + * @returns {Promise} A promise that resolves when the stream is fully stored. + */ async store_stream ({ key, size, stream, on_progress }) { const require = this.require; const fs = require('fs'); @@ -78,6 +121,17 @@ class LocalDiskStorageService extends BaseService { return await writePromise; } + + /** + * Stores a buffer to the local disk. + * + * This method writes a given buffer to a file on the local disk, identified by a key. + * + * @param {Object} params - The parameters object. + * @param {string} params.key - The key used to identify the file. + * @param {Buffer} params.buffer - The buffer containing the data to be stored. + * @returns {Promise} A promise that resolves when the buffer is successfully stored. + */ async store_buffer ({ key, buffer }) { const require = this.require; const fs = require('fs'); @@ -86,6 +140,14 @@ class LocalDiskStorageService extends BaseService { await fs.promises.writeFile(path, buffer); } + + /** + * Creates a read stream for a given key. + * + * @param {Object} options - The options object. + * @param {string} options.key - The key for which to create the read stream. + * @returns {stream.Readable} The read stream for the given key. + */ async create_read_stream ({ key }) { const require = this.require; const fs = require('fs'); @@ -94,6 +156,15 @@ class LocalDiskStorageService extends BaseService { return fs.createReadStream(path); } + + /** + * Copies a file from one key to another within the local disk storage. + * + * @param {Object} params - The parameters for the copy operation. + * @param {string} params.src_key - The source key of the file to be copied. + * @param {string} params.dst_key - The destination key where the file will be copied. + * @returns {Promise} A promise that resolves when the file is successfully copied. + */ async copy ({ src_key, dst_key }) { const require = this.require; const fs = require('fs'); @@ -104,6 +175,16 @@ class LocalDiskStorageService extends BaseService { await fs.promises.copyFile(src_path, dst_path); } + + /** + * Deletes a file from the local disk storage. + * + * This method removes the file associated with the given key from the storage. + * + * @param {Object} params - The parameters for the delete operation. + * @param {string} params.key - The key of the file to be deleted. + * @returns {Promise} - A promise that resolves when the file is successfully deleted. + */ async delete ({ key }) { const require = this.require; const fs = require('fs'); diff --git a/src/backend/src/services/LockService.js b/src/backend/src/services/LockService.js index f9eb78a6..2a3c3fce 100644 --- a/src/backend/src/services/LockService.js +++ b/src/backend/src/services/LockService.js @@ -1,3 +1,4 @@ +// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o"}} /* * Copyright (C) 2024 Puter Technologies Inc. * @@ -25,10 +26,31 @@ const BaseService = require("./BaseService"); * * This serivces uses RWLock but always locks in write mode. */ +/** +* Represents the LockService class responsible for managing locks +* using reader-writer locks (RWLock). This service ensures that +* critical sections are properly handled by enforcing write locks +* exclusively, enabling safe concurrent access to shared resources +* while preventing race conditions and ensuring data integrity. +*/ class LockService extends BaseService { + /** + * Initializes the LockService by setting up the locks object + * and registering the 'lock' commands. This method is called + * during the service initialization phase. + */ async _construct () { this.locks = {}; } + /** + * Initializes the locks object to store lock instances. + * + * This method is called during the construction of the LockService + * instance to ensure that the locks property is ready for use. + * + * @returns {Promise} A promise that resolves when the + * initialization is complete. + */ async _init () { const svc_commands = this.services.get('commands'); svc_commands.registerCommands('lock', [ @@ -66,6 +88,17 @@ class LockService extends BaseService { ]); } + + /** + * Acquires a lock for the specified name, allowing for a callback to be executed while the lock is held. + * If the name is an array, all locks will be acquired in sequence. The method supports optional + * configurations, including a timeout feature. It returns the result of the callback execution. + * + * @param {string|string[]} name - The name(s) of the lock(s) to acquire. + * @param {Object} [opt_options] - Optional configuration options. + * @param {function} callback - The function to call while the lock is held. + * @returns {Promise} The result of the callback. + */ async lock (name, opt_options, callback) { if ( typeof opt_options === 'function' ) { callback = opt_options; @@ -78,6 +111,17 @@ class LockService extends BaseService { // TODO: verbose log option by service // console.log('LOCKING NAMES', names) const section = names.reduce((current_callback, name) => { + /** + * Acquires a lock for the specified name or names. + * + * If the name is an array, it locks each specified name in sequence. + * The method ensures that all specified locks are acquired before executing the callback. + * + * @param {string|string[]} name - The name(s) of the lock(s) to acquire. + * @param {Object} [opt_options={}] - Optional configuration for the lock operation. + * @param {Function} callback - Function to be executed once the lock is acquired. + * @returns {Promise} Resolves when the callback execution is complete. + */ return async () => { return await this.lock(name, opt_options, current_callback); }; @@ -98,6 +142,17 @@ class LockService extends BaseService { let timeout, timed_out; if ( opt_options.timeout ) { + /** + * Attempts to acquire a write lock on the specified name, executes the provided callback, + * and ensures the lock is released afterward. Supports options for timeout and handles + * multiple locks if the name parameter is an array. + * + * @param {string|string[]} name - The lock name(s) to acquire. + * @param {Object} [opt_options={}] - Optional configuration for the lock. + * @param {function} callback - The function to execute while holding the lock. + * @returns {Promise} The result of the callback execution. + * @throws {Error} If the lock acquisition times out. + */ timeout = setTimeout(() => { handle.unlock(); // TODO: verbose log option by service diff --git a/src/backend/src/services/MakeProdDebuggingLessAwfulService.js b/src/backend/src/services/MakeProdDebuggingLessAwfulService.js index 04295868..65e3b544 100644 --- a/src/backend/src/services/MakeProdDebuggingLessAwfulService.js +++ b/src/backend/src/services/MakeProdDebuggingLessAwfulService.js @@ -1,3 +1,4 @@ +// METADATA // {"ai-commented":{"service":"claude"}} /* * Copyright (C) 2024 Puter Technologies Inc. * @@ -27,23 +28,58 @@ const { stringify_log_entry } = require("./runtime-analysis/LogService"); * Consequentially, the value of X-PUTER-DEBUG will included in all * log messages produced by the request. */ +/** +* @class MakeProdDebuggingLessAwfulService +* @extends BaseService +* @description Service that improves production debugging by capturing and processing debug headers. +* Extends the base service to provide middleware functionality that captures the X-PUTER-DEBUG header +* value and incorporates it into the request's Context object. This enables detailed logging and +* debugging capabilities in production environments while maintaining system security. The service +* also handles the creation and management of debug-specific log files for better traceability. +*/ class MakeProdDebuggingLessAwfulService extends BaseService { static MODULES = { fs: require('fs'), } + /** + * Inner class that defines the modules required by the MakeProdDebuggingLessAwfulService. + * Currently includes the file system (fs) module for writing debug logs to files. + * @static + * @memberof MakeProdDebuggingLessAwfulService + */ static ProdDebuggingMiddleware = class ProdDebuggingMiddleware { + /** + * Middleware class that handles production debugging functionality + * by capturing and processing the X-PUTER-DEBUG header value. + * + * This middleware extracts the debug header value and makes it + * available through the Context for logging and debugging purposes. + */ constructor () { this.header_name_ = 'x-puter-debug'; } install (app) { app.use(this.run.bind(this)); } + /** + * Installs the middleware into the Express application + * + * @param {Express} app - The Express application instance + * @returns {void} + */ async run (req, res, next) { const x = Context.get(); x.set('prod-debug', req.headers[this.header_name_]); next(); } } + /** + * Handles the actual middleware execution for production debugging + * @param {Object} req - Express request object containing headers + * @param {Object} res - Express response object + * @param {Function} next - Express next middleware function + * @returns {Promise} + */ async _init () { // Initialize express middleware this.mw = new this.constructor.ProdDebuggingMiddleware(); @@ -81,6 +117,13 @@ class MakeProdDebuggingLessAwfulService extends BaseService { }; }); } + /** + * Handles installation of the context-aware middleware for production debugging + * @param {*} _ Unused parameter + * @param {Object} options Installation options + * @param {Express} options.app Express application instance + * @returns {Promise} + */ async ['__on_install.middlewares.context-aware'] (_, { app }) { // Add express middleware this.mw.install(app); diff --git a/src/backend/src/services/MountpointService.js b/src/backend/src/services/MountpointService.js index 2b49426d..82fb9d55 100644 --- a/src/backend/src/services/MountpointService.js +++ b/src/backend/src/services/MountpointService.js @@ -1,3 +1,4 @@ +// METADATA // {"ai-commented":{"service":"claude"}} /* * Copyright (C) 2024 Puter Technologies Inc. * @@ -28,7 +29,22 @@ const BaseService = require("./BaseService"); * in situations where ContextInitService isn't able to * initialize a context. */ +/** +* @class MountpointService +* @extends BaseService +* @description Service class responsible for managing storage backends for mountpoints. +* Currently provides a temporary solution for accessing storage backend when context +* initialization is not possible. Will be expanded to handle multiple mountpoints +* and their associated storage backends in future implementations. +*/ class MountpointService extends BaseService { + /** + * Initializes the MountpointService instance + * Sets up initial state with null storage backend + * @private + * @async + * @returns {Promise} + */ async _init () { // this.mountpoints_ = {}; @@ -40,6 +56,10 @@ class MountpointService extends BaseService { set_storage (storage) { this.storage_ = storage; } + /** + * Gets the current storage backend instance + * @returns {Object} The storage backend instance + */ get_storage () { return this.storage_; } diff --git a/src/backend/src/services/NotificationService.js b/src/backend/src/services/NotificationService.js index 3be1d189..76edcb2b 100644 --- a/src/backend/src/services/NotificationService.js +++ b/src/backend/src/services/NotificationService.js @@ -1,3 +1,4 @@ +// METADATA // {"ai-commented":{"service":"mistral","model":"mistral-large-latest"}} /* * Copyright (C) 2024 Puter Technologies Inc. * @@ -33,16 +34,62 @@ const UserIDNotifSelector = user_id => async (self) => { return [user_id]; }; + +/** +* @class NotificationService +* @extends BaseService +* +* The NotificationService class is responsible for managing notifications within the application. +* It handles creating, storing, and sending notifications to users, as well as updating the status of notifications +* (e.g., marking them as read or acknowledged). +* +* @property {Object} MODULES - Static object containing modules used by the service, such as uuidv4 and express. +* @property {Object} merged_on_user_connected_ - Object to track connected users and manage delayed actions. +* @property {Object} notifs_pending_write - Object to track pending write operations for notifications. +* +* @method _construct - Initializes the service's internal state. +* @method _init - Initializes the service, setting up database connections and event listeners. +* @method __on_install.routes - Registers API routes for notification-related endpoints. +* @method on_user_connected - Handles actions when a user connects to the application. +* @method do_on_user_connected - Queries and updates unread notifications for a connected user. +* @method on_sent_to_user - Updates the status of a notification when it is sent to a user. +* @method notify - Sends a notification to a list of users and persists it in the database. +* +* @example +* const notificationService = new NotificationService(); +* notificationService.notify(UsernameNotifSelector('user123'), { +* source: 'notification-testing', +* icon_source: 'builtin', +* icon: 'logo.svg', +* title: 'Test Notification', +* text: 'This is a test notification.' +* }); +*/ class NotificationService extends BaseService { static MODULES = { uuidv4: require('uuid').v4, express: require('express'), } + + /** + * Initializes the NotificationService instance. + * This method sets up the initial state of the service, including any necessary + * data structures or configurations. + * + * @private + */ _construct () { this.merged_on_user_connected_ = {}; } + + /** + * Initializes the NotificationService by setting up necessary services, + * registering event listeners, and preparing the database connection. + * This method is called once during the service's lifecycle. + * @returns {Promise} A promise that resolves when initialization is complete. + */ async _init () { const svc_database = this.services.get('database'); this.db = svc_database.get(DB_WRITE, 'notification'); @@ -116,13 +163,44 @@ class NotificationService extends BaseService { }); } + + /** + * Handles the event when a user connects. + * + * This method checks if there is a timeout set for the user's connection event and clears it if it exists. + * If not, it sets a timeout to call `do_on_user_connected` after 2000 milliseconds. + * + * @param {object} params - The parameters object containing user data. + * @param {object} params.user - The user object with a `uuid` property. + * + * @returns {void} + */ async on_user_connected ({ user }) { if ( this.merged_on_user_connected_[user.uuid] ) { clearTimeout(this.merged_on_user_connected_[user.uuid]); } this.merged_on_user_connected_[user.uuid] = + /** + * Schedules the `do_on_user_connected` method to be called after a delay. + * + * This method sets a timer to call `do_on_user_connected` after 2000 milliseconds. + * If a timer already exists for the user, it clears the existing timer before setting a new one. + * + * @param {Object} params - The parameters object. + * @param {Object} params.user - The user object containing the user's UUID. + * @returns {Promise} A promise that resolves when the timer is set. + */ setTimeout(() => this.do_on_user_connected({ user }), 2000); } + /** + * Handles the event when a user connects. + * Sets a timeout to delay the execution of the `do_on_user_connected` method by 2 seconds. + * This helps in merging multiple events that occur in a short period. + * + * @param {Object} obj - The event object containing user information. + * @param {Object} obj.user - The user object with a `uuid` property. + * @async + */ async do_on_user_connected ({ user }) { // query the users unread notifications const notifications = await this.db.read( @@ -144,6 +222,14 @@ class NotificationService extends BaseService { for ( const n of notifications ) { n.value = this.db.case({ mysql: () => n.value, + /** + * Adjusts the value of a notification based on the database type. + * + * This method modifies the value of a notification to be JSON parsed + * if the database is not MySQL. + * + * @returns {Object} The adjusted notification value. + */ otherwise: () => JSON.parse(n.value ?? '{}'), })(); } @@ -166,6 +252,20 @@ class NotificationService extends BaseService { }); } + + /** + * Handles the action when a notification is sent to a user. + * + * This method is triggered when a notification is sent to a user, + * updating the notification's status to 'shown' in the database. + * It logs the user ID and response, updates the 'shown' timestamp, + * and ensures the notification is written to the database. + * + * @param {Object} params - The parameters containing the user ID and response. + * @param {number} params.user_id - The ID of the user receiving the notification. + * @param {Object} params.response - The response object containing the notification details. + * @param {string} params.response.uid - The unique identifier of the notification. + */ async on_sent_to_user ({ user_id, response }) { console.log('GOT IT AND IT WORKED!!!', user_id, response); const shown_ts = Math.floor(Date.now() / 1000); @@ -180,6 +280,17 @@ class NotificationService extends BaseService { ])); } + + /** + * Sends a notification to specified users. + * + * This method sends a notification to a list of users determined by the provided selector. + * It generates a unique identifier for the notification, emits an event to notify the GUI, + * and inserts the notification into the database. + * + * @param {Function} selector - A function that takes the service instance and returns a list of user IDs. + * @param {Object} notification - The notification details to be sent. + */ async notify (selector, notification) { const uid = this.modules.uuidv4(); const svc_event = this.services.get('event'); diff --git a/src/backend/src/services/OperationTraceService.js b/src/backend/src/services/OperationTraceService.js index 7ade53f4..1260ac66 100644 --- a/src/backend/src/services/OperationTraceService.js +++ b/src/backend/src/services/OperationTraceService.js @@ -1,3 +1,4 @@ +// METADATA // {"ai-commented":{"service":"mistral","model":"mistral-large-latest"}} /* * Copyright (C) 2024 Puter Technologies Inc. * @@ -23,8 +24,15 @@ const { OtelFeature } = require("../traits/OtelFeature"); const APIError = require("../api/APIError"); const { AssignableMethodsFeature } = require("../traits/AssignableMethodsFeature"); +// CONTEXT_KEY is used to create a unique context key for operation tracing +// and is utilized throughout the OperationTraceService to manage frames. const CONTEXT_KEY = Context.make_context_key('operation-trace'); + +/** +* @class OperationFrame +* @description The `OperationFrame` class represents a frame within an operation trace. It is designed to manage the state, attributes, and hierarchy of frames within an operational context. This class provides methods to set status, calculate effective status, add tags, attributes, messages, errors, children, and describe the frame. It also includes methods to recursively search through frames to find attributes and handle frame completion. +*/ class OperationFrame { constructor ({ parent, label, x }) { this.parent = parent; @@ -68,6 +76,12 @@ class OperationFrame { this.parent._calc_effective_status(); } } + /** + * Sets the status of the frame and updates the effective status. + * This method logs the status change and updates the parent frame's effective status if necessary. + * + * @param {Object} status - The new status to set. + */ _calc_effective_status () { for ( const child of this.children ) { if ( child.status === OperationFrame.FRAME_STATUS_STUCK ) { @@ -97,6 +111,17 @@ class OperationFrame { } } + + /** + * Gets the effective status of the operation frame. + * + * This method returns the effective status of the current operation frame, + * considering the statuses of its children. The effective status is the + * aggregated status of the frame and its children, reflecting the current + * progress or state of the operation. + * + * @return {Object} The effective status of the operation frame. + */ get status () { return this.effective_status_; } @@ -132,6 +157,12 @@ class OperationFrame { return this; } + + /** + * Recursively traverses the frame hierarchy to find the root frame. + * + * @returns {OperationFrame} The root frame of the current frame hierarchy. + */ get_root_frame () { let frame = this; while ( frame.parent ) { @@ -140,6 +171,13 @@ class OperationFrame { return frame; } + + /** + * Marks the operation frame as done. + * This method sets the status of the operation frame to 'done' and updates + * the effective status accordingly. It triggers a recalculation of the + * effective status for parent frames if necessary. + */ done () { this.status = OperationFrame.FRAME_STATUS_DONE; } @@ -161,6 +199,14 @@ class OperationFrame { const prefix_deep = '│ '; const prefix_deep_end = ' '; + + /** + * Recursively builds a string representation of the frame and its children. + * + * @param {boolean} show_tree - If true, includes the tree structure of child frames. + * @param {OperationFrame} highlight_frame - The frame to highlight in the output. + * @returns {string} - A string representation of the frame and its children. + */ const recurse = (frame, prefix) => { const children = frame.children; for ( let i = 0; i < children.length; i++ ) { @@ -178,6 +224,13 @@ class OperationFrame { } } + +/** +* @class OperationTraceService +* @classdesc The OperationTraceService class manages operation frames and their statuses. +* It provides methods to add frames, track their progress, and handle their completion. +* This service is essential for monitoring and logging the lifecycle of operations within the system. +*/ class OperationTraceService { constructor ({ services }) { this.log = services.get('log-service').create('operation-trace'); @@ -186,6 +239,19 @@ class OperationTraceService { this.ongoing = {}; } + + /** + * Adds a new operation frame to the trace. + * + * This method creates a new frame with the given label and context, + * and adds it to the ongoing operations. If a context is provided, + * it logs the context description. The frame is then added to the + * parent frame if one exists, and the frame's description is logged. + * + * @param {string} label - The label for the new operation frame. + * @param {?Object} [x] - The context for the operation frame. + * @returns {OperationFrame} The new operation frame. + */ async add_frame (label) { return this.add_frame_sync(label); } @@ -219,6 +285,16 @@ class OperationTraceService { } } + +/** +* @class BaseOperation +* @extends AdvancedBase +* @description The BaseOperation class extends AdvancedBase and serves as the foundation for +* operations within the system. It integrates various features such as context awareness, +* observability through OpenTelemetry (OtelFeature), and assignable methods. This class is +* designed to be extended by specific operation classes to provide a common structure and +* functionality for running and tracing operations. +*/ class BaseOperation extends AdvancedBase { static FEATURES = [ new ContextAwareFeature(), @@ -226,6 +302,18 @@ class BaseOperation extends AdvancedBase { new AssignableMethodsFeature(), ] + + /** + * Executes the operation with the provided values. + * + * This method initiates an operation frame within the context, sets the operation status to working, + * executes the `_run` method, and handles post-run logic. It also manages the status of child frames + * and handles errors, updating the frame's attributes accordingly. + * + * @param {Object} values - The values to be used in the operation. + * @returns {Promise<*>} - The result of the operation. + * @throws {Error} - If the frame is missing or any other error occurs during the operation. + */ async run (values) { this.values = values; @@ -247,6 +335,17 @@ class BaseOperation extends AdvancedBase { // Run operation in new context try { + /** + * Runs an operation within a new context. + * + * This method sets up a new operation frame, updates the context, and runs the + * operation. It handles the operation's lifecycle, logging, and error handling. + * + * @async + * @function run + * @param {Object} values - The values to be passed to the operation. + * @returns {Promise<*>} The result of the operation. + */ return await x.arun(async () => { const x = Context.get(); const operationTraceSvc = x.get('services').get('operationTrace'); @@ -283,6 +382,13 @@ class BaseOperation extends AdvancedBase { this.frame.attributes[key] = value; } + + /** + * Updates the checkpoint for the current operation frame. + * + * @param {string} name - The name of the checkpoint to set. + * @returns {void} + */ _post_run () { let any_async = false; for ( const child of this.frame.children ) { diff --git a/src/backend/src/services/ParameterService.js b/src/backend/src/services/ParameterService.js index 8ff72367..21dfd32f 100644 --- a/src/backend/src/services/ParameterService.js +++ b/src/backend/src/services/ParameterService.js @@ -1,3 +1,4 @@ +// METADATA // {"ai-commented":{"service":"claude"}} /* * Copyright (C) 2024 Puter Technologies Inc. * @@ -37,11 +38,34 @@ const BaseService = require("./BaseService"); * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ +/** +* @class ParameterService +* @extends BaseService +* @description Service class for managing system parameters and their values. +* Provides functionality for creating, getting, setting, and subscribing to parameters. +* Supports parameter binding to instances and includes command registration for parameter management. +* Parameters can have constraints, default values, and change listeners. +*/ class ParameterService extends BaseService { + /** + * Parameter service for managing system-wide parameters + * @extends BaseService + * @class + * @description Handles registration, storage, and access of parameters across services. + * Parameters can be bound to instances, subscribed to for changes, and accessed via commands. + * Each parameter has a unique service-scoped ID and optional constraints. + */ _construct () { this.parameters_ = []; } + + /** + * Initializes the service by registering commands with the command service. + * This method is called during service startup to set up command handlers + * for parameter management. + * @private + */ _init () { this._registerCommands(this.services.get('commands')); } @@ -63,6 +87,13 @@ class ParameterService extends BaseService { } } + + /** + * Gets the value of a parameter by its ID + * @param {string} id - The unique identifier of the parameter to retrieve + * @returns {Promise<*>} The current value of the parameter + * @throws {Error} If parameter with given ID is not found + */ async get(id) { const parameter = this._get_param(id); return await parameter.get(); @@ -87,6 +118,10 @@ class ParameterService extends BaseService { } _registerCommands (commands) { + /** + * Registers parameter-related commands with the command service + * @param {Object} commands - The command service instance to register with + */ const completeParameterName = (args) => { // The parameter name is the first argument, so return no results if we're on the second or later. if (args.length > 1) @@ -146,6 +181,13 @@ class ParameterService extends BaseService { } } + +/** +* @class Parameter +* @description Represents a configurable parameter with value management, constraints, and change notification capabilities. +* Provides functionality for setting/getting values, binding to object instances, and subscribing to value changes. +* Supports validation through configurable constraints and maintains a list of value change listeners. +*/ class Parameter { constructor(spec) { this.spec_ = spec; @@ -156,6 +198,14 @@ class Parameter { } } + + /** + * Sets a new value for the parameter after validating against constraints + * @param {*} value - The new value to set for the parameter + * @throws {Error} If the value fails any constraint checks + * @fires valueListeners with new value and old value + * @async + */ async set (value) { for ( const constraint of (this.spec_.constraints ?? []) ) { if ( ! await constraint.check(value) ) { @@ -170,6 +220,11 @@ class Parameter { } } + + /** + * Gets the current value of this parameter + * @returns {Promise<*>} The parameter's current value + */ async get () { return this.value_; } diff --git a/src/backend/src/services/PermissionAPIService.js b/src/backend/src/services/PermissionAPIService.js index 58355311..02715a45 100644 --- a/src/backend/src/services/PermissionAPIService.js +++ b/src/backend/src/services/PermissionAPIService.js @@ -1,3 +1,4 @@ +// METADATA // {"ai-commented":{"service":"claude"}} /* * Copyright (C) 2024 Puter Technologies Inc. * @@ -22,11 +23,28 @@ const { Endpoint } = require("../util/expressutil"); const { whatis } = require("../util/langutil"); const BaseService = require("./BaseService"); + +/** +* @class PermissionAPIService +* @extends BaseService +* @description Service class that handles API endpoints for permission management, including user-app permissions, +* user-user permissions, and group management. Provides functionality for creating groups, managing group memberships, +* granting/revoking various types of permissions, and checking access control lists (ACLs). Implements RESTful +* endpoints for group operations like creation, adding/removing users, and listing groups. +*/ class PermissionAPIService extends BaseService { static MODULES = { express: require('express'), }; + + /** + * Installs routes for authentication and permission management into the Express app + * @param {Object} _ Unused parameter + * @param {Object} options Installation options + * @param {Express} options.app Express application instance to install routes on + * @returns {Promise} + */ async ['__on_install.routes'] (_, { app }) { app.use(require('../routers/auth/get-user-app-token')) app.use(require('../routers/auth/grant-user-app')) @@ -44,6 +62,11 @@ class PermissionAPIService extends BaseService { }).attach(app); // track: scoping iife + /** + * Creates a scoped router for group-related endpoints using an IIFE pattern + * @private + * @returns {express.Router} Express router instance with isolated require scope + */ const r_group = (() => { const require = this.require; const express = require('express'); diff --git a/src/backend/src/services/ProtectedAppService.js b/src/backend/src/services/ProtectedAppService.js index 17adfe67..fea585d7 100644 --- a/src/backend/src/services/ProtectedAppService.js +++ b/src/backend/src/services/ProtectedAppService.js @@ -1,3 +1,4 @@ +// METADATA // {"ai-commented":{"service":"mistral","model":"mistral-large-latest"}} /* * Copyright (C) 2024 Puter Technologies Inc. * @@ -21,7 +22,28 @@ const { UserActorType } = require("./auth/Actor"); const { PermissionImplicator, PermissionUtil, PermissionRewriter } = require("./auth/PermissionService"); const BaseService = require("./BaseService"); + +/** +* @class ProtectedAppService +* @extends BaseService +* @classdesc This class represents a service that handles protected applications. It extends the BaseService and includes +* methods for initializing permissions and registering rewriters and implicators for permission handling. The class +* ensures that the owner of a protected app has implicit permission to access it. +*/ class ProtectedAppService extends BaseService { + /** + * Class representing a service for protected applications. + * Extends the BaseService class to provide additional functionality specific to protected apps. + */ + + /** + * Initializes the ProtectedAppService. + * Registers a permission rewriter and implicator to handle application-specific permissions. + * @async + * @method _init + * @memberof ProtectedAppService + * @returns {Promise} A promise that resolves when the initialization is complete. + */ async _init () { const svc_permission = this.services.get('permission'); diff --git a/src/backend/src/services/PuterAPIService.js b/src/backend/src/services/PuterAPIService.js index d8a7f53b..01cff4bb 100644 --- a/src/backend/src/services/PuterAPIService.js +++ b/src/backend/src/services/PuterAPIService.js @@ -1,3 +1,4 @@ +// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o-mini"}} /* * Copyright (C) 2024 Puter Technologies Inc. * @@ -18,7 +19,25 @@ */ const BaseService = require("./BaseService"); + +/** +* @class PuterAPIService +* @extends BaseService +* +* The PuterAPIService class is responsible for integrating various routes +* into the web server for the Puter application. It acts as a middleware +* support layer, providing necessary API endpoints for handling various +* functionality such as authentication, user management, and application +* operations. This class is designed to extend the core functionalities +* of BaseService, ensuring that all routes are properly configured and +* available for use. +*/ class PuterAPIService extends BaseService { + /** + * Sets up the routes for the Puter API service. + * This method registers various API endpoints with the web server. + * It does not return a value as it configures the server directly. + */ async ['__on_install.routes'] () { const { app } = this.services.get('web-server'); diff --git a/src/backend/src/services/PuterHomepageService.js b/src/backend/src/services/PuterHomepageService.js index 754b4de7..3cb72d39 100644 --- a/src/backend/src/services/PuterHomepageService.js +++ b/src/backend/src/services/PuterHomepageService.js @@ -1,3 +1,4 @@ +// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o"}} /* * Copyright (C) 2024 Puter Technologies Inc. * @@ -24,16 +25,38 @@ const {is_valid_url} = require('../helpers'); * PuterHomepageService serves the initial HTML page that loads the Puter GUI * and all of its assets. */ +/** +* This class serves the initial HTML page that loads the Puter GUI and all of its assets. +* It extends the BaseService class to provide common functionality. +*/ class PuterHomepageService extends BaseService { static MODULES = { fs: require('node:fs'), } + + /** + * This method sets a parameter for the GUI. + * It takes a key and value as arguments and adds them to the `gui_params` object. + * + * @param {string} key - The key for the parameter. + * @param {any} val - The value for the parameter. + */ _construct () { this.service_scripts = []; this.gui_params = {}; } + + /** + * @description This method initializes the PuterHomepageService by loading the manifest file. + * It reads the manifest file located at the specified path and parses its JSON content. + * The parsed data is then assigned to the `manifest` property of the instance. + * @returns {Promise} A promise that resolves with the initialized PuterHomepageService instance. + */ + PuterHomepageService._init() { + // code here... + } async _init () { // Load manifest const config = this.global_config; @@ -56,6 +79,12 @@ class PuterHomepageService extends BaseService { this.gui_params[key] = val; } + + /** + * @description This method sets a GUI parameter. It allows you to assign a value to a key within the `gui_params` object. + * @param {string} key - The key for the parameter. + * @param {any} val - The value for the parameter. + */ async send ({ req, res }, meta, launch_options) { const config = this.global_config; @@ -238,6 +267,20 @@ class PuterHomepageService extends BaseService {