doc: ai generated comments

This commit is contained in:
KernelDeimos 2024-11-29 15:59:35 -05:00
parent 2dfeeeda27
commit ea5a8ef906
11 changed files with 547 additions and 0 deletions

View File

@ -1,3 +1,4 @@
// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o"}}
/* /*
* Copyright (C) 2024 Puter Technologies Inc. * Copyright (C) 2024 Puter Technologies Inc.
* *
@ -18,9 +19,26 @@
*/ */
const BaseService = require("./BaseService"); const BaseService = require("./BaseService");
// Symbol used to indicate a denial of service instruction in anomaly handling.
const DENY_SERVICE_INSTRUCTION = Symbol('DENY_SERVICE_INSTRUCTION'); const DENY_SERVICE_INSTRUCTION = Symbol('DENY_SERVICE_INSTRUCTION');
/**
* @class AnomalyService
* @extends BaseService
* @description The AnomalyService class is responsible for managing and processing anomaly detection types and configurations.
* It allows the registration of different types with associated handlers, enabling the detection of anomalies based on specified criteria.
*/
class AnomalyService extends BaseService { class AnomalyService extends BaseService {
/**
* AnomalyService class that extends BaseService and provides methods
* for registering anomaly types and handling incoming data for those anomalies.
*
* The register method allows the registration of different anomaly types
* and their respective configurations, including custom handlers for data
* evaluation. It supports two modes of operation: a direct handler or
* a threshold-based evaluation.
*/
_construct () { _construct () {
this.types = {}; this.types = {};
} }
@ -39,6 +57,16 @@ class AnomalyService extends BaseService {
} }
this.types[type] = type_instance; this.types[type] = type_instance;
} }
/**
* Registers a new type with the service, including its configuration and handler.
*
* @param {string} type - The name of the type to register.
* @param {Object} config - The configuration object for the type.
* @param {Function} [config.handler] - An optional handler function for the type.
* @param {number} [config.high] - An optional threshold value; triggers the handler if exceeded.
*
* @returns {void}
*/
async note (id, data) { async note (id, data) {
const type = this.types[id]; const type = this.types[id];
if ( ! type ) return; if ( ! type ) return;

View File

@ -1,3 +1,4 @@
// METADATA // {"ai-commented":{"service":"xai"}}
/* /*
* Copyright (C) 2024 Puter Technologies Inc. * Copyright (C) 2024 Puter Technologies Inc.
* *
@ -23,6 +24,17 @@ const { DB_READ } = require("./database/consts");
const uuidv4 = require('uuid').v4; const uuidv4 = require('uuid').v4;
/**
* @class AppInformationService
* @description
* The AppInformationService class manages application-related information,
* including caching, statistical data, and tags for applications within the Puter ecosystem.
* It provides methods for refreshing application data, managing app statistics,
* and handling tags associated with apps. This service is crucial for maintaining
* up-to-date information about applications, facilitating features like app listings,
* recent apps, and tag-based app discovery.
*/
class AppInformationService { class AppInformationService {
constructor ({ services }) { constructor ({ services }) {
this.services = services; this.services = services;
@ -37,33 +49,87 @@ class AppInformationService {
// await new Promise(rslv => setTimeout(rslv, 500)) // await new Promise(rslv => setTimeout(rslv, 500))
await this._refresh_app_cache(); await this._refresh_app_cache();
/**
* Refreshes the application cache by querying the database for all apps and updating the key-value store.
*
* This method is called periodically to ensure that the in-memory cache reflects the latest
* state from the database. It uses the 'database' service to fetch app data and then updates
* multiple cache entries for quick lookups by name, ID, and UID.
*
* @async
*/
asyncSafeSetInterval(async () => { asyncSafeSetInterval(async () => {
this._refresh_app_cache(); this._refresh_app_cache();
}, 30 * 1000); }, 30 * 1000);
await this._refresh_app_stats(); await this._refresh_app_stats();
/**
* Refreshes the cache of recently opened apps.
* This method updates the 'recent' collection with the UIDs of apps sorted by their most recent timestamp.
*
* @async
* @returns {Promise<void>} A promise that resolves when the cache has been refreshed.
*/
asyncSafeSetInterval(async () => { asyncSafeSetInterval(async () => {
this._refresh_app_stats(); this._refresh_app_stats();
}, 120 * 1000); }, 120 * 1000);
// This stat is more expensive so we don't update it as often // This stat is more expensive so we don't update it as often
await this._refresh_app_stat_referrals(); await this._refresh_app_stat_referrals();
/**
* Refreshes the app referral statistics.
* This method is computationally expensive and thus runs less frequently.
* It queries the database for user counts referred by each app's origin URL.
*
* @async
*/
asyncSafeSetInterval(async () => { asyncSafeSetInterval(async () => {
this._refresh_app_stat_referrals(); this._refresh_app_stat_referrals();
}, 15 * MINUTE); }, 15 * MINUTE);
await this._refresh_recent_cache(); await this._refresh_recent_cache();
/**
* Refreshes the recent cache by updating the list of recently added or updated apps.
* This method fetches all app data, filters for approved apps, sorts them by timestamp,
* and updates the `this.collections.recent` array with the UIDs of the most recent 50 apps.
*
* @async
* @private
*/
asyncSafeSetInterval(async () => { asyncSafeSetInterval(async () => {
this._refresh_recent_cache(); this._refresh_recent_cache();
}, 120 * 1000); }, 120 * 1000);
await this._refresh_tags(); await this._refresh_tags();
/**
* Refreshes the tags cache by iterating through all approved apps,
* extracting their tags, and organizing them into a structured format.
* This method updates the `this.tags` object with the latest tag information.
*
* @async
* @method
* @memberof AppInformationService
*/
asyncSafeSetInterval(async () => { asyncSafeSetInterval(async () => {
this._refresh_tags(); this._refresh_tags();
} , 120 * 1000); } , 120 * 1000);
})(); })();
} }
/**
* Retrieves and returns statistical data for a specific application.
*
* This method fetches various metrics such as the number of times the app has been opened,
* the count of unique users who have opened the app, and the number of referrals attributed to the app.
* It uses cached data where available to improve performance.
*
* @param {string} app_uid - The unique identifier for the application.
* @returns {Promise<Object>} An object containing:
* - {number} open_count - The total number of times the app has been opened.
* - {number} user_count - The count of unique users who have opened the app.
* - {number|null} referral_count - The number of referrals, or null if the data is not available or too expensive to retrieve.
*/
async get_stats (app_uid) { async get_stats (app_uid) {
const db = this.services.get('database').get(DB_READ, 'apps'); const db = this.services.get('database').get(DB_READ, 'apps');
@ -100,6 +166,19 @@ class AppInformationService {
}; };
} }
/**
* Retrieves various statistics for a given app.
*
* This method fetches the open count, user count, and referral count for an app identified by its UID.
* It uses cached values where available to improve performance, but will query the database if necessary.
*
* @param {string} app_uid - The unique identifier of the app for which to retrieve stats.
* @returns {Promise<Object>} An object containing:
* - {number} open_count - Total number of times the app was opened.
* - {number} user_count - Number of unique users who opened the app.
* - {number|null} referral_count - Number of referrals attributed to the app. This value might not be reported if not cached.
*/
async _refresh_app_cache () { async _refresh_app_cache () {
this.log.tick('refresh app cache'); this.log.tick('refresh app cache');
@ -114,6 +193,17 @@ class AppInformationService {
} }
} }
/**
* Refreshes the application cache by querying the database for all apps and updating the key-value store.
*
* @async
* @returns {Promise<void>} A promise that resolves when the cache refresh operation is complete.
*
* @notes
* - This method logs a tick event for performance monitoring.
* - It populates the cache with app data indexed by name, id, and uid.
*/
async _refresh_app_stats () { async _refresh_app_stats () {
this.log.tick('refresh app stats'); this.log.tick('refresh app stats');
@ -144,6 +234,13 @@ class AppInformationService {
} }
} }
/**
* Refreshes the cache of app statistics including open and user counts.
* This method updates the cache every 120 seconds to ensure data freshness.
*
* @async
*/
async _refresh_app_stat_referrals () { async _refresh_app_stat_referrals () {
this.log.tick('refresh app stat referrals'); this.log.tick('refresh app stat referrals');
@ -176,6 +273,17 @@ class AppInformationService {
this.log.info('DONE refresh app stat referrals'); this.log.info('DONE refresh app stat referrals');
} }
/**
* Updates the cache with recently updated apps.
*
* @description This method refreshes the cache containing the most recently updated applications.
* It fetches all app UIDs, retrieves the corresponding app data, filters for approved apps,
* sorts them by timestamp in descending order, and updates the 'recent' collection with
* the UIDs of the top 50 most recent apps.
*
* @returns {Promise<void>} Resolves when the cache has been updated.
*/
async _refresh_recent_cache () { async _refresh_recent_cache () {
const app_keys = kv.keys(`apps:uid:*`); const app_keys = kv.keys(`apps:uid:*`);
// console.log('APP KEYS', app_keys); // console.log('APP KEYS', app_keys);
@ -194,6 +302,16 @@ class AppInformationService {
this.collections.recent = apps.map(app => app.uid).slice(0, 50); this.collections.recent = apps.map(app => app.uid).slice(0, 50);
} }
/**
* Refreshes the cache of recently added or updated apps.
*
* This method retrieves all apps from the cache, filters for approved listings,
* sorts them by timestamp in descending order, and updates the `recent` collection
* with the UIDs of the most recent 50 apps.
*
* @returns {Promise<void>}
*/
async _refresh_tags () { async _refresh_tags () {
const app_keys = kv.keys(`apps:uid:*`); const app_keys = kv.keys(`apps:uid:*`);
// console.log('APP KEYS', app_keys); // console.log('APP KEYS', app_keys);
@ -229,6 +347,22 @@ class AppInformationService {
this.tags = new_tags; this.tags = new_tags;
} }
/**
* Deletes an application from the system.
*
* This method performs the following actions:
* - Retrieves the app data from cache or database if not provided.
* - Deletes the app record from the database.
* - Removes the app from all relevant caches (by name, id, and uid).
* - Removes the app from the recent collection if present.
* - Removes the app from any associated tags.
*
* @param {string} app_uid - The unique identifier of the app to be deleted.
* @param {Object} [app] - The app object, if already fetched. If not provided, it will be retrieved.
* @throws {Error} If the app is not found in either cache or database.
* @returns {Promise<void>} A promise that resolves when the app has been successfully deleted.
*/
async delete_app (app_uid, app) { async delete_app (app_uid, app) {
const db = this.services.get('database').get(DB_READ, 'apps'); const db = this.services.get('database').get(DB_READ, 'apps');

View File

@ -1,3 +1,4 @@
// METADATA // {"ai-params":{"service":"xai"},"ai-refs":["../../doc/contributors/boot-sequence.md"],"ai-commented":{"service":"xai"}}
/* /*
* Copyright (C) 2024 Puter Technologies Inc. * Copyright (C) 2024 Puter Technologies Inc.
* *
@ -18,8 +19,25 @@
*/ */
const { concepts } = require("@heyputer/putility"); const { concepts } = require("@heyputer/putility");
// This is a no-op function that AI is incapable of writing a comment for.
// That said, I suppose it didn't need one anyway.
const NOOP = async () => {}; const NOOP = async () => {};
/**
* @class BaseService
* @extends concepts.Service
* @description
* BaseService is the foundational class for all services in the Puter backend.
* It provides lifecycle methods like `construct` and `init` that are invoked during
* different phases of the boot sequence. This class ensures that services can be
* instantiated, initialized, and activated in a coordinated manner through
* events emitted by the Kernel. It also manages common service resources like
* logging and error handling, and supports legacy services by allowing
* instantiation after initialization but before consolidation.
*/
class BaseService extends concepts.Service { class BaseService extends concepts.Service {
constructor (service_resources, ...a) { constructor (service_resources, ...a) {
const { services, config, my_config, name, args } = service_resources; const { services, config, my_config, name, args } = service_resources;
@ -36,10 +54,29 @@ class BaseService extends concepts.Service {
} }
} }
/**
* Initializes the service with configuration and dependencies.
* This method sets up logging and error handling, and calls a custom `_init` method if defined.
*
* @param {Object} args - Arguments passed to the service for initialization.
* @returns {Promise<void>} A promise that resolves when initialization is complete.
*/
async construct () { async construct () {
await (this._construct || NOOP).call(this, this.args); await (this._construct || NOOP).call(this, this.args);
} }
/**
* Performs the initialization phase of the service lifecycle.
* This method sets up logging and error handling for the service,
* then calls the service-specific initialization logic if defined.
*
* @async
* @memberof BaseService
* @instance
* @returns {Promise<void>} A promise that resolves when initialization is complete.
*/
async init () { async init () {
const services = this.services; const services = this.services;
this.log = services.get('log-service').create(this.service_name); this.log = services.get('log-service').create(this.service_name);
@ -48,6 +85,15 @@ class BaseService extends concepts.Service {
await (this._init || NOOP).call(this, this.args); await (this._init || NOOP).call(this, this.args);
} }
/**
* Handles an event by retrieving the appropriate event handler
* and executing it with the provided arguments.
*
* @param {string} id - The identifier of the event to handle.
* @param {Array<any>} args - The arguments to pass to the event handler.
* @returns {Promise<any>} The result of the event handler execution.
*/
async __on (id, args) { async __on (id, args) {
const handler = this.__get_event_handler(id); const handler = this.__get_event_handler(id);

View File

@ -1,10 +1,29 @@
// METADATA // {"ai-commented":{"service":"mistral","model":"mistral-large-latest"}}
const { Context } = require("../util/context"); const { Context } = require("../util/context");
const BaseService = require("./BaseService"); const BaseService = require("./BaseService");
/**
* @class BootScriptService
* @extends BaseService
* @description The BootScriptService class extends BaseService and is responsible for
* managing and executing boot scripts. It provides methods to handle boot scripts when
* the system is ready and to run individual script commands.
*/
class BootScriptService extends BaseService { class BootScriptService extends BaseService {
static MODULES = { static MODULES = {
fs: require('fs'), fs: require('fs'),
} }
/**
* Loads and executes a boot script if specified in the arguments.
*
* This method reads the provided boot script file, parses it, and runs the script using the `run_script` method.
* If no boot script is specified in the arguments, the method returns immediately.
*
* @async
* @function
* @returns {Promise<void>}
*/
async ['__on_boot.ready'] () { async ['__on_boot.ready'] () {
const args = Context.get('args'); const args = Context.get('args');
if ( ! args['boot-script'] ) return; if ( ! args['boot-script'] ) return;
@ -17,6 +36,17 @@ class BootScriptService extends BaseService {
await this.run_script(boot_json); await this.run_script(boot_json);
} }
/**
* Executes a series of commands defined in a JSON boot script.
*
* This method processes each command in the boot_json array.
* If the command is recognized within the predefined scope, it will be executed.
* If not, an error is thrown.
*
* @param {Array} boot_json - An array of commands to execute.
* @throws {Error} Thrown if an unknown command is encountered.
*/
async run_script (boot_json) { async run_script (boot_json) {
const scope = { const scope = {
runner: 'boot-script', runner: 'boot-script',

View File

@ -1,6 +1,15 @@
// METADATA // {"ai-commented":{"service":"claude"}}
const { can } = require("../util/langutil"); const { can } = require("../util/langutil");
const BaseService = require("./BaseService"); const BaseService = require("./BaseService");
/**
* CleanEmailService - A service class for cleaning and validating email addresses
* Handles email normalization by applying provider-specific rules (e.g. Gmail's dot-insensitivity),
* manages subaddressing (plus addressing), and validates against blocked domains.
* Extends BaseService to integrate with the application's service infrastructure.
* @extends BaseService
*/
class CleanEmailService extends BaseService { class CleanEmailService extends BaseService {
static NAMED_RULES = { static NAMED_RULES = {
// For some providers, dots don't matter // For some providers, dots don't matter
@ -54,6 +63,12 @@ class CleanEmailService extends BaseService {
static DOMAIN_NONDISTINCT = { static DOMAIN_NONDISTINCT = {
'googlemail.com': 'gmail.com', 'googlemail.com': 'gmail.com',
} }
/**
* Maps non-distinct email domains to their canonical equivalents.
* For example, 'googlemail.com' is mapped to 'gmail.com' since they
* represent the same email service.
* @type {Object.<string, string>}
*/
_construct () { _construct () {
this.named_rules = this.constructor.NAMED_RULES; this.named_rules = this.constructor.NAMED_RULES;
this.providers = this.constructor.PROVIDERS; this.providers = this.constructor.PROVIDERS;
@ -62,6 +77,16 @@ class CleanEmailService extends BaseService {
} }
clean (email) { clean (email) {
/**
* Cleans an email address by applying provider-specific rules and standardizations
* @param {string} email - The email address to clean
* @returns {string} The cleaned email address with applied rules and standardizations
*
* Splits email into local and domain parts, applies provider-specific rules like:
* - Removing dots for certain providers (Gmail, iCloud)
* - Handling subaddressing (removing +suffix)
* - Normalizing domains (e.g. googlemail.com -> gmail.com)
*/
const eml = (() => { const eml = (() => {
const [local, domain] = email.split('@'); const [local, domain] = email.split('@');
return { local, domain }; return { local, domain };
@ -101,6 +126,15 @@ class CleanEmailService extends BaseService {
return eml.local + '@' + eml.domain; return eml.local + '@' + eml.domain;
} }
/**
* Validates an email address against blocked domains and custom validation rules
* @param {string} email - The email address to validate
* @returns {Promise<boolean>} True if email is valid, false if blocked or invalid
* @description First cleans the email, then checks against blocked domains from config.
* Emits 'email.validate' event to allow custom validation rules. Event handlers can
* set event.allow=false to reject the email.
*/
async validate (email) { async validate (email) {
email = this.clean(email); email = this.clean(email);
const config = this.global_config; const config = this.global_config;

View File

@ -1,3 +1,4 @@
// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o"}}
/* /*
* Copyright (C) 2024 Puter Technologies Inc. * Copyright (C) 2024 Puter Technologies Inc.
* *
@ -18,7 +19,14 @@
*/ */
const { Context } = require("../util/context"); const { Context } = require("../util/context");
// Key for tracing operations in the context, used for logging and tracking.
const CONTEXT_KEY = Context.make_context_key('operation-trace'); const CONTEXT_KEY = Context.make_context_key('operation-trace');
/**
* Class representing a tracker for individual client operations.
* The ClientOperationTracker class is designed to handle the metadata
* and attributes associated with each operation, allowing for better
* management and organization of client data during processing.
*/
class ClientOperationTracker { class ClientOperationTracker {
constructor (parameters) { constructor (parameters) {
this.name = parameters.name || 'untitled'; this.name = parameters.name || 'untitled';
@ -29,11 +37,26 @@ class ClientOperationTracker {
} }
} }
/**
* Class representing the ClientOperationService, which manages the
* operations related to client interactions. It provides methods to
* add new operations and handle their associated client operation
* trackers, ensuring efficient management and tracking of client-side
* operations during their lifecycle.
*/
class ClientOperationService { class ClientOperationService {
constructor ({ services }) { constructor ({ services }) {
this.operations_ = []; this.operations_ = [];
} }
/**
* Adds a new operation to the service by creating a ClientOperationTracker instance.
*
* @param {Object} parameters - The parameters for the new operation.
* @returns {Promise<ClientOperationTracker>} A promise that resolves to the created ClientOperationTracker instance.
*/
async add_operation (parameters) { async add_operation (parameters) {
const tracker = new ClientOperationTracker(parameters); const tracker = new ClientOperationTracker(parameters);

View File

@ -1,3 +1,4 @@
// METADATA // {"ai-commented":{"service":"claude"}}
/* /*
* Copyright (C) 2024 Puter Technologies Inc. * Copyright (C) 2024 Puter Technologies Inc.
* *
@ -18,15 +19,36 @@
*/ */
const BaseService = require("./BaseService"); const BaseService = require("./BaseService");
/**
* Represents a Command class that encapsulates command execution functionality.
* Each Command instance contains a specification (spec) that defines its ID,
* name, description, handler function, and optional argument completer.
* The class provides methods for executing commands and handling command
* argument completion.
*/
class Command { class Command {
constructor(spec) { constructor(spec) {
this.spec_ = spec; this.spec_ = spec;
} }
/**
* Gets the unique identifier for this command
* @returns {string} The command's ID as specified in the constructor
*/
get id() { get id() {
return this.spec_.id; return this.spec_.id;
} }
/**
* Executes the command with given arguments and logging
* @param {Array} args - Command arguments to pass to the handler
* @param {Object} [log=console] - Logger object for output, defaults to console
* @returns {Promise<void>}
* @throws {Error} Logs any errors that occur during command execution
*/
async execute(args, log) { async execute(args, log) {
log = log ?? console; log = log ?? console;
const { id, name, description, handler } = this.spec_; const { id, name, description, handler } = this.spec_;
@ -46,10 +68,30 @@ class Command {
} }
} }
/**
* CommandService class manages the registration, execution, and handling of commands in the Puter system.
* Extends BaseService to provide command-line interface functionality. Maintains a collection of Command
* objects, supports command registration with namespaces, command execution with arguments, and provides
* command lookup capabilities. Includes built-in help command functionality.
* @extends BaseService
*/
class CommandService extends BaseService { class CommandService extends BaseService {
/**
* Service for managing and executing commands in the system.
* Extends BaseService to provide command registration, execution and lookup functionality.
* Commands are stored internally with unique IDs and can be executed with arguments.
* Built-in 'help' command is registered during initialization.
*/
async _construct () { async _construct () {
this.commands_ = []; this.commands_ = [];
} }
/**
* Initializes the command service's internal state
* Called during service construction to set up the empty commands array
* @private
* @returns {Promise<void>}
*/
async _init () { async _init () {
this.commands_.push(new Command({ this.commands_.push(new Command({
id: 'help', id: 'help',
@ -73,6 +115,14 @@ class CommandService extends BaseService {
} }
} }
/**
* Executes a command with the given arguments and logging context
* @param {string[]} args - Array of command arguments where first element is command name
* @param {Object} log - Logger object for output (defaults to console if not provided)
* @returns {Promise<void>}
* @throws {Error} If command execution fails
*/
async executeCommand(args, log) { async executeCommand(args, log) {
const [commandName, ...commandArgs] = args; const [commandName, ...commandArgs] = args;
const command = this.commands_.find(c => c.spec_.id === commandName); const command = this.commands_.find(c => c.spec_.id === commandName);
@ -80,17 +130,37 @@ class CommandService extends BaseService {
log.error(`unknown command: ${commandName}`); log.error(`unknown command: ${commandName}`);
return; return;
} }
/**
* Executes a command with the given arguments in a global context
* @param {string[]} args - Array of command arguments where first element is command name
* @param {Object} log - Logger object for output
* @returns {Promise<void>}
* @throws {Error} If command execution fails
*/
await globalThis.root_context.arun(async () => { await globalThis.root_context.arun(async () => {
await command.execute(commandArgs, log); await command.execute(commandArgs, log);
}); });
} }
/**
* Executes a raw command string by splitting it into arguments and executing the command
* @param {string} text - Raw command string to execute
* @param {object} log - Logger object for output (defaults to console if not provided)
* @returns {Promise<void>}
* @todo Replace basic whitespace splitting with proper tokenizer (obvious-json)
*/
async executeRawCommand(text, log) { async executeRawCommand(text, log) {
// TODO: add obvious-json as a tokenizer // TODO: add obvious-json as a tokenizer
const args = text.split(/\s+/); const args = text.split(/\s+/);
await this.executeCommand(args, log); await this.executeCommand(args, log);
} }
/**
* Gets a list of all registered command names/IDs
* @returns {string[]} Array of command identifier strings
*/
get commandNames() { get commandNames() {
return this.commands_.map(command => command.id); return this.commands_.map(command => command.id);
} }

View File

@ -1,3 +1,4 @@
// METADATA // {"ai-commented":{"service":"claude"}}
const APIError = require("../api/APIError"); const APIError = require("../api/APIError");
const FSNodeParam = require("../api/filesystem/FSNodeParam"); const FSNodeParam = require("../api/filesystem/FSNodeParam");
const { get_user } = require("../helpers"); const { get_user } = require("../helpers");
@ -6,15 +7,37 @@ const { Endpoint } = require("../util/expressutil");
const BaseService = require("./BaseService"); const BaseService = require("./BaseService");
const { DB_WRITE } = require("./database/consts"); const { DB_WRITE } = require("./database/consts");
/**
* CommentService class handles all comment-related functionality in the system.
* Extends BaseService to provide comment creation, retrieval, and attachment capabilities
* for filesystem entries. Manages database operations for user comments and their
* associations with filesystem nodes. Provides REST API endpoints for comment
* operations including posting new comments and listing existing comments.
* @extends BaseService
*/
class CommentService extends BaseService { class CommentService extends BaseService {
static MODULES = { static MODULES = {
uuidv4: require('uuid').v4, uuidv4: require('uuid').v4,
} }
/**
* Static module dependencies used by the CommentService class
* @property {Function} uuidv4 - UUID v4 generator function from the uuid package
*/
_init () { _init () {
const svc_database = this.services.get('database'); const svc_database = this.services.get('database');
this.db = svc_database.get(DB_WRITE, 'notification'); this.db = svc_database.get(DB_WRITE, 'notification');
} }
['__on_install.routes'] (_, { app }) { ['__on_install.routes'] (_, { app }) {
/**
* Installs route handlers for comment-related endpoints
* Sets up POST routes for creating and listing comments on filesystem entries
*
* @param {*} _ Unused parameter
* @param {Object} options Installation options
* @param {Express} options.app Express application instance
* @private
*/
const r_comment = (() => { const r_comment = (() => {
const require = this.require; const require = this.require;
const express = require('express'); const express = require('express');
@ -113,6 +136,16 @@ class CommentService extends BaseService {
} }
/**
* Creates a new comment with the given text
*
* @param {Object} params - The parameters object
* @param {Object} params.req - Express request object containing user and body data
* @param {Object} params.res - Express response object
* @returns {Promise<Object>} The created comment object with id and uid
* @throws {APIError} If text field is missing from request body
*/
async create_comment_ ({ req, res }) { async create_comment_ ({ req, res }) {
if ( ! req.body.text ) { if ( ! req.body.text ) {
throw APIError.create('field_missing', null, { key: 'text' }); throw APIError.create('field_missing', null, { key: 'text' });
@ -135,6 +168,15 @@ class CommentService extends BaseService {
}; };
} }
/**
* Attaches a comment to a filesystem entry
*
* @param {Object} params - The parameters object
* @param {Object} params.node - The filesystem node to attach the comment to
* @param {Object} params.comment - The comment object containing id and other details
* @returns {Promise<void>} Resolves when comment is successfully attached
*/
async attach_comment_to_fsentry ({ node, comment }) { async attach_comment_to_fsentry ({ node, comment }) {
await this.db.write( await this.db.write(
'INSERT INTO `user_fsentry_comments` ' + 'INSERT INTO `user_fsentry_comments` ' +
@ -144,6 +186,14 @@ class CommentService extends BaseService {
); );
} }
/**
* Retrieves all comments associated with a filesystem entry
*
* @param {Object} params - The parameters object
* @param {Object} params.node - The filesystem node to get comments for
* @returns {Promise<Array>} Array of comment objects with user info attached
*/
async get_comments_for_fsentry ({ node }) { async get_comments_for_fsentry ({ node }) {
const comments = await this.db.read( const comments = await this.db.read(
'SELECT * FROM `user_comments` ' + 'SELECT * FROM `user_comments` ' +

View File

@ -1,3 +1,4 @@
// METADATA // {"ai-commented":{"service":"mistral","model":"mistral-large-latest"}}
/* /*
* Copyright (C) 2024 Puter Technologies Inc. * Copyright (C) 2024 Puter Technologies Inc.
* *
@ -27,6 +28,16 @@ const hash = v => {
return sum.digest(); return sum.digest();
} }
/**
* @class ConfigurableCountingService
* @extends BaseService
* @description The ConfigurableCountingService class extends BaseService and is responsible for managing and incrementing
* configurable counting types for different services. It handles the initialization of the database connection,
* defines counting types and SQL columns, and provides a method to increment counts based on specific service
* types and values. This class is used to manage usage counts for various services, ensuring accurate tracking
* and updating of counts in the database.
*/
class ConfigurableCountingService extends BaseService { class ConfigurableCountingService extends BaseService {
static counting_types = { static counting_types = {
gpt: { gpt: {
@ -73,10 +84,34 @@ class ConfigurableCountingService extends BaseService {
], ],
} }
/**
* Initializes the database connection for the ConfigurableCountingService.
* This method sets up the database service for writing counting data.
*
* @async
* @function _init
* @returns {Promise<void>} A promise that resolves when the database connection is established.
* @memberof ConfigurableCountingService
*/
async _init () { async _init () {
this.db = this.services.get('database').get(DB_WRITE, 'counting'); this.db = this.services.get('database').get(DB_WRITE, 'counting');
} }
/**
* Increments the count for a given service based on the provided parameters.
* This method builds an SQL query to update the count and other custom values
* in the database. It handles different SQL dialects (MySQL and SQLite) and
* ensures that the pricing category is correctly hashed and stored.
*
* @param {Object} params - The parameters for incrementing the count.
* @param {string} params.service_name - The name of the service.
* @param {string} params.service_type - The type of the service.
* @param {Object} params.values - The values to be incremented.
* @throws {Error} If the service type is unknown or if there are no more available columns.
* @returns {Promise<void>} A promise that resolves when the count is successfully incremented.
*/
async increment ({ service_name, service_type, values }) { async increment ({ service_name, service_type, values }) {
values = values ? {...values} : {}; values = values ? {...values} : {};

View File

@ -1,3 +1,4 @@
// METADATA // {"ai-commented":{"service":"mistral","model":"mistral-large-latest"}}
/* /*
* Copyright (C) 2024 Puter Technologies Inc. * Copyright (C) 2024 Puter Technologies Inc.
* *
@ -23,6 +24,15 @@ const { CompositeError } = require("../util/errorutil");
const { TeePromise } = require("../util/promise"); const { TeePromise } = require("../util/promise");
// 17 lines of code instead of an entire dependency-injection framework // 17 lines of code instead of an entire dependency-injection framework
/**
* The `Container` class is a lightweight dependency-injection container designed to manage
* service instances within the application. It provides functionality for registering,
* retrieving, and managing the lifecycle of services, including initialization and event
* handling. This class is intended to simplify dependency management and ensure that services
* are properly initialized and available throughout the application.
*
* @class
*/
class Container { class Container {
constructor ({ logger }) { constructor ({ logger }) {
this.logger = logger; this.logger = logger;
@ -122,6 +132,12 @@ class Container {
} }
} }
has (name) { return !! this.instances_[name]; } has (name) { return !! this.instances_[name]; }
/**
* Checks if a service is registered in the container.
*
* @param {String} name - The name of the service to check.
* @returns {Boolean} - Returns true if the service is registered, false otherwise.
*/
get values () { get values () {
const values = {}; const values = {};
for ( const k in this.instances_ ) { for ( const k in this.instances_ ) {
@ -141,6 +157,18 @@ class Container {
return this.instances_; return this.instances_;
} }
/**
* Initializes all registered services in the container.
*
* This method first constructs each service by calling its `construct` method,
* and then initializes each service by calling its `init` method. If any service
* initialization fails, it logs the failures and throws a `CompositeError`
* containing details of all failed initializations.
*
* @returns {Promise<void>} A promise that resolves when all services are
* initialized or rejects if any service initialization fails.
*/
async init () { async init () {
for ( const k in this.instances_ ) { for ( const k in this.instances_ ) {
this.logger.info(`constructing ${k}`); this.logger.info(`constructing ${k}`);
@ -166,6 +194,17 @@ class Container {
} }
} }
/**
* Emits an event to all registered services.
*
* This method sends an event identified by `id` along with any additional arguments to all
* services registered in the container. If a logger is available, it logs the event.
*
* @param {string} id - The identifier of the event.
* @param {...*} args - Additional arguments to pass to the event handler.
* @returns {Promise<void>} A promise that resolves when all event handlers have completed.
*/
async emit (id, ...args) { async emit (id, ...args) {
if ( this.logger ) { if ( this.logger ) {
this.logger.noticeme(`services:event ${id}`, { args }); this.logger.noticeme(`services:event ${id}`, { args });
@ -180,6 +219,14 @@ class Container {
} }
} }
/**
* @class ProxyContainer
* @classdesc The ProxyContainer class is a proxy for the Container class, allowing for delegation of service management tasks.
* It extends the functionality of the Container class by providing a delegation mechanism.
* This class is useful for scenarios where you need to manage services through a proxy,
* enabling additional flexibility and control over service instances.
*/
class ProxyContainer { class ProxyContainer {
constructor (delegate) { constructor (delegate) {
this.delegate = delegate; this.delegate = delegate;
@ -200,6 +247,12 @@ class ProxyContainer {
} }
return this.delegate.has(name); return this.delegate.has(name);
} }
/**
* Checks if the container has a service with the specified name.
*
* @param {string} name - The name of the service to check.
* @returns {boolean} - Returns true if the service exists, false otherwise.
*/
get values () { get values () {
const values = {}; const values = {};
Object.assign(values, this.delegate.values); Object.assign(values, this.delegate.values);

View File

@ -1,3 +1,4 @@
// METADATA // {"ai-commented":{"service":"claude"}}
/* /*
* Copyright (C) 2024 Puter Technologies Inc. * Copyright (C) 2024 Puter Technologies Inc.
* *
@ -20,7 +21,24 @@ const { Context } = require("../util/context");
const BaseService = require("./BaseService"); const BaseService = require("./BaseService");
// DRY: (2/3) - src/util/context.js; move install() to base class // DRY: (2/3) - src/util/context.js; move install() to base class
/**
* @class ContextInitExpressMiddleware
* @description Express middleware that initializes context values for requests.
* Manages a collection of value initializers that can be synchronous values
* or asynchronous factory functions. Each initializer sets a key-value pair
* in the request context. Part of a DRY implementation shared with context.js.
* TODO: Consider moving install() method to base class.
*/
class ContextInitExpressMiddleware { class ContextInitExpressMiddleware {
/**
* Express middleware class that initializes context values for requests
*
* Manages a list of value initializers that populate the Context with
* either static values or async-generated values when handling requests.
* Part of DRY pattern with src/util/context.js.
*
* @class
*/
constructor () { constructor () {
this.value_initializers_ = []; this.value_initializers_ = [];
} }
@ -30,6 +48,11 @@ class ContextInitExpressMiddleware {
install (app) { install (app) {
app.use(this.run.bind(this)); 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) { async run (req, res, next) {
const x = Context.get(); const x = Context.get();
for ( const initializer of this.value_initializers_ ) { for ( const initializer of this.value_initializers_ ) {
@ -43,7 +66,23 @@ class ContextInitExpressMiddleware {
} }
} }
/**
* @class ContextInitService
* @extends BaseService
* @description Service responsible for initializing and managing context values in the application.
* Provides methods to register both synchronous values and asynchronous factories for context
* initialization. Works in conjunction with Express middleware to ensure proper context setup
* for each request. Extends BaseService to integrate with the application's service architecture.
*/
class ContextInitService extends BaseService { class ContextInitService extends BaseService {
/**
* Service for initializing request context with values and async factories.
* Extends BaseService to provide middleware for Express that populates the Context
* with registered values and async-generated values at the start of each request.
*
* @extends BaseService
*/
_construct () { _construct () {
this.mw = new ContextInitExpressMiddleware(); this.mw = new ContextInitExpressMiddleware();
} }
@ -57,6 +96,11 @@ class ContextInitService extends BaseService {
key, async_factory, key, async_factory,
}); });
} }
/**
* Registers an asynchronous factory function to initialize a context value
* @param {string} key - The key to store the value under in the context
* @param {Function} async_factory - Async function that returns the value to store
*/
async ['__on_install.middlewares.context-aware'] (_, { app }) { async ['__on_install.middlewares.context-aware'] (_, { app }) {
this.mw.install(app); this.mw.install(app);
await this.services.emit('install.context-initializers'); await this.services.emit('install.context-initializers');