mirror of
https://github.com/HeyPuter/puter.git
synced 2025-02-02 23:28:39 +08:00
doc: (skip if using git bisect) ai comments
This commit is contained in:
parent
0a1be464d1
commit
f75010b355
@ -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<Object>} The populated output object after all
|
||||
* providers have been processed.
|
||||
*/
|
||||
async get_details (context, out) {
|
||||
out = out || {};
|
||||
|
||||
|
@ -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<void>} 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<void>} 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);
|
||||
|
@ -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<void>}
|
||||
*/
|
||||
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<void>}
|
||||
*/
|
||||
this.tod_widget = () => {
|
||||
const lines = [
|
||||
...random_tip,
|
||||
|
@ -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<void>} - A promise that resolves when the email is sent.
|
||||
*/
|
||||
async send_email (user, template, values) {
|
||||
const email = user.email;
|
||||
|
||||
|
@ -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<Array>} 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<Array>} 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<Object>} 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();
|
||||
|
@ -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<void>} 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<Object>} 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<Object>} 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<Object>} 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<Array>} 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<Object>} 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 ) {
|
||||
|
@ -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 ) {
|
||||
|
@ -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<void>}
|
||||
*/
|
||||
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<boolean>} 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>} 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() ) {
|
||||
|
@ -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<void>} A promise that resolves when the routes are set up.
|
||||
*/
|
||||
async ['__on_install.routes'] () {
|
||||
const { app } = this.services.get('web-server');
|
||||
|
||||
|
@ -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<void>} 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<Object|null>} The user object if found, else null.
|
||||
*/
|
||||
async get_user_ (options) {
|
||||
const services = this.services;
|
||||
|
||||
|
@ -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}!`;
|
||||
|
@ -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<void>} 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(),
|
||||
|
@ -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');
|
||||
|
@ -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<void>} 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<void>} 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<void>} 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<void>} 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');
|
||||
|
@ -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<void>} 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<any>} The result of the callback execution.
|
||||
* @throws {Error} If the lock acquisition times out.
|
||||
*/
|
||||
timeout = setTimeout(() => {
|
||||
handle.unlock();
|
||||
// TODO: verbose log option by service
|
||||
|
@ -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<void>}
|
||||
*/
|
||||
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<void>}
|
||||
*/
|
||||
async ['__on_install.middlewares.context-aware'] (_, { app }) {
|
||||
// Add express middleware
|
||||
this.mw.install(app);
|
||||
|
@ -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<void>}
|
||||
*/
|
||||
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_;
|
||||
}
|
||||
|
@ -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<void>} 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<void>} 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');
|
||||
|
@ -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 ) {
|
||||
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @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_;
|
||||
}
|
||||
|
@ -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<void>}
|
||||
*/
|
||||
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');
|
||||
|
@ -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<void>} A promise that resolves when the initialization is complete.
|
||||
*/
|
||||
async _init () {
|
||||
const svc_permission = this.services.get('permission');
|
||||
|
||||
|
@ -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');
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
<script>
|
||||
if ( ! window.service_script ) {
|
||||
/**
|
||||
* This method initializes the service by registering any necessary scripts and setting up GUI parameters.
|
||||
* It is called after the PuterHomepageService instance has been constructed and initialized.
|
||||
*
|
||||
* @param {import('express').Request} req - The Express request object.
|
||||
* @param {import('express').Response} res - The Express response object.
|
||||
* @param {object} meta - Metadata about the Puter instance, including the environment, manifest, and launch options.
|
||||
*/
|
||||
// Add this comment above line 240
|
||||
// method: send
|
||||
// purpose: Send the initial HTML page that loads the Puter GUI and its assets.
|
||||
// notes: If the request contains certain query parameters, an error message will be returned instead.
|
||||
// parameters: req, res, meta, launch_options
|
||||
// return value: None, instead it sends an HTML response.
|
||||
window.service_script_api_promise = (() => {
|
||||
let resolve, reject;
|
||||
const promise = new Promise((res, rej) => {
|
||||
@ -279,6 +322,20 @@ class PuterHomepageService extends BaseService {
|
||||
<script src="/dist/bundle.min.js"></script>
|
||||
<!-- Initialize GUI when document is loaded -->
|
||||
<script type="module">
|
||||
/**
|
||||
* This method generates the HTML for the initial Puter page, including script tags and other necessary metadata.
|
||||
* It takes in an object containing various parameters to customize the page.
|
||||
* It returns the generated HTML string.
|
||||
* @param {Object} params - An object containing the following properties:
|
||||
* - env: The environment (e.g., 'dev' or 'prod')
|
||||
* - manifest: The Puter GUI manifest
|
||||
* - use_bundled_gui: A boolean indicating whether to use the bundled GUI or not
|
||||
* - app_origin: The origin of the application
|
||||
* - api_origin: The origin of the API
|
||||
* - meta: The page metadata
|
||||
* - launch_options: Launch options for the GUI
|
||||
* - gui_params: GUI parameters
|
||||
*/
|
||||
window.addEventListener('load', function() {
|
||||
gui(${
|
||||
// TODO: override JSON.stringify to ALWAYS to this...
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"xai"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -23,7 +24,23 @@ const { PermissionUtil, PermissionRewriter, PermissionImplicator } = require("./
|
||||
const BaseService = require("./BaseService");
|
||||
const { DB_WRITE } = require("./database/consts");
|
||||
|
||||
|
||||
/**
|
||||
* The `PuterSiteService` class manages site-related operations within the Puter platform.
|
||||
* This service extends `BaseService` to provide functionalities like:
|
||||
* - Initializing database connections for site data.
|
||||
* - Handling subdomain permissions and rewriting them as necessary.
|
||||
* - Managing permissions for site files, ensuring that sites can access their own resources.
|
||||
* - Retrieving subdomain information by name or unique identifier (UID).
|
||||
* This class is crucial for controlling access and operations related to different sites hosted or managed by the Puter system.
|
||||
*/
|
||||
class PuterSiteService extends BaseService {
|
||||
/**
|
||||
* Initializes the PuterSiteService by setting up database connections,
|
||||
* registering permission rewriters and implicators, and preparing service dependencies.
|
||||
*
|
||||
* @returns {Promise<void>} A promise that resolves when initialization is complete.
|
||||
*/
|
||||
async _init () {
|
||||
const services = this.services;
|
||||
this.db = services.get('database').get(DB_WRITE, 'sites');
|
||||
@ -89,6 +106,14 @@ class PuterSiteService extends BaseService {
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves subdomain information by its name.
|
||||
*
|
||||
* @param {string} subdomain - The name of the subdomain to retrieve.
|
||||
* @returns {Promise<Object|null>} Returns an object with subdomain details or null if not found.
|
||||
* @note In development environment, 'devtest' subdomain returns hardcoded values.
|
||||
*/
|
||||
async get_subdomain (subdomain) {
|
||||
if ( subdomain === 'devtest' && this.global_config.env === 'dev' ) {
|
||||
return {
|
||||
@ -104,6 +129,13 @@ class PuterSiteService extends BaseService {
|
||||
return rows[0];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves a subdomain by its unique identifier (UID).
|
||||
*
|
||||
* @param {string} uid - The unique identifier of the subdomain to fetch.
|
||||
* @returns {Promise<Object|null>} A promise that resolves to the subdomain object if found, or null if not found.
|
||||
*/
|
||||
async get_subdomain_by_uid (uid) {
|
||||
const rows = await this.db.read(
|
||||
`SELECT * FROM subdomains WHERE uuid = ? LIMIT 1`,
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o-mini"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -18,16 +19,52 @@
|
||||
*/
|
||||
const BaseService = require("./BaseService");
|
||||
|
||||
|
||||
/**
|
||||
* Class representing the PuterVersionService.
|
||||
*
|
||||
* The PuterVersionService extends the BaseService and provides methods
|
||||
* to initialize the service, handle routing for version information,
|
||||
* and retrieve the current version of the application. It is responsible
|
||||
* for managing version-related operations within the Puter framework.
|
||||
*/
|
||||
class PuterVersionService extends BaseService {
|
||||
/**
|
||||
* Initializes the service by recording the current boot time.
|
||||
* This method is called asynchronously to ensure that any necessary
|
||||
* setup can be completed before the service begins handling requests.
|
||||
*/
|
||||
async _init () {
|
||||
this.boot_time = Date.now();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets up the routes for the versioning API.
|
||||
* This method registers the version router with the web server application.
|
||||
*
|
||||
* @async
|
||||
* @returns {Promise<void>} Resolves when the routes are successfully registered.
|
||||
*/
|
||||
async ['__on_install.routes'] () {
|
||||
const { app } = this.services.get('web-server');
|
||||
app.use(require('../routers/version'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the current version information of the application along with
|
||||
* the environment and deployment details. The method fetches the version
|
||||
* from the npm package or the local package.json file and returns an
|
||||
* object containing the version, environment, server location, and
|
||||
* deployment timestamp.
|
||||
*
|
||||
* @returns {Object} An object containing version details.
|
||||
* @returns {string} return.version - The current application version.
|
||||
* @returns {string} return.environment - The environment in which the app is running.
|
||||
* @returns {string} return.location - The server ID where the application is deployed.
|
||||
* @returns {number} return.deploy_timestamp - The timestamp when the application was deployed.
|
||||
*/
|
||||
get_version () {
|
||||
const version = process.env.npm_package_version ||
|
||||
require('../../package.json').version;
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o-mini"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -24,13 +25,46 @@ const { DB_WRITE } = require('./database/consts');
|
||||
const BaseService = require('./BaseService');
|
||||
const { UsernameNotifSelector, UserIDNotifSelector } = require('./NotificationService');
|
||||
|
||||
|
||||
/**
|
||||
* Class ReferralCodeService
|
||||
*
|
||||
* This class is responsible for managing the generation and handling of referral codes
|
||||
* within the application. It extends the BaseService and provides methods to initialize
|
||||
* referral code generation for users, verify referrals, and manage updates to user
|
||||
* storage based on successful referrals. The service ensures that referral codes are
|
||||
* unique and properly assigned during user interactions.
|
||||
*/
|
||||
class ReferralCodeService extends BaseService {
|
||||
/**
|
||||
* Generates a unique referral code for the specified user.
|
||||
* The method attempts to create a referral code and store it
|
||||
* in the database. It will retry up to a maximum number of
|
||||
* attempts if a collision occurs. If the user is missing or
|
||||
* not properly defined, an error is reported.
|
||||
*
|
||||
* @param {Object} user - The user object for whom the referral
|
||||
* code is being generated.
|
||||
* @returns {Promise<string>} The generated referral code.
|
||||
* @throws {Error} If the user is not defined or if an error
|
||||
* occurs while writing to the database.
|
||||
*/
|
||||
_construct () {
|
||||
this.REFERRAL_INCREASE_LEFT = 1 * 1024 * 1024 * 1024; // 1 GB
|
||||
this.REFERRAL_INCREASE_RIGHT = 1 * 1024 * 1024 * 1024; // 1 GB
|
||||
this.STORAGE_INCREASE_STRING = '1 GB';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the ReferralCodeService by setting up event listeners
|
||||
* for user email confirmation. Listens for the 'user.email-confirmed'
|
||||
* event and triggers the on_verified method when a user confirms their
|
||||
* email address.
|
||||
*
|
||||
* @async
|
||||
* @returns {Promise<void>} A promise that resolves when initialization is complete.
|
||||
*/
|
||||
async _init () {
|
||||
const svc_event = this.services.get('event');
|
||||
svc_event.on('user.email-confirmed', async (_, { user_uid }) => {
|
||||
@ -39,6 +73,17 @@ class ReferralCodeService extends BaseService {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates a unique referral code for the specified user.
|
||||
* This method attempts to create a referral code and store it in the database.
|
||||
* It retries the generation process up to a predefined number of attempts if
|
||||
* any errors occur during the database write operation.
|
||||
*
|
||||
* @param {Object} user - The user for whom the referral code is being generated.
|
||||
* @returns {Promise<string>} The generated referral code.
|
||||
* @throws Will throw an error if the user is missing or if the code generation fails after retries.
|
||||
*/
|
||||
async gen_referral_code (user) {
|
||||
let iteration = 0;
|
||||
let rng = seedrandom(`gen1-${user.id}`);
|
||||
@ -54,6 +99,7 @@ class ReferralCodeService extends BaseService {
|
||||
throw err;
|
||||
}
|
||||
|
||||
// Constant representing the number of attempts to generate a unique referral code.
|
||||
const TRIES = 5;
|
||||
|
||||
const db = Context.get('services').get('database').get(DB_WRITE, 'referrals');
|
||||
@ -84,6 +130,15 @@ class ReferralCodeService extends BaseService {
|
||||
throw last_error ?? new Error('unknown error from gen_referral_code');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles the logic when a user is verified.
|
||||
* This method checks if the user has been referred by another user and updates
|
||||
* the storage of both the referring user and the newly verified user accordingly.
|
||||
*
|
||||
* @param {Object} user - The user object representing the verified user.
|
||||
* @returns {Promise<void>} - A promise that resolves when the operation is complete.
|
||||
*/
|
||||
async on_verified (user) {
|
||||
if ( ! user.referred_by ) return;
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -19,15 +20,59 @@
|
||||
const { Context } = require("../util/context");
|
||||
const BaseService = require("./BaseService");
|
||||
|
||||
|
||||
/**
|
||||
* Class RefreshAssociationsService
|
||||
*
|
||||
* This class is responsible for managing the refresh of associations in the system.
|
||||
* It extends the BaseService and provides methods to handle the refreshing operations
|
||||
* with context fallback capabilities to ensure reliability during the execution of tasks.
|
||||
*/
|
||||
class RefreshAssociationsService extends BaseService {
|
||||
/**
|
||||
* Executes the consolidation process to refresh the associations cache.
|
||||
* This method is triggered on the '__on_boot.consolidation' event and
|
||||
* ensures that the cache is updated periodically. The first update occurs
|
||||
* after a delay of 15 seconds, followed by continuous updates every 30 seconds.
|
||||
*
|
||||
* @async
|
||||
* @returns {Promise<void>} - A promise that resolves when the cache refresh process is complete.
|
||||
*/
|
||||
async ['__on_boot.consolidation'] () {
|
||||
const { refresh_associations_cache } = require('../helpers');
|
||||
|
||||
|
||||
/**
|
||||
* Executes the consolidation process on boot, refreshing the associations cache.
|
||||
* This method invokes the `refresh_associations_cache` function within a fallback context.
|
||||
* The cache refresh is scheduled to run every 30 seconds after an initial delay of 15 seconds.
|
||||
*/
|
||||
await Context.allow_fallback(async () => {
|
||||
refresh_associations_cache();
|
||||
});
|
||||
/**
|
||||
* Executes the refresh associations cache function within a fallback context.
|
||||
* This method ensures that the cache is refreshed properly, handling any
|
||||
* potential errors that may occur during execution. It utilizes the Context
|
||||
* utility to allow error handling without interrupting the main application flow.
|
||||
*/
|
||||
setTimeout(() => {
|
||||
/**
|
||||
* Schedules periodic refresh of associations cache after a timeout.
|
||||
*
|
||||
* This method initiates a cache refresh operation that is run at a specified interval.
|
||||
* The initial refresh occurs after a delay, followed by regular refreshes every 30 seconds.
|
||||
*
|
||||
* @returns {Promise<void>} A promise that resolves when the refresh process starts.
|
||||
*/
|
||||
setInterval(async () => {
|
||||
/**
|
||||
* Initializes a periodic refresh of associations in the cache.
|
||||
* The method sets a timeout before starting an interval that calls
|
||||
* the `refresh_associations_cache` function every 30 seconds.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
await Context.allow_fallback(async () => {
|
||||
await refresh_associations_cache();
|
||||
})
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"claude"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -21,7 +22,20 @@ const { PropType } = require("../om/definitions/PropType");
|
||||
const { Context } = require("../util/context");
|
||||
const BaseService = require("./BaseService");
|
||||
|
||||
|
||||
/**
|
||||
* RegistrantService class handles the registration and initialization of property types and object mappings
|
||||
* in the system registry. It extends BaseService and provides functionality to populate the registry with
|
||||
* property types and their mappings, ensuring type validation and proper inheritance relationships.
|
||||
* @extends BaseService
|
||||
*/
|
||||
class RegistrantService extends BaseService {
|
||||
/**
|
||||
* Service class responsible for registering and managing property types and object mappings
|
||||
* in the system registry. Extends BaseService to provide core functionality.
|
||||
*
|
||||
* @extends BaseService
|
||||
*/
|
||||
async _init () {
|
||||
const svc_systemValidation = this.services.get('system-validation');
|
||||
try {
|
||||
@ -33,11 +47,28 @@ class RegistrantService extends BaseService {
|
||||
);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Initializes the registrant service by populating the registry.
|
||||
* Attempts to populate the registry with property types and mappings.
|
||||
* If population fails, marks the system as invalid through system validation.
|
||||
* @throws {Error} Propagates any errors from registry population to system validation
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async _populate_registry () {
|
||||
const svc_registry = this.services.get('registry');
|
||||
|
||||
// This context will be provided to the `create` methods
|
||||
// that transform the raw data into objects.
|
||||
/**
|
||||
* Populates the registry with property types and object mappings.
|
||||
* Loads property type definitions and mappings from configuration files,
|
||||
* validates them for duplicates and dependencies, and registers them
|
||||
* in the registry service.
|
||||
*
|
||||
* @throws {Error} If duplicate property types are found or if a property type
|
||||
* references an undefined super type
|
||||
* @private
|
||||
*/
|
||||
const ctx = Context.get().sub({
|
||||
registry: svc_registry,
|
||||
});
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"mistral","model":"mistral-large-latest"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -19,11 +20,26 @@
|
||||
const { AdvancedBase } = require("@heyputer/putility");
|
||||
const BaseService = require("./BaseService");
|
||||
|
||||
|
||||
/**
|
||||
* @class MapCollection
|
||||
* @extends AdvancedBase
|
||||
*
|
||||
* The `MapCollection` class extends the `AdvancedBase` class and is responsible for managing a collection of key-value pairs in a distributed system.
|
||||
* It leverages the `kvjs` library for key-value storage and the `uuid` library for generating unique identifiers for each key-value pair.
|
||||
* This class provides methods for basic CRUD operations (create, read, update, delete) on the key-value pairs, as well as methods for checking the existence of a key and retrieving all keys in the collection.
|
||||
*/
|
||||
class MapCollection extends AdvancedBase {
|
||||
static MODULES = {
|
||||
kv: globalThis.kv,
|
||||
uuidv4: require('uuid').v4,
|
||||
}
|
||||
/**
|
||||
* @method MapCollection#_mk_key
|
||||
* @description Creates a unique key for the map collection.
|
||||
* @param {string} key - The key to be prefixed.
|
||||
* @returns {string} The prefixed key.
|
||||
*/
|
||||
constructor () {
|
||||
super();
|
||||
// We use kvjs instead of a plain object because it doesn't
|
||||
@ -48,6 +64,15 @@ class MapCollection extends AdvancedBase {
|
||||
return this.kv.del(this._mk_key(key));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves all keys in the map collection, excluding the prefix.
|
||||
*
|
||||
* This method fetches all keys that match the pattern for the current map collection.
|
||||
* The prefix `registry:map:${this.map_id}:` is stripped from each key before returning.
|
||||
*
|
||||
* @returns {string[]} An array of keys without the prefix.
|
||||
*/
|
||||
keys () {
|
||||
const keys = this.kv.keys(`registry:map:${this.map_id}:*`);
|
||||
return keys.map(k => k.slice(`registry:map:${this.map_id}:`.length));
|
||||
@ -58,15 +83,39 @@ class MapCollection extends AdvancedBase {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @class RegistryService
|
||||
* @extends BaseService
|
||||
* @description The RegistryService class manages collections of key-value pairs, allowing for dynamic registration and retrieval of collections.
|
||||
* It extends the BaseService class and provides methods to register new collections, retrieve existing collections, and handle consolidation tasks upon boot.
|
||||
*/
|
||||
class RegistryService extends BaseService {
|
||||
static MODULES = {
|
||||
MapCollection,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the RegistryService by setting up the collections.
|
||||
*
|
||||
* This method is called during the construction phase of the service.
|
||||
* It initializes an empty object to hold collections.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_construct () {
|
||||
this.collections_ = {};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the service by setting up the collections object.
|
||||
* This method is called during the construction phase of the service.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
async ['__on_boot.consolidation'] () {
|
||||
const services = this.services;
|
||||
await services.emit('registry.collections');
|
||||
|
@ -1,14 +1,32 @@
|
||||
// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o-mini"}}
|
||||
const { get_user } = require("../helpers");
|
||||
const { Context } = require("../util/context");
|
||||
const { TeePromise } = require("../util/promise");
|
||||
const { Actor, UserActorType } = require("./auth/Actor");
|
||||
const BaseService = require("./BaseService");
|
||||
|
||||
|
||||
/**
|
||||
* SUService is a specialized service that extends BaseService,
|
||||
* designed to manage system user and actor interactions. It
|
||||
* handles the initialization of system-level user and actor
|
||||
* instances, providing methods to retrieve the system actor
|
||||
* and perform actions with elevated privileges.
|
||||
*/
|
||||
class SUService extends BaseService {
|
||||
/**
|
||||
* Initializes the SUService instance, creating promises for system user
|
||||
* and system actor. This method does not take any parameters and does
|
||||
* not return a value.
|
||||
*/
|
||||
_construct () {
|
||||
this.sys_user_ = new TeePromise();
|
||||
this.sys_actor_ = new TeePromise();
|
||||
}
|
||||
/**
|
||||
* Initializes the SUService by creating instances of TeePromise for system user and actor.
|
||||
* This method is invoked during the construction of the SUService class.
|
||||
*/
|
||||
async ['__on_boot.consolidation'] () {
|
||||
const sys_user = await get_user({ username: 'system' });
|
||||
this.sys_user_.resolve(sys_user);
|
||||
@ -19,15 +37,44 @@ class SUService extends BaseService {
|
||||
});
|
||||
this.sys_actor_.resolve(sys_actor);
|
||||
}
|
||||
/**
|
||||
* Resolves the system actor and user upon booting the service.
|
||||
* This method fetches the system user and then creates an Actor
|
||||
* instance for the user, resolving both promises. It's called
|
||||
* automatically during the boot process.
|
||||
*
|
||||
* @async
|
||||
* @returns {Promise<void>} A promise that resolves when both the
|
||||
* system user and actor have been set.
|
||||
*/
|
||||
async get_system_actor () {
|
||||
return this.sys_actor_;
|
||||
}
|
||||
/**
|
||||
* Retrieves the system actor instance.
|
||||
*
|
||||
* This method returns a promise that resolves to the system actor. The actor
|
||||
* represents the system user and is initialized during the boot process.
|
||||
*
|
||||
* @returns {Promise<TeePromise>} A promise that resolves to the system actor.
|
||||
*/
|
||||
async sudo (actor, callback) {
|
||||
if ( ! callback ) {
|
||||
callback = actor;
|
||||
actor = await this.sys_actor_;
|
||||
}
|
||||
actor = Actor.adapt(actor);
|
||||
/**
|
||||
* Performs an operation as a specified actor, allowing for callback execution
|
||||
* within the context of that actor. If no actor is provided, the system actor
|
||||
* is used by default. The adapted actor is then utilized to execute the callback
|
||||
* under the appropriate user context.
|
||||
*
|
||||
* @param {Actor} actor - The actor to perform the operation as.
|
||||
* If omitted, defaults to the system actor.
|
||||
* @param {Function} callback - The function to execute within the actor's context.
|
||||
* @returns {Promise} A promise that resolves with the result of the callback execution.
|
||||
*/
|
||||
return await Context.get().sub({
|
||||
actor,
|
||||
user: actor.type.user,
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o-mini"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -18,23 +19,61 @@
|
||||
*/
|
||||
const BaseService = require("./BaseService");
|
||||
|
||||
|
||||
/**
|
||||
* Class representing a service for managing and executing scripts.
|
||||
* The ScriptService extends the BaseService and provides functionality
|
||||
* to register scripts and execute them based on commands.
|
||||
*/
|
||||
class BackendScript {
|
||||
constructor (name, fn) {
|
||||
this.name = name;
|
||||
this.fn = fn;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Executes the script function with the provided context and arguments.
|
||||
*
|
||||
* @async
|
||||
* @param {Object} ctx - The context in which the script is run.
|
||||
* @param {Array} args - The arguments to be passed to the script function.
|
||||
* @returns {Promise<any>} The result of the script function execution.
|
||||
*/
|
||||
async run (ctx, args) {
|
||||
return await this.fn(ctx, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Class ScriptService extends BaseService to manage and execute scripts.
|
||||
* It provides functionality to register scripts and run them through defined commands.
|
||||
*/
|
||||
class ScriptService extends BaseService {
|
||||
/**
|
||||
* Initializes the service by registering script-related commands.
|
||||
*
|
||||
* This method retrieves the command service and sets up the commands
|
||||
* related to script execution. It also defines a command handler that
|
||||
* looks up and executes a script based on user input arguments.
|
||||
*
|
||||
* @async
|
||||
* @function _init
|
||||
*/
|
||||
_construct () {
|
||||
this.scripts = [];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the script service by registering command handlers
|
||||
* and setting up the environment for executing scripts.
|
||||
*
|
||||
* @async
|
||||
* @returns {Promise<void>} A promise that resolves when the initialization is complete.
|
||||
*/
|
||||
async _init () {
|
||||
const svc_commands = this.services.get('commands');
|
||||
svc_commands.registerCommands('script', [
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o-mini"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -21,7 +22,21 @@ const BaseService = require("./BaseService");
|
||||
const express = require('express');
|
||||
const _path = require('path');
|
||||
|
||||
|
||||
/**
|
||||
* Class representing the ServeGUIService, which extends the BaseService.
|
||||
* This service is responsible for setting up the GUI-related routes
|
||||
* and serving static files for the Puter application.
|
||||
*/
|
||||
class ServeGUIService extends BaseService {
|
||||
/**
|
||||
* Handles the installation of GUI-related routes for the web server.
|
||||
* This method sets up the routing for Puter site domains and other cases,
|
||||
* including static file serving from the public directory.
|
||||
*
|
||||
* @async
|
||||
* @returns {Promise<void>} Resolves when routing is successfully set up.
|
||||
*/
|
||||
async ['__on_install.routes-gui'] () {
|
||||
const { app } = this.services.get('web-server');
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"claude"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -20,4 +21,11 @@
|
||||
/**
|
||||
* ServeLandingService is for "landing" pages, like payment success or failure.
|
||||
*/
|
||||
/**
|
||||
* @class ServeLandingService
|
||||
* @description Handles the serving and rendering of landing pages throughout the Puter platform.
|
||||
* This service manages various landing pages such as payment success/failure pages, confirmation
|
||||
* pages, and other static informational pages. It provides a centralized way to serve these
|
||||
* pages while maintaining consistent styling and functionality across the platform.
|
||||
*/
|
||||
class ServceLandingService {}
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o-mini"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -18,6 +19,17 @@
|
||||
*/
|
||||
const { AdvancedBase } = require("@heyputer/putility");
|
||||
|
||||
|
||||
/**
|
||||
* Class ServicePatch
|
||||
*
|
||||
* This class extends the AdvancedBase class and provides functionality
|
||||
* to apply patches to service methods dynamically. The patching mechanism
|
||||
* ensures that the methods defined in the PATCH_METHODS static object
|
||||
* are replaced with their respective patch implementations while maintaining
|
||||
* a reference to the original service methods for potential fallback or
|
||||
* additional processing.
|
||||
*/
|
||||
class ServicePatch extends AdvancedBase {
|
||||
patch ({ original_service }) {
|
||||
const patch_methods = this._get_merged_static_object('PATCH_METHODS');
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"xai"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -26,27 +27,75 @@ const { DB_WRITE } = require("./database/consts");
|
||||
* This service is responsible for updating session activity
|
||||
* timestamps and maintaining the number of active sessions.
|
||||
*/
|
||||
/**
|
||||
* @class SessionService
|
||||
* @description
|
||||
* The SessionService class manages session-related operations within the Puter application.
|
||||
* It handles the creation, retrieval, updating, and deletion of user sessions. This service:
|
||||
* - Tracks session activity with timestamps.
|
||||
* - Maintains a cache of active sessions.
|
||||
* - Periodically updates session information in the database.
|
||||
* - Ensures the integrity of session data across different parts of the application.
|
||||
* - Provides methods to interact with sessions, including session creation, retrieval, and termination.
|
||||
*/
|
||||
class SessionService extends BaseService {
|
||||
static MODULES = {
|
||||
// uuidv5: require('uuid').v5,
|
||||
uuidv4: require('uuid').v4,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the SessionService by setting up the database connection and scheduling periodic session updates.
|
||||
* @async
|
||||
* @memberof SessionService
|
||||
* @instance
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
_construct () {
|
||||
this.sessions = {};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the session storage by setting up the database connection
|
||||
* and starting a periodic session update interval.
|
||||
*
|
||||
* @async
|
||||
* @memberof SessionService
|
||||
* @method _init
|
||||
*/
|
||||
async _init () {
|
||||
this.db = await this.services.get('database').get(DB_WRITE, 'session');
|
||||
|
||||
(async () => {
|
||||
// TODO: change to 5 minutes or configured value
|
||||
/**
|
||||
* Initializes periodic session updates.
|
||||
*
|
||||
* This method sets up an interval to call `_update_sessions` every 2 minutes.
|
||||
*
|
||||
* @memberof SessionService
|
||||
* @private
|
||||
* @async
|
||||
* @param {none} - No parameters are required.
|
||||
* @returns {Promise<void>} - Resolves when the interval is set.
|
||||
*/
|
||||
asyncSafeSetInterval(async () => {
|
||||
await this._update_sessions();
|
||||
}, 2 * MINUTE);
|
||||
})();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the session service by setting up the database connection and scheduling periodic session updates.
|
||||
*
|
||||
* @async
|
||||
* @private
|
||||
* @returns {Promise<void>} A promise that resolves when initialization is complete.
|
||||
* @note This method is called internally during the service setup. It does not need to be invoked manually.
|
||||
*/
|
||||
async create_session (user, meta) {
|
||||
const unix_ts = Math.floor(Date.now() / 1000);
|
||||
|
||||
@ -76,6 +125,14 @@ class SessionService extends BaseService {
|
||||
return session;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves a session by its UUID, updates the session's last touch timestamp,
|
||||
* and prepares the session data for external use by removing internal values.
|
||||
*
|
||||
* @param {string} uuid - The UUID of the session to retrieve.
|
||||
* @returns {Object|undefined} The session object with internal values removed, or undefined if the session does not exist.
|
||||
*/
|
||||
async get_session_ (uuid) {
|
||||
let session = this.sessions[uuid];
|
||||
if ( session ) {
|
||||
@ -90,6 +147,11 @@ class SessionService extends BaseService {
|
||||
session.last_store = Date.now();
|
||||
session.meta = this.db.case({
|
||||
mysql: () => session.meta,
|
||||
/**
|
||||
* Parses session metadata based on the database type.
|
||||
* @param {Object} session - The session object from the database.
|
||||
* @returns {Object} The parsed session metadata.
|
||||
*/
|
||||
otherwise: () => JSON.parse(session.meta ?? "{}")
|
||||
})();
|
||||
const user = await get_user(session.user_id);
|
||||
@ -97,6 +159,11 @@ class SessionService extends BaseService {
|
||||
this.sessions[uuid] = session;
|
||||
return session;
|
||||
}
|
||||
/**
|
||||
* Retrieves a session by its UUID, updates its last touch time, and prepares it for external use.
|
||||
* @param {string} uuid - The unique identifier for the session to retrieve.
|
||||
* @returns {Promise<Object|undefined>} The session object with internal values removed, or undefined if not found.
|
||||
*/
|
||||
async get_session (uuid) {
|
||||
const session = await this.get_session_(uuid);
|
||||
if ( session ) {
|
||||
@ -136,6 +203,13 @@ class SessionService extends BaseService {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes a session from the in-memory cache and the database.
|
||||
*
|
||||
* @param {string} uuid - The UUID of the session to remove.
|
||||
* @returns {Promise} A promise that resolves to the result of the database write operation.
|
||||
*/
|
||||
async _update_sessions () {
|
||||
this.log.tick('UPDATING SESSIONS');
|
||||
const now = Date.now();
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -28,6 +29,10 @@ const BaseService = require("./BaseService");
|
||||
const { DB_WRITE } = require("./database/consts");
|
||||
const { UsernameNotifSelector } = require("./NotificationService");
|
||||
|
||||
|
||||
/**
|
||||
401: * @classdesc ShareService - Handles share related operations.
|
||||
402: */
|
||||
class ShareService extends BaseService {
|
||||
static MODULES = {
|
||||
uuidv4: require('uuid').v4,
|
||||
@ -35,6 +40,19 @@ class ShareService extends BaseService {
|
||||
express: require('express'),
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Method to handle the creation of a new share
|
||||
*
|
||||
* This method creates a new share and saves it to the database.
|
||||
* It takes three parameters: the issuer of the share, the recipient's email address, and the data to be shared.
|
||||
* The method returns the UID of the created share.
|
||||
*
|
||||
* @param {Actor} issuer - The actor who is creating the share
|
||||
* @param {string} email - The email address of the recipient
|
||||
* @param {object} data - The data to be shared
|
||||
* @returns {string} - The UID of the created share
|
||||
*/
|
||||
async _init () {
|
||||
this.db = await this.services.get('database').get(DB_WRITE, 'share');
|
||||
|
||||
@ -104,6 +122,15 @@ class ShareService extends BaseService {
|
||||
|
||||
install_sharelink_endpoints ({ app }) {
|
||||
// track: scoping iife
|
||||
/**
|
||||
* This method is responsible for processing the share link application request.
|
||||
* It checks if the share token is valid and if the user making the request is the intended recipient.
|
||||
* If both conditions are met, it grants the requested permissions to the user and deletes the share from the database.
|
||||
*
|
||||
* @param {Object} req - Express request object.
|
||||
* @param {Object} res - Express response object.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const router = (() => {
|
||||
const require = this.require;
|
||||
const express = require('express');
|
||||
@ -298,6 +325,16 @@ class ShareService extends BaseService {
|
||||
|
||||
install_share_endpoint ({ app }) {
|
||||
// track: scoping iife
|
||||
/**
|
||||
401: * Method to grant permissions to the user after they have applied the share token.
|
||||
402: *
|
||||
403: * @param {Object} req - Request object containing the application request.
|
||||
404: * @param {Object} res - Response object to send the response.
|
||||
405: * @returns {Promise<void>} Resolves when the operation is completed.
|
||||
406: */
|
||||
407: ShareService.prototype.grant_permissions_on_apply = async function (req, res) {
|
||||
408: // Your implementation here.
|
||||
409: };
|
||||
const router = (() => {
|
||||
const require = this.require;
|
||||
const express = require('express');
|
||||
@ -338,6 +375,15 @@ class ShareService extends BaseService {
|
||||
}).attach(router);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
401 * @description Method to get a share by its UID.
|
||||
402 *
|
||||
403 * @param {Object} params - An object containing the UID of the share.
|
||||
404 * @param {String} params.uid - The UID of the share.
|
||||
405 *
|
||||
406 * @returns {Promise<Share>} A promise that resolves to the Share object.
|
||||
407 */
|
||||
async get_share ({ uid }) {
|
||||
const [share] = await this.db.read(
|
||||
'SELECT * FROM share WHERE uid = ?',
|
||||
|
@ -1,5 +1,14 @@
|
||||
// METADATA // {"ai-commented":{"service":"claude"}}
|
||||
const BaseService = require("./BaseService");
|
||||
|
||||
|
||||
/**
|
||||
* Service responsible for handling graceful system shutdown operations.
|
||||
* Extends BaseService to provide shutdown functionality with optional reason and exit code.
|
||||
* Ensures proper cleanup and logging when the application needs to terminate.
|
||||
* @class ShutdownService
|
||||
* @extends BaseService
|
||||
*/
|
||||
class ShutdownService extends BaseService {
|
||||
shutdown ({ reason, code } = {}) {
|
||||
this.log.info(`Puter is shutting down: ${reason ?? 'no reason provided'}`);
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o-mini"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -18,6 +19,12 @@
|
||||
*/
|
||||
const { AdvancedBase } = require("@heyputer/putility");
|
||||
|
||||
|
||||
/**
|
||||
* Represents a Storage Service that extends the functionality of AdvancedBase.
|
||||
* This class is responsible for handling storage-related operations within the application,
|
||||
* enabling efficient management and access to data services.
|
||||
*/
|
||||
class StorageService extends AdvancedBase {
|
||||
constructor ({ services }) {
|
||||
super(services);
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -19,6 +20,13 @@
|
||||
const { TechnicalError } = require("../errors/TechnicalError");
|
||||
const { quot } = require("../util/strutil");
|
||||
|
||||
|
||||
/**
|
||||
* Represents a service that applies strategies based on provided configuration
|
||||
* and specified keys. The StrategizedService class initializes and manages
|
||||
* strategies for a given service, ensuring that the necessary configurations
|
||||
* and arguments are provided before attempting to execute any strategy logic.
|
||||
*/
|
||||
class StrategizedService {
|
||||
constructor (service_resources, ...a) {
|
||||
const { my_config, args, name } = service_resources;
|
||||
@ -53,10 +61,25 @@ class StrategizedService {
|
||||
return this.strategy;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the service and throws an error if initialization fails.
|
||||
* This method utilizes the initError property to determine if an error
|
||||
* occurred during the setup process in the constructor.
|
||||
*
|
||||
* @throws {TechnicalError} Throws a TechnicalError if initError is set.
|
||||
*/
|
||||
async init () {
|
||||
throw this.initError;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new instance of the service.
|
||||
*
|
||||
* This method initializes any necessary resources or settings for the service instance.
|
||||
* It does not accept any parameters and does not return any value.
|
||||
*/
|
||||
async construct () {}
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,40 @@
|
||||
// METADATA // {"ai-commented":{"service":"xai"}}
|
||||
const { LLRead } = require("../filesystem/ll_operations/ll_read");
|
||||
const { Context } = require("../util/context");
|
||||
const { whatis } = require("../util/langutil");
|
||||
const { stream_to_buffer } = require("../util/streamutil");
|
||||
const BaseService = require("./BaseService");
|
||||
|
||||
|
||||
/**
|
||||
* The `SystemDataService` class extends `BaseService` to provide functionality for interpreting and dereferencing data structures.
|
||||
* This service handles the recursive interpretation of complex data types including objects and arrays, as well as dereferencing
|
||||
* JSON-address pointers to fetch and process data from file system nodes. It is designed to:
|
||||
* - Interpret nested structures by recursively calling itself for each nested element.
|
||||
* - Dereference JSON pointers, which involves reading from the filesystem, parsing JSON, and optionally selecting nested properties.
|
||||
* - Manage different data types encountered during operations, ensuring proper handling or throwing errors for unrecognized types.
|
||||
*/
|
||||
class SystemDataService extends BaseService {
|
||||
/**
|
||||
* Recursively interprets the structure of the given data object.
|
||||
* This method handles dereferencing and deep interpretation of nested structures.
|
||||
*
|
||||
* @param {Object|Array|string|number|boolean|null} data - The data to interpret.
|
||||
* Can be an object, array, or primitive value.
|
||||
* @returns {Promise<Object|Array|string|number|boolean|null>} The interpreted data.
|
||||
* For objects and arrays, this method recursively interprets each element.
|
||||
* For special objects with a '$' property, it performs dereferencing.
|
||||
*/
|
||||
async _init () {}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the SystemDataService.
|
||||
* This method is called when the service is instantiated to set up any necessary state or resources.
|
||||
*
|
||||
* @async
|
||||
* @returns {Promise<void>} A promise that resolves when initialization is complete.
|
||||
*/
|
||||
async interpret (data) {
|
||||
if ( whatis(data) === 'object' && data.$ ) {
|
||||
return await this.dereference_(data);
|
||||
@ -28,6 +56,17 @@ class SystemDataService extends BaseService {
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Recursively interprets and potentially dereferences complex data structures.
|
||||
*
|
||||
* This method handles:
|
||||
* - Objects with special dereferencing syntax (indicated by the `$` property).
|
||||
* - Nested objects and arrays by recursively calling itself on each element.
|
||||
*
|
||||
* @param {Object|Array|*} data - The data to interpret, which can be of any type.
|
||||
* @returns {Promise<*>} The interpreted result, which could be a primitive, object, or array.
|
||||
*/
|
||||
async dereference_ (data) {
|
||||
const svc_fs = this.services.get('filesystem');
|
||||
if ( data.$ === 'json-address' ) {
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"mistral","model":"mistral-large-latest"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -18,6 +19,25 @@
|
||||
*/
|
||||
const BaseService = require("./BaseService");
|
||||
|
||||
|
||||
/**
|
||||
* SystemValidationService class.
|
||||
*
|
||||
* This class extends BaseService and is responsible for handling system validation
|
||||
* and marking the server as invalid. It includes methods for reporting invalid
|
||||
* system states, raising alarms, and managing the server's response in different
|
||||
* environments (e.g., development and production).
|
||||
*
|
||||
* @class
|
||||
* @extends BaseService
|
||||
*/
|
||||
|
||||
```javascript
|
||||
class SystemValidationService extends BaseService {
|
||||
```
|
||||
|
||||
```javascript
|
||||
}
|
||||
class SystemValidationService extends BaseService {
|
||||
/**
|
||||
* Marks the server is being in an invalid state.
|
||||
@ -28,6 +48,15 @@ class SystemValidationService extends BaseService {
|
||||
* @param {*} message - why mark_invalid was called
|
||||
* @param {*} source - the error that caused the invalid state, if any
|
||||
*/
|
||||
/**
|
||||
* Marks the server as being in an invalid state.
|
||||
*
|
||||
* This method is used to indicate that the server is in a serious error state. It will attempt
|
||||
* to alert the user and then shut down the server after 25 minutes.
|
||||
*
|
||||
* @param {string} message - A description of why mark_invalid was called.
|
||||
* @param {Error} [source] - The error that caused the invalid state, if any.
|
||||
*/
|
||||
async mark_invalid (message, source) {
|
||||
if ( ! source ) source = new Error('no source error');
|
||||
|
||||
@ -56,6 +85,12 @@ class SystemValidationService extends BaseService {
|
||||
// Display a permanent message in the console
|
||||
const svc_devConsole = this.services.get('dev-console');
|
||||
svc_devConsole.turn_on_the_warning_lights();
|
||||
/**
|
||||
* Turns on the warning lights in the developer console and adds a widget indicating that the system is in an invalid state.
|
||||
* This is used in development mode to provide a visual indicator of the invalid state without shutting down the server.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
svc_devConsole.add_widget(() => {
|
||||
return `\x1B[33;1m *** SYSTEM IS IN AN INVALID STATE *** \x1B[0m`;
|
||||
});
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -18,17 +19,47 @@
|
||||
*/
|
||||
const opentelemetry = require("@opentelemetry/api");
|
||||
|
||||
|
||||
/**
|
||||
* @class TraceService
|
||||
* @description This class is responsible for creating and managing
|
||||
* traces for the Puter application using the OpenTelemetry API.
|
||||
* It provides methods to start spans, which are used for tracking
|
||||
* operations and measuring performance within the application.
|
||||
*/
|
||||
class TraceService {
|
||||
/**
|
||||
* Retrieves the tracer instance used for creating spans.
|
||||
* This method is a getter that returns the current tracer object.
|
||||
*
|
||||
* @returns {Tracer} The tracer instance for the TraceService.
|
||||
*/
|
||||
constructor () {
|
||||
this.tracer_ = opentelemetry.trace.getTracer(
|
||||
'puter-filesystem-tracer'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the tracer instance used by the TraceService.
|
||||
*
|
||||
* @returns {import("@opentelemetry/api").Tracer} The tracer instance for this service.
|
||||
*/
|
||||
get tracer () {
|
||||
return this.tracer_;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Starts an active span for executing a function with tracing.
|
||||
* This method wraps the provided function `fn` in a span, managing
|
||||
* span lifecycle, error handling, and status updates.
|
||||
*
|
||||
* @param {string} name - The name of the span.
|
||||
* @param {Function} fn - The asynchronous function to execute within the span.
|
||||
* @returns {Promise} - A promise that resolves to the return value of `fn`.
|
||||
*/
|
||||
async spanify (name, fn) {
|
||||
return await this.tracer.startActiveSpan(name, async span => {
|
||||
try {
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"claude"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -22,7 +23,23 @@ const { format_as_usd } = require("../util/strutil");
|
||||
const { MINUTE, SECOND } = require("../util/time");
|
||||
const BaseService = require("./BaseService");
|
||||
|
||||
|
||||
/**
|
||||
* @class TrackSpendingService
|
||||
* @extends BaseService
|
||||
* @description Service for tracking and monitoring API spending across different vendors and strategies.
|
||||
* Implements cost tracking for various AI models (like GPT-4, DALL-E), manages spending windows,
|
||||
* and provides alerting functionality when spending thresholds are exceeded. Supports different
|
||||
* pricing strategies for chat completions and image generation services.
|
||||
*/
|
||||
class TrackSpendingService extends BaseService {
|
||||
/**
|
||||
* @class TrackSpendingService
|
||||
* @extends BaseService
|
||||
* @description Service for tracking and monitoring API spending across different vendors and strategies.
|
||||
* Implements cost tracking for chat completions and image generations, with configurable spending alerts.
|
||||
* Maintains time-windowed spending metrics and triggers alarms when spending thresholds are exceeded.
|
||||
*/
|
||||
static ChatCompletionStrategy = class ChatCompletionStrategy {
|
||||
static models = {
|
||||
'gpt-4-1106-preview': {
|
||||
@ -70,6 +87,13 @@ class TrackSpendingService extends BaseService {
|
||||
return cost;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validates pricing configurations for all models to prevent division by zero errors
|
||||
* @async
|
||||
* @throws {Error} If any model's pricing configuration would cause division by zero
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async validate () {
|
||||
// Ensure no models will cause division by zero
|
||||
for ( const model in this.constructor.models ) {
|
||||
@ -83,6 +107,14 @@ class TrackSpendingService extends BaseService {
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @class ImageGenerationStrategy
|
||||
* @description A strategy class for handling image generation cost calculations.
|
||||
* Supports different models (DALL-E 2 and 3) with varying pricing based on image
|
||||
* dimensions. Maintains a static pricing model configuration and provides methods
|
||||
* to calculate costs for image generation requests. Part of the TrackSpendingService
|
||||
* system for monitoring and tracking API usage costs.
|
||||
*/
|
||||
static ImageGenerationStrategy = class ImageGenerationStrategy {
|
||||
static models = {
|
||||
'dall-e-3': {
|
||||
@ -127,6 +159,21 @@ class TrackSpendingService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the TrackSpendingService with spending tracking strategies and alarm monitoring
|
||||
*
|
||||
* Sets up cost tracking strategies for different services (chat completion, image generation),
|
||||
* initializes spending windows for monitoring, and configures periodic alarm checks for high spending.
|
||||
*
|
||||
* Creates an interval that checks spending levels and triggers alarms when spending exceeds
|
||||
* configured thresholds.
|
||||
*
|
||||
* @private
|
||||
* @async
|
||||
* @throws {Error} If no logging service is configured
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async _init () {
|
||||
const strategies = {
|
||||
'chat-completion': new this.constructor.ChatCompletionStrategy({
|
||||
@ -168,6 +215,14 @@ class TrackSpendingService extends BaseService {
|
||||
|
||||
const svc_alarm = this.services.get('alarm');
|
||||
|
||||
|
||||
/**
|
||||
* Records spending data for a vendor using a specified strategy
|
||||
* @param {string} vendor - The vendor name/identifier
|
||||
* @param {string} strategy_key - Key identifying the pricing strategy to use
|
||||
* @param {Object} data - Data needed to calculate cost based on the strategy
|
||||
* @throws {Error} If strategy_key is invalid/unknown
|
||||
*/
|
||||
setInterval(() => {
|
||||
const spending = this.get_window_spending_();
|
||||
|
||||
@ -212,6 +267,13 @@ class TrackSpendingService extends BaseService {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the total spending across all tracked windows
|
||||
*
|
||||
* @private
|
||||
* @returns {number} The sum of all spending windows' current values
|
||||
*/
|
||||
get_window_spending_ () {
|
||||
const windows = Object.values(this.spend_windows);
|
||||
return windows.reduce((sum, win) => {
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"xai"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -40,6 +41,13 @@ class WSPushService extends AdvancedBase {
|
||||
this._on_outer_gui.bind(this));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the WSPushService by setting up event listeners for various file system operations.
|
||||
*
|
||||
* @param {Object} options - The configuration options for the service.
|
||||
* @param {Object} options.services - An object containing service dependencies.
|
||||
*/
|
||||
async _on_fs_create (key, data) {
|
||||
const { node, context } = data;
|
||||
const { socketio } = this.modules;
|
||||
@ -57,6 +65,20 @@ class WSPushService extends AdvancedBase {
|
||||
|
||||
const response = await node.getSafeEntry({ thumbnail: true });
|
||||
|
||||
|
||||
/**
|
||||
* Emits an upload or download progress event to the relevant socket.
|
||||
*
|
||||
* @param {string} key - The event key that triggered this method.
|
||||
* @param {Object} data - Contains upload_tracker, context, and meta information.
|
||||
* @param {Object} data.upload_tracker - Tracker for the upload/download progress.
|
||||
* @param {Object} data.context - Context of the operation.
|
||||
* @param {Object} data.meta - Additional metadata for the event.
|
||||
*
|
||||
* @note This method logs information about the progress event and checks for the presence of a socket ID.
|
||||
* If the socket ID is missing, it logs an error but does not throw an exception for the Puter V1 release.
|
||||
* It emits a progress event to the socket if it exists, otherwise, it does nothing if the socket has disconnected.
|
||||
*/
|
||||
const user_id_list = await (async () => {
|
||||
// NOTE: Using a set because eventually we will need to dispatch
|
||||
// to multiple users, but this is not currently the case.
|
||||
@ -74,6 +96,19 @@ class WSPushService extends AdvancedBase {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles file system update events.
|
||||
*
|
||||
* @param {string} key - The event key.
|
||||
* @param {Object} data - The event data containing node and context information.
|
||||
*
|
||||
* @description This method processes 'fs.update.*' events, retrieves necessary metadata,
|
||||
* and emits an 'outer.gui.item.updated' event to update the GUI for the relevant users.
|
||||
* It gathers user IDs, merges metadata, and prepares a response object for emission.
|
||||
*
|
||||
* @returns {Promise<void>} - Resolves when the event has been processed and emitted.
|
||||
*/
|
||||
async _on_fs_update (key, data) {
|
||||
const { node, context } = data;
|
||||
const { socketio } = this.modules;
|
||||
@ -91,6 +126,23 @@ class WSPushService extends AdvancedBase {
|
||||
|
||||
const response = await node.getSafeEntry({ debug: 'hi', thumbnail: true });
|
||||
|
||||
|
||||
/**
|
||||
* Handles file system update events.
|
||||
*
|
||||
* @param {string} key - The event key.
|
||||
* @param {Object} data - The event data containing node and context information.
|
||||
* @returns {Promise<void>} A promise that resolves when the update has been processed.
|
||||
*
|
||||
* @description
|
||||
* This method is triggered when a file or directory is updated. It retrieves
|
||||
* metadata from the context, fetches the updated node's entry, determines the
|
||||
* relevant user IDs, and emits an event to notify the GUI of the update.
|
||||
*
|
||||
* @note
|
||||
* - The method uses a set for user IDs to prepare for future multi-user dispatch.
|
||||
* - If no specific user ID is provided in the metadata, it falls back to the node's user ID.
|
||||
*/
|
||||
const user_id_list = await (async () => {
|
||||
// NOTE: Using a set because eventually we will need to dispatch
|
||||
// to multiple users, but this is not currently the case.
|
||||
@ -108,6 +160,21 @@ class WSPushService extends AdvancedBase {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles file system move events by emitting appropriate GUI update events.
|
||||
*
|
||||
* This method is triggered when a file or directory is moved within the file system.
|
||||
* It collects necessary metadata, updates the response with the old path, and
|
||||
* broadcasts the event to update the GUI for the affected users.
|
||||
*
|
||||
* @param {string} key - The event key triggering this method.
|
||||
* @param {Object} data - An object containing details about the moved item:
|
||||
* - {Node} moved - The moved file system node.
|
||||
* - {string} old_path - The previous path of the moved item.
|
||||
* - {Context} context - The context in which the move operation occurred.
|
||||
* @returns {Promise<void>} A promise that resolves when the event has been emitted.
|
||||
*/
|
||||
async _on_fs_move (key, data) {
|
||||
const { moved, old_path, context } = data;
|
||||
const { socketio } = this.modules;
|
||||
@ -125,6 +192,18 @@ class WSPushService extends AdvancedBase {
|
||||
|
||||
const response = await moved.getSafeEntry();
|
||||
|
||||
|
||||
/**
|
||||
* Handles the file system move event by emitting a GUI update event.
|
||||
* This method processes the metadata associated with the move operation,
|
||||
* retrieves safe entry details for the moved item, and notifies relevant users.
|
||||
*
|
||||
* @param {string} key - The event key for the move operation.
|
||||
* @param {Object} data - Contains details of the move operation:
|
||||
* - moved: The file system entry that was moved.
|
||||
* - old_path: The original path of the moved item.
|
||||
* - context: Contextual information for the operation.
|
||||
*/
|
||||
const user_id_list = await (async () => {
|
||||
// NOTE: Using a set because eventually we will need to dispatch
|
||||
// to multiple users, but this is not currently the case.
|
||||
@ -143,6 +222,17 @@ class WSPushService extends AdvancedBase {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles the 'fs.pending' event, preparing and emitting data for items that are pending processing.
|
||||
*
|
||||
* @param {string} key - The event key, typically starting with 'fs.pending.'.
|
||||
* @param {Object} data - An object containing the fsentry and context of the pending file system operation.
|
||||
* @param {Object} data.fsentry - The file system entry that is pending.
|
||||
* @param {Object} data.context - The operation context providing additional metadata.
|
||||
*
|
||||
* @returns {Promise<void>} Emits an event to update the GUI about the pending item.
|
||||
*/
|
||||
async _on_fs_pending (key, data) {
|
||||
const { fsentry, context } = data;
|
||||
const { socketio } = this.modules;
|
||||
@ -160,6 +250,17 @@ class WSPushService extends AdvancedBase {
|
||||
Object.assign(metadata, gui_metadata);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Emits a 'outer.gui.item.pending' event for an FS entry in a pending state.
|
||||
*
|
||||
* @param {string} key - The event key triggering this method.
|
||||
* @param {Object} data - Contains the FS entry data and context.
|
||||
* @param {Object} data.fsentry - The file system entry object.
|
||||
* @param {Object} data.context - The context object containing service information.
|
||||
*
|
||||
* @fires svc_event#outer.gui.item.pending - Emitted with user ID list and entry details.
|
||||
*/
|
||||
const user_id_list = await (async () => {
|
||||
// NOTE: Using a set because eventually we will need to dispatch
|
||||
// to multiple users, but this is not currently the case.
|
||||
@ -176,6 +277,19 @@ class WSPushService extends AdvancedBase {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles upload progress events.
|
||||
*
|
||||
* @param {string} key - The event key.
|
||||
* @param {Object} data - The event data containing upload progress information.
|
||||
* @returns {Promise<void>} A promise that resolves when the progress has been emitted to the appropriate socket.
|
||||
*
|
||||
* @description
|
||||
* This method processes upload progress events, logs information,
|
||||
* prepares metadata, and emits the progress to the client socket associated with the given socket ID.
|
||||
* If the socket ID is missing or the socket has disconnected, appropriate actions are taken.
|
||||
*/
|
||||
async _on_upload_progress (key, data) {
|
||||
this.log.info('got upload progress event');
|
||||
const { socketio } = this.modules;
|
||||
@ -228,6 +342,18 @@ class WSPushService extends AdvancedBase {
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles the 'outer.gui.*' event to emit GUI-related updates to specific users.
|
||||
*
|
||||
* @param {string} key - The event key with 'outer.gui.' prefix removed.
|
||||
* @param {Object} data - Contains user_id_list and response to emit.
|
||||
* @param {Object} meta - Additional metadata for the event.
|
||||
*
|
||||
* @note This method iterates over each user ID provided in the event data,
|
||||
* checks if the user's socket room exists and has clients, then emits
|
||||
* the event to the appropriate room.
|
||||
*/
|
||||
async _on_outer_gui (key, { user_id_list, response }, meta) {
|
||||
key = key.slice('outer.gui.'.length);
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o-mini"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -29,6 +30,12 @@ const auth = require('../middleware/auth');
|
||||
const { osclink } = require('../util/strutil');
|
||||
const { surrounding_box, es_import_promise } = require('../fun/dev-console-ui-utils');
|
||||
|
||||
|
||||
/**
|
||||
* This class, WebServerService, is responsible for starting and managing the Puter web server.
|
||||
* It initializes the Express app, sets up middlewares, routes, and handles authentication and web sockets.
|
||||
* It also validates the host header and IP addresses to prevent security vulnerabilities.
|
||||
*/
|
||||
class WebServerService extends BaseService {
|
||||
static MODULES = {
|
||||
https: require('https'),
|
||||
@ -42,6 +49,15 @@ class WebServerService extends BaseService {
|
||||
morgan: require('morgan'),
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This method initializes the backend web server for Puter. It sets up the Express app, configures middleware, and starts the HTTP server.
|
||||
*
|
||||
* @param {Express} app - The Express app instance to configure.
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
// comment above line 44 in WebServerService.js
|
||||
async ['__on_boot.consolidation'] () {
|
||||
const app = this.app;
|
||||
const services = this.services;
|
||||
@ -53,6 +69,17 @@ class WebServerService extends BaseService {
|
||||
await services.emit('install.routes-gui', { app });
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Starts the web server and listens for incoming connections.
|
||||
* This method sets up the Express app, sets up middleware, and starts the server on the specified port.
|
||||
* It also sets up the Socket.io server for real-time communication.
|
||||
*
|
||||
* @returns {Promise<void>} A promise that resolves once the server is started.
|
||||
*/
|
||||
WebServerService.prototype['__on_start.webserver'] = async function () {
|
||||
// ... rest of the method
|
||||
}
|
||||
async ['__on_boot.activation'] () {
|
||||
const services = this.services;
|
||||
await services.emit('start.webserver');
|
||||
@ -60,6 +87,19 @@ class WebServerService extends BaseService {
|
||||
this.print_puter_logo_();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method starts the web server by listening on the specified port. It tries multiple ports if the first one is in use.
|
||||
* If the `config.http_port` is set to 'auto', it will try to find an available port in a range of 4100 to 4299.
|
||||
* Once the server is up and running, it emits the 'start.webserver' and 'ready.webserver' events.
|
||||
* If the `config.env` is set to 'dev' and `config.no_browser_launch` is false, it will open the Puter URL in the default browser.
|
||||
*
|
||||
* @return {Promise} A promise that resolves when the server is up and running.
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
async ['__on_start.webserver'] () {
|
||||
// ... rest of the method code
|
||||
}
|
||||
async ['__on_start.webserver'] () {
|
||||
await es_import_promise;
|
||||
|
||||
@ -80,6 +120,14 @@ class WebServerService extends BaseService {
|
||||
let server;
|
||||
|
||||
const auto_port = config.http_port === 'auto';
|
||||
/**
|
||||
* Initializes the web server and starts listening for incoming requests.
|
||||
*
|
||||
* @param {Object} services - An object containing other services such as logger, config, etc.
|
||||
*/
|
||||
WebServerService.prototype._initWebServer = function (services) {
|
||||
// Implementation goes here
|
||||
};
|
||||
let ports_to_try = auto_port ? (() => {
|
||||
const ports = [];
|
||||
for ( let i = 0 ; i < 20 ; i++ ) {
|
||||
@ -108,6 +156,15 @@ class WebServerService extends BaseService {
|
||||
rjct(e);
|
||||
}
|
||||
});
|
||||
/**
|
||||
* Starts the web server.
|
||||
*
|
||||
* This method is responsible for creating the HTTP server, setting up middleware, and starting the server on the specified port. If the specified port is "auto", it will attempt to find an available port within a range.
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
// Add this comment above line 110
|
||||
// (line 110 of the provided code)
|
||||
server.on('listening', () => {
|
||||
rslv();
|
||||
})
|
||||
@ -137,6 +194,14 @@ class WebServerService extends BaseService {
|
||||
console.log('Error opening browser', e);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Starts the HTTP server.
|
||||
*
|
||||
* This method sets up the Express server, initializes middleware, and starts the HTTP server.
|
||||
* It handles error handling, authentication, and other necessary configurations.
|
||||
*
|
||||
* @returns {Promise} A Promise that resolves when the server is listening.
|
||||
*/
|
||||
this.startup_widget = () => {
|
||||
|
||||
const link = `\x1B[34;1m${osclink(url)}\x1B[0m`;
|
||||
@ -187,6 +252,17 @@ class WebServerService extends BaseService {
|
||||
});
|
||||
|
||||
socketio.on('connection', (socket) => {
|
||||
/**
|
||||
* Starts the web server and associated services.
|
||||
*
|
||||
* This method is responsible for starting the web server and its associated services. It first initializes the middlewares and routes for the server, then begins the server with the specified HTTP port. If the specified port is not available, it will try to find an available port within a range.
|
||||
*
|
||||
* @returns {Promise} A promise that resolves when the server is started.
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
WebServerService.prototype.__on_start_webserver = async function () {
|
||||
// ...
|
||||
};
|
||||
socket.on('disconnect', () => {
|
||||
});
|
||||
socket.on('trash.is_empty', (msg) => {
|
||||
@ -204,10 +280,29 @@ class WebServerService extends BaseService {
|
||||
await this.services.emit('install.websockets');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Starts the Puter web server and sets up routes, middleware, and error handling.
|
||||
*
|
||||
* @param {object} services - An object containing all services available to the web server.
|
||||
* @returns {Promise<void>} A promise that resolves when the web server is fully started.
|
||||
*/
|
||||
WebServerService.prototype._init = async function(services) {
|
||||
// ... existing code
|
||||
};
|
||||
get_server () {
|
||||
return this.server_;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles starting and managing the Puter web server.
|
||||
*
|
||||
* @param {Object} services - An object containing all services.
|
||||
*/
|
||||
WebServerService._init.prototype._init = function(services) {
|
||||
// Implementation goes here
|
||||
};
|
||||
async _init () {
|
||||
const app = express();
|
||||
this.app = app;
|
||||
@ -274,6 +369,16 @@ class WebServerService extends BaseService {
|
||||
app.use(morgan(':method :url :status :response-time', { stream }));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize the web server, start it, and handle any related logic.
|
||||
*
|
||||
* This method is responsible for creating the server and listening on the
|
||||
* appropriate port. It also sets up middleware, routes, and other necessary
|
||||
* configurations.
|
||||
*
|
||||
* @returns {Promise<void>} A promise that resolves once the server is up and running.
|
||||
*/
|
||||
app.use((() => {
|
||||
// const router = express.Router();
|
||||
// router.get('/wut', express.json(), (req, res, next) => {
|
||||
@ -292,6 +397,14 @@ class WebServerService extends BaseService {
|
||||
(() => {
|
||||
const onFinished = require('on-finished');
|
||||
app.use((req, res, next) => {
|
||||
/**
|
||||
* Starts the web server and sets up routes, middleware, and web sockets.
|
||||
*
|
||||
* @returns {Promise<void>} Resolves once the server is up and running.
|
||||
*/
|
||||
WebServerService.prototype._initWebServer = async function() {
|
||||
// Your comment here
|
||||
};
|
||||
onFinished(res, () => {
|
||||
if ( res.statusCode !== 500 ) return;
|
||||
if ( req.__error_handled ) return;
|
||||
@ -495,6 +608,16 @@ class WebServerService extends BaseService {
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Starts the web server and sets up the necessary middleware and routes.
|
||||
* This method is responsible for initializing the Express app, handling authentication,
|
||||
* setting up routes, and starting the HTTP server. It also sets up error handling and
|
||||
* socket.io for real-time communication.
|
||||
*
|
||||
* @param {Object} services - The services object containing all necessary services.
|
||||
*/
|
||||
// comment above line 497
|
||||
print_puter_logo_() {
|
||||
if ( this.global_config.env !== 'dev' ) return;
|
||||
const logos = require('../fun/logos.js');
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"mistral","model":"mistral-large-latest"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -19,14 +20,44 @@
|
||||
const BaseService = require("../BaseService");
|
||||
const { DB_WRITE } = require("../database/consts");
|
||||
|
||||
|
||||
/**
|
||||
* AuthAuditService Class
|
||||
*
|
||||
* The AuthAuditService class extends BaseService and is responsible for recording
|
||||
* authentication audit logs. It handles the initialization of the database connection,
|
||||
* recording audit events, and managing any errors that occur during the process.
|
||||
* This class ensures that all authentication-related actions are logged for auditing
|
||||
* and troubleshooting purposes.
|
||||
*/
|
||||
class AuthAuditService extends BaseService {
|
||||
static MODULES = {
|
||||
uuidv4: require('uuid').v4,
|
||||
}
|
||||
/**
|
||||
* Generates a unique identifier for the AuthAuditService module.
|
||||
*
|
||||
* @returns {Object} An object containing a method to generate a UUID v4.
|
||||
*/
|
||||
async _init () {
|
||||
this.db = this.services.get('database').get(DB_WRITE, 'auth:audit');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Records an audit entry for authentication actions.
|
||||
*
|
||||
* This method handles the recording of audit entries for various authentication actions.
|
||||
* It captures the requester details, action, body, and any extra information.
|
||||
* If an error occurs during the recording process, it reports the error with appropriate details.
|
||||
*
|
||||
* @param {Object} parameters - The parameters for the audit entry.
|
||||
* @param {Object} parameters.requester - The requester object.
|
||||
* @param {string} parameters.action - The action performed.
|
||||
* @param {Object} parameters.body - The body of the request.
|
||||
* @param {Object} [parameters.extra] - Any extra information.
|
||||
* @returns {Promise<void>} - A promise that resolves when the audit entry is recorded.
|
||||
*/
|
||||
async record (parameters) {
|
||||
try {
|
||||
await this._record(parameters);
|
||||
@ -39,6 +70,164 @@ class AuthAuditService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Records an authentication audit event.
|
||||
*
|
||||
* This method logs an authentication audit event with the provided parameters.
|
||||
* It generates a unique identifier for the event, serializes the requester,
|
||||
* body, and extra information, and writes the event to the database.
|
||||
*
|
||||
* @param {Object} params - The parameters for the authentication audit event.
|
||||
* @param {Object} params.requester - The requester information.
|
||||
* @param {string} params.requester.ip - The IP address of the requester.
|
||||
* @param {string} params.requester.ua - The user-agent string of the requester.
|
||||
* @param {Function} params.requester.serialize - A function to serialize the requester information.
|
||||
* @param {string} params.action - The action performed during the authentication event.
|
||||
* @param {Object} params.body - The body of the request.
|
||||
* @param {Object} params.extra - Additional information related to the event.
|
||||
* @returns {Promise<void>} - A promise that resolves when the event is recorded.
|
||||
*/
|
||||
|
||||
|
||||
+++++ src/backend/src/services/abuse-prevention/AuthAuditService.js
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
* This file is part of Puter.
|
||||
*
|
||||
* Puter is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const BaseService = require("../BaseService");
|
||||
const { DB_WRITE } = require("../database/consts");
|
||||
|
||||
/**
|
||||
* This class handles authentication audit services.
|
||||
*
|
||||
* @extends BaseService
|
||||
*/
|
||||
class AuthAuditService extends BaseService {
|
||||
/**
|
||||
* Modules used by this service.
|
||||
*
|
||||
* @static
|
||||
* @type {Object}
|
||||
*/
|
||||
static MODULES = {
|
||||
uuidv4: require('uuid').v4,
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes the AuthAuditService.
|
||||
*
|
||||
* @async
|
||||
* @private
|
||||
* @returns {Promise<void>} - A promise that resolves when initialization is complete.
|
||||
*/
|
||||
async _init () {
|
||||
this.db = this.services.get('database').get(DB_WRITE, 'auth:audit');
|
||||
}
|
||||
|
||||
/**
|
||||
* Records an authentication audit event.
|
||||
*
|
||||
* This method logs an authentication audit event with the provided parameters.
|
||||
* It generates a unique identifier for the event, serializes the requester,
|
||||
* body, and extra information, and writes the event to the database.
|
||||
*
|
||||
* @param {Object} params - The parameters for the authentication audit event.
|
||||
* @param {Object} params.requester - The requester information.
|
||||
* @param {string} params.requester.ip - The IP address of the requester.
|
||||
* @param {string} params.requester.ua - The user-agent string of the requester.
|
||||
* @param {Function} params.requester.serialize - A function to serialize the requester information.
|
||||
* @param {string} params.action - The action performed during the authentication event.
|
||||
* @param {Object} params.body - The body of the request.
|
||||
* @param {Object} params.extra - Additional information related to the event.
|
||||
* @returns {Promise<void>} - A promise that resolves when the event is recorded.
|
||||
*/
|
||||
async record (parameters) {
|
||||
try {
|
||||
await this._record(parameters);
|
||||
} catch (err) {
|
||||
this.errors.report('auth-audit-service.record', {
|
||||
source: err,
|
||||
trace: true,
|
||||
alarm: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to record an authentication audit event.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} params - The parameters for the authentication audit event.
|
||||
* @param {Object} params.requester - The requester information.
|
||||
* @param {string} params.requester.ip - The IP address of the requester.
|
||||
* @param {string} params.requester.ua - The user-agent string of the requester.
|
||||
* @param {Function} params.requester.serialize - A function to serialize the requester information.
|
||||
* @param {string} params.action - The action performed during the authentication event.
|
||||
* @param {Object} params.body - The body of the request.
|
||||
* @param {Object} params.extra - Additional information related to the event.
|
||||
* @returns {Promise<void>} - A promise that resolves when the event is recorded.
|
||||
*/
|
||||
async _record ({ requester, action, body, extra }) {
|
||||
const uid = 'aas-' + this.modules.uuidv4();
|
||||
|
||||
const json_values = {
|
||||
requester: requester.serialize(),
|
||||
body: body,
|
||||
extra: extra ?? {},
|
||||
};
|
||||
|
||||
let has_parse_error = 0;
|
||||
|
||||
for ( const k in json_values ) {
|
||||
let value = json_values[k];
|
||||
try {
|
||||
value = JSON.stringify(value);
|
||||
} catch (err) {
|
||||
has_parse_error = 1;
|
||||
value = { parse_error: err.message };
|
||||
}
|
||||
json_values[k] = value;
|
||||
}
|
||||
|
||||
await this.db.write(
|
||||
`INSERT INTO auth_audit (` +
|
||||
`uid, ip_address, ua_string, action, ` +
|
||||
`requester, body, extra, ` +
|
||||
`has_parse_error` +
|
||||
`) VALUES ( ?, ?, ?, ?, ?, ?, ?, ? )`,
|
||||
[
|
||||
uid,
|
||||
requester.ip,
|
||||
requester.ua,
|
||||
action,
|
||||
JSON.stringify(requester.serialize()),
|
||||
JSON.stringify(body),
|
||||
JSON.stringify(extra ?? {}),
|
||||
has_parse_error,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
AuthAuditService,
|
||||
};
|
||||
async _record ({ requester, action, body, extra }) {
|
||||
const uid = 'aas-' + this.modules.uuidv4();
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -29,7 +30,19 @@ const BaseService = require("../BaseService");
|
||||
follow the latter form.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Class representing an edge rate limiting service that manages
|
||||
* request limits for various scopes (e.g. login, signup)
|
||||
* to prevent abuse. It keeps track of request timestamps
|
||||
* and enforces limits based on a specified time window.
|
||||
*/
|
||||
class EdgeRateLimitService extends BaseService {
|
||||
/**
|
||||
* Initializes the EdgeRateLimitService by setting up the rate limit scopes
|
||||
* and creating a Map to store request timestamps. It also starts a periodic
|
||||
* cleanup process to remove old request logs.
|
||||
*/
|
||||
_construct () {
|
||||
this.scopes = {
|
||||
['login']: {
|
||||
@ -113,6 +126,12 @@ class EdgeRateLimitService extends BaseService {
|
||||
this.requests = new Map();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the EdgeRateLimitService by setting up a periodic cleanup interval.
|
||||
* This method sets an interval that calls the cleanup function every 5 minutes.
|
||||
* It does not take any parameters and does not return any value.
|
||||
*/
|
||||
async _init () {
|
||||
asyncSafeSetInterval(() => this.cleanup(), 5 * MINUTE);
|
||||
}
|
||||
@ -151,6 +170,12 @@ class EdgeRateLimitService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cleans up the rate limit request records by removing entries
|
||||
* that have no associated timestamps. This method is intended
|
||||
* to be called periodically to free up memory.
|
||||
*/
|
||||
cleanup() {
|
||||
this.log.tick('edge rate-limit cleanup task');
|
||||
for (const [key, timestamps] of this.requests.entries()) {
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"mistral","model":"mistral-large-latest"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -21,6 +22,16 @@ const BaseService = require("../BaseService");
|
||||
const { Context } = require("../../util/context");
|
||||
const config = require("../../config");
|
||||
|
||||
|
||||
/**
|
||||
* @class Requester
|
||||
* @classdesc This class represents a requester in the system. It encapsulates
|
||||
* information about the requester's user-agent, IP address, origin, referer, and
|
||||
* other relevant details. The class includes methods to create instances from
|
||||
* request objects, check if the referer or origin is from Puter, and serialize
|
||||
* the requester's information. It also includes a method to get a unique identifier
|
||||
* based on the requester's IP address.
|
||||
*/
|
||||
class Requester {
|
||||
constructor (o) {
|
||||
for ( const k in o ) this[k] = o[k];
|
||||
@ -52,6 +63,12 @@ class Requester {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if the referer origin is from Puter.
|
||||
*
|
||||
* @returns {boolean} True if the referer origin matches any of the configured Puter origins, otherwise false.
|
||||
*/
|
||||
is_puter_referer () {
|
||||
const puter_origins = [
|
||||
config.origin,
|
||||
@ -60,6 +77,12 @@ class Requester {
|
||||
return puter_origins.includes(this.referer_origin);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if the request origin is from a known Puter origin.
|
||||
*
|
||||
* @returns {boolean} - Returns true if the request origin matches one of the known Puter origins, false otherwise.
|
||||
*/
|
||||
is_puter_origin () {
|
||||
const puter_origins = [
|
||||
config.origin,
|
||||
@ -68,10 +91,25 @@ class Requester {
|
||||
return puter_origins.includes(this.origin);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @method get rl_identifier
|
||||
* @description Retrieves the rate-limiter identifier, which is either the forwarded IP or the direct IP.
|
||||
* @returns {string} The IP address used for rate-limiting purposes.
|
||||
*/
|
||||
get rl_identifier () {
|
||||
return this.ip_forwarded || this.ip;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the Requester object into a plain JavaScript object.
|
||||
*
|
||||
* This method converts the properties of the Requester instance into a plain object,
|
||||
* making it suitable for serialization (e.g., for JSON).
|
||||
*
|
||||
* @returns {Object} The serialized representation of the Requester object.
|
||||
*/
|
||||
serialize () {
|
||||
return {
|
||||
ua: this.ua,
|
||||
@ -85,6 +123,13 @@ class Requester {
|
||||
}
|
||||
|
||||
// DRY: (3/3) - src/util/context.js; move install() to base class
|
||||
/**
|
||||
* @class RequesterIdentificationExpressMiddleware
|
||||
* @extends AdvancedBase
|
||||
* @description This class extends AdvancedBase and provides middleware functionality for identifying the requester in an Express application.
|
||||
* It registers initializers, installs the middleware on the Express application, and runs the middleware to identify and log details about the requester.
|
||||
* The class uses the 'isbot' module to determine if the requester is a bot.
|
||||
*/
|
||||
class RequesterIdentificationExpressMiddleware extends AdvancedBase {
|
||||
static MODULES = {
|
||||
isbot: require('isbot'),
|
||||
@ -95,6 +140,13 @@ class RequesterIdentificationExpressMiddleware extends AdvancedBase {
|
||||
install (app) {
|
||||
app.use(this.run.bind(this));
|
||||
}
|
||||
/**
|
||||
* Installs the middleware into the Express application.
|
||||
* This method binds the `run` method to the current instance
|
||||
* and uses it as a middleware function in the Express app.
|
||||
*
|
||||
* @param {object} app - The Express application instance.
|
||||
*/
|
||||
async run (req, res, next) {
|
||||
const x = Context.get();
|
||||
|
||||
@ -113,13 +165,42 @@ class RequesterIdentificationExpressMiddleware extends AdvancedBase {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @class IdentificationService
|
||||
* @extends BaseService
|
||||
* @description The IdentificationService class is responsible for handling the identification of requesters in the application.
|
||||
* It extends the BaseService class and utilizes the RequesterIdentificationExpressMiddleware to process and identify requesters.
|
||||
* This service ensures that requester information is properly logged and managed within the application context.
|
||||
*/
|
||||
class IdentificationService extends BaseService {
|
||||
/**
|
||||
* Constructs the IdentificationService instance.
|
||||
*
|
||||
* This method initializes the service by creating an instance of
|
||||
* RequesterIdentificationExpressMiddleware and assigning it to the `mw` property.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_construct () {
|
||||
this.mw = new RequesterIdentificationExpressMiddleware();
|
||||
}
|
||||
/**
|
||||
* Constructor for the IdentificationService class.
|
||||
* Initializes the middleware for requester identification.
|
||||
*/
|
||||
_init () {
|
||||
this.mw.log = this.log;
|
||||
}
|
||||
/**
|
||||
* Initializes the middleware logger.
|
||||
*
|
||||
* This method sets the logger for the `RequesterIdentificationExpressMiddleware` instance.
|
||||
* It does not take any parameters and does not return any value.
|
||||
*
|
||||
* @method
|
||||
* @name _init
|
||||
*/
|
||||
async ['__on_install.middlewares.context-aware'] (_, { app }) {
|
||||
this.mw.install(app);
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"claude"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -27,11 +28,30 @@ const BaseService = require("../BaseService");
|
||||
const { AppUnderUserActorType, UserActorType, Actor, SystemActorType, AccessTokenActorType } = require("./Actor");
|
||||
const { PermissionUtil } = require("./PermissionService");
|
||||
|
||||
|
||||
/**
|
||||
* ACLService class handles Access Control List functionality for the Puter filesystem.
|
||||
* Extends BaseService to provide permission management, access control checks, and ACL operations.
|
||||
* Manages user-to-user permissions, filesystem node access, and handles special cases like
|
||||
* public folders, app data access, and system actor privileges. Provides methods for
|
||||
* checking permissions, setting ACLs, and managing access control hierarchies.
|
||||
* @extends BaseService
|
||||
*/
|
||||
class ACLService extends BaseService {
|
||||
static MODULES = {
|
||||
express: require('express'),
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the ACLService by registering the 'public-folders' feature flag
|
||||
* with the feature flag service. The flag's value is determined by the
|
||||
* global_config.enable_public_folders setting.
|
||||
*
|
||||
* @async
|
||||
* @private
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async _init () {
|
||||
const svc_featureFlag = this.services.get('feature-flag');
|
||||
svc_featureFlag.register('public-folders', {
|
||||
@ -39,8 +59,24 @@ class ACLService extends BaseService {
|
||||
value: this.global_config.enable_public_folders ?? false,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Checks if an actor has permission to perform a specific mode of access on a resource
|
||||
*
|
||||
* @param {Actor} actor - The actor requesting access (user, system, app, etc)
|
||||
* @param {FSNode} resource - The filesystem resource being accessed
|
||||
* @param {string} mode - The access mode being requested ('read', 'write', etc)
|
||||
* @returns {Promise<boolean>} True if access is allowed, false otherwise
|
||||
*/
|
||||
async check (actor, resource, mode) {
|
||||
const ld = (Context.get('logdent') ?? 0) + 1;
|
||||
/**
|
||||
* Checks if an actor has permission for a specific mode on a resource
|
||||
*
|
||||
* @param {Actor} actor - The actor requesting permission
|
||||
* @param {FSNode} resource - The filesystem resource to check permissions for
|
||||
* @param {string} mode - The permission mode to check ('see', 'list', 'read', 'write')
|
||||
* @returns {Promise<boolean>} True if actor has permission, false otherwise
|
||||
*/
|
||||
return await Context.get().sub({ logdent: ld }).arun(async () => {
|
||||
const result = await this._check_fsNode(actor, resource, mode);
|
||||
if ( this.verbose ) console.log('LOGGING ACL CHECK', {
|
||||
@ -52,7 +88,30 @@ class ACLService extends BaseService {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if an actor has permission for a specific mode on a filesystem node.
|
||||
* Handles various actor types (System, User, AppUnderUser, AccessToken) and
|
||||
* enforces access control rules including public folder access and app data permissions.
|
||||
*
|
||||
* @param {Actor} actor - The actor requesting access
|
||||
* @param {FSNode} fsNode - The filesystem node to check permissions on
|
||||
* @param {string} mode - The permission mode to check ('see', 'list', 'read', 'write')
|
||||
* @returns {Promise<boolean>} True if actor has permission, false otherwise
|
||||
* @private
|
||||
*/
|
||||
async ['__on_install.routes'] (_, { app }) {
|
||||
/**
|
||||
* Handles route installation for ACL service endpoints.
|
||||
* Sets up routes for user-to-user permission management including:
|
||||
* - /acl/stat-user-user: Get permissions between users
|
||||
* - /acl/set-user-user: Set permissions between users
|
||||
*
|
||||
* @param {*} _ Unused parameter
|
||||
* @param {Object} options Installation options
|
||||
* @param {Express} options.app Express app instance to attach routes to
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const r_acl = (() => {
|
||||
const require = this.require;
|
||||
const express = require('express');
|
||||
@ -142,6 +201,18 @@ class ACLService extends BaseService {
|
||||
}).attach(r_acl);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets user-to-user permissions for a filesystem resource
|
||||
* @param {Actor} issuer - The user granting the permission
|
||||
* @param {Actor|string} holder - The user receiving the permission, or their username
|
||||
* @param {FSNode|string} resource - The filesystem resource or permission string
|
||||
* @param {string} mode - The permission mode to set
|
||||
* @param {Object} [options={}] - Additional options
|
||||
* @param {boolean} [options.only_if_higher] - Only set permission if no higher mode exists
|
||||
* @returns {Promise<boolean>} False if permission already exists or higher mode present
|
||||
* @throws {Error} If issuer or holder is not a UserActorType
|
||||
*/
|
||||
async set_user_user (issuer, holder, resource, mode, options = {}) {
|
||||
const svc_perm = this.services.get('permission');
|
||||
const svc_fs = this.services.get('filesystem');
|
||||
@ -219,6 +290,18 @@ class ACLService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets user-to-user permissions for a filesystem resource
|
||||
* @param {Actor} issuer - The user granting the permission
|
||||
* @param {Actor|string} holder - The user receiving the permission, or their username
|
||||
* @param {FSNode|string} resource - The filesystem resource or permission string
|
||||
* @param {string} mode - The permission mode to set
|
||||
* @param {Object} [options={}] - Additional options
|
||||
* @param {boolean} [options.only_if_higher] - Only set permission if no higher mode exists
|
||||
* @returns {Promise<boolean>} False if permission already exists or higher mode present
|
||||
* @throws {Error} If issuer or holder is not a UserActorType
|
||||
*/
|
||||
async stat_user_user (issuer, holder, resource) {
|
||||
const svc_perm = this.services.get('permission');
|
||||
|
||||
@ -248,6 +331,23 @@ class ACLService extends BaseService {
|
||||
return permissions;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks filesystem node permissions for a given actor and mode
|
||||
*
|
||||
* @param {Actor} actor - The actor requesting access (User, System, AccessToken, or AppUnderUser)
|
||||
* @param {FSNode} fsNode - The filesystem node to check permissions for
|
||||
* @param {string} mode - The permission mode to check ('see', 'list', 'read', 'write')
|
||||
* @returns {Promise<boolean>} True if actor has permission, false otherwise
|
||||
*
|
||||
* @description
|
||||
* Evaluates access permissions by checking:
|
||||
* - System actors always have access
|
||||
* - Public folder access rules
|
||||
* - Access token authorizer permissions
|
||||
* - App data directory special cases
|
||||
* - Explicit permissions in the ACL hierarchy
|
||||
*/
|
||||
async _check_fsNode (actor, fsNode, mode) {
|
||||
const context = Context.get();
|
||||
|
||||
@ -269,6 +369,18 @@ class ACLService extends BaseService {
|
||||
if ( this.global_config.enable_public_folders ) {
|
||||
const public_modes = Object.freeze(['read', 'list', 'see']);
|
||||
let is_public;
|
||||
/**
|
||||
* Checks if a given mode is allowed for a public folder path
|
||||
*
|
||||
* @param {Actor} actor - The actor requesting access
|
||||
* @param {FSNode} fsNode - The filesystem node to check
|
||||
* @param {string} mode - The access mode being requested (read/write/etc)
|
||||
* @returns {Promise<boolean>} True if access is allowed, false otherwise
|
||||
*
|
||||
* Handles special case for /user/public directories when public folders are enabled.
|
||||
* Only allows read, list, and see modes for public folders, and only if the folder
|
||||
* owner has confirmed their email (except for admin user).
|
||||
*/
|
||||
await (async () => {
|
||||
if ( ! public_modes.includes(mode) ) return;
|
||||
if ( ! (await fsNode.isPublic()) ) return;
|
||||
@ -325,6 +437,17 @@ class ACLService extends BaseService {
|
||||
// under a **different user**, allow,
|
||||
// IFF that appdata directory is shared with user
|
||||
// (by "user also has permission" check above)
|
||||
/**
|
||||
* Checks if an actor has permission to perform a specific mode of access on a filesystem node.
|
||||
* Handles various actor types (System, AccessToken, AppUnderUser) and special cases like
|
||||
* public folders and app data directories.
|
||||
*
|
||||
* @param {Actor} actor - The actor requesting access
|
||||
* @param {FSNode} fsNode - The filesystem node to check access for
|
||||
* @param {string} mode - The access mode to check ('see', 'list', 'read', 'write')
|
||||
* @returns {Promise<boolean>} True if access is allowed, false otherwise
|
||||
* @private
|
||||
*/
|
||||
if (await (async () => {
|
||||
if ( ! (actor.type instanceof AppUnderUserActorType) ) {
|
||||
return false;
|
||||
@ -362,6 +485,15 @@ class ACLService extends BaseService {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets a safe error message for ACL check failures
|
||||
* @param {Actor} actor - The actor attempting the operation
|
||||
* @param {FSNode} resource - The filesystem resource being accessed
|
||||
* @param {string} mode - The access mode being checked ('read', 'write', etc)
|
||||
* @returns {APIError} Returns 'subject_does_not_exist' if actor cannot see resource,
|
||||
* otherwise returns 'forbidden' error
|
||||
*/
|
||||
async get_safe_acl_error (actor, resource, mode) {
|
||||
const can_see = await this.check(actor, resource, 'see');
|
||||
if ( ! can_see ) {
|
||||
@ -373,6 +505,16 @@ class ACLService extends BaseService {
|
||||
|
||||
// If any logic depends on knowledge of the highest ACL mode, it should use
|
||||
// this method in case a higher mode is added (ex: might add 'config' mode)
|
||||
/**
|
||||
* Gets the highest permission mode in the ACL system
|
||||
*
|
||||
* @returns {string} Returns 'write' as the highest permission mode
|
||||
*
|
||||
* @remarks
|
||||
* This method should be used by any logic that depends on knowing the highest ACL mode,
|
||||
* in case higher modes are added in the future (e.g. a potential 'config' mode).
|
||||
* Currently 'write' is the highest mode in the hierarchy: see > list > read > write
|
||||
*/
|
||||
get_highest_mode () {
|
||||
return 'write';
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -28,6 +29,13 @@ const PRIVATE_UID_NAMESPACE = config.private_uid_namespace
|
||||
const PRIVATE_UID_SECRET = config.private_uid_secret
|
||||
?? require('crypto').randomBytes(24).toString('hex');
|
||||
|
||||
|
||||
/**
|
||||
* Represents an Actor in the system, extending functionality from AdvancedBase.
|
||||
* The Actor class is responsible for managing actor instances, including
|
||||
* creating new actors, generating unique identifiers, and handling related types
|
||||
* that represent different roles within the context of the application.
|
||||
*/
|
||||
class Actor extends AdvancedBase {
|
||||
static MODULES = {
|
||||
uuidv5: require('uuid').v5,
|
||||
@ -35,6 +43,15 @@ class Actor extends AdvancedBase {
|
||||
}
|
||||
|
||||
static system_actor_ = null;
|
||||
/**
|
||||
* Retrieves the system actor instance, creating it if it doesn't exist.
|
||||
*
|
||||
* This static method ensures that there is only one instance of the system actor.
|
||||
* If the system actor has not yet been created, it will be instantiated with a
|
||||
* new SystemActorType.
|
||||
*
|
||||
* @returns {Actor} The system actor instance.
|
||||
*/
|
||||
static get_system_actor () {
|
||||
if ( ! this.system_actor_ ) {
|
||||
this.system_actor_ = new Actor({
|
||||
@ -63,6 +80,13 @@ class Actor extends AdvancedBase {
|
||||
this[k] = o[k];
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Initializes the Actor instance with the provided parameters.
|
||||
* This constructor assigns object properties from the input object to the instance.
|
||||
*
|
||||
* @param {Object} o - The object containing actor parameters.
|
||||
* @param {...any} a - Additional arguments passed to the parent class constructor.
|
||||
*/
|
||||
get uid () {
|
||||
return this.type.uid;
|
||||
}
|
||||
@ -71,6 +95,14 @@ class Actor extends AdvancedBase {
|
||||
* Generate a cryptographically-secure deterministic UUID
|
||||
* from an actor's UID.
|
||||
*/
|
||||
/**
|
||||
* Generates a cryptographically-secure deterministic UUID
|
||||
* from an actor's UID. The generated UUID is derived by
|
||||
* applying SHA-256 HMAC to the actor's UID using a secret,
|
||||
* then formatting the result as a UUID V5.
|
||||
*
|
||||
* @returns {string} The derived UUID corresponding to the actor's UID.
|
||||
*/
|
||||
get private_uid () {
|
||||
// Pass the UUID through SHA-2 first because UUIDv5
|
||||
// is not cryptographically secure (it uses SHA-1)
|
||||
@ -89,6 +121,12 @@ class Actor extends AdvancedBase {
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clones the current Actor instance, returning a new Actor object with the same type.
|
||||
*
|
||||
* @returns {Actor} A new Actor instance that is a copy of the current one.
|
||||
*/
|
||||
clone () {
|
||||
return new Actor({
|
||||
type: this.type,
|
||||
@ -102,6 +140,13 @@ class Actor extends AdvancedBase {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Class representing the system actor type within the actor framework.
|
||||
* This type serves as a specific implementation of an actor that
|
||||
* represents a system-level entity and provides methods for UID retrieval
|
||||
* and related type management.
|
||||
*/
|
||||
class SystemActorType {
|
||||
constructor (o, ...a) {
|
||||
// super(o, ...a);
|
||||
@ -109,6 +154,14 @@ class SystemActorType {
|
||||
this[k] = o[k];
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Constructs a new instance of the actor type.
|
||||
*
|
||||
* @param {Object} o - The initial properties for the actor type.
|
||||
* @param {...*} a - Additional arguments to pass to the super class constructor.
|
||||
*
|
||||
* @throws {Error} If there is an issue in initializing the actor type.
|
||||
*/
|
||||
get uid () {
|
||||
return 'system';
|
||||
}
|
||||
@ -120,6 +173,12 @@ class SystemActorType {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Represents the type of a User Actor in the system, allowing operations and relations
|
||||
* specific to user actors. This class extends the base functionality to uniquely identify
|
||||
* user actors and define how they relate to other types of actors within the system.
|
||||
*/
|
||||
class UserActorType {
|
||||
constructor (o, ...a) {
|
||||
// super(o, ...a);
|
||||
@ -127,6 +186,12 @@ class UserActorType {
|
||||
this[k] = o[k];
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Constructs a new UserActorType instance.
|
||||
*
|
||||
* @param {Object} o - The initial properties to set on the instance.
|
||||
* @param {...any} a - Additional arguments to pass to the constructor.
|
||||
*/
|
||||
get uid () {
|
||||
return 'user:' + this.user.uuid;
|
||||
}
|
||||
@ -137,6 +202,12 @@ class UserActorType {
|
||||
throw new Error(`cannot get ${type_class.name} from ${this.constructor.name}`)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Represents a user actor type in the application. This class defines the structure
|
||||
* and behavior specific to user actors, including obtaining unique identifiers and
|
||||
* retrieving related actor types. It extends the base actor type functionality
|
||||
* to cater to user-specific needs.
|
||||
*/
|
||||
class AppUnderUserActorType {
|
||||
constructor (o, ...a) {
|
||||
// super(o, ...a);
|
||||
@ -144,6 +215,17 @@ class AppUnderUserActorType {
|
||||
this[k] = o[k];
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Create a new instance of the actor type, initializing it with the given parameters.
|
||||
*
|
||||
* This method first checks for associated user and app UIDs in the params,
|
||||
* fetching their respective data asynchronously if present. It then
|
||||
* constructs a new Actor with the provided type.
|
||||
*
|
||||
* @param {Function} type - The class constructor for the actor type.
|
||||
* @param {Object} params - Initialization parameters for the actor (optional).
|
||||
* @returns {Actor} A new instance of the Actor type.
|
||||
*/
|
||||
get uid () {
|
||||
return 'app-under-user:' + this.user.uuid + ':' + this.app.uid;
|
||||
}
|
||||
@ -158,6 +240,12 @@ class AppUnderUserActorType {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Represents the type of access tokens in the system.
|
||||
* An AccessTokenActorType associates an authorizer and an authorized actor
|
||||
* with a string token, facilitating permission checks and identity management.
|
||||
*/
|
||||
class AccessTokenActorType {
|
||||
// authorizer: an Actor who authorized the token
|
||||
// authorized: an Actor who is authorized by the token
|
||||
@ -168,11 +256,26 @@ class AccessTokenActorType {
|
||||
this[k] = o[k];
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Constructs an instance of AccessTokenActorType.
|
||||
* This class represents an access token actor containing information
|
||||
* about the authorizer and authorized actors, as well as the token string.
|
||||
*
|
||||
* @param {Object} o - The object containing properties to initialize the access token actor.
|
||||
* @param {...*} a - Additional arguments for further initialization.
|
||||
*/
|
||||
get uid () {
|
||||
return 'access-token:' + this.authorizer.uid +
|
||||
':' + ( this.authorized?.uid ?? '<none>' ) +
|
||||
':' + this.token;
|
||||
}
|
||||
/**
|
||||
* Generate a unique identifier (UID) for the access token.
|
||||
* The UID is constructed based on the authorizer's UID, the authorized actor's UID (if available),
|
||||
* and the token string. This UID format is useful for identifying the access token's context.
|
||||
*
|
||||
* @returns {string} The generated UID for the access token.
|
||||
*/
|
||||
get_related_actor () {
|
||||
// This would be dangerous because of ambiguity
|
||||
// between authorizer and authorized
|
||||
@ -180,12 +283,25 @@ class AccessTokenActorType {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Represents a Site Actor Type, which encapsulates information about a site-specific actor.
|
||||
* This class is used to manage details related to the site and implement functionalities
|
||||
* pertinent to site-level operations and interactions in the actor framework.
|
||||
*/
|
||||
class SiteActorType {
|
||||
constructor (o, ...a) {
|
||||
for ( const k in o ) {
|
||||
this[k] = o[k];
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Constructor for the SiteActorType class.
|
||||
* Initializes a new instance of SiteActorType with the provided properties.
|
||||
*
|
||||
* @param {Object} o - The properties to initialize the SiteActorType with.
|
||||
* @param {...*} a - Additional arguments.
|
||||
*/
|
||||
get uid () {
|
||||
return `site:` + this.site.name
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -21,6 +22,13 @@ const config = require("../../config");
|
||||
const { subdomain } = require("../../helpers");
|
||||
const BaseService = require("../BaseService");
|
||||
|
||||
|
||||
/**
|
||||
* Class representing the AntiCSRFService, which extends BaseService.
|
||||
* This service is responsible for issuing and managing anti-CSRF tokens
|
||||
* to enhance security for web requests by validating session-based tokens
|
||||
* and preventing cross-site request forgery attacks.
|
||||
*/
|
||||
class CircularQueue {
|
||||
constructor (size) {
|
||||
this.size = size;
|
||||
@ -57,7 +65,18 @@ class CircularQueue {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Class AntiCSRFService extends BaseService to manage and protect against Cross-Site Request Forgery (CSRF) attacks.
|
||||
* It provides methods for generating, consuming, and verifying anti-CSRF tokens based on user sessions.
|
||||
*/
|
||||
class AntiCSRFService extends BaseService {
|
||||
/**
|
||||
* Initializes the AntiCSRFService instance and sets up the mapping
|
||||
* between session IDs and their associated tokens.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_construct () {
|
||||
this.map_session_to_tokens = {};
|
||||
}
|
||||
@ -99,6 +118,14 @@ class AntiCSRFService extends BaseService {
|
||||
return tokens.maybe_consume(token);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates a secure random token as a hexadecimal string.
|
||||
* The token is created using cryptographic random bytes to ensure uniqueness
|
||||
* and security for Anti-CSRF purposes.
|
||||
*
|
||||
* @returns {string} The generated token.
|
||||
*/
|
||||
generate_token_ () {
|
||||
return require('crypto').randomBytes(32).toString('hex');
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -24,10 +25,17 @@ const APIError = require("../../api/APIError");
|
||||
const { DB_WRITE } = require("../database/consts");
|
||||
const { UUIDFPE } = require("../../util/uuidfpe");
|
||||
|
||||
// This constant defines the namespace used for generating app UUIDs from their origins
|
||||
const APP_ORIGIN_UUID_NAMESPACE = '33de3768-8ee0-43e9-9e73-db192b97a5d8';
|
||||
const APP_ORIGIN_UUID_NAMESPACE = '33de3768-8ee0-43e9-9e73-db192b97a5d8';
|
||||
|
||||
const LegacyTokenError = class extends Error {};
|
||||
|
||||
|
||||
/**
|
||||
* @class AuthService
|
||||
* This class is responsible for handling authentication and authorization tasks for the application.
|
||||
*/
|
||||
class AuthService extends BaseService {
|
||||
static MODULES = {
|
||||
jwt: require('jsonwebtoken'),
|
||||
@ -36,6 +44,14 @@ class AuthService extends BaseService {
|
||||
uuidv4: require('uuid').v4,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method is responsible for authenticating the user based on the provided token.
|
||||
* It checks the type of the token and performs the appropriate actions to validate and retrieve the user.
|
||||
*
|
||||
* @param {string} token - The JWT token to be authenticated.
|
||||
* @returns {Promise<Actor>} A Promise that resolves with an Actor object representing the authenticated user.
|
||||
*/
|
||||
async _init () {
|
||||
this.db = await this.services.get('database').get(DB_WRITE, 'auth');
|
||||
this.svc_session = await this.services.get('session');
|
||||
@ -54,6 +70,14 @@ class AuthService extends BaseService {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method authenticates a user or app using a token.
|
||||
* It checks the token's type (session, app-under-user, access-token) and decodes it.
|
||||
* Depending on the token type, it returns the corresponding user/app actor.
|
||||
* @param {string} token - The token to authenticate.
|
||||
* @returns {Promise<Actor>} The authenticated user or app actor.
|
||||
*/
|
||||
async authenticate_from_token (token) {
|
||||
const decoded = this.modules.jwt.verify(
|
||||
token,
|
||||
@ -204,6 +228,22 @@ class AuthService extends BaseService {
|
||||
return token;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Description:
|
||||
* This method is responsible for checking the session validity based on the provided token.
|
||||
* It verifies the token, retrieves the user and session information, and checks if the session is still valid.
|
||||
* If the session is valid, it returns an object containing the user and token information.
|
||||
* If the session is not valid, it returns an empty object.
|
||||
*
|
||||
* Parameters:
|
||||
* cur_token (string): The token to be checked.
|
||||
* meta (object): Optional metadata to be associated with the session.
|
||||
*
|
||||
* Return value:
|
||||
* Object: If the session is valid, it returns an object containing the user and token information. If the session is not valid, it returns an empty object.
|
||||
*/
|
||||
// This is an example of how the JSDoc comment for the method at line 206 might look like.
|
||||
async create_session_ (user, meta = {}) {
|
||||
this.log.info(`CREATING SESSION`);
|
||||
|
||||
@ -247,10 +287,26 @@ class AuthService extends BaseService {
|
||||
return await this.svc_session.create_session(user, meta);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method is used to authenticate an actor using an access token. It checks if the token is valid and if the user or app associated with the token is authorized.
|
||||
*
|
||||
* @param {string} token - The access token to be authenticated.
|
||||
* @returns {Actor} The authenticated actor object.
|
||||
*/
|
||||
async get_session_ (uuid) {
|
||||
return await this.svc_session.get_session(uuid);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method is used to authenticate a user using their access token.
|
||||
* It checks the type of the token and performs the necessary actions based on that.
|
||||
* If the token is valid and the user is authenticated, it returns an Actor object representing the user.
|
||||
* If the token is invalid or the user is not authenticated, it throws an error.
|
||||
* @param {string} token - The access token to be authenticated.
|
||||
* @returns {Actor} The Actor object representing the authenticated user.
|
||||
*/
|
||||
async create_session_token (user, meta) {
|
||||
const session = await this.create_session_(user, meta);
|
||||
|
||||
@ -265,6 +321,15 @@ class AuthService extends BaseService {
|
||||
return { session, token };
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method checks if the provided session token is valid and returns the associated user and token.
|
||||
* If the token is not a valid session token or it does not exist in the database, it returns an empty object.
|
||||
*
|
||||
* @param {string} cur_token - The session token to be checked.
|
||||
* @param {object} meta - Additional metadata associated with the token.
|
||||
* @returns {object} Object containing the user and token if the token is valid, otherwise an empty object.
|
||||
*/
|
||||
async check_session (cur_token, meta) {
|
||||
const decoded = this.modules.jwt.verify(
|
||||
cur_token, this.global_config.jwt_secret
|
||||
@ -315,6 +380,13 @@ class AuthService extends BaseService {
|
||||
return { actor, user, token };
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method is responsible for authenticating a user or an app from a provided token. It checks the type of the token and performs the necessary actions.
|
||||
*
|
||||
* @param {string} token - The token to be authenticated.
|
||||
* @returns {Promise<Actor>} The authenticated user or app actor.
|
||||
*/
|
||||
async remove_session_by_token (token) {
|
||||
const decoded = this.modules.jwt.verify(
|
||||
token, this.global_config.jwt_secret
|
||||
@ -327,6 +399,14 @@ class AuthService extends BaseService {
|
||||
await this.svc_session.remove_session(decoded.uuid);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method is responsible for authenticating an user or app using an access token.
|
||||
* It decodes the token and checks if it's valid, then it returns an Actor object representing the authenticated user or app.
|
||||
* The method takes a token as input and returns an Actor object as output.
|
||||
* Parameters:
|
||||
* - token: The JWT access token to be authenticated.
|
||||
*/
|
||||
async create_access_token (authorizer, permissions) {
|
||||
const jwt_obj = {};
|
||||
const authorizer_obj = {};
|
||||
@ -384,6 +464,15 @@ class AuthService extends BaseService {
|
||||
return jwt;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method is used to authenticate a user or an application using an access token.
|
||||
* It decodes the token, checks its type, and verifies it against the database and configuration.
|
||||
* If successful, it returns an Actor object representing the authenticated user or application.
|
||||
*
|
||||
* @param {string} token - The access token to be authenticated.
|
||||
* @return {Actor} - The authenticated user or application object.
|
||||
*/
|
||||
async list_sessions (actor) {
|
||||
const seen = new Set();
|
||||
const sessions = [];
|
||||
@ -407,6 +496,12 @@ class AuthService extends BaseService {
|
||||
}
|
||||
session.meta = this.db.case({
|
||||
mysql: () => session.meta,
|
||||
/**
|
||||
* This method is responsible for authenticating a user or app using a token. It decodes the token and checks if it's valid, then returns an appropriate actor object based on the token type.
|
||||
*
|
||||
* @param {string} token - The user or app access token.
|
||||
* @returns {Actor} - Actor object representing the authenticated user or app.
|
||||
*/
|
||||
otherwise: () => JSON.parse(session.meta ?? "{}")
|
||||
})();
|
||||
sessions.push(session);
|
||||
@ -421,11 +516,33 @@ class AuthService extends BaseService {
|
||||
return sessions;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method is responsible for verifying and decoding an access token.
|
||||
* It checks the token type and if it's an 'access-token', it decodes it,
|
||||
* retrieves the user and app from the database, and returns an Actor object
|
||||
* representing the user and app. If the token type is not 'access-token',
|
||||
* it returns an empty object.
|
||||
*
|
||||
* @param {string} token - The access token to be verified and decoded.
|
||||
* @return {Promise<Actor>} - A Promise that resolves to an Actor object
|
||||
* representing the user and app, or an empty object if the token type is not 'access-token'.
|
||||
*/
|
||||
async revoke_session (actor, uuid) {
|
||||
delete this.sessions[uuid];
|
||||
this.svc_session.remove_session(uuid);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method is used to authenticate a user or an application using an access token.
|
||||
* It decodes the token and checks if it is valid, then it returns an Actor object representing the authenticated user or application.
|
||||
* If the token is invalid, it throws an error.
|
||||
*
|
||||
* @param {string} token - The access token to be authenticated.
|
||||
* @return {Actor} - An Actor object representing the authenticated user or application.
|
||||
* @throws {LegacyTokenError} - If the token is in an old format.
|
||||
*/
|
||||
async get_user_app_token_from_origin (origin) {
|
||||
origin = this._origin_from_url(origin);
|
||||
const app_uid = await this._app_uid_from_origin(origin);
|
||||
@ -459,6 +576,14 @@ class AuthService extends BaseService {
|
||||
return this.get_user_app_token(app_uid);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method is responsible for authenticating a user or an app using an access token.
|
||||
* It decodes the token and checks if it's valid. If it's valid, it returns an Actor object that represents the authenticated user or app.
|
||||
* The method handles different types of tokens: session, app-under-user, access-token, and actor-site.
|
||||
* @param {string} token - The access token to authenticate.
|
||||
* @returns {Actor} The authenticated user or app represented as an Actor object.
|
||||
*/
|
||||
async app_uid_from_origin (origin) {
|
||||
origin = this._origin_from_url(origin);
|
||||
if ( origin === null ) {
|
||||
@ -467,6 +592,12 @@ class AuthService extends BaseService {
|
||||
return await this._app_uid_from_origin(origin);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description This method decodes an access token and checks its validity. It returns the decoded token data if the token is valid, or an error if not.
|
||||
* @param {string} token - The access token to be decoded and checked.
|
||||
* @returns {Object|Error} The decoded token data if the token is valid, or an error if not.
|
||||
*/
|
||||
async _app_uid_from_origin (origin) {
|
||||
// UUIDV5
|
||||
const uuid = this.modules.uuidv5(origin, APP_ORIGIN_UUID_NAMESPACE);
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"xai"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -22,11 +23,27 @@ const { DENY_SERVICE_INSTRUCTION } = require("../AnomalyService");
|
||||
const BaseService = require("../BaseService");
|
||||
const { DB_WRITE } = require("../database/consts");
|
||||
|
||||
|
||||
/**
|
||||
* The GroupService class provides functionality for managing groups within the Puter application.
|
||||
* It extends the BaseService to handle group-related operations such as creation, retrieval,
|
||||
* listing members, adding or removing users from groups, and more. This service interacts with
|
||||
* the database to perform CRUD operations on group entities, ensuring proper management
|
||||
* of user permissions and group metadata.
|
||||
*/
|
||||
class GroupService extends BaseService {
|
||||
static MODULES = {
|
||||
uuidv4: require('uuid').v4,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the GroupService by setting up the database connection and registering
|
||||
* with the anomaly service for monitoring group creation rates.
|
||||
*
|
||||
* @memberof GroupService
|
||||
* @instance
|
||||
*/
|
||||
_init () {
|
||||
this.db = this.services.get('database').get(DB_WRITE, 'permissions');
|
||||
|
||||
@ -36,6 +53,20 @@ class GroupService extends BaseService {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves a group by its unique identifier (UID).
|
||||
*
|
||||
* @param {Object} params - The parameters object.
|
||||
* @param {string} params.uid - The unique identifier of the group.
|
||||
* @returns {Promise<Object|undefined>} The group object if found, otherwise undefined.
|
||||
* @throws {Error} If there's an issue with the database query.
|
||||
*
|
||||
* This method fetches a group from the database using its UID. If the group
|
||||
* does not exist, it returns undefined. The 'extra' and 'metadata' fields are
|
||||
* parsed from JSON strings to objects if not using MySQL, otherwise they remain
|
||||
* as strings.
|
||||
*/
|
||||
async get({ uid }) {
|
||||
const [group] =
|
||||
await this.db.read('SELECT * FROM `group` WHERE uid=?', [uid]);
|
||||
@ -51,6 +82,19 @@ class GroupService extends BaseService {
|
||||
return group;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new group with the provided owner, extra data, and metadata.
|
||||
* This method performs rate limiting checks to prevent abuse, generates a unique identifier for the group,
|
||||
* and handles the database insertion of the group details.
|
||||
*
|
||||
* @param {Object} options - The options object for creating a group.
|
||||
* @param {string} options.owner_user_id - The ID of the user who owns the group.
|
||||
* @param {Object} [options.extra] - Additional data associated with the group.
|
||||
* @param {Object} [options.metadata] - Metadata for the group, which can be used for various purposes.
|
||||
* @returns {Promise<string>} - A promise that resolves to the unique identifier of the newly created group.
|
||||
* @throws {APIError} If the rate limit is exceeded.
|
||||
*/
|
||||
async create ({ owner_user_id, extra, metadata }) {
|
||||
extra = extra ?? {};
|
||||
metadata = metadata ?? {};
|
||||
@ -88,6 +132,17 @@ class GroupService extends BaseService {
|
||||
return uid;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Lists all groups where the specified user is a member.
|
||||
*
|
||||
* This method queries the database to find groups associated with the given user_id through the junction table `jct_user_group`.
|
||||
* Each group's `extra` and `metadata` fields are parsed based on the database type to ensure compatibility.
|
||||
*
|
||||
* @param {Object} params - Parameters for the query.
|
||||
* @param {string} params.user_id - The ID of the user whose groups are to be listed.
|
||||
* @returns {Promise<Array<Group>>} A promise that resolves to an array of Group objects representing groups the user is a member of.
|
||||
*/
|
||||
async list_groups_with_owner ({ owner_user_id }) {
|
||||
const groups = await this.db.read(
|
||||
'SELECT * FROM `group` WHERE owner_user_id=?',
|
||||
@ -106,6 +161,14 @@ class GroupService extends BaseService {
|
||||
return groups.map(g => Group(g));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Lists all groups where the specified user is a member.
|
||||
*
|
||||
* @param {Object} options - The options object.
|
||||
* @param {string} options.user_id - The ID of the user whose group memberships are to be listed.
|
||||
* @returns {Promise<Array>} A promise that resolves to an array of Group objects representing the groups the user is a member of.
|
||||
*/
|
||||
async list_groups_with_member ({ user_id }) {
|
||||
const groups = await this.db.read(
|
||||
'SELECT * FROM `group` WHERE id IN (' +
|
||||
@ -125,6 +188,14 @@ class GroupService extends BaseService {
|
||||
return groups.map(g => Group(g));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Lists the members of a group by their username.
|
||||
*
|
||||
* @param {Object} options - The options object.
|
||||
* @param {string} options.uid - The unique identifier of the group.
|
||||
* @returns {Promise<string[]>} A promise that resolves to an array of usernames of the group members.
|
||||
*/
|
||||
async list_members ({ uid }) {
|
||||
const users = await this.db.read(
|
||||
'SELECT u.username FROM user u ' +
|
||||
@ -136,6 +207,16 @@ class GroupService extends BaseService {
|
||||
return users.map(u => u.username);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds specified users to a group.
|
||||
*
|
||||
* @param {Object} options - The options object.
|
||||
* @param {string} options.uid - The unique identifier of the group.
|
||||
* @param {string[]} options.users - An array of usernames to add to the group.
|
||||
* @returns {Promise<void>} A promise that resolves when the users have been added.
|
||||
* @throws {APIError} If there's an issue with the database operation or if the group does not exist.
|
||||
*/
|
||||
async add_users ({ uid, users }) {
|
||||
const question_marks =
|
||||
'(' + Array(users.length).fill('?').join(', ') + ')';
|
||||
@ -150,6 +231,18 @@ class GroupService extends BaseService {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes specified users from a group.
|
||||
*
|
||||
* This method deletes the association between users and a group from the junction table.
|
||||
* It uses the group's uid to identify the group and an array of usernames to remove.
|
||||
*
|
||||
* @param {Object} params - The parameters for the operation.
|
||||
* @param {string} params.uid - The unique identifier of the group.
|
||||
* @param {string[]} params.users - An array of usernames to be removed from the group.
|
||||
* @returns {Promise<void>} A promise that resolves when the operation is complete.
|
||||
*/
|
||||
async remove_users ({ uid, users }) {
|
||||
const question_marks =
|
||||
'(' + Array(users.length).fill('?').join(', ') + ')';
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o-mini"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -18,6 +19,12 @@
|
||||
*/
|
||||
const BaseService = require("../BaseService");
|
||||
|
||||
|
||||
/**
|
||||
* Represents the OTP (One-Time Password) service.
|
||||
* This class provides functionalities to create OTP secrets, recovery codes,
|
||||
* and verify OTPs against given secrets and codes, using the 'otpauth' and 'crypto' libraries.
|
||||
*/
|
||||
class OTPService extends BaseService {
|
||||
static MODULES = {
|
||||
otpauth: require('otpauth'),
|
||||
@ -44,6 +51,14 @@ class OTPService extends BaseService {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a recovery code for the user.
|
||||
* Generates a random byte sequence, encodes it in base32,
|
||||
* and returns a unique 8-character recovery code.
|
||||
*
|
||||
* @returns {string} The generated recovery code.
|
||||
*/
|
||||
create_recovery_code () {
|
||||
const require = this.require;
|
||||
const crypto = require('crypto');
|
||||
@ -74,6 +89,14 @@ class OTPService extends BaseService {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates a random OTP secret.
|
||||
* This method creates a 15-byte random buffer and encodes it into a base32 string.
|
||||
* The resulting string is trimmed to a maximum length of 24 characters.
|
||||
*
|
||||
* @returns {string} The generated OTP secret in base32 format.
|
||||
*/
|
||||
gen_otp_secret_ () {
|
||||
const require = this.require;
|
||||
const crypto = require('crypto');
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"xai"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -34,6 +35,12 @@ const implicit_user_permissions = {
|
||||
// 'driver': {},
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The PermissionService class manages the core functionality for handling permissions within the Puter ecosystem.
|
||||
* It provides methods for granting, revoking, and checking permissions for various entities such as users and applications.
|
||||
* This service interacts with the database to store, retrieve, and audit permission changes, and also handles complex permission logic like rewriting, implication, and explosion of permissions.
|
||||
*/
|
||||
class PermissionRewriter {
|
||||
static create ({ id, matcher, rewriter }) {
|
||||
return new PermissionRewriter({ id, matcher, rewriter });
|
||||
@ -49,11 +56,25 @@ class PermissionRewriter {
|
||||
return this.matcher(permission);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines if the given permission matches the criteria set for this rewriter.
|
||||
*
|
||||
* @param {string} permission - The permission string to check.
|
||||
* @returns {boolean} - True if the permission matches, false otherwise.
|
||||
*/
|
||||
async rewrite (permission) {
|
||||
return await this.rewriter(permission);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The PermissionImplicator class is used to manage implicit permissions.
|
||||
* It defines methods to match and check if a given permission is implicitly granted to an actor.
|
||||
* @class
|
||||
* @name PermissionImplicator
|
||||
*/
|
||||
class PermissionImplicator {
|
||||
static create ({ id, matcher, checker }) {
|
||||
return new PermissionImplicator({ id, matcher, checker });
|
||||
@ -75,11 +96,24 @@ class PermissionImplicator {
|
||||
* @param {string} permission
|
||||
* @returns
|
||||
*/
|
||||
/**
|
||||
* Rewrites a permission string if it matches any registered rewriter.
|
||||
* @param {string} permission - The permission string to potentially rewrite.
|
||||
* @returns {Promise<string>} The possibly rewritten permission string.
|
||||
*/
|
||||
async check ({ actor, permission, recurse }) {
|
||||
return await this.checker({ actor, permission, recurse });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The PermissionExploder class is responsible for expanding permissions into a set of more granular permissions.
|
||||
* It uses a matcher function to determine if a permission should be exploded and an exploder function to perform the expansion.
|
||||
* This class is part of the permission management system, allowing for dynamic and complex permission structures.
|
||||
*
|
||||
* @class PermissionExploder
|
||||
*/
|
||||
class PermissionExploder {
|
||||
static create ({ id, matcher, exploder }) {
|
||||
return new PermissionExploder({ id, matcher, exploder });
|
||||
@ -101,15 +135,36 @@ class PermissionExploder {
|
||||
* @param {string} permission
|
||||
* @returns
|
||||
*/
|
||||
/**
|
||||
* Explodes a permission into a set of implied permissions.
|
||||
*
|
||||
* This method takes a permission string and an actor object,
|
||||
* then uses the associated exploder function to derive additional
|
||||
* permissions that are implied by the given permission.
|
||||
*
|
||||
* @param {Object} options - The options object containing:
|
||||
* @param {Actor} options.actor - The actor requesting the permission explosion.
|
||||
* @param {string} options.permission - The base permission to be exploded.
|
||||
* @returns {Promise<Array<string>>} A promise resolving to an array of implied permissions.
|
||||
*/
|
||||
async explode ({ actor, permission }) {
|
||||
return await this.exploder({ actor, permission });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The PermissionUtil class provides utility methods for handling
|
||||
* permission strings and operations, including splitting, joining,
|
||||
* escaping, and unescaping permission components. It also includes
|
||||
* functionality to convert permission reading structures into options.
|
||||
*/
|
||||
class PermissionUtil {
|
||||
static unescape_permission_component (component) {
|
||||
let unescaped_str = '';
|
||||
// Constant for unescaped permission component string
|
||||
const STATE_NORMAL = {};
|
||||
// Constant for escaping special characters in permission strings
|
||||
const STATE_ESCAPE = {};
|
||||
let state = STATE_NORMAL;
|
||||
const const_escapes = { C: ':' };
|
||||
@ -197,18 +252,52 @@ class PermissionUtil {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @class PermissionService
|
||||
* @extends BaseService
|
||||
* @description
|
||||
* The PermissionService class manages and enforces permissions within the application. It provides methods to:
|
||||
* - Check, grant, and revoke permissions for users and applications.
|
||||
* - Scan for existing permissions.
|
||||
* - Handle permission implications, rewriting, and explosion to support complex permission hierarchies.
|
||||
* This service interacts with the database to manage permissions and logs actions for auditing purposes.
|
||||
*/
|
||||
class PermissionService extends BaseService {
|
||||
/**
|
||||
* Initializes the PermissionService by setting up internal arrays for permission handling.
|
||||
*
|
||||
* This method is called during the construction of the PermissionService instance to
|
||||
* prepare it for handling permissions, rewriters, implicators, and exploders.
|
||||
*/
|
||||
_construct () {
|
||||
this._permission_rewriters = [];
|
||||
this._permission_implicators = [];
|
||||
this._permission_exploders = [];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Registers a permission exploder which expands permissions into their component parts or related permissions.
|
||||
*
|
||||
* @param {PermissionExploder} exploder - The PermissionExploder instance to register.
|
||||
* @throws {Error} If the provided exploder is not an instance of PermissionExploder.
|
||||
*/
|
||||
async _init () {
|
||||
this.db = this.services.get('database').get(DB_WRITE, 'permissions');
|
||||
this._register_commands(this.services.get('commands'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rewrites the given permission string based on registered PermissionRewriters.
|
||||
*
|
||||
* @param {string} permission - The original permission string to be rewritten.
|
||||
* @returns {Promise<string>} A promise that resolves to the rewritten permission string.
|
||||
*
|
||||
* @note This method iterates through all registered rewriters. If a rewriter matches the permission,
|
||||
* it applies the rewrite transformation. The process continues until no more matches are found.
|
||||
*/
|
||||
async _rewrite_permission (permission) {
|
||||
for ( const rewriter of this._permission_rewriters ) {
|
||||
if ( ! rewriter.matches(permission) ) continue;
|
||||
@ -217,6 +306,18 @@ class PermissionService extends BaseService {
|
||||
return permission;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if the actor has any of the specified permissions.
|
||||
*
|
||||
* @param {Actor} actor - The actor to check permissions for.
|
||||
* @param {Array|string} permission_options - The permissions to check against.
|
||||
* Can be a single permission string or an array of permission strings.
|
||||
* @returns {Promise<boolean>} - True if the actor has at least one of the permissions, false otherwise.
|
||||
*
|
||||
* @note This method currently delegates to `scan()`, but a TODO suggests
|
||||
* an optimized implementation is planned.
|
||||
*/
|
||||
async check (actor, permission_options) {
|
||||
// TODO: optimized implementation for check instead of
|
||||
// delegating to the scan() method
|
||||
@ -225,6 +326,22 @@ class PermissionService extends BaseService {
|
||||
return options.length > 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scans the permissions for an actor against specified permission options.
|
||||
*
|
||||
* This method performs a comprehensive scan of permissions, considering:
|
||||
* - Direct permissions
|
||||
* - Implicit permissions
|
||||
* - Permission rewriters
|
||||
*
|
||||
* @param {Actor} actor - The actor whose permissions are being checked.
|
||||
* @param {string|string[]} permission_options - One or more permission strings to check against.
|
||||
* @param {*} _reserved - Reserved for future use, currently not utilized.
|
||||
* @param {Object} state - State object to manage recursion and prevent cycles.
|
||||
*
|
||||
* @returns {Promise<Array>} A promise that resolves to an array of permission readings.
|
||||
*/
|
||||
async scan (actor, permission_options, _reserved, state) {
|
||||
if ( ! state ) this.log.info('scan', {
|
||||
actor: actor.uid,
|
||||
@ -267,6 +384,18 @@ class PermissionService extends BaseService {
|
||||
return reading;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Grants a user permission to interact with another user.
|
||||
*
|
||||
* @param {Actor} actor - The actor granting the permission (must be a user).
|
||||
* @param {string} username - The username of the user receiving the permission.
|
||||
* @param {string} permission - The permission string to grant.
|
||||
* @param {Object} [extra={}] - Additional metadata or conditions for the permission.
|
||||
* @param {Object} [meta] - Metadata for logging or auditing purposes.
|
||||
* @throws {Error} If the user to grant permission to is not found or if attempting to grant permissions to oneself.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async grant_user_app_permission (actor, app_uid, permission, extra = {}, meta) {
|
||||
permission = await this._rewrite_permission(permission);
|
||||
|
||||
@ -313,6 +442,20 @@ class PermissionService extends BaseService {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Grants a permission to a user for a specific app.
|
||||
*
|
||||
* @param {Actor} actor - The actor granting the permission, must be a user.
|
||||
* @param {string} app_uid - The unique identifier or name of the app.
|
||||
* @param {string} permission - The permission string to be granted.
|
||||
* @param {Object} [extra={}] - Additional data associated with the permission.
|
||||
* @param {Object} [meta] - Metadata for the operation, including a reason for the grant.
|
||||
*
|
||||
* @throws {Error} If the actor is not a user or if the app is not found.
|
||||
*
|
||||
* @returns {Promise<void>} A promise that resolves when the permission is granted and logged.
|
||||
*/
|
||||
async revoke_user_app_permission (actor, app_uid, permission, meta) {
|
||||
permission = await this._rewrite_permission(permission);
|
||||
|
||||
@ -357,6 +500,15 @@ class PermissionService extends BaseService {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Revokes all permissions for a user on a specific app.
|
||||
*
|
||||
* @param {Actor} actor - The actor performing the revocation, must be a user.
|
||||
* @param {string} app_uid - The unique identifier or name of the app for which permissions are being revoked.
|
||||
* @param {Object} meta - Metadata for logging the revocation action.
|
||||
* @throws {Error} If the actor is not a user.
|
||||
*/
|
||||
async revoke_user_app_all (actor, app_uid, meta) {
|
||||
// For now, actor MUST be a user
|
||||
if ( ! (actor.type instanceof UserActorType) ) {
|
||||
@ -398,6 +550,22 @@ class PermissionService extends BaseService {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Grants a permission from one user to another.
|
||||
*
|
||||
* This method handles the process of granting permissions between users,
|
||||
* ensuring that the permission is correctly formatted, the users exist,
|
||||
* and that self-granting is not allowed.
|
||||
*
|
||||
* @param {Actor} actor - The actor granting the permission (must be a user).
|
||||
* @param {string} username - The username of the user receiving the permission.
|
||||
* @param {string} permission - The permission string to be granted.
|
||||
* @param {Object} [extra={}] - Additional metadata or conditions for the permission.
|
||||
* @param {Object} [meta] - Metadata for auditing purposes, including a reason for the action.
|
||||
* @throws {Error} Throws if the user is not found or if attempting to grant permissions to oneself.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async grant_user_user_permission (actor, username, permission, extra = {}, meta) {
|
||||
permission = await this._rewrite_permission(permission);
|
||||
const user = await get_user({ username });
|
||||
@ -445,6 +613,21 @@ class PermissionService extends BaseService {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Grants a user permission to interact with a specific group.
|
||||
*
|
||||
* @param {Actor} actor - The actor granting the permission.
|
||||
* @param {string} gid - The group identifier (UID or name).
|
||||
* @param {string} permission - The permission string to be granted.
|
||||
* @param {Object} [extra={}] - Additional metadata for the permission.
|
||||
* @param {Object} [meta] - Metadata about the grant action, including the reason.
|
||||
* @returns {Promise<void>}
|
||||
*
|
||||
* @note This method ensures the group exists before granting permission.
|
||||
* @note The permission is first rewritten using any registered rewriters.
|
||||
* @note If the permission already exists, its extra data is updated.
|
||||
*/
|
||||
async grant_user_group_permission (actor, gid, permission, extra = {}, meta) {
|
||||
permission = await this._rewrite_permission(permission);
|
||||
const svc_group = this.services.get('group');
|
||||
@ -487,6 +670,19 @@ class PermissionService extends BaseService {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Revokes a specific user-to-user permission
|
||||
*
|
||||
* @param {Actor} actor - The actor performing the revocation
|
||||
* @param {string} username - The username of the user whose permission is being revoked
|
||||
* @param {string} permission - The specific permission string to revoke
|
||||
* @param {Object} meta - Metadata for the revocation action
|
||||
*
|
||||
* @throws {Error} If the specified user is not found
|
||||
*
|
||||
* @returns {Promise<void>} A promise that resolves when the permission has been revoked and audit logs updated
|
||||
*/
|
||||
async revoke_user_user_permission (actor, username, permission, meta) {
|
||||
permission = await this._rewrite_permission(permission);
|
||||
|
||||
@ -524,6 +720,19 @@ class PermissionService extends BaseService {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Revokes a specific permission granted by the actor to a group.
|
||||
*
|
||||
* This method removes the specified permission from the `user_to_group_permissions` table,
|
||||
* ensuring that the actor no longer has that permission for the specified group.
|
||||
*
|
||||
* @param {Actor} actor - The actor revoking the permission.
|
||||
* @param {string} gid - The group ID for which the permission is being revoked.
|
||||
* @param {string} permission - The permission string to revoke.
|
||||
* @param {Object} meta - Metadata for the revocation action, including reason.
|
||||
* @returns {Promise<void>} A promise that resolves when the revocation is complete.
|
||||
*/
|
||||
async revoke_user_group_permission (actor, gid, permission, meta) {
|
||||
permission = await this._rewrite_permission(permission);
|
||||
const svc_group = this.services.get('group');
|
||||
@ -572,6 +781,14 @@ class PermissionService extends BaseService {
|
||||
* home directories of users that shared files with the
|
||||
* current user.
|
||||
*/
|
||||
/**
|
||||
* Lists users who have granted any permissions to the specified user.
|
||||
*
|
||||
* This method provides a flat, non-recursive view of permission issuers.
|
||||
*
|
||||
* @param {Object} user - The user whose permission issuers are to be listed.
|
||||
* @returns {Promise<Array>} A promise that resolves to an array of user objects.
|
||||
*/
|
||||
async list_user_permission_issuers (user) {
|
||||
const rows = await this.db.read(
|
||||
'SELECT DISTINCT issuer_user_id FROM `user_to_user_permissions` ' +
|
||||
@ -601,6 +818,16 @@ class PermissionService extends BaseService {
|
||||
* - This was written for FSNodeContext.fetchShares to query
|
||||
* all the "shares" associated with a file.
|
||||
*/
|
||||
/**
|
||||
* Queries permissions granted by an issuer to various users and apps based on a permission prefix.
|
||||
*
|
||||
* This method retrieves permissions from the database where the permission key starts with a specified prefix.
|
||||
* It is designed for "flat" (non-cascading) queries.
|
||||
*
|
||||
* @param {Object} issuer - The actor granting the permissions.
|
||||
* @param {string} prefix - The prefix to match in the permission key.
|
||||
* @returns {Object} An object containing arrays of user and app permissions matching the prefix.
|
||||
*/
|
||||
async query_issuer_permissions_by_prefix (issuer, prefix) {
|
||||
const user_perms = await this.db.read(
|
||||
'SELECT DISTINCT holder_user_id, permission ' +
|
||||
@ -654,6 +881,17 @@ class PermissionService extends BaseService {
|
||||
* @param {*} prefix
|
||||
* @returns
|
||||
*/
|
||||
/**
|
||||
* Queries the permissions granted by an issuer to a holder with a specific prefix.
|
||||
*
|
||||
* This method retrieves permissions that match a given prefix from the database.
|
||||
* It's a flat view, meaning it does not include cascading permissions.
|
||||
*
|
||||
* @param {Object} issuer - The actor granting the permissions.
|
||||
* @param {Object} holder - The actor receiving the permissions.
|
||||
* @param {string} prefix - The prefix of the permission keys to match.
|
||||
* @returns {Promise<Array<string>>} An array of permission strings matching the prefix.
|
||||
*/
|
||||
async query_issuer_holder_permissions_by_prefix (issuer, holder, prefix) {
|
||||
const user_perms = await this.db.read(
|
||||
'SELECT permission ' +
|
||||
@ -667,6 +905,18 @@ class PermissionService extends BaseService {
|
||||
return user_perms.map(row => row.permission);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves permissions granted by an issuer to a specific holder with a given prefix.
|
||||
*
|
||||
* @param {Actor} issuer - The actor granting the permissions.
|
||||
* @param {Actor} holder - The actor receiving the permissions.
|
||||
* @param {string} prefix - The prefix to filter permissions by.
|
||||
* @returns {Promise<Array<string>>} A promise that resolves to an array of permission strings.
|
||||
*
|
||||
* @note This method performs a database query to fetch permissions. It does not handle
|
||||
* recursion or implication of permissions, providing only a direct, flat list.
|
||||
*/
|
||||
async get_higher_permissions (permission) {
|
||||
const higher_perms = new Set()
|
||||
higher_perms.add(permission);
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o-mini"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -105,15 +106,38 @@ const compression = {
|
||||
}),
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* TokenService class for managing token creation and verification.
|
||||
* This service extends the BaseService class and provides methods
|
||||
* for signing and verifying JWTs, as well as compressing and decompressing
|
||||
* payloads to and from a compact format.
|
||||
*/
|
||||
class TokenService extends BaseService {
|
||||
static MODULES = {
|
||||
jwt: require('jsonwebtoken'),
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new TokenService instance and initializes the compression settings.
|
||||
* This method is called when a TokenService object is created.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_construct () {
|
||||
this.compression = compression;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the TokenService instance by setting the JWT secret
|
||||
* from the global configuration.
|
||||
*
|
||||
* @function
|
||||
* @returns {void}
|
||||
* @throws {Error} Throws an error if the jwt_secret is not defined in global_config.
|
||||
*/
|
||||
_init () {
|
||||
// TODO: move to service config
|
||||
this.secret = this.global_config.jwt_secret;
|
||||
|
@ -1,6 +1,21 @@
|
||||
// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o"}}
|
||||
const BaseService = require("../BaseService");
|
||||
|
||||
|
||||
/**
|
||||
* Class representing a VirtualGroupService.
|
||||
* This service extends the BaseService and provides methods to manage virtual groups,
|
||||
* allowing for the registration of membership implicators and the retrieval of virtual group data.
|
||||
*/
|
||||
class VirtualGroupService extends BaseService {
|
||||
/**
|
||||
* Retrieves a list of virtual groups based on the provided actor,
|
||||
* utilizing registered membership implicators to determine group membership.
|
||||
*
|
||||
* @param {Object} params - The parameters object.
|
||||
* @param {Object} params.actor - The actor to check against the membership implicators.
|
||||
* @returns {Array} An array of virtual group objects that the actor is a member of.
|
||||
*/
|
||||
_construct () {
|
||||
this.groups_ = {};
|
||||
this.membership_implicators_ = [];
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o-mini"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -20,6 +21,13 @@ const { AdvancedBase } = require("../../../../putility");
|
||||
const BaseService = require("../BaseService");
|
||||
const { DB_WRITE, DB_READ } = require("./consts");
|
||||
|
||||
|
||||
/**
|
||||
* BaseDatabaseAccessService class extends BaseService to provide
|
||||
* an abstraction layer for database access, enabling operations
|
||||
* like reading, writing, and inserting data while managing
|
||||
* different database configurations and optimizations.
|
||||
*/
|
||||
class BaseDatabaseAccessService extends BaseService {
|
||||
static DB_WRITE = DB_WRITE;
|
||||
static DB_READ = DB_READ;
|
||||
@ -36,6 +44,15 @@ class BaseDatabaseAccessService extends BaseService {
|
||||
// future it can be used to audit the behaviour
|
||||
// of other services or handle service-specific
|
||||
// database optimizations.
|
||||
/**
|
||||
* Retrieves the current instance of the service.
|
||||
* This method currently returns `this`, but it is designed
|
||||
* to allow for future enhancements such as auditing behavior
|
||||
* or implementing service-specific optimizations for database
|
||||
* interactions.
|
||||
*
|
||||
* @returns {BaseDatabaseAccessService} The current instance of the service.
|
||||
*/
|
||||
get () {
|
||||
return this;
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o-mini"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -23,6 +24,11 @@ const { CompositeError } = require("../../util/errorutil");
|
||||
const structutil = require("../../util/structutil");
|
||||
const { BaseDatabaseAccessService } = require("./BaseDatabaseAccessService");
|
||||
|
||||
|
||||
/**
|
||||
* Class SqliteDatabaseAccessService
|
||||
*
|
||||
* This service provides
|
||||
class SqliteDatabaseAccessService extends BaseDatabaseAccessService {
|
||||
static ENGINE_NAME = 'sqlite';
|
||||
|
||||
@ -32,6 +38,13 @@ class SqliteDatabaseAccessService extends BaseDatabaseAccessService {
|
||||
Database: require('better-sqlite3'),
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @description Method to handle database schema upgrades.
|
||||
* This method checks the current database version against the available migration scripts and performs any necessary upgrades.
|
||||
* @param {void}
|
||||
* @returns {void}
|
||||
*/
|
||||
async _init () {
|
||||
const require = this.require;
|
||||
const Database = require('better-sqlite3');
|
||||
@ -149,6 +162,12 @@ class SqliteDatabaseAccessService extends BaseDatabaseAccessService {
|
||||
// Database upgrade logic
|
||||
const HIGHEST_VERSION =
|
||||
available_migrations[available_migrations.length - 1][0] + 1;
|
||||
/**
|
||||
* Upgrades the database schema to the specified version.
|
||||
*
|
||||
* @param {number} targetVersion - The target version to upgrade the database to.
|
||||
* @returns {Promise<void>} A promise that resolves when the database has been upgraded.
|
||||
*/
|
||||
const TARGET_VERSION = (() => {
|
||||
const args = Context.get('args');
|
||||
if ( args['database-target-version'] ) {
|
||||
@ -220,6 +239,15 @@ class SqliteDatabaseAccessService extends BaseDatabaseAccessService {
|
||||
await this.db.exec(`PRAGMA user_version = ${TARGET_VERSION};`);
|
||||
|
||||
// Add sticky notification
|
||||
/**
|
||||
* This method is responsible for applying database migrations. It checks the current version of the database against the available migrations, and if the database is out of date, it applies the necessary SQL files to bring it up to date.
|
||||
*
|
||||
* @param {void}
|
||||
* @returns {void}
|
||||
*/
|
||||
// Add this comment above line 222
|
||||
// It describes the purpose of the method and its behavior
|
||||
// It does not include any parameters or return values since the method does not take any inputs and does not return any output.
|
||||
this.database_update_notice = () => {
|
||||
const lines = [
|
||||
`Database has been updated!`,
|
||||
@ -239,6 +267,14 @@ class SqliteDatabaseAccessService extends BaseDatabaseAccessService {
|
||||
|
||||
const svc_serverHealth = this.services.get('server-health');
|
||||
|
||||
|
||||
/**
|
||||
* @description This method is used to register SQLite database-related commands with the dev-console service.
|
||||
* @param {object} commands - The dev-console service commands object.
|
||||
*/
|
||||
241: _register_commands(commands) {
|
||||
242: ...
|
||||
243: }
|
||||
svc_serverHealth.add_check('sqlite', async () => {
|
||||
const [{ user_version }] = await this._requireRead('PRAGMA user_version');
|
||||
if ( user_version !== TARGET_VERSION ) {
|
||||
@ -249,16 +285,39 @@ class SqliteDatabaseAccessService extends BaseDatabaseAccessService {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Method to perform database upgrade if necessary.
|
||||
*
|
||||
* This method checks if the current database version is outdated and applies any necessary migration scripts to bring it up to date.
|
||||
*
|
||||
* @param {none}
|
||||
* @returns {void}
|
||||
*/
|
||||
async _read (query, params = []) {
|
||||
query = this.sqlite_transform_query_(query);
|
||||
params = this.sqlite_transform_params_(params);
|
||||
return this.db.prepare(query).all(...params);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description This method initializes the SQLite database connection and performs any necessary upgrades to the database schema.
|
||||
* @notes This method is responsible for creating a new instance of the SQLite database connection, registering commands for the SQLite CLI, and upgrading the database schema if necessary.
|
||||
* @param {none}
|
||||
* @return {Promise} A promise that resolves when the database connection is initialized and any necessary upgrades are completed.
|
||||
*/
|
||||
async _requireRead (query, params) {
|
||||
return this._read(query, params);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method is responsible for performing database upgrades. It checks the current version of the database, compares it to the desired version, and applies any necessary migration scripts.
|
||||
*
|
||||
* It accepts no parameters and returns nothing.
|
||||
*/
|
||||
// line 261, method begins here.
|
||||
async _write (query, params) {
|
||||
query = this.sqlite_transform_query_(query);
|
||||
params = this.sqlite_transform_params_(params);
|
||||
@ -272,7 +331,30 @@ class SqliteDatabaseAccessService extends BaseDatabaseAccessService {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method initializes the SQLite database by checking if it exists, setting up the connection, and performing any necessary database upgrades based on the current version.
|
||||
*
|
||||
* @param {object} config - The configuration object for the database.
|
||||
* @returns {Promise} A promise that resolves when the database is initialized.
|
||||
*/
|
||||
async _batch_write (entries) {
|
||||
/**
|
||||
* @description This method is used to execute SQL queries in batch mode.
|
||||
* It accepts an array of objects, where each object contains a SQL query as the `statement` property and an array of parameters as the `values` property.
|
||||
* The method executes each SQL query in the transaction block, ensuring that all operations are atomic.
|
||||
* @param {Array<{statement: string, values: any[]}>} entries - An array of SQL queries and their corresponding parameters.
|
||||
* @return {void} This method does not return any value.
|
||||
*/
|
||||
275: async _batch_write (entries) {
|
||||
276: this.db.transaction(() => {
|
||||
277: for ( let { statement, values } of entries ) {
|
||||
278: statement = this.sqlite_transform_query_(statement);
|
||||
279: values = this.sqlite_transform_params_(values);
|
||||
280: this.db.prepare(statement).run(values);
|
||||
281: }
|
||||
282: })();
|
||||
283: }
|
||||
this.db.transaction(() => {
|
||||
for ( let { statement, values } of entries ) {
|
||||
statement = this.sqlite_transform_query_(statement);
|
||||
@ -299,7 +381,33 @@ class SqliteDatabaseAccessService extends BaseDatabaseAccessService {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description This method is responsible for performing database upgrades. It checks the current database version against the available versions and applies any necessary migrations.
|
||||
* @param {object} options - Optional parameters for the method.
|
||||
* @returns {Promise} A promise that resolves when the database upgrade is complete.
|
||||
*/
|
||||
async run_js_migration_ ({ filename, contents }) {
|
||||
/**
|
||||
* Method to run JavaScript migrations. This method is used to apply JavaScript code to the SQLite database during the upgrade process.
|
||||
*
|
||||
* @param {Object} options - An object containing the following properties:
|
||||
* - `filename`: The name of the JavaScript file containing the migration code.
|
||||
* - `contents`: The contents of the JavaScript file.
|
||||
*
|
||||
* @returns {Promise<void>} A promise that resolves when the migration is completed.
|
||||
*/
|
||||
303: async run_js_migration_ ({ filename, contents }) {
|
||||
304: contents = `(async () => {${contents}})()`;
|
||||
305: const vm = require('vm');
|
||||
306: const context = vm.createContext({
|
||||
307: read: this.read.bind(this),
|
||||
308: write: this.write.bind(this),
|
||||
309: log: this.log,
|
||||
310: structutil,
|
||||
311: });
|
||||
312: await vm.runInContext(contents, context);
|
||||
313: }
|
||||
contents = `(async () => {${contents}})()`;
|
||||
const vm = require('vm');
|
||||
const context = vm.createContext({
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"xai"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"claude"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o"}}
|
||||
/*
|
||||
Add a user called `system`.
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o"}}
|
||||
const { insertId: temp_group_id } = await write(
|
||||
'INSERT INTO `group` (`uid`, `owner_user_id`, `extra`, `metadata`) '+
|
||||
'VALUES (?, ?, ?, ?)',
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"xai"}}
|
||||
const insert = async (tbl, subject) => {
|
||||
const keys = Object.keys(subject);
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -20,15 +21,40 @@ const BaseService = require("../BaseService");
|
||||
const { TypeSpec } = require("./meta/Construct");
|
||||
const { TypedValue } = require("./meta/Runtime");
|
||||
|
||||
|
||||
/**
|
||||
* CoercionService class is responsible for handling coercion operations
|
||||
* between TypedValue instances and their target TypeSpec representations.
|
||||
* It provides functionality to construct and initialize coercions that
|
||||
* can convert one type into another, based on specified produces and
|
||||
* consumes specifications.
|
||||
*/
|
||||
class CoercionService extends BaseService {
|
||||
static MODULES = {
|
||||
axios: require('axios'),
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Attempt to coerce a TypedValue to a target TypeSpec.
|
||||
* This method checks if the current TypedValue can be adapted to the specified target TypeSpec,
|
||||
* using the available coercions defined in the service. It implements caching for previously calculated coercions.
|
||||
*
|
||||
* @param {*} target - the target TypeSpec
|
||||
* @param {*} typed_value - the TypedValue to coerce
|
||||
* @returns {TypedValue|undefined} - the coerced TypedValue, or undefined if coercion cannot be performed
|
||||
*/
|
||||
async _construct () {
|
||||
this.coercions_ = [];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the coercion service by populating the coercions_ array
|
||||
* with predefined coercion rules that specify how TypedValues should
|
||||
* be processed. This method should be called before any coercion
|
||||
* operations are performed.
|
||||
*/
|
||||
async _init () {
|
||||
this.coercions_.push({
|
||||
produces: {
|
||||
@ -61,6 +87,17 @@ class CoercionService extends BaseService {
|
||||
* @param {*} typed_value - the TypedValue to coerce
|
||||
* @returns {TypedValue|undefined} - the coerced TypedValue, or undefined
|
||||
*/
|
||||
/**
|
||||
* Attempt to coerce a TypedValue to a target TypeSpec.
|
||||
* This method first adapts the target and the current type of the
|
||||
* TypedValue. If they are equal, it returns the original TypedValue.
|
||||
* Otherwise, it checks if the coercion has been calculated before,
|
||||
* retrieves applicable coercions, and applies them to the TypedValue.
|
||||
*
|
||||
* @param {*} target - the target TypeSpec to which the TypedValue should be coerced
|
||||
* @param {*} typed_value - the TypedValue to coerce
|
||||
* @returns {TypedValue|undefined} - the coerced TypedValue if successful, or undefined
|
||||
*/
|
||||
async coerce (target, typed_value) {
|
||||
target = TypeSpec.adapt(target);
|
||||
const target_hash = target.hash();
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"xai"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -16,6 +17,12 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Represents an error that occurs within the Driver system of Puter.
|
||||
* This class provides a structured way to handle, report, and serialize errors
|
||||
* originating from various drivers or backend services in Puter.
|
||||
* @class DriverError
|
||||
*/
|
||||
class DriverError {
|
||||
static create (source) {
|
||||
return new DriverError({ source });
|
||||
@ -25,6 +32,12 @@ class DriverError {
|
||||
this.message = source?.message || message;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the DriverError instance into a standardized object format.
|
||||
* @returns {Object} An object with keys '$' for type identification and 'message' for error details.
|
||||
* @note The method uses a custom type identifier for compatibility with Puter's error handling system.
|
||||
*/
|
||||
serialize () {
|
||||
return {
|
||||
$: 'heyputer:api/DriverError',
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -29,11 +30,24 @@ const { get_user } = require("../../helpers");
|
||||
/**
|
||||
* DriverService provides the functionality of Puter drivers.
|
||||
*/
|
||||
/**
|
||||
* @classdesc DriverService provides the functionality of Puter drivers.
|
||||
* This class is responsible for managing and interacting with Puter drivers.
|
||||
* It provides methods for registering drivers, calling driver methods, and handling driver errors.
|
||||
*/
|
||||
class DriverService extends BaseService {
|
||||
static MODULES = {
|
||||
types: require('./types'),
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description This method is responsible for registering collections in the registry service.
|
||||
* It registers 'interfaces', 'drivers', and 'types' collections.
|
||||
* It also populates the 'interfaces' and 'types' collections with default interfaces and types.
|
||||
* @returns {void}
|
||||
*/
|
||||
// Add this comment above line 37, before the method definition.
|
||||
_construct () {
|
||||
this.drivers = {};
|
||||
this.interface_to_implementation = {};
|
||||
@ -41,12 +55,25 @@ class DriverService extends BaseService {
|
||||
this.service_aliases = {};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method is responsible for registering collections in the service registry.
|
||||
* It registers 'interfaces', 'drivers', and 'types' collections.
|
||||
*/
|
||||
DriverService.prototype['__on_registry.collections'] = function() {
|
||||
// Your code here
|
||||
};
|
||||
async ['__on_registry.collections'] () {
|
||||
const svc_registry = this.services.get('registry');
|
||||
svc_registry.register_collection('interfaces');
|
||||
svc_registry.register_collection('drivers');
|
||||
svc_registry.register_collection('types');
|
||||
}
|
||||
/**
|
||||
* This method is responsible for initializing the collections in the driver service registry.
|
||||
* It registers 'interfaces', 'drivers', and 'types' collections.
|
||||
* It also populates the 'interfaces' collection with default interfaces and registers the collections with the driver service registry.
|
||||
*/
|
||||
async ['__on_registry.entries'] () {
|
||||
const services = this.services;
|
||||
const svc_registry = services.get('registry');
|
||||
@ -71,6 +98,14 @@ class DriverService extends BaseService {
|
||||
{ col_drivers });
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method is responsible for calling a driver's method with provided arguments.
|
||||
* It checks for permissions, selects the best option, and applies rate and monthly usage limits before invoking the driver.
|
||||
*
|
||||
* @param {Object} o - An object containing driver, interface, method, and arguments.
|
||||
* @returns {Promise<{success: boolean, service: DriverService.Driver, result: any, metadata: any}>}
|
||||
*/
|
||||
_init () {
|
||||
const svc_registry = this.services.get('registry');
|
||||
svc_registry.register_collection('');
|
||||
@ -111,6 +146,21 @@ class DriverService extends BaseService {
|
||||
return options[0];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method is responsible for calling the specified driver method with the given arguments.
|
||||
* It first processes the arguments to ensure they are in the correct format, then it checks if the driver and method exist,
|
||||
* and if the user has the necessary permissions to call them. If all checks pass, it calls the method and returns the result.
|
||||
* If any check fails, it throws an error or returns an error response.
|
||||
*
|
||||
* @param {Object} o - An object containing the driver name, interface name, method name, and arguments.
|
||||
* @returns {Promise<Object>} A promise that resolves to an object containing the result of the method call,
|
||||
* or rejects with an error if any check fails.
|
||||
*/
|
||||
113: * @param {Object} o - An object containing the driver, interface, method, and arguments.
|
||||
114: * @returns {Promise<Object>} A promise that resolves to an object containing the result of the method call,
|
||||
115: * or rejects with an error if any check fails.
|
||||
116: */
|
||||
async call (o) {
|
||||
try {
|
||||
return await this._call(o);
|
||||
@ -126,6 +176,14 @@ class DriverService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method is responsible for making a call to a driver using its implementation and interface.
|
||||
* It handles various aspects such as argument processing, permission checks, and invoking the driver's method.
|
||||
* It returns a promise that resolves to an object containing the result, metadata, and an error if one occurred.
|
||||
*/
|
||||
128: async _call ({ driver, iface, method, args }) {
|
||||
// comment above method _call in DriverService.js
|
||||
async _call ({ driver, iface, method, args }) {
|
||||
console.log('??', driver, iface, method, args);
|
||||
const processed_args = await this._process_args(iface, method, args);
|
||||
@ -170,6 +228,16 @@ class DriverService extends BaseService {
|
||||
};
|
||||
driver = this.service_aliases[driver] ?? driver;
|
||||
|
||||
|
||||
/**
|
||||
* This method retrieves the driver service for the provided interface name.
|
||||
* It first checks if the driver service already exists in the registry,
|
||||
* and if not, it creates a new instance of the driver service and registers it.
|
||||
*
|
||||
* @param {string} interfaceName - The name of the interface for which to retrieve the driver service.
|
||||
* @returns {DriverService} The driver service instance for the provided interface.
|
||||
*/
|
||||
// line 172
|
||||
const driver_service_exists = (() => {
|
||||
console.log('CHECKING FOR THIS', driver, iface);
|
||||
return this.services.has(driver) &&
|
||||
@ -209,6 +277,21 @@ class DriverService extends BaseService {
|
||||
if ( ! instance ) {
|
||||
throw APIError.create('no_implementation_available', null, { iface })
|
||||
}
|
||||
/**
|
||||
* This method is responsible for calling a driver method. It performs various checks and preparations before making the actual call.
|
||||
*
|
||||
* It first checks if the driver service exists for the given driver and interface. If it does, it checks if the driver supports the test mode. If it does, it skips the usage of the driver.
|
||||
*
|
||||
* If the driver service does not exist, it looks for a default implementation for the given interface and uses it if found.
|
||||
*
|
||||
* The method then calls the driver method with the processed arguments and returns the result. If an error occurs during the call, it is caught and handled accordingly.
|
||||
*
|
||||
* @param {Object} o - An object containing the driver name, interface name, method name, and arguments.
|
||||
* @returns {Promise<Object>} - A promise that resolves to an object containing the result of the driver method call, or an error object if an error occurred.
|
||||
*/
|
||||
DriverService.prototype._call = function(o) {
|
||||
// Your comment here
|
||||
};
|
||||
const meta = await (async () => {
|
||||
if ( instance instanceof Driver ) {
|
||||
return await instance.get_response_meta();
|
||||
@ -252,6 +335,14 @@ class DriverService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method is used to call a specific method on a driver implementation.
|
||||
* It takes in an object containing the driver name, interface name, method name, and arguments, and returns the result of the call.
|
||||
* It also handles error cases, such as missing required arguments, and returns appropriate error messages.
|
||||
* @param {object} o - An object containing the driver name, interface name, method name, and arguments.
|
||||
* @return {Promise<object>} - A promise that resolves to an object containing the result of the call, or an error object if an error occurred.
|
||||
*/
|
||||
async get_policies_for_option_ (option) {
|
||||
// NOT FINAL: before implementing cascading monthly usage,
|
||||
// this return will be removed and the code below it will
|
||||
@ -262,6 +353,19 @@ class DriverService extends BaseService {
|
||||
const svc_su = this.services.get('su');
|
||||
|
||||
const policies = await Promise.all(option.path.map(async path_node => {
|
||||
/**
|
||||
* This method is responsible for executing a driver call by invoking the appropriate service and method.
|
||||
* It takes an object containing the driver name, interface name, method name, and arguments as input.
|
||||
* The method first checks if the driver service exists and if the user has the necessary permissions.
|
||||
* If the driver service does not exist, it throws an error. If the user has the necessary permissions,
|
||||
* the method calls the appropriate method on the driver service with the provided arguments.
|
||||
* If an error occurs during the call, it is wrapped in an APIError and returned to the client.
|
||||
*
|
||||
* @param {Object} o - Object containing the driver name, interface name, method name, and arguments.
|
||||
* @returns {Promise<Object>} - A Promise that resolves to an object containing the result of the driver call or an error object.
|
||||
*/
|
||||
// Add comment above line 264
|
||||
// (line 254 in your provided code)
|
||||
const policy = await svc_su.sudo(async () => {
|
||||
return await svc_systemData.interpret(option.data);
|
||||
});
|
||||
@ -274,6 +378,17 @@ class DriverService extends BaseService {
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Description: This method is responsible for handling driver calls. It takes an object containing the driver name, interface name, method name, and arguments, and calls the corresponding method in the driver implementation.
|
||||
*
|
||||
* Behavior: The method first checks if the driver service exists for the given driver name and interface name. If it does, it calls the method in the driver implementation with the provided arguments. If the method is not found, it throws an error. If the driver service does not exist, it throws an error.
|
||||
*
|
||||
* Parameters: The method takes an object containing the driver name, interface name, method name, and arguments.
|
||||
*
|
||||
* Return Value: The method returns a promise that resolves with an object containing the result of the method call, the metadata of the driver call, and a boolean indicating whether the call was successful.
|
||||
*/
|
||||
// Add this comment above line 276 in DriverService.js
|
||||
async select_best_option_ (options) {
|
||||
return options[0];
|
||||
}
|
||||
@ -336,6 +451,15 @@ class DriverService extends BaseService {
|
||||
// when cascading monthly usage is implemented.
|
||||
const svc_systemData = this.services.get('system-data');
|
||||
const svc_su = this.services.get('su');
|
||||
/**
|
||||
* This method is used to call a driver method with provided arguments.
|
||||
* It first processes the arguments to ensure they are of the correct type and format.
|
||||
* Then it checks if the method exists in the interface and if the driver service for that interface is available.
|
||||
* If the method exists and the driver service is available, it calls the method using the driver service.
|
||||
* If the method does not exist or the driver service is not available, it throws an error.
|
||||
* @param {object} o - Object containing driver, interface, method and arguments
|
||||
* @returns {Promise<object>} - Promise that resolves to an object containing the result of the driver method call
|
||||
*/
|
||||
effective_policy = await svc_su.sudo(async () => {
|
||||
return await svc_systemData.interpret(effective_policy.data);
|
||||
});
|
||||
@ -359,6 +483,13 @@ class DriverService extends BaseService {
|
||||
if ( ! effective_policy?.['rate-limit'] ) return args;
|
||||
const svc_su = this.services.get('su');
|
||||
const svc_rateLimit = this.services.get('rate-limit');
|
||||
/**
|
||||
* This method is used to initialize a driver instance with the provided options.
|
||||
* It sets up the necessary dependencies and configurations for the driver to function correctly.
|
||||
*
|
||||
* @param {DriverService} driverService - The instance of the driver service.
|
||||
* @param {Object} options - An object containing the options for the driver instance, such as the driver's implementation, test service, and service aliases.
|
||||
*/
|
||||
await svc_su.sudo(policy_holder, async () => {
|
||||
await svc_rateLimit.check_and_increment(
|
||||
`V1:${service_name}:${iface}:${method}`,
|
||||
@ -454,6 +585,20 @@ class DriverService extends BaseService {
|
||||
return await invoker.run(args);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method is responsible for handling driver calls and returns the result or an error message.
|
||||
* It takes a call object with the driver name, interface name, method name, and arguments as input.
|
||||
* The method first checks if the driver and interface exist, then it checks if the user has the required permissions.
|
||||
* If the user has the required permissions, it calls the method on the driver and returns the result.
|
||||
* If the user does not have the required permissions, it throws an APIError with the 'forbidden' message.
|
||||
* If there's an error while calling the method, it throws the error.
|
||||
* If the method returns a TypedValue, it coerces the result to the desired type.
|
||||
* If the method returns an error, it serializes the error and returns it with the original metadata.
|
||||
* @param {Object} call - The call object with driver name, interface name, method name, and arguments.
|
||||
* @returns {Object} The result of the method call or an error object.
|
||||
*/
|
||||
// Add this comment above line 456.
|
||||
async _driver_response_from_error (e, meta) {
|
||||
let serializable = (e instanceof APIError) || (e instanceof DriverError);
|
||||
return {
|
||||
@ -463,10 +608,24 @@ class DriverService extends BaseService {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method is responsible for registering collections in the service registry.
|
||||
* It registers 'interfaces', 'drivers', and 'types' collections.
|
||||
*/
|
||||
// Above line 465
|
||||
async list_interfaces () {
|
||||
return this.interfaces;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method is responsible for registering collections in the service registry.
|
||||
* It registers 'interfaces', 'drivers', and 'types' collections.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
// line 469
|
||||
async _process_args (interface_name, method_name, args) {
|
||||
const svc_registry = this.services.get('registry');
|
||||
const c_interfaces = svc_registry.get('interfaces');
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o"}}
|
||||
const APIError = require("../../api/APIError");
|
||||
const { PermissionUtil } = require("../auth/PermissionService");
|
||||
const BaseService = require("../BaseService");
|
||||
@ -5,7 +6,25 @@ const BaseService = require("../BaseService");
|
||||
// DO WE HAVE enough information to get the policy for the newer drivers?
|
||||
// - looks like it: service:<name of service>:<name of trait>
|
||||
|
||||
|
||||
/**
|
||||
* Class representing the DriverUsagePolicyService.
|
||||
* This service manages the retrieval and application of usage policies
|
||||
* for drivers, handling permission checks and policy interpretation
|
||||
* using the provided service architecture.
|
||||
*/
|
||||
class DriverUsagePolicyService extends BaseService {
|
||||
/**
|
||||
* Retrieves the usage policies for a given option.
|
||||
*
|
||||
* This method takes an option containing a path and returns the corresponding
|
||||
* policies. Note that the implementation is not final and may include cascading
|
||||
* monthly usage logic in the future.
|
||||
*
|
||||
* @param {Object} option - The option for which policies are to be retrieved.
|
||||
* @param {Array} option.path - The path representing the request to get policies.
|
||||
* @returns {Promise<Array>} A promise that resolves to the policies associated with the given option.
|
||||
*/
|
||||
async get_policies_for_option_ (option) {
|
||||
// NOT FINAL: before implementing cascading monthly usage,
|
||||
// this return will be removed and the code below it will
|
||||
@ -16,6 +35,15 @@ class DriverUsagePolicyService extends BaseService {
|
||||
const svc_su = this.services.get('su');
|
||||
|
||||
const policies = await Promise.all(option.path.map(async path_node => {
|
||||
/**
|
||||
* Retrieves the policies associated with a given option.
|
||||
* This method processes the provided option's path and interprets its data
|
||||
* using the system services. It returns a list of policies for each path node
|
||||
* by using sudo access to ensure the actions are performed with the required permissions.
|
||||
*
|
||||
* @param {Object} option - An object containing the path and data used for policy retrieval.
|
||||
* @returns {Promise<Array>} A promise that resolves to an array of policy objects for the given option.
|
||||
*/
|
||||
const policy = await svc_su.sudo(async () => {
|
||||
return await svc_systemData.interpret(option.data);
|
||||
});
|
||||
@ -28,6 +56,17 @@ class DriverUsagePolicyService extends BaseService {
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Selects the best option from the provided list of options.
|
||||
*
|
||||
* This method assumes that the options array is not empty and will
|
||||
* return the first option found. It does not perform any sorting
|
||||
* or decision-making beyond this.
|
||||
*
|
||||
* @param {Array} options - An array of options to select from.
|
||||
* @returns {Object} The best option from the provided list.
|
||||
*/
|
||||
async select_best_option_ (options) {
|
||||
return options[0];
|
||||
}
|
||||
@ -35,6 +74,19 @@ class DriverUsagePolicyService extends BaseService {
|
||||
// TODO: DRY: This is identical to the method of the same name in
|
||||
// DriverService, except after the line with a comment containing
|
||||
// the string "[DEVIATION]".
|
||||
/**
|
||||
* Retrieves the effective policy for a given actor, service name, and trait name.
|
||||
* This method checks for permissions associated with the provided actor and then generates
|
||||
* a list of policies based on the permissions read. If no policies are found, it returns
|
||||
* `undefined`. Otherwise, it selects the best option and retrieves the corresponding
|
||||
* policies.
|
||||
*
|
||||
* @param {Object} parameters - The parameters for the method.
|
||||
* @param {string} parameters.actor - The actor for which the policy is being requested.
|
||||
* @param {string} parameters.service_name - The name of the service to which the policy applies.
|
||||
* @param {string} parameters.trait_name - The name of the trait for which the effective policy is needed.
|
||||
* @returns {Object|undefined} - Returns the effective policy object or `undefined` if no policies are available.
|
||||
*/
|
||||
async get_effective_policy ({ actor, service_name, trait_name }) {
|
||||
const svc_permission = this.services.get('permission');
|
||||
const reading = await svc_permission.scan(
|
||||
@ -73,6 +125,13 @@ class DriverUsagePolicyService extends BaseService {
|
||||
// === [DEVIATION] In DriverService, this is part of call_new_ ===
|
||||
const svc_systemData = this.services.get('system-data');
|
||||
const svc_su = this.services.get('su');
|
||||
/**
|
||||
* Retrieves and interprets the effective policy for a given holder.
|
||||
* Utilizes system data and super-user privileges to interpret the policy data.
|
||||
*
|
||||
* @param {Object} effective_policy - The policy object for the current holder.
|
||||
* @returns {Promise<Object>} - The interpreted policy object after applying the necessary logic.
|
||||
*/
|
||||
effective_policy = await svc_su.sudo(async () => {
|
||||
return await svc_systemData.interpret(effective_policy.data);
|
||||
});
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"mistral","model":"mistral-large-latest"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -35,6 +36,11 @@ const { LLRead } = require("../../filesystem/ll_operations/ll_read");
|
||||
*
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* @class FileFacade
|
||||
* @extends AdvancedBase
|
||||
* @description This class provides a unified interface for passing files through the Puter Driver API. It aims to avoid unnecessary operations such as downloading files from S3 when a Puter file is specified, especially if the underlying implementation can accept S3 bucket information instead of the file's contents.
|
||||
*/
|
||||
class FileFacade extends AdvancedBase {
|
||||
static OUT_TYPES = {
|
||||
S3_INFO: { key: 's3-info' },
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"xai"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"mistral","model":"mistral-large-latest"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -20,6 +21,15 @@ const { BasicBase } = require("../../../../../putility/src/bases/BasicBase");
|
||||
const types = require("../types");
|
||||
const { hash_serializable_object, stringify_serializable_object } = require("../../../util/datautil");
|
||||
|
||||
|
||||
/**
|
||||
* @class Construct
|
||||
* @extends BasicBase
|
||||
* @classdesc The Construct class is a base class for building various types of constructs.
|
||||
* It extends the BasicBase class and provides a framework for processing and serializing
|
||||
* constructs. This class includes methods for processing raw data and serializing the
|
||||
* constructed object into a JSON-compatible format.
|
||||
*/
|
||||
class Construct extends BasicBase {
|
||||
constructor (json, { name } = {}) {
|
||||
super();
|
||||
@ -28,10 +38,24 @@ class Construct extends BasicBase {
|
||||
this.__process();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes the raw JSON data to initialize the object's properties.
|
||||
* If a process function is defined, it will be executed with the raw JSON data.
|
||||
*/
|
||||
__process () {
|
||||
if ( this._process ) this._process(this.raw);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the properties of the object into a JSON-compatible format.
|
||||
*
|
||||
* This method iterates over the properties defined in the static `PROPERTIES`
|
||||
* object and serializes each property according to its type.
|
||||
*
|
||||
* @returns {Object} The serialized representation of the object.
|
||||
*/
|
||||
serialize () {
|
||||
const props = this._get_merged_static_object('PROPERTIES');
|
||||
const serialized = {};
|
||||
@ -54,6 +78,13 @@ class Construct extends BasicBase {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @class Parameter
|
||||
* @extends Construct
|
||||
* @description The Parameter class extends the Construct class and is used to define a parameter in a method.
|
||||
* It includes properties such as type, optional status, and a description. The class processes raw data to initialize these properties.
|
||||
*/
|
||||
class Parameter extends Construct {
|
||||
static PROPERTIES = {
|
||||
type: { type: 'object' },
|
||||
@ -66,6 +97,13 @@ class Parameter extends Construct {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @class Method
|
||||
* @extends Construct
|
||||
* @description Represents a method in the system, including its description, parameters, and result.
|
||||
* This class processes raw method data and structures it into a usable format.
|
||||
*/
|
||||
class Method extends Construct {
|
||||
static PROPERTIES = {
|
||||
description: { type: 'string' },
|
||||
@ -89,6 +127,15 @@ class Method extends Construct {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @class Interface
|
||||
* @extends Construct
|
||||
* @description The Interface class represents a collection of methods and their descriptions.
|
||||
* It extends the Construct class and defines static properties and methods to process raw data
|
||||
* into a structured format. Each method in the Interface is an instance of the Method class,
|
||||
* which in turn contains Parameter instances for its parameters and result.
|
||||
*/
|
||||
class Interface extends Construct {
|
||||
static PROPERTIES = {
|
||||
description: { type: 'string' },
|
||||
@ -107,6 +154,14 @@ class Interface extends Construct {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @class TypeSpec
|
||||
* @extends BasicBase
|
||||
* @description The TypeSpec class is used to represent a type specification.
|
||||
* It provides methods to adapt raw data into a TypeSpec instance, check equality,
|
||||
* convert the raw data to a string, and generate a hash of the raw data.
|
||||
*/
|
||||
class TypeSpec extends BasicBase {
|
||||
static adapt (raw) {
|
||||
if ( raw instanceof TypeSpec ) return raw;
|
||||
@ -121,10 +176,25 @@ class TypeSpec extends BasicBase {
|
||||
return this.raw.$ === other.raw.$;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts the TypeSpec object to its string representation.
|
||||
*
|
||||
* @returns {string} The string representation of the TypeSpec object.
|
||||
*/
|
||||
toString () {
|
||||
return stringify_serializable_object(this.raw);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates a hash value for the serialized object.
|
||||
*
|
||||
* This method uses the `hash_serializable_object` utility function to create a hash
|
||||
* from the internal `raw` object. This hash can be used for comparison or indexing.
|
||||
*
|
||||
* @returns {string} The hash value of the serialized object.
|
||||
*/
|
||||
hash () {
|
||||
return hash_serializable_object(this.raw);
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o-mini"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -19,9 +20,24 @@
|
||||
const { BasicBase } = require("../../../../../putility/src/bases/BasicBase");
|
||||
const { TypeSpec } = require("./Construct");
|
||||
|
||||
|
||||
/**
|
||||
* Represents an entity in the runtime environment that extends the BasicBase class.
|
||||
* This class serves as a foundational type for creating various runtime constructs
|
||||
* within the Puter framework, enabling the implementation of specialized behaviors
|
||||
* and properties.
|
||||
*/
|
||||
class RuntimeEntity extends BasicBase {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Represents a base runtime entity that extends functionality
|
||||
* from the BasicBase class. This entity can be used as a
|
||||
* foundation for creating more specific runtime objects
|
||||
* within the application, enabling consistent behavior across
|
||||
* derived entities.
|
||||
*/
|
||||
class TypedValue extends RuntimeEntity {
|
||||
constructor (type, value) {
|
||||
super();
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"claude"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -22,9 +23,31 @@ const { is_valid_url, is_valid_uuid4 } = require("../../helpers");
|
||||
const { FileFacade } = require("./FileFacade");
|
||||
const APIError = require("../../api/APIError");
|
||||
|
||||
|
||||
/**
|
||||
* @class BaseType
|
||||
* @extends AdvancedBase
|
||||
* @description Base class for all type validators in the Puter type system.
|
||||
* Extends AdvancedBase to provide core functionality for type checking and validation.
|
||||
* Serves as the foundation for specialized type classes like String, Flag, NumberType, etc.
|
||||
*/
|
||||
class BaseType extends AdvancedBase {}
|
||||
|
||||
|
||||
/**
|
||||
* @class BaseType
|
||||
* @extends AdvancedBase
|
||||
* @description Base class for all type validators in the Puter type system.
|
||||
* Extends AdvancedBase to provide core functionality for type checking and validation.
|
||||
* Serves as the foundation for specialized type classes that implement specific validation logic.
|
||||
*/
|
||||
class String extends BaseType {
|
||||
/**
|
||||
* Consolidates input into a string value
|
||||
* @param {Object} ctx - The context object
|
||||
* @param {*} input - The input value to consolidate
|
||||
* @returns {string|undefined} The consolidated string value, or undefined if input is null/undefined
|
||||
*/
|
||||
async consolidate (ctx, input) {
|
||||
// undefined means the optional parameter was not provided,
|
||||
// which is different from an empty string.
|
||||
@ -34,18 +57,61 @@ class String extends BaseType {
|
||||
) ? undefined : '' + input;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the type to a string representation
|
||||
* @returns {string} Always returns 'string' to identify this as a string type
|
||||
*/
|
||||
serialize () { return 'string'; }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @class Flag
|
||||
* @description A class that handles boolean flag values in the type system.
|
||||
* Converts any input value to a boolean using double negation,
|
||||
* making it useful for command line flags and boolean parameters.
|
||||
* Extends BaseType to integrate with the type validation framework.
|
||||
*/
|
||||
class Flag extends BaseType {
|
||||
/**
|
||||
* Consolidates input into a boolean flag value
|
||||
* @param {Object} ctx - The context object
|
||||
* @param {*} input - The input value to consolidate
|
||||
* @returns {boolean} The consolidated boolean value, using double negation to coerce to boolean
|
||||
*/
|
||||
async consolidate (ctx, input) {
|
||||
return !! input;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the Flag type to a string representation
|
||||
* @returns {string} Returns 'flag' as the type identifier
|
||||
*/
|
||||
serialize () { return 'flag'; }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @class NumberType
|
||||
* @extends BaseType
|
||||
* @description Represents a number type validator and consolidator for API parameters.
|
||||
* Handles both regular and unsigned numbers, performs type checking, and validates
|
||||
* numeric constraints. Supports optional values and throws appropriate API errors
|
||||
* for invalid inputs.
|
||||
*/
|
||||
class NumberType extends BaseType {
|
||||
/**
|
||||
* Validates and consolidates number inputs for API parameters
|
||||
* @param {Object} ctx - The context object
|
||||
* @param {*} input - The input value to validate
|
||||
* @param {Object} options - Options object containing arg_name and arg_descriptor
|
||||
* @param {string} options.arg_name - Name of the argument being validated
|
||||
* @param {Object} options.arg_descriptor - Descriptor containing validation rules
|
||||
* @returns {number|undefined} The validated number or undefined if input was undefined
|
||||
* @throws {APIError} If input is not a valid number or violates unsigned constraint
|
||||
*/
|
||||
async consolidate (ctx, input, { arg_name, arg_descriptor }) {
|
||||
// Case for optional values
|
||||
if ( input === undefined ) return undefined;
|
||||
@ -67,10 +133,38 @@ class NumberType extends BaseType {
|
||||
return input;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validates and consolidates a number input value
|
||||
* @param {Object} ctx - The context object
|
||||
* @param {number} input - The input number to validate
|
||||
* @param {Object} options - Options object containing arg_name and arg_descriptor
|
||||
* @param {string} options.arg_name - The name of the argument being validated
|
||||
* @param {Object} options.arg_descriptor - Descriptor containing validation rules like 'unsigned'
|
||||
* @returns {number|undefined} The validated number or undefined if input was undefined
|
||||
* @throws {APIError} If input is not a valid number or violates unsigned constraint
|
||||
*/
|
||||
serialize () { return 'number'; }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @class URL
|
||||
* @description A class for validating and handling URL inputs. This class extends BaseType and provides
|
||||
* functionality to validate whether a given input is a properly formatted URL. It throws an APIError if
|
||||
* the input is invalid. Used within the type system to ensure URL parameters meet the required format
|
||||
* specifications.
|
||||
*/
|
||||
class URL extends BaseType {
|
||||
/**
|
||||
* Validates and consolidates URL inputs
|
||||
* @param {Object} ctx - The context object
|
||||
* @param {string} input - The URL string to validate
|
||||
* @param {Object} options - Options object containing arg_name
|
||||
* @param {string} options.arg_name - Name of the argument being validated
|
||||
* @returns {string} The validated URL string
|
||||
* @throws {APIError} If the input is not a valid URL
|
||||
*/
|
||||
async consolidate (ctx, input, { arg_name }) {
|
||||
if ( ! is_valid_url(input) ) {
|
||||
throw APIError.create('field_invalid', null, {
|
||||
@ -81,9 +175,26 @@ class URL extends BaseType {
|
||||
return input;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the URL type identifier
|
||||
* @returns {string} Returns 'url' as the type identifier for URL validation
|
||||
*/
|
||||
serialize () { return 'url'; }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @class File
|
||||
* @description Represents a file type that can handle various input formats for files in the Puter system.
|
||||
* Accepts and processes multiple file reference formats including:
|
||||
* - Puter filepaths
|
||||
* - Filesystem UUIDs
|
||||
* - URLs
|
||||
* - Base64 encoded data strings
|
||||
* Converts these inputs into a FileFacade instance for standardized file handling.
|
||||
* @extends BaseType
|
||||
*/
|
||||
class File extends BaseType {
|
||||
static DOC_INPUT_FORMATS = [
|
||||
'A puter filepath, like /home/user/file.txt',
|
||||
@ -97,6 +208,23 @@ class File extends BaseType {
|
||||
_path: require('path'),
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validates and consolidates file input into a FileFacade instance.
|
||||
* Handles multiple input formats including:
|
||||
* - Puter filepaths
|
||||
* - Filesystem UUIDs
|
||||
* - URLs (web and data URLs)
|
||||
* - Existing FileFacade instances
|
||||
* Resolves home directory (~) references for authenticated users.
|
||||
*
|
||||
* @param {Object} ctx - Context object containing user info
|
||||
* @param {string|FileFacade} input - The file input to consolidate
|
||||
* @param {Object} options - Options object
|
||||
* @param {string} options.arg_name - Name of the argument for error messages
|
||||
* @returns {Promise<FileFacade>} A FileFacade instance representing the file
|
||||
* @throws {APIError} If input format is invalid
|
||||
*/
|
||||
async consolidate (ctx, input, { arg_name }) {
|
||||
if ( input instanceof FileFacade ) {
|
||||
return input;
|
||||
@ -143,10 +271,35 @@ class File extends BaseType {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the File type identifier
|
||||
* @returns {string} Returns 'file' as the type identifier for File parameters
|
||||
*/
|
||||
serialize () { return 'file'; }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @class JSONType
|
||||
* @extends BaseType
|
||||
* @description Handles JSON data type validation and consolidation. This class validates JSON input
|
||||
* against specified subtypes (array, object, string, etc) if provided in the argument descriptor.
|
||||
* It ensures type safety for JSON data structures while allowing null and undefined values when
|
||||
* appropriate. The class supports optional parameters and performs type checking against the
|
||||
* specified subtype constraint.
|
||||
*/
|
||||
class JSONType extends BaseType {
|
||||
/**
|
||||
* Validates and processes JSON input values according to specified type constraints
|
||||
* @param {Context} ctx - The execution context
|
||||
* @param {*} input - The input value to validate and process
|
||||
* @param {Object} options - Validation options
|
||||
* @param {string} options.arg_descriptor - Descriptor containing subtype constraints
|
||||
* @param {string} options.arg_name - Name of the argument being validated
|
||||
* @returns {*} The validated input value, or undefined if input is undefined
|
||||
* @throws {APIError} If input type doesn't match specified subtype constraint
|
||||
*/
|
||||
async consolidate (ctx, input, { arg_descriptor, arg_name }) {
|
||||
if ( input === undefined ) return undefined;
|
||||
|
||||
@ -171,9 +324,23 @@ class JSONType extends BaseType {
|
||||
return input;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the type identifier for JSON type parameters
|
||||
* @returns {string} Returns 'json' as the type identifier
|
||||
*/
|
||||
serialize () { return 'json'; }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @class WebURLString
|
||||
* @extends BaseType
|
||||
* @description A class for validating and handling web URL strings. This class extends BaseType
|
||||
* and is designed to specifically handle and validate web-based URL strings. Currently commented
|
||||
* out in the codebase, it would provide functionality for ensuring URLs conform to web standards
|
||||
* and protocols (http/https).
|
||||
*/
|
||||
// class WebURLString extends BaseType {
|
||||
// }
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"xai"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -28,6 +29,27 @@ const { pausing_tee } = require("../../util/streamutil");
|
||||
* It is assumed that files are only accessed by one server at a given time,
|
||||
* so this will need to be revised when ACL and sharing is implemented.
|
||||
*/
|
||||
/**
|
||||
* @class FileCacheService
|
||||
* @extends AdvancedBase
|
||||
* @description
|
||||
* The FileCacheService class manages a cache for file storage and retrieval in the Puter system.
|
||||
* This service provides functionalities to:
|
||||
* - Cache files either in memory (precache) or on disk.
|
||||
* - Track file usage with FileTracker instances to manage cache eviction policies.
|
||||
* - Ensure files are stored within configured limits for both disk and memory usage.
|
||||
* - Provide methods for initializing the cache, storing, retrieving, and invalidating cached files.
|
||||
* - Register commands for managing and inspecting the cache status.
|
||||
*
|
||||
* @property {Object} MODULES - Static property containing module dependencies.
|
||||
* @property {number} disk_limit - The maximum size allowed for disk storage of cached files.
|
||||
* @property {number} disk_max_size - The maximum size of a file that can be cached on disk.
|
||||
* @property {number} precache_size - The size limit for memory (precache) storage.
|
||||
* @property {string} path - The directory path where cached files are stored on disk.
|
||||
* @property {number} ttl - Time-to-live for cached files, after which they are considered for eviction.
|
||||
* @property {Map} precache - A Map to hold files in memory.
|
||||
* @property {Map} uid_to_tracker - A Map to track each file with its FileTracker instance.
|
||||
*/
|
||||
class FileCacheService extends AdvancedBase {
|
||||
static MODULES = {
|
||||
fs: require('fs'),
|
||||
@ -55,6 +77,12 @@ class FileCacheService extends AdvancedBase {
|
||||
this._register_commands(services.get('commands'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the amount of precache space currently used.
|
||||
*
|
||||
* @returns {number} The total size in bytes of files stored in the precache.
|
||||
*/
|
||||
get _precache_used () {
|
||||
let used = 0;
|
||||
|
||||
@ -67,6 +95,12 @@ class FileCacheService extends AdvancedBase {
|
||||
return used;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculates the total disk space used by files in the PHASE_DISK phase.
|
||||
*
|
||||
* @returns {number} The total size of all files currently stored on disk.
|
||||
*/
|
||||
get _disk_used () {
|
||||
let used = 0;
|
||||
|
||||
@ -79,6 +113,15 @@ class FileCacheService extends AdvancedBase {
|
||||
return used;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the cache by ensuring the storage directory exists.
|
||||
*
|
||||
* @async
|
||||
* @method init
|
||||
* @returns {Promise<void>} A promise that resolves when the initialization is complete.
|
||||
* @throws {Error} If there's an error creating the directory.
|
||||
*/
|
||||
async init () {
|
||||
const { fs } = this.modules;
|
||||
// Ensure storage path exists
|
||||
@ -90,6 +133,13 @@ class FileCacheService extends AdvancedBase {
|
||||
return path_.join(this.path, uid);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the file path for a given file UID.
|
||||
*
|
||||
* @param {string} uid - The unique identifier of the file.
|
||||
* @returns {string} The full path where the file is stored on disk.
|
||||
*/
|
||||
async try_get (fsNode, opt_log) {
|
||||
const tracker = this.uid_to_tracker.get(await fsNode.get('uid'));
|
||||
|
||||
@ -138,6 +188,19 @@ class FileCacheService extends AdvancedBase {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Attempts to retrieve a cached file.
|
||||
*
|
||||
* This method first checks if the file exists in the cache by its UID.
|
||||
* If found, it verifies the file's age against the TTL (time-to-live).
|
||||
* If the file is expired, it invalidates the cache entry. Otherwise,
|
||||
* it returns the cached data or null if not found or invalidated.
|
||||
*
|
||||
* @param {Object} fsNode - The file system node representing the file.
|
||||
* @param {Object} [opt_log] - Optional logging service to log cache hits.
|
||||
* @returns {Promise<Buffer|null>} - The file data if found, or null.
|
||||
*/
|
||||
async maybe_store (fsNode, stream) {
|
||||
const size = await fsNode.get('size');
|
||||
|
||||
@ -179,6 +242,18 @@ class FileCacheService extends AdvancedBase {
|
||||
return { cached: true, stream: replace_stream };
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Invalidates a file from the cache.
|
||||
*
|
||||
* @param {FsNode} fsNode - The file system node to invalidate.
|
||||
* @returns {Promise<void>} A promise that resolves when the file has been invalidated.
|
||||
*
|
||||
* @description
|
||||
* This method checks if the given file is in the cache, and if so, removes it from both
|
||||
* the precache and disk storage, ensuring that any references to this file are cleaned up.
|
||||
* If the file is not found in the cache, the method does nothing.
|
||||
*/
|
||||
async invalidate (fsNode) {
|
||||
const key = await fsNode.get('uid');
|
||||
if ( ! this.uid_to_tracker.has(key) ) return;
|
||||
@ -192,6 +267,16 @@ class FileCacheService extends AdvancedBase {
|
||||
this.uid_to_tracker.delete(key);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Invalidates a file from the cache.
|
||||
*
|
||||
* @param {Object} fsNode - The file system node representing the file to invalidate.
|
||||
* @returns {Promise<void>} A promise that resolves when the file has been invalidated from both precache and disk.
|
||||
*
|
||||
* @note This method removes the file's tracker from the cache, deletes the file from precache if present,
|
||||
* and ensures the file is evicted from disk storage if it exists there.
|
||||
*/
|
||||
async _precache_make_room (size) {
|
||||
if (this._precache_used + size > this.precache_size) {
|
||||
await this._precache_evict(
|
||||
@ -200,6 +285,14 @@ class FileCacheService extends AdvancedBase {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Evicts files from precache to make room for new files.
|
||||
* This method sorts all trackers by score and evicts the lowest scoring
|
||||
* files in precache phase until the specified capacity is freed.
|
||||
*
|
||||
* @param {number} capacity_needed - The amount of capacity (in bytes) that needs to be freed in precache.
|
||||
*/
|
||||
async _precache_evict (capacity_needed) {
|
||||
// Sort by score from tracker
|
||||
const sorted = Array.from(this.uid_to_tracker.values())
|
||||
@ -214,6 +307,17 @@ class FileCacheService extends AdvancedBase {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Evicts files from the precache to make room for new files.
|
||||
*
|
||||
* @param {number} capacity_needed - The amount of space needed to be freed in bytes.
|
||||
*
|
||||
* @description
|
||||
* This method sorts all cached files by their score in descending order,
|
||||
* then iterates through them to evict files from the precache to disk
|
||||
* until the required capacity is met. If a file is already on disk, it is skipped.
|
||||
*/
|
||||
async _maybe_promote_to_disk (tracker) {
|
||||
if (tracker.phase !== FileTracker.PHASE_PRECACHE) return;
|
||||
|
||||
@ -255,6 +359,15 @@ class FileCacheService extends AdvancedBase {
|
||||
tracker.phase = FileTracker.PHASE_DISK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Evicts a file from disk cache.
|
||||
*
|
||||
* @param {FileTracker} tracker - The FileTracker instance representing the file to be evicted.
|
||||
* @returns {Promise<void>} A promise that resolves when the file is evicted or if the tracker is not in the disk phase.
|
||||
*
|
||||
* @note This method ensures that the file is removed from the disk cache and the tracker's phase is updated to GONE.
|
||||
*/
|
||||
async _disk_evict (tracker) {
|
||||
if (tracker.phase !== FileTracker.PHASE_DISK) return;
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"claude"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -21,6 +22,13 @@
|
||||
*
|
||||
* Tracks information about cached files for LRU and LFU eviction.
|
||||
*/
|
||||
/**
|
||||
* @class FileTracker
|
||||
* @description A class that manages and tracks metadata for cached files, including their lifecycle phases,
|
||||
* access patterns, and timing information. Used for implementing cache eviction strategies like LRU (Least
|
||||
* Recently Used) and LFU (Least Frequently Used). Maintains state about file size, access count, last access
|
||||
* time, and creation time to help determine which files should be evicted from cache when necessary.
|
||||
*/
|
||||
class FileTracker {
|
||||
static PHASE_PENDING = { label: 'pending' };
|
||||
static PHASE_PRECACHE = { label: 'precache' };
|
||||
@ -36,6 +44,14 @@ class FileTracker {
|
||||
this.birth = Date.now();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculates a score for cache eviction prioritization
|
||||
* Combines access frequency and recency using weighted formula
|
||||
* Higher scores indicate files that should be kept in cache
|
||||
*
|
||||
* @returns {number} Eviction score - higher values mean higher priority to keep
|
||||
*/
|
||||
get score () {
|
||||
const weight_recency = 0.5;
|
||||
const weight_access_count = 0.5;
|
||||
@ -47,11 +63,22 @@ class FileTracker {
|
||||
(weight_recency * recency);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the age of the file in milliseconds since creation
|
||||
* @returns {number} Time in milliseconds since this tracker was created
|
||||
*/
|
||||
get age () {
|
||||
return Date.now() - this.birth;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Updates the access count and timestamp for this file
|
||||
* Increments access_count and sets last_access to current time
|
||||
* Used to track file usage for cache eviction scoring
|
||||
*/
|
||||
touch () {
|
||||
this.access_count++;
|
||||
this.last_access = Date.now();
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -20,14 +21,37 @@ const { RWLock } = require("../../util/lockutil");
|
||||
const { TeePromise } = require("../../util/promise");
|
||||
const BaseService = require("../BaseService");
|
||||
|
||||
// Constant representing the read lock mode used for distinguishing between read and write operations.
|
||||
const MODE_READ = Symbol('read');
|
||||
// Constant representing the read mode for locks, used to distinguish between read and write operations.
|
||||
const MODE_WRITE = Symbol('write');
|
||||
|
||||
// TODO: DRY: could use LockService now
|
||||
/**
|
||||
* FSLockService is a service class that manages file system locks using read-write locks.
|
||||
* It provides functionality to create, list, and manage locks on file paths,
|
||||
* allowing concurrent read and exclusive write operations.
|
||||
*/
|
||||
class FSLockService extends BaseService {
|
||||
/**
|
||||
* FSLockService handles file system locking mechanisms,
|
||||
* providing methods to acquire read and write locks on paths.
|
||||
* @param {string} path - The path to lock.
|
||||
* @param {string} name - The name of the resource to lock.
|
||||
* @param {symbol} mode - The mode of the lock (read or write).
|
||||
* @returns {Promise} A promise that resolves when the lock is acquired.
|
||||
* @throws {Error} Throws an error if an invalid mode is provided.
|
||||
*/
|
||||
async _construct () {
|
||||
this.locks = {};
|
||||
}
|
||||
/**
|
||||
* Initializes the FSLockService by setting up the locks object.
|
||||
* This method should be called before using the service to ensure
|
||||
* that the locks property is properly instantiated.
|
||||
*
|
||||
* @returns {Promise<void>} A promise that resolves when the initialization is complete.
|
||||
*/
|
||||
async _init () {
|
||||
const svc_commands = this.services.get('commands');
|
||||
svc_commands.registerCommands('fslock', [
|
||||
@ -64,16 +88,40 @@ class FSLockService extends BaseService {
|
||||
}
|
||||
]);
|
||||
}
|
||||
/**
|
||||
* Initializes the filesystem lock service by registering the 'fslock' commands.
|
||||
* This method sets up the necessary command handlers and prepares the service
|
||||
* for use. It does not return any value.
|
||||
*/
|
||||
async lock_child (path, name, mode) {
|
||||
if ( path.endsWith('/') ) path = path.slice(0, -1);
|
||||
return await this.lock_path(path + '/' + name, mode);
|
||||
}
|
||||
/**
|
||||
* Acquires a lock on a child resource, given a path and name.
|
||||
* This method modifies the path to ensure it ends with the correct formatting.
|
||||
*
|
||||
* @param {string} path - The base path of the resource to lock.
|
||||
* @param {string} name - The name of the child resource to lock.
|
||||
* @param {Symbol} mode - The mode of the lock (either MODE_READ or MODE_WRITE).
|
||||
* @returns {Promise} Resolves when the lock has been successfully acquired.
|
||||
*/
|
||||
async lock_path (path, mode) {
|
||||
// TODO: Why???
|
||||
// if ( this.locks === undefined ) this.locks = {};
|
||||
|
||||
if ( ! this.locks[path] ) {
|
||||
const rwlock = new RWLock();
|
||||
/**
|
||||
* Acquires a lock for the specified path and mode. If the lock does not exist,
|
||||
* a new RWLock instance is created and associated with the path. The lock is
|
||||
* released when there are no more active locks.
|
||||
*
|
||||
* @param {string} path - The path for which to acquire the lock.
|
||||
* @param {Symbol} mode - The mode of the lock, either MODE_READ or MODE_WRITE.
|
||||
* @returns {Promise} A promise that resolves once the lock is successfully acquired.
|
||||
* @throws {Error} Throws an error if the mode provided is invalid.
|
||||
*/
|
||||
rwlock.on_empty_ = () => {
|
||||
delete this.locks[path];
|
||||
};
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"mistral","model":"mistral-large-latest"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -37,6 +38,10 @@ const BaseService = require("../BaseService");
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @class InformationProvider
|
||||
* @classdesc The InformationProvider class facilitates the registration of strategies for providing information based on given inputs. It allows services to register methods for obtaining information and optimizes the process by determining the most efficient methods for retrieving the required information.
|
||||
*/
|
||||
class InformationProvider {
|
||||
constructor (informationService, input) {
|
||||
this.informationService = informationService;
|
||||
@ -56,6 +61,15 @@ class InformationProvider {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Class InformationObtainer
|
||||
*
|
||||
* This class is responsible for obtaining information from various services. It takes an
|
||||
* InformationService instance and an input, allowing it to obtain specific outputs based on
|
||||
* the input provided. The class provides methods to specify the desired output and execute
|
||||
* the information retrieval process.
|
||||
*/
|
||||
class InformationObtainer {
|
||||
constructor (informationService, input) {
|
||||
this.informationService = informationService;
|
||||
@ -67,9 +81,32 @@ class InformationObtainer {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Executes the information obtaining process asynchronously.
|
||||
*
|
||||
* This method wraps the process of obtaining information in a trace span for monitoring purposes.
|
||||
* It retrieves the necessary services and traces, then delegates the actual obtaining process
|
||||
* to the `obtain_` method of the `InformationService`.
|
||||
*
|
||||
* @async
|
||||
* @function exec
|
||||
* @param {...*} args - Variable number of arguments to be passed to the obtaining process.
|
||||
* @returns {Promise<*>} - A promise that resolves to the obtained information.
|
||||
*/
|
||||
async exec (...args) {
|
||||
const services = this.informationService.services;
|
||||
const traces = services.get('traceService');
|
||||
/**
|
||||
* Executes the obtaining process for the specified output from the specified input.
|
||||
* This method retrieves the relevant information service, traces, and spans the execution to
|
||||
* obtain the information asynchronously. It uses the informationService.obtain_ method to perform the actual retrieval.
|
||||
*
|
||||
* @async
|
||||
* @method exec
|
||||
* @param {...*} args - The arguments required for the obtaining process.
|
||||
* @returns {Promise<*>} - A promise that resolves to the obtained information.
|
||||
*/
|
||||
return await traces.spanify(`OBTAIN ${this.output} FROM ${this.input}`, async () => {
|
||||
return (await this.informationService.obtain_(
|
||||
this.output, this.input, ...args)).result;
|
||||
@ -98,11 +135,51 @@ class InformationObtainer {
|
||||
* // code to obtain fsentry from path
|
||||
* });
|
||||
*/
|
||||
/**
|
||||
* The `InformationService` class is a core component of the information management system.
|
||||
* It facilitates the registration and management of information providers and obtainers,
|
||||
* allowing various services to provide methods for obtaining information and other services
|
||||
* to retrieve that information efficiently. This class optimizes the process by determining
|
||||
* the most efficient strategies for obtaining the required information.
|
||||
*
|
||||
* @class InformationService
|
||||
* @extends BaseService
|
||||
*
|
||||
* @example Obtain an fsentry given a path:
|
||||
*
|
||||
* const infosvc = services.get('information');
|
||||
* const fsentry = await infosvc
|
||||
* .with('fs.fsentry:path').obtain('fs.fsentry')
|
||||
* .exec(path);
|
||||
*
|
||||
* @example Register a method for obtaining an fsentry given a path:
|
||||
*
|
||||
* const infosvc = services.get('information');
|
||||
* infosvc.given('fs.fsentry:path').provide('fs.fsentry')
|
||||
* .addStrategy(async path => {
|
||||
* // code to obtain fsentry from path
|
||||
* });
|
||||
*/
|
||||
class InformationService extends BaseService {
|
||||
/**
|
||||
* @class
|
||||
* @extends BaseService
|
||||
* @description Provides a service for managing information providers and obtaining information efficiently.
|
||||
* @notes This class extends BaseService and includes methods for registering providers, obtaining information,
|
||||
* and managing command registrations.
|
||||
*/
|
||||
_construct () {
|
||||
this.providers_ = {};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the service by registering commands.
|
||||
*
|
||||
* @private
|
||||
* @method _init
|
||||
* @returns {void}
|
||||
*/
|
||||
_init () {
|
||||
this._register_commands(this.services.get('commands'));
|
||||
}
|
||||
@ -122,6 +199,20 @@ class InformationService extends BaseService {
|
||||
this.providers_[output][input].push(provider);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Asynchronously obtains information based on the provided output and input parameters.
|
||||
* This method iterates through registered providers, sorts them for optimization,
|
||||
* and attempts to fetch the desired information.
|
||||
*
|
||||
* @async
|
||||
* @function obtain_
|
||||
* @param {string} output - The type of information to obtain.
|
||||
* @param {string} input - The input parameter required to obtain the information.
|
||||
* @param {...*} args - Additional arguments to pass to the provider functions.
|
||||
* @returns {Promise<Object>} An object containing the provider ID and the result.
|
||||
* @throws {Error} If no providers are available for the given output and input.
|
||||
*/
|
||||
async obtain_ (output, input, ...args) {
|
||||
const providers = this.providers_[output][input];
|
||||
if ( ! providers ) {
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"claude"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -22,6 +23,14 @@ const { generate_random_code } = require("../../util/identifier");
|
||||
const { DB_MODE_WRITE } = require("../MysqlAccessService");
|
||||
const { DB_MODE_READ } = require("../MysqlAccessService");
|
||||
|
||||
|
||||
/**
|
||||
* Base Job class for handling migration tasks in the FSEntryMigrateService.
|
||||
* Provides common functionality for managing job state (green/yellow/red),
|
||||
* progress tracking, and graceful stopping of migration jobs.
|
||||
* Contains methods for state management, progress visualization,
|
||||
* and controlled execution flow.
|
||||
*/
|
||||
class Job {
|
||||
static STATE_GREEN = {};
|
||||
static STATE_YELLOW = {};
|
||||
@ -32,6 +41,11 @@ class Job {
|
||||
this.log = log;
|
||||
this.state = this.constructor.STATE_RED;
|
||||
}
|
||||
/**
|
||||
* Checks if the job should stop based on its current state
|
||||
* @returns {boolean} True if the job should stop, false if it can continue
|
||||
* @private
|
||||
*/
|
||||
maybe_stop_ () {
|
||||
if ( this.state !== this.constructor.STATE_GREEN ) {
|
||||
this.log.info(`Stopping job`);
|
||||
@ -40,11 +54,19 @@ class Job {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Checks if the job should stop based on its current state
|
||||
* @returns {boolean} True if the job should stop, false if it can continue
|
||||
* @private
|
||||
*/
|
||||
stop () {
|
||||
this.state = this.constructor.STATE_YELLOW;
|
||||
}
|
||||
set_progress (progress) {
|
||||
let bar = '';
|
||||
// Progress bar string to display migration progress in the console
|
||||
const WIDTH = 30;
|
||||
// Width of the progress bar display in characters
|
||||
const WIDTH = 30;
|
||||
const N = Math.floor(WIDTH * progress);
|
||||
for ( let i = 0 ; i < WIDTH ; i++ ) {
|
||||
@ -58,7 +80,28 @@ class Job {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @class Mig_StorePath
|
||||
* @extends Job
|
||||
* @description Handles the migration of file system entries to include path information.
|
||||
* This class processes fsentries that don't have path data set, calculating and storing
|
||||
* their full paths in batches. It includes rate limiting and progress tracking to prevent
|
||||
* server overload during migration.
|
||||
*/
|
||||
class Mig_StorePath extends Job {
|
||||
/**
|
||||
* Handles migration of file system entries to update storage paths
|
||||
* @param {Object} args - Command line arguments for the migration
|
||||
* @param {string[]} args.verbose - If --verbose is included, logs detailed path info
|
||||
* @returns {Promise<void>} Resolves when migration is complete
|
||||
*
|
||||
* Migrates fsentry records that have null paths by:
|
||||
* - Processing entries in batches of 50
|
||||
* - Converting UUIDs to full paths
|
||||
* - Updating the path column in the database
|
||||
* - Includes throttling between batches to reduce server load
|
||||
*/
|
||||
async start (args) {
|
||||
this.state = this.constructor.STATE_GREEN;
|
||||
const { dbrr, dbrw, log } = this;
|
||||
@ -111,7 +154,25 @@ class Mig_StorePath extends Job {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @class Mig_IndexAccessed
|
||||
* @extends Job
|
||||
* @description Migration job that updates the 'accessed' timestamp for file system entries.
|
||||
* Sets the 'accessed' field to match the 'created' timestamp for entries where 'accessed' is NULL.
|
||||
* Processes entries in batches of 10000 to avoid overloading the database, with built-in delays
|
||||
* between batches for server load management.
|
||||
*/
|
||||
class Mig_IndexAccessed extends Job {
|
||||
/**
|
||||
* Migrates fsentries to include 'accessed' timestamps by setting null values to their 'created' time
|
||||
* @param {Array} args - Command line arguments passed to the migration
|
||||
* @returns {Promise<void>}
|
||||
*
|
||||
* Processes fsentries in batches of 10000, updating any null 'accessed' fields
|
||||
* to match their 'created' timestamp. Includes built-in delays between batches
|
||||
* to reduce server load. Continues until no more records need updating.
|
||||
*/
|
||||
async start (args) {
|
||||
this.state = this.constructor.STATE_GREEN;
|
||||
const { dbrr, dbrw, log } = this;
|
||||
@ -146,7 +207,29 @@ class Mig_IndexAccessed extends Job {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @class Mig_FixTrash
|
||||
* @extends Job
|
||||
* @description Migration job that ensures each user has a Trash directory in their root folder.
|
||||
* Creates missing Trash directories with proper UUIDs, updates user records with trash_uuid,
|
||||
* and sets appropriate timestamps and permissions. The Trash directory is marked as immutable
|
||||
* and is created with standardized path '/Trash'.
|
||||
*/
|
||||
class Mig_FixTrash extends Job {
|
||||
/**
|
||||
* Handles migration to fix missing Trash directories for users
|
||||
* Creates a new Trash directory and updates necessary records if one doesn't exist
|
||||
*
|
||||
* @param {Array} args - Command line arguments passed to the migration
|
||||
* @returns {Promise<void>} Resolves when migration is complete
|
||||
*
|
||||
* @description
|
||||
* - Identifies users without a Trash directory
|
||||
* - Creates new Trash directory with UUID for each user
|
||||
* - Updates user table with new trash_uuid
|
||||
* - Includes throttling between operations to reduce server load
|
||||
*/
|
||||
async start (args) {
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
|
||||
@ -201,18 +284,37 @@ class Mig_FixTrash extends Job {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Class for managing referral code migrations in the user database.
|
||||
* Generates and assigns unique referral codes to users who don't have them.
|
||||
* Uses deterministic random generation with seeding to ensure consistent codes
|
||||
* while avoiding collisions with existing codes. Processes users in batches
|
||||
* and provides progress tracking.
|
||||
*/
|
||||
class Mig_AddReferralCodes extends Job {
|
||||
/**
|
||||
* Adds referral codes to users who don't have them yet.
|
||||
* Generates unique 8-character random codes using a seeded RNG.
|
||||
* If a generated code conflicts with existing ones, it iterates with
|
||||
* a new seed until finding an unused code.
|
||||
* Updates users in batches, showing progress every 500 users.
|
||||
* Can be stopped gracefully via stop() method.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async start (args) {
|
||||
this.state = this.constructor.STATE_GREEN;
|
||||
const { dbrr, dbrw, log } = this;
|
||||
|
||||
let existing_codes = new Set();
|
||||
// Set to store existing referral codes to avoid duplicates during migration
|
||||
const SQL_EXISTING_CODES = `SELECT referral_code FROM user`;
|
||||
let [codes] = await dbrr.promise().execute(SQL_EXISTING_CODES);
|
||||
for ( const { referal_code } of codes ) {
|
||||
existing_codes.add(referal_code);
|
||||
}
|
||||
|
||||
// SQL query to fetch all user IDs and their referral codes from the user table
|
||||
const SQL_USER_IDS = `SELECT id, referral_code FROM user`;
|
||||
|
||||
let [users] = await dbrr.promise().execute(SQL_USER_IDS);
|
||||
@ -243,7 +345,24 @@ class Mig_AddReferralCodes extends Job {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @class Mig_AuditInitialStorage
|
||||
* @extends Job
|
||||
* @description Migration class responsible for adding audit logs for users' initial storage capacity.
|
||||
* This migration is designed to retroactively create audit records for each user's storage capacity
|
||||
* from before the implementation of the auditing system. Inherits from the base Job class to
|
||||
* handle migration state management and progress tracking.
|
||||
*/
|
||||
class Mig_AuditInitialStorage extends Job {
|
||||
/**
|
||||
* Handles migration for auditing initial storage capacity for users
|
||||
* before auditing was implemented. Creates audit log entries for each
|
||||
* user's storage capacity from before the auditing system existed.
|
||||
*
|
||||
* @param {Array} args - Command line arguments passed to the migration
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async start (args) {
|
||||
this.state = this.constructor.STATE_GREEN;
|
||||
const { dbrr, dbrw, log } = this;
|
||||
@ -253,6 +372,15 @@ class Mig_AuditInitialStorage extends Job {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @class FSEntryMigrateService
|
||||
* @description Service responsible for managing and executing database migrations for filesystem entries.
|
||||
* Provides functionality to run various migrations including path storage updates, access time indexing,
|
||||
* trash folder fixes, and referral code generation. Exposes commands to start and stop migrations through
|
||||
* a command interface. Each migration is implemented as a separate Job class that can be controlled
|
||||
* independently.
|
||||
*/
|
||||
class FSEntryMigrateService {
|
||||
constructor ({ services }) {
|
||||
const mysql = services.get('mysql');
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -30,13 +31,28 @@ const BaseService = require('../BaseService.js');
|
||||
const { split_lines } = require('../../util/stdioutil.js');
|
||||
const { Context } = require('../../util/context.js');
|
||||
|
||||
|
||||
/**
|
||||
* @classdesc AlarmService class is responsible for managing alarms. It provides methods for creating, clearing, and handling alarms.
|
||||
*/
|
||||
class AlarmService extends BaseService {
|
||||
/**
|
||||
* This method initializes the AlarmService by setting up its internal data structures and initializing any required dependencies.
|
||||
*
|
||||
* It reads in the known errors from a JSON5 file and sets them as the known_errors property of the AlarmService instance.
|
||||
*
|
||||
* It also registers commands with the provided commands service.
|
||||
*/
|
||||
async _construct () {
|
||||
this.alarms = {};
|
||||
this.alarm_aliases = {};
|
||||
|
||||
this.known_errors = [];
|
||||
}
|
||||
/**
|
||||
* Method to initialize AlarmService. Sets the known errors and registers commands.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async _init () {
|
||||
const services = this.services;
|
||||
this.pager = services.get('pager');
|
||||
@ -62,6 +78,13 @@ class AlarmService extends BaseService {
|
||||
this._register_commands(services.get('commands'));
|
||||
|
||||
if ( this.global_config.env === 'dev' ) {
|
||||
/**
|
||||
* This method initializes the AlarmService instance by registering commands, setting up the pager, and initializing the known errors.
|
||||
* It also sets up the widget to display alarms in the dev environment.
|
||||
*
|
||||
* @param {BaseService} services - The BaseService instance that provides access to other services.
|
||||
* @returns {void}
|
||||
*/
|
||||
this.alarm_widget = () => {
|
||||
// return `\x1B[31;1m alarms (${
|
||||
// Object.keys(this.alarms)
|
||||
@ -100,6 +123,14 @@ class AlarmService extends BaseService {
|
||||
create (id, message, fields) {
|
||||
this.log.error(`upcoming alarm: ${id}: ${message}`);
|
||||
let existing = false;
|
||||
/**
|
||||
* Method to create an alarm with the given ID, message, and fields.
|
||||
* If the ID already exists, it will be updated with the new fields.
|
||||
* @param {string} id - Unique identifier for the alarm.
|
||||
* @param {string} message - Message associated with the alarm.
|
||||
* @param {object} fields - Additional information about the alarm.
|
||||
* @returns {void}
|
||||
*/
|
||||
const alarm = (() => {
|
||||
const short_id = this.adapt_id_(id);
|
||||
|
||||
@ -116,12 +147,34 @@ class AlarmService extends BaseService {
|
||||
};
|
||||
|
||||
Object.defineProperty(alarm, 'count', {
|
||||
/**
|
||||
* Method to create a new alarm.
|
||||
*
|
||||
* This method takes an id, message, and optional fields as parameters.
|
||||
* It creates a new alarm object with the provided id and message,
|
||||
* and adds it to the alarms object. It also keeps track of the number of occurrences of the alarm.
|
||||
* If the alarm already exists, it increments the occurrence count and calls the handle\_alarm\_repeat\_ method.
|
||||
* If it's a new alarm, it calls the handle\_alarm\_on\_ method.
|
||||
*
|
||||
* @param {string} id - The unique identifier for the alarm.
|
||||
* @param {string} message - The message associated with the alarm.
|
||||
* @param {object} [fields] - Optional fields associated with the alarm.
|
||||
* @returns {void}
|
||||
*/
|
||||
get () {
|
||||
return alarm.timestamps?.length ?? 0;
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(alarm, 'id_string', {
|
||||
/**
|
||||
* Method to handle creating a new alarm with given parameters.
|
||||
* This method adds the alarm to the `alarms` object, updates the occurrences count,
|
||||
* and processes any known errors that may apply to the alarm.
|
||||
* @param {string} id - The unique identifier for the alarm.
|
||||
* @param {string} message - The message associated with the alarm.
|
||||
* @param {Object} fields - Additional fields to associate with the alarm.
|
||||
*/
|
||||
get () {
|
||||
if ( alarm.id.length < 20 ) {
|
||||
return alarm.id;
|
||||
@ -318,6 +371,21 @@ class AlarmService extends BaseService {
|
||||
}
|
||||
|
||||
_register_commands (commands) {
|
||||
// Function to handle a specific alarm event.
|
||||
// This comment can be added above line 320.
|
||||
// This function is responsible for processing specific events related to alarms.
|
||||
// It can be used for tasks such as updating alarm status, sending notifications, or triggering actions.
|
||||
// This function is called internally by the AlarmService class.
|
||||
|
||||
// /*
|
||||
// * handleAlarmEvent - Handles a specific alarm event.
|
||||
// *
|
||||
// * @param {Object} alarm - The alarm object containing relevant information.
|
||||
// * @param {Function} callback - Optional callback function to be called when the event is handled.
|
||||
// */
|
||||
// function handleAlarmEvent(alarm, callback) {
|
||||
// // Implementation goes here.
|
||||
// }
|
||||
const completeAlarmID = (args) => {
|
||||
// The alarm ID is the first argument, so return no results if we're on the second or later.
|
||||
if (args.length > 1)
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"mistral","model":"mistral-large-latest"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -18,6 +19,18 @@
|
||||
*/
|
||||
const BaseService = require("../BaseService");
|
||||
|
||||
|
||||
/**
|
||||
* **ErrorContext Class**
|
||||
*
|
||||
* The `ErrorContext` class is designed to encapsulate error reporting functionality within a specific logging context.
|
||||
* It facilitates the reporting of errors by providing a method to log error details along with additional contextual information.
|
||||
*
|
||||
* @class
|
||||
* @classdesc Provides a context for error reporting with specific logging details.
|
||||
* @param {ErrorService} error_service - The error service instance to use for reporting errors.
|
||||
* @param {object} log_context - The logging context to associate with the error reports.
|
||||
*/
|
||||
class ErrorContext {
|
||||
constructor (error_service, log_context) {
|
||||
this.error_service = error_service;
|
||||
@ -32,7 +45,22 @@ class ErrorContext {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @class ErrorService
|
||||
* @extends BaseService
|
||||
* @description The ErrorService class is responsible for handling and reporting errors within the system.
|
||||
* It provides methods to initialize the service, create error contexts, and report errors with detailed logging and alarm mechanisms.
|
||||
*/
|
||||
class ErrorService extends BaseService {
|
||||
/**
|
||||
* Initializes the ErrorService, setting up the alarm and backup logger services.
|
||||
*
|
||||
* @async
|
||||
* @function init
|
||||
* @memberof ErrorService
|
||||
* @returns {Promise<void>} A promise that resolves when the initialization is complete.
|
||||
*/
|
||||
async init () {
|
||||
const services = this.services;
|
||||
this.alarm = services.get('alarm');
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"mistral","model":"mistral-large-latest"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -20,10 +21,33 @@ const { v4: uuidv4 } = require('uuid');
|
||||
const { quot } = require('../../util/strutil');
|
||||
const BaseService = require('../BaseService');
|
||||
|
||||
|
||||
/**
|
||||
* @class WorkUnit
|
||||
* @description The WorkUnit class represents a unit of work that can be tracked and monitored for checkpoints.
|
||||
* It includes methods to create instances, set checkpoints, and manage the state of the work unit.
|
||||
*/
|
||||
class WorkUnit {
|
||||
/**
|
||||
* Represents a unit of work with checkpointing capabilities.
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates and returns a new instance of WorkUnit.
|
||||
*
|
||||
* @static
|
||||
* @returns {WorkUnit} A new instance of WorkUnit.
|
||||
*/
|
||||
static create () {
|
||||
return new WorkUnit();
|
||||
}
|
||||
/**
|
||||
* Creates a new instance of the WorkUnit class.
|
||||
* @static
|
||||
* @returns {WorkUnit} A new WorkUnit instance.
|
||||
*/
|
||||
constructor () {
|
||||
this.id = uuidv4();
|
||||
this.checkpoint_ = null;
|
||||
@ -34,11 +58,24 @@ class WorkUnit {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @class CheckpointExpectation
|
||||
* @classdesc The CheckpointExpectation class is used to represent an expectation that a specific checkpoint
|
||||
* will be reached during the execution of a work unit. It includes methods to check if the checkpoint has
|
||||
* been reached and to report the results of this check.
|
||||
*/
|
||||
class CheckpointExpectation {
|
||||
constructor (workUnit, checkpoint) {
|
||||
this.workUnit = workUnit;
|
||||
this.checkpoint = checkpoint;
|
||||
}
|
||||
/**
|
||||
* Constructor for CheckpointExpectation class.
|
||||
* Initializes the instance with a WorkUnit and a checkpoint label.
|
||||
* @param {WorkUnit} workUnit - The work unit associated with the checkpoint.
|
||||
* @param {string} checkpoint - The checkpoint label to be checked.
|
||||
*/
|
||||
check () {
|
||||
// TODO: should be true if checkpoint was ever reached
|
||||
return this.workUnit.checkpoint_ == this.checkpoint;
|
||||
@ -57,15 +94,55 @@ class CheckpointExpectation {
|
||||
* This service helps diagnose errors involving the potentially
|
||||
* complex relationships between asynchronous operations.
|
||||
*/
|
||||
/**
|
||||
* @class ExpectationService
|
||||
* @extends BaseService
|
||||
*
|
||||
* The `ExpectationService` is a specialized service designed to assist in the diagnosis and
|
||||
* management of errors related to the intricate interactions among asynchronous operations.
|
||||
* It facilitates tracking and reporting on expectations, enabling better fault isolation
|
||||
* and resolution in systems where synchronization and timing of operations are crucial.
|
||||
*
|
||||
* This service inherits from the `BaseService` and provides methods for registering,
|
||||
* purging, and handling expectations, making it a valuable tool for diagnosing complex
|
||||
* runtime behaviors in a system.
|
||||
*/
|
||||
class ExpectationService extends BaseService {
|
||||
/**
|
||||
* Constructs the ExpectationService and initializes its internal state.
|
||||
* This method is intended to be called asynchronously.
|
||||
* It sets up the `expectations_` array which will be used to track expectations.
|
||||
*
|
||||
* @async
|
||||
*/
|
||||
async _construct () {
|
||||
this.expectations_ = [];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the ExpectationService, setting up interval functions and registering commands.
|
||||
*
|
||||
* This method sets up a periodic interval to purge expectations and registers a command
|
||||
* to list pending expectations. The interval invokes `purgeExpectations_` every second.
|
||||
* The command 'pending' allows users to list and log all pending expectations.
|
||||
*
|
||||
* @returns {Promise<void>} A promise that resolves when initialization is complete.
|
||||
*/
|
||||
async _init () {
|
||||
const services = this.services;
|
||||
|
||||
// TODO: service to track all interval functions?
|
||||
/**
|
||||
* Initializes the service by setting up interval functions and registering commands.
|
||||
* This method sets up a periodic interval function to purge expectations and registers
|
||||
* a command to list pending expectations.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
```javascript
|
||||
// The comment should be placed above the method at line 68
|
||||
setInterval(() => {
|
||||
this.purgeExpectations_();
|
||||
}, 1000);
|
||||
@ -89,6 +166,16 @@ class ExpectationService extends BaseService {
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Purges expectations that have been met.
|
||||
*
|
||||
* This method iterates through the list of expectations and removes
|
||||
* those that have been satisfied. Currently, this functionality is
|
||||
* disabled and needs to be re-enabled.
|
||||
*
|
||||
* @returns {void} This method does not return anything.
|
||||
*/
|
||||
purgeExpectations_ () {
|
||||
return;
|
||||
// TODO: Re-enable this
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -18,6 +19,14 @@
|
||||
*/
|
||||
const memwatch = require('@airbnb/node-memwatch');
|
||||
|
||||
|
||||
/**
|
||||
* The HeapMonService class monitors the application's memory usage,
|
||||
* utilizing the memwatch library to detect heap memory leaks and
|
||||
* gather heap statistics at specified intervals. It interfaces with
|
||||
* logging and alarm services to report memory conditions and
|
||||
* trigger alerts as necessary.
|
||||
*/
|
||||
class HeapMonService {
|
||||
constructor ({ services, my_config }) {
|
||||
this.log = services.get('log-service').create('heap-monitor');
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"mistral","model":"mistral-large-latest"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -17,12 +18,23 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
const logSeverity = (ordinal, label, esc, winst) => ({ ordinal, label, esc, winst });
|
||||
// Defines a function to create log severity objects used for logging
|
||||
// The function is used to define various log levels and their properties
|
||||
const LOG_LEVEL_ERRO = logSeverity(0, 'ERRO', '31;1', 'error');
|
||||
// Defines the log level for error messages, used throughout the logging system.
|
||||
const LOG_LEVEL_WARN = logSeverity(1, 'WARN', '33;1', 'warn');
|
||||
// Defines the WARN log level, used for warning messages in the logging system.
|
||||
const LOG_LEVEL_INFO = logSeverity(2, 'INFO', '36;1', 'info');
|
||||
// Defines the log level constant for informational messages. Used throughout the code for logging informational events.
|
||||
const LOG_LEVEL_TICK = logSeverity(10, 'TICK', '34;1', 'info');
|
||||
// LOG_LEVEL_TICK is a constant defining a specific log level for tick events, used for periodic logging.
|
||||
const LOG_LEVEL_DEBU = logSeverity(4, 'DEBU', '37;1', 'debug');
|
||||
// Defines the debug log level used for detailed logging and debugging purposes.
|
||||
|
||||
```javascript
|
||||
const LOG_LEVEL_DEBU = logSeverity(4, 'DEBU', '37;1', 'debug');
|
||||
const LOG_LEVEL_NOTICEME = logSeverity(4, 'NOTICE_ME', '33;1', 'error');
|
||||
// Defines a log level constant for special notifications, used in logging functions.
|
||||
const LOG_LEVEL_SYSTEM = logSeverity(4, 'SYSTEM', '33;1', 'system');
|
||||
|
||||
const winston = require('winston');
|
||||
@ -41,6 +53,14 @@ const WINSTON_LEVELS = {
|
||||
silly: 60
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @class LogContext
|
||||
* @classdesc The LogContext class provides a structured way to handle logging within the application.
|
||||
* It encapsulates the logging service, breadcrumbs for context, and fields for additional information.
|
||||
* This class includes methods for different logging levels such as info, warn, debug, error, tick,
|
||||
* and system logs. It also provides utility methods for sub-contexts, caching, and trace identification.
|
||||
*/
|
||||
class LogContext {
|
||||
constructor (logService, { crumbs, fields }) {
|
||||
this.logService = logService;
|
||||
@ -98,6 +118,13 @@ class LogContext {
|
||||
|
||||
// convenience method to get a trace id that isn't as difficult
|
||||
// for a human to read as a uuid.
|
||||
/**
|
||||
* Generates a human-readable trace ID.
|
||||
* This method creates a trace ID that is easier for humans to read compared to a UUID.
|
||||
* The trace ID is composed of two random alphanumeric strings joined by a hyphen.
|
||||
*
|
||||
* @returns {string} A human-readable trace ID.
|
||||
*/
|
||||
mkid () {
|
||||
// generate trace id
|
||||
const trace_id = [];
|
||||
@ -108,21 +135,55 @@ class LogContext {
|
||||
}
|
||||
|
||||
// add a trace id to this logging context
|
||||
/**
|
||||
* Adds a trace ID to the logging context.
|
||||
* This method generates a new trace ID and assigns it to the logging context's fields.
|
||||
* It then returns the modified logging context.
|
||||
*
|
||||
* @returns {LogContext} The modified logging context with the trace ID added.
|
||||
*/
|
||||
traceOn () {
|
||||
this.fields.trace_id = this.mkid();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the current log buffer.
|
||||
*
|
||||
* @returns {Array} The current log buffer containing log entries.
|
||||
*/
|
||||
get_log_buffer () {
|
||||
return this.logService.get_log_buffer();
|
||||
}
|
||||
}
|
||||
|
||||
let log_epoch = Date.now();
|
||||
/**
|
||||
* Function to initialize the log epoch timestamp.
|
||||
* This timestamp is used to calculate the time difference for log entries.
|
||||
*/
|
||||
const stringify_log_entry = ({ prefix, log_lvl, crumbs, message, fields, objects }) => {
|
||||
const { colorize } = require('json-colorizer');
|
||||
|
||||
let lines = [], m;
|
||||
/**
|
||||
* Converts a log entry into a formatted string for display.
|
||||
*
|
||||
* This method formats log entries by combining the prefix, log level, crumbs (breadcrumbs),
|
||||
* message, fields, and objects into a readable string. It includes color coding for log levels
|
||||
* and timestamp information if available. The method processes each log entry into a multi-line
|
||||
* string for enhanced readability.
|
||||
*
|
||||
* @param {Object} entry - The log entry object to be stringified.
|
||||
* @param {string} entry.prefix - The optional prefix to prepend to the log message.
|
||||
* @param {Object} entry.log_lvl - The log level object containing label and escape sequences.
|
||||
* @param {Array} entry.crumbs - An array of breadcrumbs for context.
|
||||
* @param {string} entry.message - The main log message.
|
||||
* @param {Object} entry.fields - Additional fields to include in the log entry.
|
||||
* @param {Object} entry.objects - Additional objects to include in the log entry.
|
||||
* @returns {string} - The formatted log entry string.
|
||||
*/
|
||||
const lf = () => {
|
||||
if ( ! m ) return;
|
||||
lines.push(m);
|
||||
@ -156,6 +217,14 @@ const stringify_log_entry = ({ prefix, log_lvl, crumbs, message, fields, objects
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @class DevLogger
|
||||
* @description The DevLogger class is responsible for handling logging operations in a development environment.
|
||||
* It can delegate logging to another logger and manage log output to a file. This class provides methods for
|
||||
* logging messages at different levels and managing the state of logging, such as turning logging on or off
|
||||
* and recording log output to a file. It is particularly useful for debugging and development purposes.
|
||||
*/
|
||||
class DevLogger {
|
||||
// TODO: this should eventually delegate to winston logger
|
||||
constructor (log, opt_delegate) {
|
||||
@ -195,6 +264,14 @@ class DevLogger {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @class
|
||||
* @classdesc The `NullLogger` class is a logging utility that does not perform any actual logging.
|
||||
* It is designed to be used as a placeholder or for environments where logging is not desired.
|
||||
* This class can be extended or used as a base for other logging implementations that need to
|
||||
* delegate logging responsibilities to another logger.
|
||||
*/
|
||||
class NullLogger {
|
||||
// TODO: this should eventually delegate to winston logger
|
||||
constructor (log, opt_delegate) {
|
||||
@ -204,10 +281,27 @@ class NullLogger {
|
||||
this.delegate = opt_delegate;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Constructor for the NullLogger class.
|
||||
* This method initializes a new instance of the NullLogger class.
|
||||
* It optionally accepts a delegate logger to which it can pass log messages.
|
||||
*
|
||||
* @param {function} log - The logging function to use (e.g., console.log).
|
||||
* @param {Object} opt_delegate - An optional delegate logger to pass log messages to.
|
||||
*/
|
||||
onLogMessage () {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @class WinstonLogger
|
||||
* @classdesc The WinstonLogger class is responsible for integrating the Winston logging library
|
||||
* into the logging system. It handles forwarding log messages to Winston transports, which can
|
||||
* include various logging destinations such as files, consoles, and remote logging services.
|
||||
* This class is a key component in ensuring that log messages are appropriately recorded and
|
||||
* managed, providing a structured and configurable logging mechanism.
|
||||
*/
|
||||
class WinstonLogger {
|
||||
constructor (winst) {
|
||||
this.winst = winst;
|
||||
@ -222,6 +316,101 @@ class WinstonLogger {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @class LogContext
|
||||
* @description The `LogContext` class provides a context for logging messages within the application.
|
||||
* It encapsulates the log service, a list of breadcrumbs (contextual information for the logs),
|
||||
* and fields that can be attached to log messages.
|
||||
*
|
||||
* This class includes methods for various log levels (info, warn, debug, error, etc.),
|
||||
* allowing for structured logging with contextual information.
|
||||
*
|
||||
* It also provides methods for creating sub-contexts, generating trace IDs, and managing log buffers.
|
||||
*/
|
||||
/**
|
||||
* @class DevLogger
|
||||
* @description The `DevLogger` class is a simple logger that outputs log messages to the console.
|
||||
* It is primarily used in development environments. This logger can also delegate log messages to another logger.
|
||||
*
|
||||
* The class includes methods for toggling log output, recording log messages to a file,
|
||||
* and adding timestamps to log entries.
|
||||
*/
|
||||
/**
|
||||
* @class NullLogger
|
||||
* @description The `NullLogger` class is a logger that does not output any log messages.
|
||||
* It is used when logging is disabled or when logging is not required.
|
||||
*/
|
||||
/**
|
||||
* @class WinstonLogger
|
||||
* @description The `WinstonLogger` class is a logger that integrates with the Winston logging library.
|
||||
* It provides a structured way to log messages with different log levels and transports.
|
||||
*
|
||||
* This logger can be used to log messages to files, with options for daily rotation and compression.
|
||||
*/
|
||||
/**
|
||||
* @class TimestampLogger
|
||||
* @description The `TimestampLogger` class is a decorator logger that adds timestamps to log messages.
|
||||
* It delegates the actual logging to another logger.
|
||||
*
|
||||
* This class ensures that each log message includes a timestamp, which can be useful for debugging and performance monitoring.
|
||||
*/
|
||||
/**
|
||||
* @class BufferLogger
|
||||
* @description The `BufferLogger` class is a logger that maintains a buffer of log messages.
|
||||
* It delegates the actual logging to another logger.
|
||||
*
|
||||
* This logger can be used to keep a limited number of recent log messages in memory,
|
||||
* which can be useful for debugging and troubleshooting.
|
||||
*/
|
||||
/**
|
||||
* @class CustomLogger
|
||||
* @description The `CustomLogger` class is a flexible logger that allows for custom log message processing.
|
||||
* It delegates the actual logging to another logger.
|
||||
*
|
||||
* This logger can be used to modify log messages before they are logged,
|
||||
* allowing for custom log message formatting and processing.
|
||||
*/
|
||||
/**
|
||||
* @class LogService
|
||||
* @description The `LogService` class is a service that provides logging functionality for the application.
|
||||
* It manages a collection of loggers, a buffer of recent log messages, and log directories.
|
||||
*
|
||||
* This class includes methods for registering log middleware, creating log contexts,
|
||||
* and logging messages at various log levels. It also ensures that log messages are only output if they are at or above the configured output level.
|
||||
*
|
||||
* The `LogService` class is responsible for initializing loggers based on the application's configuration,
|
||||
* ensuring that log directories exist, and providing methods for retrieving log files and buffers.
|
||||
*/
|
||||
/**
|
||||
* @class
|
||||
* @description The `TimestampLogger` class is a logger that adds timestamps to log messages. It delegates the actual logging to another logger.
|
||||
* This class ensures that each log message includes a timestamp, which can be useful for debugging and performance monitoring.
|
||||
*/
|
||||
/**
|
||||
* @class BufferLogger
|
||||
* @description The `BufferLogger` class is a logger that maintains a buffer of log messages. It delegates the actual logging to another logger.
|
||||
* This logger can be used to keep a limited number of recent log messages in memory, which can be useful for debugging and troubleshooting.
|
||||
*/
|
||||
/**
|
||||
* @class CustomLogger
|
||||
* @description The `CustomLogger` class is a flexible logger that allows for custom log message processing. It delegates the actual logging to another logger.
|
||||
* This logger can be used to modify log messages before they are logged, allowing for custom log message formatting and processing.
|
||||
*/
|
||||
/**
|
||||
* @class
|
||||
* @description The `LogService` class is a service that provides logging functionality for the application.
|
||||
* It manages a collection of loggers, a buffer of recent log messages, and log directories.
|
||||
* This class includes methods for registering log middleware, creating log contexts,
|
||||
* and logging messages at various log levels. It also ensures that log messages are only output if they are at or above the configured output level.
|
||||
* The `LogService` class is responsible for initializing loggers based on the application's configuration,
|
||||
* ensuring that log directories exist, and providing methods for retrieving log files and buffers.
|
||||
*/
|
||||
/**
|
||||
* @class
|
||||
* @description The `TimestampLogger` class is a logger that adds timestamps to log messages. It delegates the actual logging to another logger.
|
||||
* This class ensures that each log message includes a timestamp, which can be useful for debugging and performance monitoring.
|
||||
*/
|
||||
class TimestampLogger {
|
||||
constructor (delegate) {
|
||||
this.delegate = delegate;
|
||||
@ -232,6 +421,20 @@ class TimestampLogger {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The LogService class is a core service that manages logging across the application.
|
||||
* It facilitates the creation and management of various logging middleware, such as
|
||||
* DevLogger, NullLogger, WinstonLogger, and more. This class extends BaseService and
|
||||
* includes methods for initializing and configuring loggers, ensuring log directories,
|
||||
* and handling log messages. It also allows for the registration of custom log middleware
|
||||
* via the register_log_middleware method.
|
||||
*
|
||||
* The LogService class supports multiple logging levels, each with its own file and
|
||||
* transport mechanisms. It includes utility methods for creating new log contexts,
|
||||
* logging messages, and getting the log buffer. This class is essential for tracking
|
||||
* and monitoring application behavior, errors, and system events.
|
||||
*/
|
||||
class BufferLogger {
|
||||
constructor (size, delegate) {
|
||||
this.size = size;
|
||||
@ -247,6 +450,14 @@ class BufferLogger {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The `CustomLogger` class is a specialized logger that allows for custom
|
||||
* logging behavior by applying a callback function to modify log entries
|
||||
* before they are passed to the delegate logger. This class is part of the
|
||||
* logging infrastructure, providing flexibility to alter log messages, fields,
|
||||
* or other parameters dynamically based on the context in which the logging occurs.
|
||||
*/
|
||||
class CustomLogger {
|
||||
constructor (delegate, callback) {
|
||||
this.delegate = delegate;
|
||||
@ -279,10 +490,24 @@ class CustomLogger {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The `LogService` class extends the `BaseService` and is responsible for managing logging operations.
|
||||
* It handles the registration of log middleware, initializes various logging mechanisms, and provides
|
||||
* methods to log messages at different severity levels. The class ensures that log directories are
|
||||
* properly set up and manages the logging output levels based on configuration.
|
||||
*/
|
||||
class LogService extends BaseService {
|
||||
static MODULES = {
|
||||
path: require('path'),
|
||||
}
|
||||
/**
|
||||
* Initializes the log service by setting up the logging directory, configuring loggers,
|
||||
* and registering commands for log management.
|
||||
*
|
||||
* @async
|
||||
* @returns {Promise<void>} A promise that resolves when the initialization is complete.
|
||||
*/
|
||||
async _construct () {
|
||||
this.loggers = [];
|
||||
this.bufferLogger = null;
|
||||
@ -332,6 +557,16 @@ class LogService extends BaseService {
|
||||
}
|
||||
]);
|
||||
}
|
||||
/**
|
||||
* Registers log-related commands for the service.
|
||||
*
|
||||
* This method defines a set of commands for managing log output,
|
||||
* such as toggling log visibility, starting/stopping log recording to a file,
|
||||
* and toggling log indentation.
|
||||
*
|
||||
* @param {Object} commands - The commands object to register commands to.
|
||||
*/
|
||||
```
|
||||
async _init () {
|
||||
const config = this.global_config;
|
||||
|
||||
@ -457,6 +692,13 @@ class LogService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Ensures that the log directory exists by attempting to create it in several
|
||||
* predefined locations. If none of the locations are available, an error is thrown.
|
||||
*
|
||||
* @throws {Error} If the log directory cannot be created or found.
|
||||
*/
|
||||
ensure_log_directory_ () {
|
||||
// STEP 1: Try /var/puter/logs/heyputer
|
||||
{
|
||||
@ -512,6 +754,15 @@ class LogService extends BaseService {
|
||||
return this.modules.path.join(this.log_directory, name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the log buffer.
|
||||
*
|
||||
* This method returns the current log buffer, which is an array of log entries.
|
||||
* Each log entry contains details such as the log level, crumbs, message, and fields.
|
||||
*
|
||||
* @returns {Array} The log buffer containing log entries.
|
||||
*/
|
||||
get_log_buffer () {
|
||||
return this.bufferLogger.buffer;
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"mistral","model":"mistral-large-latest"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -21,12 +22,35 @@ const BaseService = require('../BaseService');
|
||||
const util = require('util');
|
||||
const { Context } = require('../../util/context');
|
||||
|
||||
|
||||
/**
|
||||
* @class PagerService
|
||||
* @extends BaseService
|
||||
* @description The PagerService class is responsible for handling pager alerts.
|
||||
* It extends the BaseService class and provides methods for constructing,
|
||||
* initializing, and managing alert handlers. The class interacts with PagerDuty
|
||||
* through the pdjs library to send alerts and integrates with other services via
|
||||
* command registration.
|
||||
*/
|
||||
class PagerService extends BaseService {
|
||||
/**
|
||||
* Class representing a PagerService.
|
||||
* @extends BaseService
|
||||
*/
|
||||
class PagerService extends BaseService {
|
||||
async _construct () {
|
||||
this.config = this.global_config.pager;
|
||||
this.alertHandlers_ = [];
|
||||
|
||||
}
|
||||
/**
|
||||
* Initializes the PagerService instance by setting the configuration and
|
||||
* initializing an empty alert handler array.
|
||||
*
|
||||
* @async
|
||||
* @memberOf PagerService
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async _init () {
|
||||
const services = this.services;
|
||||
|
||||
@ -41,6 +65,15 @@ class PagerService extends BaseService {
|
||||
this._register_commands(services.get('commands'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes PagerDuty configuration and registers alert handlers.
|
||||
* If PagerDuty is enabled in the configuration, it sets up an alert handler
|
||||
* to send alerts to PagerDuty.
|
||||
*
|
||||
* @method onInit
|
||||
*/
|
||||
```
|
||||
onInit () {
|
||||
if ( this.config.pagerduty && this.config.pagerduty.enabled ) {
|
||||
this.alertHandlers_.push(async alert => {
|
||||
@ -89,6 +122,15 @@ class PagerService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends an alert to all registered alert handlers.
|
||||
*
|
||||
* This method iterates through all alert handlers and attempts to send the alert.
|
||||
* If any handler fails to send the alert, an error message is logged.
|
||||
*
|
||||
* @param {Object} alert - The alert object containing details about the alert.
|
||||
*/
|
||||
async alert (alert) {
|
||||
for ( const handler of this.alertHandlers_ ) {
|
||||
try {
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"claude"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -18,6 +19,15 @@
|
||||
*/
|
||||
const { Context } = require("../../util/context");
|
||||
|
||||
|
||||
/**
|
||||
* Service class that handles process-wide events and errors.
|
||||
* Provides centralized error handling for uncaught exceptions and unhandled promise rejections.
|
||||
* Sets up event listeners on the process object to capture and report critical errors
|
||||
* through the logging and error reporting services.
|
||||
*
|
||||
* @class ProcessEventService
|
||||
*/
|
||||
class ProcessEventService {
|
||||
constructor ({ services }) {
|
||||
const log = services.get('log-service').create('process-event-service');
|
||||
@ -27,6 +37,13 @@ class ProcessEventService {
|
||||
// in the init hook
|
||||
|
||||
process.on('uncaughtException', async (err, origin) => {
|
||||
/**
|
||||
* Handles uncaught exceptions in the process
|
||||
* Sets up an event listener that reports errors when uncaught exceptions occur
|
||||
* @param {Error} err - The uncaught exception error object
|
||||
* @param {string} origin - The origin of the uncaught exception
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
await Context.allow_fallback(async () => {
|
||||
errors.report('process:uncaughtException', {
|
||||
source: err,
|
||||
@ -39,6 +56,12 @@ class ProcessEventService {
|
||||
});
|
||||
|
||||
process.on('unhandledRejection', async (reason, promise) => {
|
||||
/**
|
||||
* Handles unhandled promise rejections by reporting them to the error service
|
||||
* @param {*} reason - The rejection reason/error
|
||||
* @param {Promise} promise - The rejected promise
|
||||
* @returns {Promise<void>} Resolves when error is reported
|
||||
*/
|
||||
await Context.allow_fallback(async () => {
|
||||
errors.report('process:unhandledRejection', {
|
||||
source: reason,
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"xai"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -21,14 +22,38 @@ const { SECOND } = require("../../util/time");
|
||||
const { parse_meminfo } = require("../../util/linux");
|
||||
const { asyncSafeSetInterval, TeePromise } = require("../../util/promise");
|
||||
|
||||
|
||||
/**
|
||||
* The ServerHealthService class provides comprehensive health monitoring for the server.
|
||||
* It extends the BaseService class to include functionality for:
|
||||
* - Periodic system checks (e.g., RAM usage, service checks)
|
||||
* - Managing health check results and failures
|
||||
* - Triggering alarms for critical conditions
|
||||
* - Logging and managing statistics for health metrics
|
||||
*
|
||||
* This service is designed to work primarily on Linux systems, reading system metrics
|
||||
* from `/proc/meminfo` and handling alarms via an external 'alarm' service.
|
||||
*/
|
||||
class ServerHealthService extends BaseService {
|
||||
static MODULES = {
|
||||
fs: require('fs'),
|
||||
}
|
||||
/**
|
||||
* Defines the modules used by ServerHealthService.
|
||||
* This static property is used to initialize and access system modules required for health checks.
|
||||
* @type {Object}
|
||||
* @property {fs} fs - The file system module for reading system information.
|
||||
*/
|
||||
_construct () {
|
||||
this.checks_ = [];
|
||||
this.failures_ = [];
|
||||
}
|
||||
/**
|
||||
* Initializes the internal checks and failure tracking for the service.
|
||||
* This method sets up empty arrays to store health checks and their failure statuses.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
async _init () {
|
||||
this.init_service_checks_();
|
||||
|
||||
@ -59,6 +84,14 @@ class ServerHealthService extends BaseService {
|
||||
|
||||
if ( this.config.no_system_checks ) return;
|
||||
|
||||
|
||||
/**
|
||||
* Adds a health check to the service.
|
||||
*
|
||||
* @param {string} name - The name of the health check.
|
||||
* @param {Function} fn - The function to execute for the health check.
|
||||
* @returns {Object} A chainable object to add failure handlers.
|
||||
*/
|
||||
this.add_check('ram-usage', async () => {
|
||||
const meminfo_text = await this.modules.fs.promises.readFile(
|
||||
'/proc/meminfo', 'utf8'
|
||||
@ -78,13 +111,40 @@ class ServerHealthService extends BaseService {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes service health checks by setting up periodic checks.
|
||||
* This method configures an interval-based execution of health checks,
|
||||
* handles timeouts, and manages failure states.
|
||||
*
|
||||
* @param {none} - This method does not take any parameters.
|
||||
* @returns {void} - This method does not return any value.
|
||||
*/
|
||||
init_service_checks_ () {
|
||||
const svc_alarm = this.services.get('alarm');
|
||||
/**
|
||||
* Initializes periodic health checks for the server.
|
||||
*
|
||||
* This method sets up an interval to run all registered health checks
|
||||
* at a specified frequency. It manages the execution of checks, handles
|
||||
* timeouts, and logs errors or triggers alarms when checks fail.
|
||||
*
|
||||
* @private
|
||||
* @method init_service_checks_
|
||||
* @memberof ServerHealthService
|
||||
* @param {none} - No parameters are passed to this method.
|
||||
* @returns {void}
|
||||
*/
|
||||
asyncSafeSetInterval(async () => {
|
||||
this.log.tick('service checks');
|
||||
const check_failures = [];
|
||||
for ( const { name, fn, chainable } of this.checks_ ) {
|
||||
const p_timeout = new TeePromise();
|
||||
/**
|
||||
* Creates a TeePromise to handle potential timeouts during health checks.
|
||||
*
|
||||
* @returns {Promise} A promise that can be resolved or rejected from multiple places.
|
||||
*/
|
||||
const timeout = setTimeout(() => {
|
||||
p_timeout.reject(new Error('Health check timed out'));
|
||||
}, 5 * SECOND);
|
||||
@ -131,6 +191,14 @@ class ServerHealthService extends BaseService {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the current server health statistics.
|
||||
*
|
||||
* @returns {Object} An object containing the current health statistics.
|
||||
* This method returns a shallow copy of the internal `stats_` object to prevent
|
||||
* direct manipulation of the service's data.
|
||||
*/
|
||||
async get_stats () {
|
||||
return { ...this.stats_ };
|
||||
}
|
||||
@ -147,6 +215,14 @@ class ServerHealthService extends BaseService {
|
||||
return chainable;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the current health status of the server.
|
||||
*
|
||||
* @returns {Object} An object containing:
|
||||
* - `ok` {boolean}: Indicates if all health checks passed.
|
||||
* - `failed` {Array<string>}: An array of names of failed health checks, if any.
|
||||
*/
|
||||
get_status () {
|
||||
const failures = this.failures_.map(v => v.name);
|
||||
return {
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"xai"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -20,11 +21,47 @@ const BaseService = require("../BaseService");
|
||||
const { UserActorType, AppUnderUserActorType } = require("../auth/Actor");
|
||||
const { DB_WRITE } = require("../database/consts");
|
||||
|
||||
|
||||
/**
|
||||
* MonthlyUsageService - A service class for managing and tracking monthly usage statistics.
|
||||
*
|
||||
* This class extends BaseService to provide functionalities related to:
|
||||
* - Incrementing usage counts for actors (users or applications under users).
|
||||
* - Checking current usage against specified criteria for both users and applications.
|
||||
* - Handling different types of actors (UserActorType, AppUnderUserActorType) to ensure
|
||||
* appropriate data segregation and usage limits enforcement.
|
||||
*
|
||||
* @extends BaseService
|
||||
*/
|
||||
class MonthlyUsageService extends BaseService {
|
||||
/**
|
||||
* Initializes the MonthlyUsageService by setting up the database connection.
|
||||
*
|
||||
* @memberof MonthlyUsageService
|
||||
* @method
|
||||
* @instance
|
||||
* @async
|
||||
* @returns {Promise<void>} A promise that resolves when the initialization is complete.
|
||||
*
|
||||
* @note This method sets the `db` property to a write-enabled database connection for usage data.
|
||||
*/
|
||||
async _init () {
|
||||
this.db = this.services.get('database').get(DB_WRITE, 'usage');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Increments the usage count for a specific actor and key.
|
||||
*
|
||||
* @param {Object} actor - The actor object whose usage is being tracked.
|
||||
* @param {string} key - The usage key to increment.
|
||||
* @param {Object} extra - Additional metadata to store with the usage record.
|
||||
*
|
||||
* @note This method generates a unique key based on the actor's UID and the provided key.
|
||||
* @note The method performs an UPSERT operation, ensuring the count is incremented or set to 1 if new.
|
||||
* @note The `extra` parameter is stringified before being stored in the database.
|
||||
* @returns {Promise<void>} - A promise that resolves when the increment operation is complete.
|
||||
*/
|
||||
async increment (actor, key, extra) {
|
||||
key = `${actor.uid}:${key}`;
|
||||
|
||||
@ -52,6 +89,17 @@ class MonthlyUsageService extends BaseService {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks the monthly usage for the given actor based on specific criteria.
|
||||
*
|
||||
* This method determines the type of actor and delegates the check to the appropriate
|
||||
* method for further processing. It supports both user and app-under-user actors.
|
||||
*
|
||||
* @param {Object} actor - The actor whose usage needs to be checked.
|
||||
* @param {Object} specifiers - JSON object specifying conditions for the usage check.
|
||||
* @returns {Promise<number>} The total usage count or 0 if no matching records found.
|
||||
*/
|
||||
async check (actor, specifiers) {
|
||||
if ( actor.type instanceof UserActorType ) {
|
||||
return await this._user_check(actor, specifiers);
|
||||
@ -63,6 +111,13 @@ class MonthlyUsageService extends BaseService {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks usage for an actor, routing to specific check methods based on actor type.
|
||||
* @param {Object} actor - The actor to check usage for.
|
||||
* @param {Object} specifiers - Additional specifiers for the usage check.
|
||||
* @returns {Promise<Number>} The usage count or 0 if no usage is found.
|
||||
*/
|
||||
async check_2 (actor, key, ver) {
|
||||
// TODO: get 'ver' working here for future updates
|
||||
key = `${actor.uid}:${key}`;
|
||||
@ -76,6 +131,16 @@ class MonthlyUsageService extends BaseService {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Performs a secondary check on usage for either user or app under user actors.
|
||||
*
|
||||
* @param {Object} actor - The actor performing the action.
|
||||
* @param {string} key - The usage key to check.
|
||||
* @param {string} ver - The version, currently not implemented for future updates.
|
||||
* @returns {Promise<number>} A promise that resolves to the count of usage, or 0 if not found.
|
||||
* @note The 'ver' parameter is planned for future use to handle version-specific checks.
|
||||
*/
|
||||
async _user_check (actor, specifiers) {
|
||||
const year = new Date().getUTCFullYear();
|
||||
// months are zero-indexed by getUTCMonth, which could be confusing
|
||||
@ -94,6 +159,15 @@ class MonthlyUsageService extends BaseService {
|
||||
return rows[0]?.sum || 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Performs a usage check for a user based on a specific key.
|
||||
*
|
||||
* @param {Object} actor - The actor object representing the user or app.
|
||||
* @param {string} key - The unique key to check usage for.
|
||||
* @returns {Promise<number>} The sum of usage count for the specified key, or 0 if not found.
|
||||
* @note This method is intended for future updates where version control might be implemented.
|
||||
*/
|
||||
async _user_check_2 (actor, key) {
|
||||
const year = new Date().getUTCFullYear();
|
||||
// months are zero-indexed by getUTCMonth, which could be confusing
|
||||
@ -122,6 +196,16 @@ class MonthlyUsageService extends BaseService {
|
||||
return rows[0]?.sum || 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks the monthly usage for an app under a user account.
|
||||
*
|
||||
* @param {Object} actor - The actor object representing the user and app context.
|
||||
* @param {Object} specifiers - An object containing usage specifiers to filter the query.
|
||||
* @returns {Promise<number>} - The count of usage for the specified criteria or 0 if not found.
|
||||
* @note This method queries the database for usage data specific to an app within a user's account.
|
||||
* It uses JSON_CONTAINS to match specifiers within the extra field of the database entry.
|
||||
*/
|
||||
async _app_under_user_check (actor, specifiers) {
|
||||
const year = new Date().getUTCFullYear();
|
||||
// months are zero-indexed by getUTCMonth, which could be confusing
|
||||
@ -145,6 +229,15 @@ class MonthlyUsageService extends BaseService {
|
||||
return rows[0]?.count || 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Performs a check for usage under an app, identified by a specific key.
|
||||
* This method queries the database to retrieve the usage count for a given user, app, and key.
|
||||
*
|
||||
* @param {Actor} actor - The actor object containing user and app information.
|
||||
* @param {string} key - The usage key to check against.
|
||||
* @returns {Promise<number>} - A promise that resolves to the usage count or 0 if no record exists.
|
||||
*/
|
||||
async _app_under_user_check_2 (actor, key) {
|
||||
const year = new Date().getUTCFullYear();
|
||||
// months are zero-indexed by getUTCMonth, which could be confusing
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"claude"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -26,6 +27,14 @@ const { DB_WRITE } = require("../database/consts");
|
||||
const ts_to_sql = (ts) => Math.floor(ts / 1000);
|
||||
const ts_fr_sql = (ts) => ts * 1000;
|
||||
|
||||
|
||||
/**
|
||||
* RateLimitService class handles rate limiting functionality for API requests.
|
||||
* Implements a fixed window counter strategy to track and limit request rates
|
||||
* per user/consumer. Manages rate limit data both in memory (KV store) and
|
||||
* persistent storage (database). Extends BaseService and includes SyncFeature
|
||||
* for synchronized rate limit checking and incrementing.
|
||||
*/
|
||||
class RateLimitService extends BaseService {
|
||||
static MODULES = {
|
||||
kv: globalThis.kv,
|
||||
@ -37,10 +46,28 @@ class RateLimitService extends BaseService {
|
||||
]),
|
||||
]
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the service by setting up the database connection
|
||||
* for rate limiting operations. Gets a database instance from
|
||||
* the database service using the 'rate-limit' namespace.
|
||||
* @private
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async _init () {
|
||||
this.db = this.services.get('database').get(DB_WRITE, 'rate-limit');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if a rate limit has been exceeded and increments the counter
|
||||
* @param {string} key - The rate limit key/identifier
|
||||
* @param {number} max - Maximum number of requests allowed in the period
|
||||
* @param {number} period - Time window in milliseconds
|
||||
* @param {Object} [options={}] - Additional options
|
||||
* @param {boolean} [options.global] - Whether this is a global rate limit across servers
|
||||
* @throws {APIError} When rate limit is exceeded
|
||||
*/
|
||||
async check_and_increment (key, max, period, options = {}) {
|
||||
const { kv } = this.modules;
|
||||
const consumer_id = this._get_consumer_id();
|
||||
@ -125,6 +152,12 @@ class RateLimitService extends BaseService {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the consumer ID for rate limiting based on the current user context
|
||||
* @returns {string} Consumer ID in format 'user:{id}' if user exists, or 'missing' if no user
|
||||
* @private
|
||||
*/
|
||||
_get_consumer_id () {
|
||||
const context = Context.get();
|
||||
const user = context.get('user');
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"claude"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -23,7 +24,23 @@ const BaseService = require("../BaseService");
|
||||
* driver or service endpoint, including limits with respect to the actor
|
||||
* and server-wide limits.
|
||||
*/
|
||||
/**
|
||||
* @class SLAService
|
||||
* @extends BaseService
|
||||
* @description Service class responsible for managing Service Level Agreement (SLA) configurations.
|
||||
* Handles rate limiting and usage quotas for various API endpoints and drivers. Provides access
|
||||
* to system-wide limits, user-specific limits (both verified and unverified), and maintains
|
||||
* hardcoded limits for different service categories. Extends BaseService to integrate with
|
||||
* the core service infrastructure.
|
||||
*/
|
||||
class SLAService extends BaseService {
|
||||
/**
|
||||
* Initializes the service by setting up hardcoded SLA limits for different categories and endpoints.
|
||||
* Contains rate limits and monthly usage limits for various driver implementations.
|
||||
* @private
|
||||
* @async
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async _construct () {
|
||||
// I'm not putting this in config for now until we have checks
|
||||
// for production configuration. - EAD
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"xai"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -27,6 +28,20 @@ const FormData = require("form-data");
|
||||
const { stream_to_the_void, buffer_to_stream } = require('../../util/streamutil');
|
||||
const BaseService = require('../BaseService');
|
||||
|
||||
|
||||
/**
|
||||
* @class HTTPThumbnailService
|
||||
* @extends BaseService
|
||||
* @description
|
||||
* The `HTTPThumbnailService` class manages the creation and retrieval of thumbnails for various file types
|
||||
* over HTTP. It handles queueing thumbnail generation requests, executing them, and interfacing with
|
||||
* a thumbnail generation server. This service supports:
|
||||
* - Queuing and batch processing of thumbnail requests
|
||||
* - Size and MIME type validation for supported files
|
||||
* - Integration with an external thumbnail service via HTTP requests
|
||||
* - Periodic updates of supported MIME types
|
||||
* - Health checks and status reporting for the thumbnail generation process
|
||||
*/
|
||||
class ThumbnailOperation extends TeePromise {
|
||||
// static MAX_RECYCLE_COUNT = 5*3;
|
||||
static MAX_RECYCLE_COUNT = 3;
|
||||
@ -36,6 +51,15 @@ class ThumbnailOperation extends TeePromise {
|
||||
this.recycle_count = 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Recycles the ThumbnailOperation instance.
|
||||
*
|
||||
* Increments the recycle count and checks if the operation can be recycled again.
|
||||
* If the recycle count exceeds the maximum allowed, the operation is resolved with undefined.
|
||||
*
|
||||
* @returns {boolean} Returns true if the operation can be recycled, false otherwise.
|
||||
*/
|
||||
recycle () {
|
||||
this.recycle_count++;
|
||||
|
||||
@ -48,6 +72,17 @@ class ThumbnailOperation extends TeePromise {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @class HTTPThumbnailService
|
||||
* @extends BaseService
|
||||
* @description
|
||||
* This class implements a service for generating thumbnails from various file types via HTTP requests.
|
||||
* It manages a queue of thumbnail generation operations, handles the execution of these operations,
|
||||
* and provides methods to check file support, manage service status, and interact with an external
|
||||
* thumbnail generation service. The service can be configured to periodically query supported MIME types
|
||||
* and handles file size limitations and recycling of thumbnail generation attempts.
|
||||
*/
|
||||
class HTTPThumbnailService extends BaseService {
|
||||
static STATUS_IDLE = {};
|
||||
static STATUS_RUNNING = {};
|
||||
@ -98,13 +133,43 @@ class HTTPThumbnailService extends BaseService {
|
||||
this.LIMIT = my_config?.limit ?? this.constructor.LIMIT;
|
||||
|
||||
if ( my_config?.query_supported_types !== false ) {
|
||||
/**
|
||||
* Periodically queries the thumbnail service for supported MIME types.
|
||||
*
|
||||
* @memberof HTTPThumbnailService
|
||||
* @private
|
||||
* @method query_supported_mime_types_
|
||||
* @returns {Promise<void>} A promise that resolves when the query is complete.
|
||||
* @notes
|
||||
* - This method is called every minute if `query_supported_types` in the config is not set to false.
|
||||
* - Updates the `SUPPORTED_MIMETYPES` static property of the class with the latest MIME types.
|
||||
*/
|
||||
setInterval(() => {
|
||||
this.query_supported_mime_types_();
|
||||
}, 60 * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets up the HTTP routes for the thumbnail service.
|
||||
* This method is called during the installation process of the service.
|
||||
*
|
||||
* @param {Object} _ - Unused parameter, typically the context or request object.
|
||||
* @param {Object} options - An object containing the Express application instance.
|
||||
* @param {Object} options.app - The Express application object to mount the routes onto.
|
||||
*/
|
||||
async ['__on_install.routes'] (_, { app }) {
|
||||
/**
|
||||
* Sets up the routes for the thumbnail service.
|
||||
*
|
||||
* This method is called when the service is installed to configure the Express application
|
||||
* with the necessary routes for handling thumbnail-related HTTP requests.
|
||||
*
|
||||
* @param {Object} _ - Unused parameter, part of the installation context.
|
||||
* @param {Object} context - The context object containing the Express application.
|
||||
* @param {Express.Application} context.app - The Express application to configure routes on.
|
||||
*/
|
||||
const r_thumbs = (() => {
|
||||
const require = this.require;
|
||||
const express = require('express');
|
||||
@ -114,6 +179,11 @@ class HTTPThumbnailService extends BaseService {
|
||||
app.use('/thumbs', r_thumbs);
|
||||
|
||||
r_thumbs.get('/status', (req, res) => {
|
||||
/**
|
||||
* Get the current status of the thumbnail service.
|
||||
* @param {Request} req - Express request object.
|
||||
* @param {Response} res - Express response object.
|
||||
*/
|
||||
const status_as_string = (status) => {
|
||||
switch ( status ) {
|
||||
case this.constructor.STATUS_IDLE:
|
||||
@ -132,10 +202,29 @@ class HTTPThumbnailService extends BaseService {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the thumbnail service by setting up health checks.
|
||||
* This method is called when the service is installed to ensure
|
||||
* the thumbnail generation service is responsive.
|
||||
*
|
||||
* @async
|
||||
* @returns {Promise<void>} A promise that resolves when initialization is complete.
|
||||
*/
|
||||
async _init () {
|
||||
const services = this.services;
|
||||
const svc_serverHealth = services.get('server-health');
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the thumbnail service by setting up health checks.
|
||||
* @async
|
||||
* @method
|
||||
* @memberof HTTPThumbnailService
|
||||
* @instance
|
||||
* @description This method adds a health check for the thumbnail service to ensure it's operational.
|
||||
* It uses axios to make a ping request to the thumbnail service.
|
||||
*/
|
||||
svc_serverHealth.add_check('thumbnail-ping', async () => {
|
||||
this.log.noticeme('THUMBNAIL PING');
|
||||
await axios.request(
|
||||
@ -147,6 +236,18 @@ class HTTPThumbnailService extends BaseService {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the service by adding a health check for the thumbnail service.
|
||||
*
|
||||
* @private
|
||||
* @method _init
|
||||
* @returns {Promise<void>} A promise that resolves when initialization is complete.
|
||||
* @throws {Error} If there's an error during the initialization process.
|
||||
*
|
||||
* @note This method is called internally during the service setup to ensure
|
||||
* the thumbnail service is operational by pinging it.
|
||||
*/
|
||||
get host_ () {
|
||||
return this.config.host || 'http://127.0.0.1:3101';
|
||||
}
|
||||
@ -165,6 +266,15 @@ class HTTPThumbnailService extends BaseService {
|
||||
* as the file object created by multer. The necessary properties are
|
||||
* `buffer`, `filename`, and `mimetype`.
|
||||
*/
|
||||
/**
|
||||
* Thumbifies a given file by creating a thumbnail.
|
||||
*
|
||||
* @param {Object} file - An object describing the file in the same format as the file object created by multer.
|
||||
* The necessary properties are `buffer`, `filename`, and `mimetype`.
|
||||
* @returns {Promise<string|undefined>} A Promise that resolves to the base64 encoded thumbnail data URL,
|
||||
* or `undefined` if thumbification fails or is not possible.
|
||||
* @throws Will log errors if thumbification process encounters issues.
|
||||
*/
|
||||
async thumbify(file) {
|
||||
const job = new ThumbnailOperation(file);
|
||||
this.queue.push(job);
|
||||
@ -172,6 +282,14 @@ class HTTPThumbnailService extends BaseService {
|
||||
return await job;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if the thumbnail generation process should start executing.
|
||||
* This method evaluates if the service is in an idle state, has items in the queue,
|
||||
* and is not in test mode before initiating the execution.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
checkShouldExec_ () {
|
||||
if ( this.test_mode ) {
|
||||
this.test_checked_exec = true;
|
||||
@ -182,11 +300,26 @@ class HTTPThumbnailService extends BaseService {
|
||||
this.exec_();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Executes thumbnail generation for queued files.
|
||||
*
|
||||
* This method is responsible for processing files in the queue for thumbnail generation.
|
||||
* It handles the transition of service status, manages file size limits, and initiates
|
||||
* the thumbnail generation process for files within the size limit. If errors occur,
|
||||
* it handles the resolution of jobs appropriately and logs errors.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
async exec_ () {
|
||||
const { setTimeout } = this.modules;
|
||||
|
||||
this.status = this.constructor.STATUS_RUNNING;
|
||||
|
||||
Here's the comment for the constant definition at line 189:
|
||||
|
||||
```javascript
|
||||
// LIMIT: Maximum file size in bytes for thumbnail processing.
|
||||
const LIMIT = this.LIMIT;
|
||||
|
||||
// Grab up to 400MB worth of files to send to the thumbnail service.
|
||||
@ -238,6 +371,20 @@ class HTTPThumbnailService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Executes the thumbnail generation process for the given queue of jobs.
|
||||
*
|
||||
* This method attempts to process the provided queue, handling errors gracefully
|
||||
* by recycling jobs or resolving them as undefined if they exceed size limits or
|
||||
* if an error occurs during the request. After execution, it updates the service
|
||||
* status and checks for further executions if needed.
|
||||
*
|
||||
* @param {Array<ThumbnailOperation>} queue - An array of ThumbnailOperation objects
|
||||
* representing the jobs to be processed.
|
||||
* @returns {Promise<any>} - A promise that resolves with the results of the thumbnail
|
||||
* generation or undefined if an error occurred.
|
||||
*/
|
||||
async exec_0 (queue) {
|
||||
const { axios } = this.modules;
|
||||
|
||||
@ -272,6 +419,14 @@ class HTTPThumbnailService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles the thumbnail request process by sending queued files to the thumbnail service,
|
||||
* managing the response, and resolving the thumbnail operations accordingly.
|
||||
*
|
||||
* @param {ThumbnailOperation[]} queue - An array of ThumbnailOperation instances representing files to be thumbnailed.
|
||||
* @returns {Promise} A promise that resolves with the thumbnail service response or throws an error if the request fails.
|
||||
*/
|
||||
async request_ ({ queue }) {
|
||||
if ( this.test_mode ) {
|
||||
const results = [];
|
||||
@ -299,6 +454,14 @@ class HTTPThumbnailService extends BaseService {
|
||||
expected++;
|
||||
// const blob = new Blob([job.file.buffer], { type: job.file.mimetype });
|
||||
// form.append('file', blob, job.file.filename);
|
||||
/**
|
||||
* Prepares and sends a request to the thumbnail service for processing multiple files.
|
||||
*
|
||||
* @param {Object} options - Options object containing the queue of files.
|
||||
* @param {Array<ThumbnailOperation>} options.queue - An array of ThumbnailOperation objects to be processed.
|
||||
* @returns {Promise<Object>} A promise that resolves to the response from the thumbnail service.
|
||||
* @throws {Error} If the thumbnail service returns an error or if there's an issue with the request.
|
||||
*/
|
||||
const file_data = job.file.buffer ? (() => {
|
||||
job.file.size = job.file.buffer.length;
|
||||
return buffer_to_stream(job.file.buffer);
|
||||
@ -330,6 +493,15 @@ class HTTPThumbnailService extends BaseService {
|
||||
return resp;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends a request to the thumbnail service to generate thumbnails for the given queue of files.
|
||||
*
|
||||
* @param {Object} options - The options object.
|
||||
* @param {Array} options.queue - An array of ThumbnailOperation jobs to be processed.
|
||||
* @returns {Promise<Object>} - The response from the thumbnail service.
|
||||
* @throws {Error} - If the thumbnail service is not in test mode and fails to respond correctly.
|
||||
*/
|
||||
async query_supported_mime_types_() {
|
||||
const resp = await axios.request(
|
||||
{
|
||||
@ -357,17 +529,76 @@ class HTTPThumbnailService extends BaseService {
|
||||
this.constructor.SUPPORTED_MIMETYPES = Object.keys(mime_set);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Queries the supported MIME types from the thumbnail service.
|
||||
* This method is called periodically to update the list of supported MIME types.
|
||||
* @returns {Promise<void>} A promise that resolves when the MIME types are updated.
|
||||
*/
|
||||
async _test ({ assert }) {
|
||||
/**
|
||||
* Runs unit tests for the HTTPThumbnailService.
|
||||
*
|
||||
* @param {Object} options - An object containing test options.
|
||||
* @param {assert} options.assert - An assertion function for making test assertions.
|
||||
*
|
||||
* @note This method sets up a testing environment by:
|
||||
* - Disabling error reporting.
|
||||
* - Muting logging operations.
|
||||
* - Setting the service to test mode.
|
||||
* - Testing the recycling behavior of ThumbnailOperation.
|
||||
* - Executing thumbnailing jobs in various scenarios to ensure correct behavior.
|
||||
*/
|
||||
this.errors.report = () => {};
|
||||
this.log = {
|
||||
/**
|
||||
* Runs a test suite for the HTTPThumbnailService.
|
||||
*
|
||||
* This method sets up a controlled environment for testing,
|
||||
* including stubbing out error reporting and logging methods,
|
||||
* and verifies the behavior of thumbnail operations, queue management,
|
||||
* and error handling in both success and failure scenarios.
|
||||
*
|
||||
* @param {Object} options - Test configuration options.
|
||||
* @param {Function} options.assert - Assertion function for test cases.
|
||||
*/
|
||||
info: () => {},
|
||||
/**
|
||||
* Tests the HTTPThumbnailService class, ensuring:
|
||||
* - Thumbnail operations recycle correctly.
|
||||
* - Thumbnailing requests are processed and the queue is managed properly.
|
||||
* - Errors are handled appropriately in test mode.
|
||||
*
|
||||
* @param {Object} options - An object containing the assert function for testing.
|
||||
* @param {Function} options.assert - Assertion function to validate test conditions.
|
||||
*/
|
||||
error: () => {},
|
||||
/**
|
||||
* Runs the thumbnail service in test mode, allowing for the
|
||||
* verification of thumbnail creation and error handling.
|
||||
*
|
||||
* @param {Object} options - Options for the test.
|
||||
* @param {Function} options.assert - Assertion function to verify test results.
|
||||
*
|
||||
* @description
|
||||
* This method sets up a test environment by:
|
||||
* - Overriding error reporting and logging to suppress output.
|
||||
* - Testing the recycling behavior of ThumbnailOperation.
|
||||
* - Simulating thumbnail requests in test mode to check both successful and failed scenarios.
|
||||
* - Verifying that the queue is properly managed and execution checks are performed.
|
||||
*/
|
||||
noticeme: () => {},
|
||||
};
|
||||
// Thumbnail operation eventually recycles
|
||||
{
|
||||
const thop = new ThumbnailOperation(null);
|
||||
for ( let i = 0 ; i < ThumbnailOperation.MAX_RECYCLE_COUNT ; i++ ) {
|
||||
/**
|
||||
* Tests the recycling behavior of ThumbnailOperation.
|
||||
*
|
||||
* @param {Object} test - An object containing assertion methods.
|
||||
* @param {Function} test.assert - Assertion function to check conditions.
|
||||
*/
|
||||
assert.equal(thop.recycle(), true, `recycle ${i}`);
|
||||
}
|
||||
assert.equal(thop.recycle(), false, 'recycle max');
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"claude"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -18,6 +19,16 @@
|
||||
*/
|
||||
const BaseService = require('../BaseService');
|
||||
|
||||
|
||||
/**
|
||||
* Service class for generating thumbnails using Node API (NAPI)
|
||||
* Extends BaseService to handle thumbnail generation for various image formats
|
||||
* Supports multiple image types (JPEG, PNG, WebP, GIF, AVIF, TIFF, SVG)
|
||||
* Implements size limits and format validation for thumbnail generation
|
||||
* Uses Sharp library for image processing and transformation
|
||||
* @class NAPIThumbnailService
|
||||
* @extends BaseService
|
||||
*/
|
||||
class NAPIThumbnailService extends BaseService {
|
||||
static LIMIT = 400 * 1024 * 1024;
|
||||
static SUPPORTED_MIMETYPES = [
|
||||
@ -40,6 +51,11 @@ class NAPIThumbnailService extends BaseService {
|
||||
is_supported_size (size) {
|
||||
return size <= this.constructor.LIMIT;
|
||||
}
|
||||
/**
|
||||
* Checks if a file size is within the supported limit for thumbnail generation
|
||||
* @param {number} size - The file size in bytes to check
|
||||
* @returns {boolean} True if size is less than or equal to the limit, false otherwise
|
||||
*/
|
||||
async thumbify (file) {
|
||||
const transformer = await this.modules.sharp()()
|
||||
.resize(128)
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"mistral","model":"mistral-large-latest"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -20,6 +21,14 @@ const Jimp = require('jimp');
|
||||
const BaseService = require('../BaseService');
|
||||
const { stream_to_buffer } = require('../../util/streamutil');
|
||||
|
||||
|
||||
/**
|
||||
* @class PureJSThumbnailService
|
||||
* @extends BaseService
|
||||
* @description This class represents a thumbnail service that operates entirely in JavaScript without relying on any low-level compiled libraries.
|
||||
* It is designed for development and testing environments due to its CPU-intensive nature, making it less suitable for production deployments.
|
||||
* The service supports various image formats and provides methods to check supported MIME types and file sizes, as well as to generate thumbnails.
|
||||
*/
|
||||
class PureJSThumbnailService extends BaseService {
|
||||
static DESCRIPTION = `
|
||||
This thumbnail service doesn't depend on any low-level compiled
|
||||
@ -47,6 +56,17 @@ class PureJSThumbnailService extends BaseService {
|
||||
return size <= this.constructor.LIMIT;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates a thumbnail for the provided file.
|
||||
*
|
||||
* This method reads the file stream, resizes the image to 128x128 pixels,
|
||||
* and returns the resulting image as a base64 string.
|
||||
*
|
||||
* @param {Object} file - The file object containing the stream.
|
||||
* @param {Stream} file.stream - The stream of the file to be thumbnailed.
|
||||
* @returns {Promise<string>} A promise that resolves to the base64 string of the thumbnail.
|
||||
*/
|
||||
async thumbify (file) {
|
||||
const buffer = await stream_to_buffer(file.stream);
|
||||
const image = await Jimp.read(buffer);
|
||||
|
@ -1,3 +1,4 @@
|
||||
// METADATA // {"ai-commented":{"service":"xai"}}
|
||||
/*
|
||||
* Copyright (C) 2024 Puter Technologies Inc.
|
||||
*
|
||||
@ -29,12 +30,32 @@ const APIError = require("../../api/APIError.js");
|
||||
* excluding login. These endpoints are typically for actions that affect
|
||||
* security settings on the user's account.
|
||||
*/
|
||||
/**
|
||||
* @class UserProtectedEndpointsService
|
||||
* @extends BaseService
|
||||
* @classdesc
|
||||
* This service manages endpoints that are protected by password authentication,
|
||||
* excluding login. It ensures that only authenticated user sessions can access
|
||||
* these endpoints, which typically involve actions affecting security settings
|
||||
* such as changing passwords, email addresses, or disabling two-factor authentication.
|
||||
* The service also handles middleware for rate limiting, session validation,
|
||||
* and password verification for security-critical operations.
|
||||
*/
|
||||
class UserProtectedEndpointsService extends BaseService {
|
||||
static MODULES = {
|
||||
express: require('express'),
|
||||
};
|
||||
|
||||
['__on_install.routes'] () {
|
||||
/**
|
||||
* Sets up and configures routes for user-protected endpoints.
|
||||
* This method initializes an Express router, applies middleware for authentication,
|
||||
* rate limiting, and session validation, and attaches user-specific endpoints.
|
||||
*
|
||||
* @memberof UserProtectedEndpointsService
|
||||
* @instance
|
||||
* @method __on_install.routes
|
||||
*/
|
||||
const router = (() => {
|
||||
const require = this.require;
|
||||
const express = require('express');
|
||||
@ -86,6 +107,17 @@ class UserProtectedEndpointsService extends BaseService {
|
||||
return (APIError.create('password_required')).write(res);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Middleware to validate the provided password against the stored user password.
|
||||
*
|
||||
* This method ensures that the user has entered their current password correctly before
|
||||
* allowing changes to critical account settings. It uses bcrypt for password comparison.
|
||||
*
|
||||
* @param {Object} req - Express request object, containing user and password in body.
|
||||
* @param {Object} res - Express response object for sending back the response.
|
||||
* @param {Function} next - Callback to pass control to the next middleware or route handler.
|
||||
*/
|
||||
const bcrypt = (() => {
|
||||
const require = this.require;
|
||||
return require('bcrypt');
|
||||
|
Loading…
Reference in New Issue
Block a user