Sockets

There might be rare occasions where you need to extend the existing socket interface; e.g. after adding a new database table to CARE and you want to make this data available to the frontend. In this chapter we outline the steps necessary for either extending an existing socket or creating an entirely new one.

Note

Please also see the code conventions for the socket interface in Code Conventions

Sockets in CARE

The socket architecture is realized in the backend; in the following all paths are provided relative to the directory backend/webserver including all relevant components.

During start-up of the webserver all sockets specified in the sockets directory that extends the Socket base class are loaded. Each socket is specified in an individual .js-file exporting a class that extends the Socket base class. Hence, adding a socket means creating such a new file and overriding the abstract class.

This base class (specified in Socket.js) defines several convenience methods to communicate with the frontend, as well as attributes and methods to interact with the web server class starting the respective socket. In the following sections we elaborate which methods and attributes you should use or override to realize your own service.

Conceptually, a socket class encapsulates a socket-io connection established by the webserver, which you can use to listen to certain messages and which you can use to send back messages. For each frontend client that connects to the webserver, all socket classes are instantiated anew having an associated client id. This means, all messages you send and receive via the socket attribute are always specific to this one client connection; in other words, there is no state transfer between different connections or broadcasting possible. However, some of this functionality is made available through the server object – we address these options in the Extending a Socket section below.

See also

For the generic create/update entry point used by the frontend, see AppDataUpdate Socket.

Creating a Socket

Let’s say we want to add a new socket called TestSocket to CARE. First, we add a new file test.js to the backend/webserver/sockets directory that exports the new socket class:

const Socket = require("../Socket.js");
/**
 * <doc>
 */
module.exports = class TestSocket extends Socket {
    constructor(server, io, socket) {
        super(server, io, socket);
    }
}

While this would already add the socket automatically to CARE on restart of the webserver, you should always override the init() method of the base class. Here, you will add the message listeners; for now, we will just log that the socket has been initialized.

/**
 * HEADER BOILERPLATE...
 */
module.exports = class TestSocket extends Socket {
    constructor(server, io, socket) {
        super(server, io, socket);
    }

     init() {
        this.logger.debug(`TestSocket was created for client ${this.socket.id}`);
     }
}

In the next section we elaborate on how to populate a socket by message listeners and response functionality.

Extending a Socket

Let’s assume we want to extend a socket TestSocket defined in ./backend/webserver/sockets/test.js. Generally, socket logic should be kept lean and easy. The primary responsibility of a socket is to forward a new client request to another module for processing (e.g. the database interface), handle possible errors and send the results back to the client. In the following we explain the possible use-cases for extending a socket

Listening to a New Message Type

Let’s say we want to listen to new message types testIncrement and testReset that provides a data object parameters. For the sake of brevity we do not interact with the database, but write to a class attribute.

/**
 * HEADER BOILERPLATE...
 */
module.exports = class TestSocket extends Socket {
    constructor(server, io, socket) {
        super(server, io, socket);

        this.testVar = 0;
    }

    /* ... */

    // method for updating the variable
    updateTestVar(newVal){
        this.testVar = newVal;
        return this.testVar;
    }

    init() {
        /* ... */

        // listen to testIncrement
        this.socket.on("testIncrement", (data) => {
            try {
                this.socket.emit("testResult", {success: true, val: this.updateTestVar(this.testVar + data.inc)});
            } catch (e) {
                this.logger.error(e);
            }
        });

        // listen to testReset
        this.socket.on("testReset", () => {
            try {
                this.socket.emit("testResult", {success: true, val: this.updateTestVar(0)});
            } catch (e) {
                this.logger.error(e);
            }
        });
    }
}

Let’s decompose the steps to realize this example. First, we extend the init() method adding event listeners on the socket using the on(msg, callback) function of the socket.io client. We then call the class method that allows us to modify the state of the test variable (this.updateTestVar(...)). Finally, we return the resulting value to the client via emit(msg, data) with the resulting value.

Error and Rights Management

When interacting with the database the key challenge is error handling, marshalling (i.e. the translation of the DB data representation into a suitable format for the frontend) and rights management. For now, we assume that we want to call an already defined database a model Test specified in ./backend/db/models/test.js and integrate this call into the above example. Let’s also assume that only administrators are allowed to change this value.

Note

Please refer to the guide on how to extend the database and add interface methods in Database.

/**
 * HEADER BOILERPLATE...
 */
module.exports = class TestSocket extends Socket {
    /* ... */

    // method for updating the variable
    async updateTestVar(newVal){
        // use base class method to check for admin rights
        if (this.isAdmin()) {
            try {
                const result = await this.models["test"].updateById("x", {val: newVal});
                this.socket.emit("testResult", {success: true, val: result});
            } catch (e) {
                this.socket.emit("testResult", {success: false, message: "Failed to update test!"});
                this.logger.error("DB error while updating test" + JSON.stringify(e));
            }
        } else {
            // respond with a negative message
            this.socket.emit("testResult", {success: false, message: "User rights and argument mismatch"});
        }
    }

    init() {
        /* ... */

        // listen to testIncrement
        this.socket.on("testIncrement", async (data) => {
            try {
                await this.updateTestVar(this.testVar + data.inc);
            } catch (e) {
                this.logger.error(e);
            }
        });

        // listen to testReset
        this.socket.on("testReset", async () => {
            try {
                await this.updateTestVar(0);
            } catch (e) {
                this.logger.error(e);
            }
        }
    }
}

Let’s decompose the example again! We first update the updateTestVar to use the dbUpdateTest function interfacing the database. Before making the actual call to the database, we check whether the user is an admin through the base class isAdmin() method accessing the backend user information associated with the current connection. Because the database query might fail, we add a try-catch-block around it. In case of an error at any stage, we send a negative testResult back to the client.

Note

It is crucial that you do full error handling on the socket level, i.e. at some point all exceptions should have been caught by a catch block. Otherwise, the webserver can crash due to minor errors during database interaction.

Broadcasting Responses

To realize collaboration features, it might be desirable to join clients into rooms and multicast messages to these groups. In exceptional cases, even a broadcast might make sense. You should use these two options very sparingly, because they imply a lot of network traffic.

To join a client to a socketio room, you can simply use the join(name) function on the socket object:

this.socket.join("roomName");

To multicast a message to a room, you need to access the io object of the base class.

this.io.to("roomName").emit("msg", data);

Testing

Please think about how to test your socket. In general, you should test the functionality in isolation. We refer to the section on Tests for more information on how to test your code.

Socket Communication Schema

In order to streamline socket communication, especially when interacting with the database, CARE defines a standardized way to register and handle socket events using the createSocket method provided by ./backend/webserver/Socket.js. This schema helps to ensure consistent error handling, optional transaction safety, and frontend compatibility.

createSocket: How it works

The createSocket method allows you to define a socket event with built-in support for:

  • Error handling

  • Optional transaction wrapping

  • Automatic frontend success/failure callbacks

  • Clean backend-to-frontend event emission after database changes

createSocket(eventName, func, options = {}, transaction = false)
Arguments:
  • eventName: Name of the socket event to register

  • func: Async function handling the request, with parameters (data, options)

  • options: Object passed through the socket pipeline; can contain any contextual data (e.g., transaction info, user session, metadata)

  • transaction: If true, wraps execution in a Sequelize DB transaction

Warning

If your handler function performs any database write operations (e.g., create, update), you must set transaction = true and use the provided transaction object inside the function. Failing to do so can result in inconsistent or partial data states.

Note

The createSocket function should always be called within the init() method of socket classes located in ./backend/webserver/sockets/*. The handler function passed to it should be a method of the class itself, not an inline function or a function defined inside init().

See a full example of the usage of createSocket in Example Lifecycle.

See also

How DB writes propagate to the UI (transactions, commit broadcasts, Vuex refresh): Data Transfer — especially Data Flow.

Transaction Usage

If your socket operation modifies data, set transaction = true. This will:
  • Open a Sequelize transaction

  • Pass it to your function via options.transaction

  • Automatically commit/rollback

  • Allow you to define afterCommit() behavior

init() {
    this.createSocket("studySaveAsTemplate", this.saveStudyAsTemplate, {}, true);
    }
/**
* Save the current study as a template (creates a new study with template: true).
*
* @param {object} data - The input data from the socket call.
* @param {number} data.id - ID of the study to duplicate as a template.
* @param {object} options - Context passed through the socket pipeline.
* @returns {Promise<object>} The newly created template study.
*/
async saveStudyAsTemplate(data, options) {
    const study = await this.models["study"].getById(data.id);

    if (this.checkUserAccess(study.userId)) {
        const template = await this.models["study"].add({
            ...study,
            id: undefined,
            hash: undefined,
            template: true,
        }, {transaction: options.transaction});

        // Code executed after transaction commit success (code only, without further DB changes!)
        options.transaction.afterCommit(() => {
            this.emit("studyRefresh", template);
        });

        return template; // The value sent back to the frontend via the socket callback
    } else {
        throw new Error("No permission to save study as template");
    }
}

Note

  • Socket handler functions should always include clear JSDoc-style docstrings describing the expected data parameters, transaction context, and return type. For full documentation standards, refer to: Conventions and Paradigms.

  • For a detailed explanation of how to notify the frontend after a transaction, including automatic tracking (via autoTable) and manual afterCommit hooks, see: Tracking DB Changes (afterCommit).

See a further example of the transaction usage in Example Lifecycle.

Transaction Failure Handling

If an error is thrown inside a socket using transaction = true, the transaction will be automatically rolled back.

Warning

Do not call transaction.commit() or transaction.rollback() manually when using createSocket. These are handled automatically, and calling them yourself can lead to runtime errors — they can only be called once per transaction.

Try-Catch Behavior

Using createSocket wraps your function call in an automatic try-catch block. If an error is thrown:
  • The transaction is rolled back (if enabled)

  • A callback with success: false and the error message is sent to the frontend

See the section above on createSocket: How it works for more details.

No need to write:

try {
    ...
} catch (e) {
    ...
}

Just throw an error inside your function:

if (!this.checkUserAccess(...)) {
    throw new Error("Access denied");
}

Socket Callback Responses

When a socket event completes, a callback function on the frontend receives a standardized response object:

{
    success: true,        // true if the backend handler completed without errors
    data: {...},          // the return value from the backend function (if successful)
    message: "..."        // an optional error message if an error was thrown
}

This structure is automatically handled by createSocket:

  • If the backend handler throws an error, success is set to false and message contains the error.

  • If the function returns a value, it is included as data and success is set to true.

The following example uses CARE’s global toast system to display success or failure. For full documentation on toast messages, see ../frontend/toasts.

this.$socket.emit("studySaveAsTemplate", {id: 1}, (result) => {
    if (!result.success) {
        this.eventBus.emit("toast", {
            title: "Template Save Failed",
            message: result.message,
            variant: "danger",
        });
    } else {
        this.eventBus.emit("toast", {
            title: "Success",
            message: "Study saved as template",
            variant: "success",
        });
    }
});

Tracking DB Changes (afterCommit)

In most cases, when using models with autoTable = true, CARE will automatically track database changes and push updates directly to the frontend. This includes creation, updates, and deletions — no need to manually emit a ...Refresh event.

However, in special cases where autoTable is not used or doesn’t apply, you may need to manually notify the frontend after a successful transaction. Examples include:

  • Writing files to disk

  • Returning plain objects or custom responses

  • Using models that do not have autoTable = true

In these cases, you can register a manual afterCommit hook on the transaction object:

const doc = await this.models["document"].add({
    name: data.name,
    type: data.type,
    userId: this.userId
}, {transaction: options.transaction});

fs.writeFileSync(target, data.file);

options.transaction.afterCommit(() => {
    this.emit("documentRefresh", doc);
});

Warning

In normal use cases (e.g., with autoTable = true), you do not need to use `afterCommit()` or manually emit refresh events. CARE tracks database changes automatically and handles frontend updates for you.

See also

When using autoTable models, commit-time change tracking and <table>Refresh emits are automatic. Details in Store Updates &.

Example Lifecycle

Here is a full example showing how the backend and frontend work together when using createSocket for creating a new document.

Frontend: emit event and handle result with a toast

this.$socket.emit("documentCreate", {
  type: 1,
  name: this.name,
}, (res) => {
  if (res.success) {
    this.$refs.createModal.close();
    this.eventBus.emit("toast", {
    title: "Success",
    message: res.data, // This will be: "Document successfully created"
    variant: "success",
    });
  } else {
    this.$refs.createModal.waiting = false;
    this.eventBus.emit("toast", {
    title: "Error",
    message: res.message, // This will be: "Missing required fields: name and type"
    variant: "danger",
    });
  }
});

Backend: register the socket

init() {
    this.createSocket("documentCreate", this.createDocument, {}, true);
    }

Backend: handler function implementation

/**
 * Create document (HTML)
 * @param data {type: number, name: string}
 * @param options
 * @returns {Promise<object>}
 */
async createDocument(data, options) {
    const doc = await this.models["document"].add({
        name: data.name,
        type: data.type,
        userId: this.userId
    }, {transaction: options.transaction});

    options.transaction.afterCommit(() => {
        // Backend log entry instead of manual frontend emit; frontend is auto-updated via autoTable
        this.logger.info(`Document created (id: ${doc.id}) by user ${this.userId}`);
    });

    return "Document successfully created";
}

What happens internally:

  1. createSocket listens for “documentCreate” on the socket

  2. When the event is triggered from the frontend:

    • The handler createDocument runs

    • A Sequelize transaction is opened and passed as options.transaction

  3. If the function completes without error:

    • The transaction is committed

    • The afterCommit hook logs a backend info message (no manual frontend emit is needed; updates are handled automatically via autoTable)

    • The frontend receives { success: true, data: "Document successfully created" }

    • A success toast is displayed using that message

  4. If an error is thrown:

    • The transaction is rolled back

    • The frontend receives { success: false, message: "..." } with the error message

    • An error toast is displayed using that message

Socket functions

Annotation Socket Functions (annotation.js)

class AnnotationSocket()

Handle all annotation through websocket

AnnotationSocket.embedAnnotationsForDocument(data, options)

Socket Event: annotationEmbed

Embed all annotations into the PDF for a document.

Arguments:
  • data (Object) – The data containing the document ID and other parameters.

  • options (Object) – Additional options for the emmfbedding process.

  • data.documentId (number) – The ID of the document to embed annotations into.

Returns:

Promise.<Object> – The response from the PDFRPC embedAnnotations call.

AnnotationSocket.getAnnotationsByDoc(data, options)

Socket Event: annotationGetByDocument

Returns the annotations for a given document by its id and sends the complete list to the client via an ‘annotationRefresh’ event.

Arguments:
  • data – The request data containing the document identifier.

  • data.documentId (number) – the id of the document to retrieve the annotations for

  • options – Additional configuration parameters (currently unused).

Returns:

Promise.<void> – A promise that resolves (with no value) once the annotations have been successfully fetched and sent to the client.

AnnotationSocket.loadCommentsByAnnotation(annotationId)

Load all comments for a document by annotation. Errors during the database operation are caught and handled internally by logging the error and sending a toast notification to the client.

Arguments:
  • annotationId (number) – The ID of the annotation whose comments are to be loaded.

Returns:

Promise.<void> – A promise that resolves (with no value) once the comments have been sent or an error has been handled.

AnnotationSocket.sendAnnotation(data, options)

Socket Event: annotationGet

Send an annotation to the client by id. A permission check is performed to ensure the user has access to the document containing the annotation.

Arguments:
  • data (Object) – the input data from the frontend

  • options (Object) – Additional configuration parameters (currently unused).

  • data.annotationId (number) – the id of the annotation

Throws:

Error – Throws an error if the user does not have permission to access the document associated with the annotation.

Returns:

Promise.<*> – A promise that resolves (with no value) once the annotation and its comments have been processed and sent.

AnnotationSocket.updateAnnotation(data, options)

Socket Event: annotationUpdate

Updates the annotations in the database. If the provided annotation is a new annotation, it will be created in the database otherwise the existing entry is overriden.

Arguments:
  • data (Object) – the input data from the frontend

  • options (Object) – containing transactions

  • options.transaction – the DB transaction

  • data.annotationId (number) – the id of the annotation to update

  • data.documentId (number) – the id of the document to update

  • data.studySessionId (number) – the id of the study session

  • data.studyStepId (number) – the id of the study step

  • data.tagId (number) – the id of the tag

  • data.selectors (string) – the selectors of the annotation

  • data.deleted (boolean) – indicates if the data is deleted

  • data.anonymous (boolean) – indicates if the data is anonymous

Throws:

Error – Throws an error if a user attempts to modify an annotation they do not have access to.

Returns:

Promise.<void> – A promise that resolves (with no value) once the annotation has been saved and related events have been emitted.

App Socket Functions (app.js)

class AppSocket()

Send data for building the frontend app

AppSocket.sendData(data, options)

Socket Event: appData

Handles the ‘appData’ socket event. It acts as a wrapper to fetch and send filtered data from a specific table to the client.

Arguments:
  • data (Object) – The input data from the frontend

  • data.table (String) – Table to send

  • data.filter (Object) – Filters

  • data.include (Object) – What data to include

  • options (Object) – holds the managed transaction of the database (see createSocket function)

Returns:

Promise.<void> – Resolves when data is successfully retrieved and emitted to the client.

AppSocket.sendDataByHash(data, options)

Socket Event: appDataByHash

Fetches and sends a single record from a specified table identified by a hash.

This function also serves as a permission check, ensuring data is only sent if found

Arguments:
  • data (Object) – The input data from the frontend

  • data.hash (String) – The hash value

  • data.table (String) – Table to send the data from

  • options (Object) – Additional configuration parameter

  • options.transaction (Object) – A Sequelize DB transaction object for future use.

Throws:

Error – Throws error if results is empty (no record found or operation fails)

Returns:

Promise.<void> – A promise that resolves if the data is found and sent successfully

AppSocket.sendInit(data, options)

Socket Event: appInit

Send all data needed for the frontend app for initialization.

Creates a series of socket events to provide user details, system tables, configuration settings, and system roles to the client.

Arguments:
  • data (Object) – The input data from the frontend

  • options (Object) – Sequelize transaction options

  • options.transaction (Object) – Sequelize DB transaction options

Returns:

Promise.<void> – Resolves once all the initial data has been sent successfully

AppSocket.sendOverallSetting(data, options)

Socket Event: appSettingSet

Updates a single user-specific setting and then broadcasts the complete, refreshed settings to the client.

Saves the key–value pair for the current user in the database, then triggers an update by emitting the complete set of user settings to the client.

Arguments:
  • data (Object) – The input data from the frontend

  • data.key (String) – The key in the user setting table

  • data.userId (String) – The user ID to set the setting for, if not provided, it will use the current user

  • data.value (String) – The value in the user setting table

  • options (Object) – Additional configuration parameter

Returns:

Promise.<void> – A promise that resolves after the setting is saved and the new configuration is sent

AppSocket.sendSettings(sendToAll=false)

Fetches and merges global application settings with user-specific settings, then sends the complete list to the client via a socket event.

Arguments:
  • sendToAll (boolean) – [sendToAll=false] - If true, the settings are broadcast to all connected clients. If false, they are sent only to the client that initiated the request.

Returns:

Promise.<void> – A promise that resolves (with no value) once the settings have been sent or an error has been caught and logged.

AppSocket.sendSystemRoles()

Sends all the roles CARE has from the DB.

Retrieves role data from the database, including only role IDs and names, and emits it via the appSystemRoles socket event.

Returns:

Promise.<void> – Resolves after the list of system roles has been successfully sent to the client via a socket event

AppSocket.sendTables()

Gathers the schema for all models configured for automatic table generation if ‘autoTable = true’.

This structural data is then sent to the client via an ‘appTables’ socket event, allowing the frontend to dynamically generate tables or forms.

Returns:

Promise.<void> – A promise that resolves (with no value) once the table schemas have been sent.

AppSocket.sendUser()

Sends the user information for this user loaded from the db.

This includes core user data, associated role IDs, rights derived from those roles, and whether the user has administrative privileges. The data is emitted via the appUser socket event.

Returns:

Promise.<void> – Resolves after the user information has been successfully sent to the client via a socket event

AppSocket.subscribeAppData(data, options)

Socket Event: subscribeAppData

Subscribe to app data. Creates and manages a subscription for real-time updates on a specific database table.

Merges filters from all active subscriptions for a table and sends the client the initial data set required for the new subscription

Arguments:
  • data (Object) – The configuration for the data subscription

  • data.table (string) – The name of the database table to subscribe to

  • data.filter (Array.<Object>) – An optional filter array (e.g., Sequelize ‘where’ conditions) to specify a subset of data

  • data.inject (Array.<Object>) – Optional instructions for including related data (e.g., Sequelize ‘include’ models)

  • options (Object) – Additional configuration parameters

  • options.transaction (Object) – May contain a Sequelize DB transaction for future use

Throws:

Error – Throws an error if data.table is not provided

Returns:

Promise.<string> – A promise that resolves with the unique ID for the newly created subscription

AppSocket.unsubscribeAppData(data, options)

Socket Event: unsubscribeAppData

Unsubscribe from app data, removes a data subscription for the client.

Removes the corresponding data subscription entry from the socket’s internal tracking lists based on the provided identifier.

Arguments:
  • data (Object) – The input data from the frontend. The identifier for the subscription to remove

  • options (Object) – Additional configuration parameter

Returns:

Promise.<void> – A promise that resolves once the subscription has been removed from the tracking lists

AppSocket.updateAppData(data, options)

Socket Event: appDataUpdate

Update data for a specific table to the client.

Acts as a wrapper around the underlying updateData method, using a Sequelize transaction if provided, and returns the outcome to the caller.

Arguments:
  • data (Object) – The input data from the frontend

  • options (Object) – Additional configuration parameter

  • options.transaction (Object) – Sequelize DB transaction options

Returns:

Promise.<*> – A promise that resolves with the result from the underlying updateData method

AppSocket.updateData(data, options)

A generic and powerful method to create or update records in a specified database table.

It determines whether to create a new record or update an existing one based on the presence of an id in the data.data payload. The function includes schema-based validation for required fields, handles default values, and supports recursive updates for nested table data.

Note: This works only for autoTable tables (see documentation for more information)

Arguments:
  • data (Object) – The input data from the frontend

  • data.table (String) – The name of the table to update

  • data.data (Object) – New data to update

  • options (Object) – holds the managed transaction of the database (see createSocket function)

  • options.transaction (Object) – Sequelize DB transaction options

Throws:

Error – Throws an error under several conditions: If a non-admin user attempts to update a record for another user, If a required field (as defined in the model’s schema) is missing from the data.data payload, If the database operation fails to return a new or updated entry after an add/update call.

Returns:

Promise.<void> – A promise that resolves with the ID of the newly created or updated primary record.

Assignment Socket Functions (assignment.js)

class AssignmentSocket()

Handle user through websocket

AssignmentSocket.addReviewer(data, options)

Socket Event: assignmentAdd

Adds new sessions to a study.

If the number of reviewers being added exceeds the current session limit of the study, the session limit is updated accordingly. Each reviewer is added as a new study_session.

Arguments:
  • data (Object) – The data for adding reviewers.

  • data.studyId (number) – The ID of the study to which reviewers are to be added.

  • data.reviewer (Array.<number>) – An array of user IDs representing the reviewers to be added.

  • options (Object) – holds the managed transaction of the database (see createSocket function)

Returns:

Promise.<void> – A promise that resolves when the reviewers have been added to the study.

AssignmentSocket.createAssignment(data, options)

Socket Event: assignmentCreate

Assigns a peer review task to a list of reviewers based on a given template.

Constructs a study from the provided template and assignment data, assigns it to the specified user, and links it to the given documents. Reviewers are then associated with the study.

Arguments:
  • data (Object) – The data for assigning peer reviews.

  • data.assignment (Object) – The assignment object containing details of the assignment.

  • data.reviewers (Array) – An array of reviewer IDs who will be assigned to the peer review.

  • data.template (Object) – The template object containing the configuration for the peer review.

  • data.documents (Array) – The documents to be reviewed.

  • options (Object) – holds the managed transaction of the database (see createSocket function)

Returns:

Promise.<void> – A promise that resolves when the peer review has been assigned.

AssignmentSocket.createAssignmentBulk(data, options)

Socket Event: assignmentCreateBulk

Creates multiple assignments based on the provided data.

Two assignment modes are supported: - “role”: reviewers are grouped by their roles, and documents are assigned to users in each role. - “reviewer”: reviewers are explicitly selected, and assignments are distributed to them directly.

In both cases, the function ensures: - A reviewer never reviews their own document. - Fair distribution of review tasks. - A fallback swapping mechanism is used if optimal assignment fails.

Arguments:
  • data – The data for creating assignments.

  • data.template (Object) – The template to be used for the assignments.

  • data.selectedReviewer (Array.<Object>) – An array of reviewer objects to be assigned to the assignments.

  • data.selectedAssignments (Array.<Object>) – An array of assignment objects to be reviewed.

  • data.mode (String) – The mode of the assignment creation (i.e, role or reviewer)

  • data.documents (Array.<Array>) – List of document assignments

  • data.roleSelection (Object) – If the mode is role, the role selection object

  • data.reviewerSelection (Object) – If the mode is reviewer, the reviewer selection object

  • options – holds the managed transaction of the database (see createSocket function), passed down to the individual assignment creation step.

Throws:

Error – Throws an error under several conditions: If an invalid data.mode is provided, In ‘role’ mode: if no users are found for a selected role, In ‘role’ mode: if there are not enough unique documents to satisfy the assignment requirements for a role or a specific user, In ‘role’ mode: if the algorithm is unable to find a valid assignment for a user, even after attempting to swap, In ‘reviewer’ mode: if the algorithm cannot assign all reviewers after attempting to swap, If the underlying this.createAssignment method fails.

Returns:

Promise.<void> – A promise that resolves with an object detailing the final assignment distribution.

AssignmentSocket.getAssignmentInfoFromCourse(data)

Socket Event: assignmentGetInfo

Retrieve all the assignments a course has.

Arguments:
  • data (Object) – The data required for getting the relevant assignment info.

  • data.options (Object) – The options object containing the API key and URL of the Moodle instance.

  • data.options.courseID (number) – The ID of the course to fetch users from.

  • data.options.apiKey (string) – The API token for the Moodle instance

  • data.options.apiUrl (string) – The URL of the Moodle instance.

Returns:

Promise.<ArrayLike.<T>> – A promise that resolves with an array of assignment objects from Moodle.

AssignmentSocket.replaceTemplateValues(config, context, options)

Recursively processes template markers in a configuration object and adds appropriate ID properties.

This method processes configuration objects, arrays, and nested structures, identifying template markers (objects with isTemplate === true) and adding the appropriate ID property based on the assignment type. For submission assignments, it adds submissionId; for document assignments, it adds documentId. The original template object properties are preserved.

Arguments:
  • config (Object|Array) – The configuration object or array to process. Can contain nested objects and arrays.

  • context (Object) – The context object containing assignment information.

  • context.assignmentType (string) – The type of assignment (‘submission’ or ‘document’).

  • context.documentId (number|null) – The document ID to use when assignmentType is ‘document’.

  • context.submissionId (number|null) – The submission ID to use when assignmentType is ‘submission’.

  • options (Object) – The options object (currently unused but kept for API consistency).

Returns:

Promise.<(Object|Array)> – A promise that resolves with the configuration object with ID properties added to template markers.

Collab Socket Functions (collab.js)

class CollabSocket()

Handle collaboration through sockets

CollabSocket.updateCollab(data, options)

Socket Event: collabUpdate

Updates the collaboration status in the database. If there is non existent on the given entity, it will create one, otherwise it will be updated.

Arguments:
  • data (Object) – the input collab object

  • data.collabId (number) – the id of the collaboration if existent

  • data.documentId (number) – The ID of the document being collaborated on. Required when creating a new record.

  • options (Object) – Additional configuration, primarily for database transactions.

  • options.transaction (Object) – A Sequelize DB transaction object to ensure atomicity.

Throws:

Error – Throws an error if the database updateById or add operations fail.

Returns:

Promise.<void> – A promise that resolves once the database operation is complete and events have been emitted.

Comment Socket Functions (comment.js)

class CommentSocket()

Loading the comments through websocket

CommentSocket.addComment(data, options)

Adds a new comment to the database after verifying user permissions.

Arguments:
  • data (Object) – The data required to create the new comment.

  • data.documentId (number) – The ID of the document the comment belongs to.

  • data.studySessionId (number) – The ID of the study session associated with the comment.

  • data.studyStepId (number) – The ID of the study step associated with the comment.

  • data.text (string) – The content of the comment.

  • data.userId (number|string) – The ID or name of the user creating the comment. Defaults to the current user if omitted.

  • data.draft (boolean) – Whether the comment is a draft (defaults to true if not provided).

  • data.annotationId (number) – The ID of the associated annotation, if any.

  • data.parentCommentId (number) – The ID of the parent comment, if this is a reply.

  • data.anonymous (boolean) – Whether the comment should be anonymous (defaults to false).

  • options (Object) – Additional configuration parameters.

Throws:

Error – Throws an error if the user is not authorized to add the comment (permission check failure), or if any database operation fails.

Returns:

Promise.<void> – A promise that resolves (with no value) once the comment is successfully added and the event is emitted.

CommentSocket.addOrUpdateComment(data, options)

Socket Event: commentUpdate

Add or update a comment if it has already existed.

Calls either addComment or updateComment based on the presence of commentId.

Arguments:
  • data (object) – The input data from the frontend

  • data.commentId (number) – The id of the comment

  • options (object) – holds the transaction

Throws:

Error – Throws an error if the underlying updateComment or addComment function fails. This can be due to permission violations or database-level errors.

Returns:

Promise.<void> – A promise that resolves (with no value) once the underlying add or update operation is complete.

CommentSocket.getCommentsByDocument(data, options)

Socket Event: commentGetByDocument

Retrieve all comments associated with a specific document.

Fetches comments by document ID and sends them to the client.

Arguments:
  • data (object) – The input data from the frontend

  • data.documentId (number) – The ID of the document whose comments are to be fetched.

  • options (object) – Additional configuration parameters, not used

Throws:

Error – Throws an error if the database operation to fetch the comments fails.

Returns:

Promise.<void> – A promise that resolves (with no value) once the comments have been successfully fetched and sent to the client.

CommentSocket.sendComment(data, options)

Socket Event: commentGet

Send a comment to the client.

Fetches a single comment by its ID and sends it to the client after verifying access rights.

Arguments:
  • data (object) – The input data from the frontend

  • data.commentId (number) – The id of the comment

  • options (object) – not used

Throws:

Error – Throws an error if the user does not have access to the document the comment is on, or if the comment ID does not exist in the database.

Returns:

Promise.<void> – A promise that resolves after the comment has been successfully sent to the client.

CommentSocket.updateComment(data, options)

Update comments in the database after verifying the user has permission to edit it.

Arguments:
  • data (Object) – containing the inputs

  • data.commentId (number) – the id of the comment

  • data.comment (object) – the comment object

  • options (object) – containing transaction

Throws:

Error – Throws an error if the user is not allowed to edit the comment, or if any database operation fails.

Returns:

Promise.<void> – A promise that resolves once the comment has been updated and the notification has been sent.

Document Socket Functions (document.js)

class DocumentSocket()

Handle all document through websocket

Loading the document through websocket

DocumentSocket.addDocument(data, options)

Uploads the given data object as a document.

Stores the given pdf file in the files path and creates an entry in the database.

Arguments:
  • data (Object) – The data object containing the document details.

  • data.name (string) – The name of the document.

  • data.file (Buffer) – The binary content of the document.

  • data.importAnnotations (boolean) – indicates whether to import annotations from the PDF (optional).

  • data.submissionId (number) – The submission that the document will belong to.

  • data.userId (number) – The ID of the user who owns the document (optional).

  • data.projectId (number) – The ID of the project the document belongs to (optional).

  • data.isUploaded (boolean) – Indicates if the document is uploaded by an admin (optional).

  • options (Object) – The options object containing the transaction.

Returns:

Promise.<void>

DocumentSocket.checkDocumentAccess(documentId)

Check if user has rights to read the document data

The user has access to the document if:

  • The document is public

  • The document is owned by the user

  • The user is an admin

  • The document is used in a study where the user is a participant

Arguments:
  • documentId – The ID of the document for which access is being checked.

Returns:

Promise.<boolean> – A promise that resolves with true if the user has access, and false otherwise.

DocumentSocket.checkSubmissionsExist(data, options)

Check a list of submissions if they have already existed in the database by extId

Arguments:
  • data – The data object containing the submissions to check at least extId key is required

  • options – The options object

Returns:

Promise.<Array.<Object>> – An array of objects containing the status of the submissions

DocumentSocket.closeDocument(data, options)

Close the document and save it if necessary.

This method saves the document if there is no study session and removes the document from the list of open documents.

Arguments:
  • data (object) – The data object containing documentId and studySessionId.

  • data.documentId (number) – The ID of the document to close.

  • data.studySessionId (number) – The ID of the study session.

  • options (object) – Additional configuration parameters.

Returns:

Promise.<void> – A promise that resolves (with no value) once the document has been processed.

DocumentSocket.createDocument(data, options)

Creates a new HTML-based document record in the database.

Arguments:
  • data (Object) – The data for the new document.

  • data.name (string) – The name of the new document.

  • data.type (number) – The type identifier for the document (e.g., HTML, MODAL).

  • options (Object) – The options object containing the transaction.

Returns:

Promise.<Object> – A promise that resolves with the newly created document’s database record.

DocumentSocket.documentGetMoodleSubmissions(data, options)

Get Moodle submissions from an assignment. This function acts as a wrapper, forwarding the request to the MoodleRPC service.

Arguments:
  • data (Object) – The data required for fetching the submission information.

  • data.options (Object) – The configuration object for the Moodle API connection.

  • data.options.courseID (number) – The ID of the Moodle course.

  • data.options.assignmentID (number) – The ID of the Moodle assignment.

  • data.options.apiKey (string) – The Moodle API token required for authentication.

  • data.options.apiUrl (string) – The base URL of the Moodle instance.

  • options (Object) – Additional configuration parameters (currently unused).

Returns:

Promise.<Array.<Object>> – A promise that resolves with an array of submission objects returned from the Moodle service.

DocumentSocket.downloadMoodleSubmissions(data, options)

Downloads multiple submission files from Moodle URLs, creating a local document record for each one. Each file is processed in its own database transaction to ensure atomicity. Progress is reported to the client via a socket event after each file is processed.

Arguments:
  • data (Object) – The input data from the frontend

  • data.submissions (Array.<Object>) – The submissions from Moodle

  • data.options (Object) – The configuration options (e.g., API key, URL) passed to the Moodle RPC service

  • data.progressId (string) – The unique ID used for reporting progress back to the frontend.

  • data.group (number) – The group number to be assigned to the submissions

  • data.validationConfigurationId (number) – Configuration ID referring to the validation schema

  • options (Object) – Additional configuration parameters

  • options.transaction (Object) – Sequelize DB transaction options

Throws:

Error

  • If the download fails, if the assignment ID is invalid, or if saving to server fails

Returns:

Promise.<Array.<T>> – - The results of the processed submissions

DocumentSocket.editDocument(data, options)

Socket Event: documentEdit

Edits the document based on the provided data.

This method is called when the client requests to edit a document. It first checks if the user has access to the document, and if so, it applies the edits to the document and sends the updated document to the client.

Arguments:
  • data (Object) – The data payload containing the edits and their context.

  • data.documentId (number) – The ID of the document being edited.

  • data.ops (Array.<Object>) – An array of edit operations, where each object represents a single change (e.g., insert, delete).

  • data.studySessionId (number) – If provided, associates the edits with a study session and suppresses the client-side event emission.

  • data.studyStepId (number) – If provided, associates the edits with a specific study step.

  • options (Object) – The options object containing the transaction.

  • options.transaction (Object) – A Sequelize DB transaction object to ensure all edits are added atomically.

Returns:

Promise.<void> – A promise that resolves (with no value) once all edits have been processed and saved.

DocumentSocket.getData(data, options)

Fetches and sends a comprehensive set of data related to a document, with behavior that varies significantly based on the document type and user context.

For HTML documents, it defers to this.getDocument. For other types, it sends annotations, comments, votes, and tags based on the following logic:

  • If a studySessionId is provided and the study is collaborative, it sends data from ALL participants.

  • If a studySessionId is provided and the study is NOT collaborative, it sends data for the CURRENT session only.

  • If no studySessionId is provided, it sends data from closed studies or data not linked to any session.

Arguments:
  • data (Object) – The request data specifying the context.

  • data.documentId (number) – The ID of the document to fetch data for.

  • data.studySessionId (number) – The ID of the current study session, if applicable.

  • data.studyStepId (number) – The ID of the current study step, required when a studySessionId is provided.

  • options (Object) – Additional configuration parameters (passed down to sub-methods).

Returns:

Promise.<void> – A promise that resolves (with no value) once all relevant data has been fetched and emitted to the client.

DocumentSocket.getDocument(data, options)

Send a document to the client

This method checks if the user has access to the document and then retrieves and sends the document data. For HTML documents, it fetches and combines draft edits with the existing content before sending.

Arguments:
  • data (Object) – The data required to fetch the document and its specific version.

  • data.documentId (number) – The ID of the document to retrieve.

  • data.studySessionId (number) – The ID of the study session, used to scope document edits.

  • data.studyStepId (number) – The ID of the study step, used to scope document edits.

  • data.history (boolean) – If true, emits the edit history instead of returning composed content.

  • options (Object) – Additional configuration parameters.

  • options.transaction (Object) – A Sequelize DB transaction object (passed to underlying functions).

Throws:

Error – Throws an error under the following conditions: If the user does not have access to the requested document, If the document’s delta file (.delta) for an HTML document is missing from the filesystem, If the document’s PDF file (.pdf) for a PDF document is missing from the filesystem.

Returns:

Promise.<({document: Document, deltas: Delta}|{document: Document, file: Buffer})> – A promise that resolves with an object containing the document metadata and its content, which is either a Quill Delta object for HTML types or a file Buffer for other types.

DocumentSocket.getDocumentData(data, options)

Retrieve document data for a particular document/study_session/study_step from the document_data table.

Arguments:
  • data (Object) – The data payload containing the retrieval parameters.

  • data.documentId (number) – The ID of the associated document.

  • data.studySessionId (number) – The ID of the associated study session.

  • data.studyStepId (number) – The ID of the associated study step.

  • data.key (string) – The key for the data being retrieved (e.g., ‘assessment_results’).

  • options (Object) – Additional configuration for the operation.

  • options.transaction (Object) – A Sequelize DB transaction object to ensure atomicity.

Returns:

Promise.<Object> – A promise that resolves with the retrieved document_data record object from the database.

DocumentSocket.getPreviousStepId(studyStepId)

Helper method to get the previous step ID for a given study step ID

Arguments:
  • studyStepId (number) – The ID of the study step

Returns:

Promise.<(number|null)> – The ID of the previous study step, or null if not found

DocumentSocket.loadDocument(filePath)

Load document delta from disk (for HTML documents) This method reads the delta file from the disk and returns it as a Delta object.

Arguments:
  • filePath (string) – The absolute path to the delta file to be loaded.

Throws:

Error – Throws an error if the file cannot be read (e.g., file not found, permissions error) or if the file content is not valid JSON.

Returns:

Promise.<Delta> – A promise that resolves with a new Delta object representing the file’s content.

DocumentSocket.openDocument(data, documentId, options)

Open the document and track it, if not already tracked

This method adds the document to the list of open documents, being tracked by the socket.

Arguments:
  • data (object) – The data object containing the documentId.

  • documentId (number) – The ID of the document to open and track.

  • options (object) – Additional configuration parameters

Returns:

Promise.<void> – A promise that resolves (with no value) once the document is being tracked.

DocumentSocket.publishDocument(data, options)

Socket Event: documentPublish

Makes a document publicly accessible by setting its ‘public’ flag to true. This operation is only permitted if the current user has access rights to the document’s owner. Upon success, it emits a ‘documentRefresh’ event with the updated document.

Arguments:
  • data (object) – The data object containing the document identifier.

  • data.documentId (number) – The ID of the document to publish.

  • options (object) – The options object containing the transaction.

Throws:

Error – Throws an error if the user does not have permission to publish the document, or if any underlying database operation fails.

Returns:

Promise.<void> – A promise that resolves (with no value) once the document is successfully published and the event is emitted.

Uploads review links to a Moodle assignment as feedback comments.

Arguments:
  • data (Object) – The data required for uploading login data.

  • data.options (Object) – The options object containing the API key and URL of the Moodle instance.

  • data.options.courseID (number) – The ID of the course to fetch users from.

  • data.options.assignmentID (number) – The ID of the Moodle assignment.

  • data.options.apiKey (string) – The API token for the Moodle instance

  • data.options.apiUrl (string) – The URL of the Moodle instance.

  • data.feedback (Array.<Object>) – An array of objects containing the feedback to send

Returns:

Promise.<Object> – A promise that resolves when the passwords have been uploaded.

DocumentSocket.refreshAllDocuments(data, options)

Refresh all documents. Fetches a list of documents and emits them to the client via a ‘documentRefresh’ event. The scope of the documents sent depends on the user’s administrative rights and the provided parameters.

  • Non-admins will only receive their own documents.

  • Admins can receive all documents, or filter for a specific user’s documents.

Arguments:
  • data (Object) – The data object containing the request parameters.

  • data.userId (number) – For administrators only. If provided, fetches documents belonging to this specific user ID. If omitted, all documents are fetched.

  • options (Object) – Additional configuration parameters (currently unused).

Returns:

Promise.<void> – A promise that resolves (with no value) once the document list has been successfully fetched and emitted.

DocumentSocket.saveData(data, options)

Save additional document data for a particular document/study_session/study_step like the nlpResults, links etc., to the document_data table.

Arguments:
  • data (Object) – The data payload to be saved.

  • data.documentId (number) – The ID of the associated document.

  • data.studySessionId (number) – The ID of the associated study session.

  • data.studyStepId (number) – The ID of the associated study step.

  • data.key (string) – The key for the data being stored (e.g., ‘nlpResults’).

  • data.value (any) – The value to be stored, which can be any serializable type.

  • options (Object) – Additional configuration for the operation.

  • options.transaction (Object) – A Sequelize DB transaction object to ensure atomicity.

Returns:

Promise.<Object> – A promise that resolves with the upserted document_data record object from the database.

DocumentSocket.saveDocument(documentId)

Save document delta to disk and mark edits as applied (for HTML documents) This method saves the combined delta of the document on the disk and updates the edits in the database to mark them as applied.

Arguments:
  • documentId (number) – The ID of the document to save.

Throws:

Error – Throws an error if: The document is not of a supported type (HTML or MODAL), Reading from or writing to the filesystem fails for reasons other than the initial file not existing, Any of the underlying database operations (getById, findAll, update) fail.

Returns:

Promise.<void> – A promise that resolves (with no value) upon successful completion. Note: The function returns early without error if the document ID is not found.

DocumentSocket.sendByHash(data, options)

Socket Event: documentGetByHash

Send document by hash.

Fetches a document by its hash, checks for user access, and then either sends the document or a “toast” error message to the client.

Arguments:
  • data (object) – The data object containing the document hash.

  • data.documentHash (string) – The hash of the document to send.

  • options (object) – The options object containing the transaction.

Returns:

Promise.<void> – A promise that resolves (with no value) once the operation (either sending the document or a toast) is complete.

DocumentSocket.sendDocumentDeltas(data, options)

Send merged deltas (from disk and database) to client (for HTML documents)

Arguments:
  • data (object) – The request data containing the document identifier.

  • data.documentId (number) – The ID of the document to send deltas for.

  • options (object) – The options for the transaction.

Throws:

Error – Throws an error if: The user does not have access to the document, The document is not of a supported type (HTML or MODAL).

Returns:

Promise.<void> – A promise that resolves with the final, composed Delta object representing the document’s current state.

DocumentSocket.subscribeDocument(data, options)

Subscribe the client’s socket to a document-specific communication channel.

Arguments:
  • data (Object) – The data object containing the document identifier.

  • data.documentId (number) – The ID of the document to subscribe to.

  • options (Object) – The options object containing the transaction.

Returns:

Promise.<void> – A promise that resolves (with no value) once the subscription command has been executed.

DocumentSocket.unsubscribeDocument(data, options)

Unsubscribe from a document

Arguments:
  • data (Object)

  • data.documentId (number) – The ID of the document to unsubscribe from.

  • options (Object) – The options object containing the transaction.

Returns:

Promise.<void>

DocumentSocket.updateDocument(data, options)

Updates a document’s properties after verifying the current user has ownership rights.

Arguments:
  • data – The data object containing the new document object.

  • data.id (number) – The ID of the document to be updated.

  • options – The options object containing the transaction.

Returns:

Promise.<void> – A promise that resolves (with no value) once the update operation is complete and the ‘afterCommit’ hook is registered.

DocumentSocket.uploadSingleSubmission(data, options)

Upload a single submission to the DB.

Arguments:
  • data (Object) – The input data from the frontend

  • data.userId (number) – The ID of the user who owns the submission

  • data.files (Array.<Object>) – The submissions files

  • data.group (number) – The group number to be assigned to the submissions

  • data.validationConfigurationId (number) – Configuration ID referring to the validation schema

  • options (Object) – Additional configuration parameters

  • options.transaction (Object) – Sequelize DB transaction options

Throws:

Error

  • If the upload fails, or if saving to server fails

Returns:

Promise.<Array.<T>> – - The result of the processed submission

Logger Socket Functions (log.js)

class LoggerSocket()

Handle logs through websocket

LoggerSocket.getAllLogs(data, options)

Socket Event: logGetAll

Get log messages.

This operation is restricted to users with administrator privileges.

Arguments:
  • data – The log entry selection criteria

  • data.filter – An object defining filters for the log entries (e.g., by level or source).

  • data.order – An array specifying the order of the results.

  • data.limit – The maximum number of log entries to return.

  • data.page – Log entry page

  • options – Additional configuration parameters.

Returns:

Promise<void>} A promise that resolves (with no value) once the logs have been sent to the client, or immediately if the user is not an admin.

LoggerSocket.log(data, options)

Socket Event: log

Log a message received from the client, if frontend logging is enabled via environment variables

Arguments:
  • data (Object) – The log entry payload.

  • data.level (string) – The log level (e.g., ‘info’, ‘warn’, ‘error’).

  • data.message (string) – The primary message to be logged.

  • data.meta (Object) – Optional metadata to include with the log entry.

  • options (Object) – Additional configuration parameters.

Returns:

Promise.<void> – A promise that resolves (with no value) once the log attempt is complete.

Service Socket Functions (service.js)

class ServiceSocket()

Handling Services through websockets

ServiceSocket.connectService(data, options)

Socket Event: serviceConnect

Connects a service to the client

Arguments:
  • data (Object) – The input data

  • data.service (string) – The name of the service to connect

  • data.data (Object) – Additional data to pass to the service

  • options (Object) – Additional configuration parameters (currently unused).

Returns:

Promise.<void> – A promise that resolves (with no value) once the service connection attempt is complete.

ServiceSocket.disconnectService(data, options)

Socket Event: serviceDisconnect

Disconnects a service from the client

Arguments:
  • data (Object) – The input data

  • data.service (string) – The name of the service to disconnect

  • data.data (Object) – Additional data to pass to the service

  • options (Object) – Additional configuration parameters (currently unused).

Returns:

Promise.<void> – A promise that resolves (with no value) once the service disconnection attempt is complete.

ServiceSocket.requestService(data, options)

Socket Event: serviceRequest

Request a service (with default command)

Arguments:
  • data (Object) – The input data

  • data.service (string) – The name of the service to disconnect

  • data.data (Object) – Additional data to pass to the service

  • options (Object) – Additional configuration parameters (currently unused).

Returns:

Promise.<void> – A promise that resolves (with no value) once the service request attempt is complete.

ServiceSocket.serviceCommand(data, options)

Socket Event: serviceCommand

Request a service but with a specific command

Arguments:
  • data (Object) – The input data

  • data.service (string) – The name of the service to disconnect

  • data.data (Object) – Additional data to pass to the service

  • options (Object) – Additional configuration parameters (currently unused).

Returns:

Promise.<void> – A promise that resolves (with no value) once the service command attempt is complete.

Setting Socket Functions (setting.js)

class SettingSocket()

Handle settings through websocket

SettingSocket.saveSettings(data, options)

Socket Event: settingSave

Save settings to the database

Arguments:
  • data (Array.<{key: string, value: any}>) – List of settings to be saved

  • options (object) – Context passed through the socket pipeline

  • options.transaction (Object) – A Sequelize DB transaction object to ensure all settings are saved atomically.

Throws:

Error – Throws an error if the requesting user is not an administrator.

Returns:

Promise.<string> – A promise that resolves with a success message once the save operations are queued within the transaction.

SettingSocket.sendSettings(data, options)

Socket Event: settingGetData

Fetches all system settings from the database. This operation is restricted to users with administrator privileges.

Arguments:
  • data (any) – Currently unused.

  • options (object) – Additional configuration parameters (currently unused).

Throws:

Error – Throws an error if the requesting user is not an administrator.

Returns:

Promise.<Array.<{key: string, value: any}>> – All settings in flat key-value format

Statistic Socket Functions (statistic.js)

class StatisticSocket()

Add statistics about the website usage

Therefore, use in frontend:

this.$socket.emit(“stats”, {action: “action”, data: {}});

The data object can hold additional information!

StatisticSocket.addStats(data, options)

Socket Event: stats

Adds a new statistic entry to the database for the current user. This function is only performed if the user has consented to statistics collection (acceptStats is true). Errors during the database operation are caught and logged internally.

Arguments:
  • data (Object) – The data object containing the userId

  • data.action (Number) – The type of action (e.g. ‘mouseMove’)

  • options (Object) – Additional configuration parameters (currently unused).

Returns:

Promise.<void> – A promise that resolves (with no value) once the statistic has been processed.

StatisticSocket.getStats(data, options)

Socket Event: statsGet

Fetches system statistics, either for all users or a specific user. This function is restricted to users with administrator privileges.

Arguments:
  • data (Object) – The data object containing the userId

  • data.userId (Number) – The userId to get statistics for (optional)

  • options (Object) – Additional configuration parameters (currently unused).

Throws:

Error – Throws an error if the requesting user is not an administrator.

Returns:

Promise.<Object> – A promise that resolves with an array of statistic record objects from the database.

StatisticSocket.sendStatsByUser(data, options)

Send statistics to the user This function is restricted to administrators and will only send data if the target user has consented to statistics collection (acceptStats is true).

Arguments:
  • data.userId (number) – The ID of the user whose statistics are to be fetched.

  • options (Object) – Additional configuration parameters (currently unused).

Returns:

Promise.<void> – A promise that resolves (with no value) after the operation is complete.

Study Session Socket Functions (study_session.js)

class StudySessionSocket()

Handle all study sessions through websocket

Loading the study sessions through websocket

StudySessionSocket.sendSessionsByStudyId(studyId)

Send all study sessions to the client If the user has access, it emits a ‘study_sessionRefresh’ event with the session data. If access is denied, it sends a toast notification to the client with an error message.

Arguments:
  • studyId – The ID of the study whose sessions are to be fetched and sent.

Returns:

Promise.<void> – A promise that resolves (with no value) once the sessions have been sent or the access-denied notification has been sent.

StudySessionSocket.startStudySession(data, options)

Socket Event: studySessionStart

Start a study session by either updating the start time of an existing session or creating a new one.

Arguments:
  • data (object) – The data required to start the session.

  • data.studyId (number) – The ID of the study to create a new session for. Required if studySessionId is not provided.

  • data.studySessionId (number) – The ID of an existing study session to update. If omitted, a new session is created.

  • options (object) – Configuration for the database operation.

  • options.transaction (Object) – A Sequelize DB transaction object to ensure atomicity.

Returns:

Promise.<void> – A promise that resolves with the newly created or updated study session object from the database.

StudySessionSocket.subscribeToStudySession(data, options)

Socket Event: studySessionSubscribe

Subscribes the client to a study-specific communication channel and sends the initial list of sessions. This allows the client to receive real-time events for the study and get the current state.

Arguments:
  • data (object) – The data object containing the study identifier.

  • data.studyId (number) – The ID of the study to subscribe to.

  • options (object) – Additional configuration parameters (currently unused).

Returns:

Promise.<void> – A promise that resolves (with no value) once the client has subscribed and the initial session data has been sent.

StudySessionSocket.unsubscribeFromStudySession(data, options)

Socket Event: studySessionUnsubscribe

Unsubscribes the client’s socket from a study-specific communication channel. This stops the client from receiving real-time events for that study.

Arguments:
  • data (object) – The data object containing the study identifier.

  • data.studyId (number) – The study id

  • options (object) – Additional configuration parameters (currently unused).

Returns:

Promise.<void> – This function does not return a value.

Study Socket Functions (study.js)

class StudySocket()

Handle all studies through websocket

Loading the studies through websocket

StudySocket.closeBulk(data, options)

Socket Event: studyCloseBulk

Closes all studies associated with a given project ID in a loop. Each study is updated in its own database transaction. Progress is reported to the client after each study is processed.

Arguments:
  • data – The data required for the bulk close operation.

  • data.projectId – the project ID of the studies to close

  • data.ignoreClosedState – if true, also close studies that are already closed

  • data.progressId – the ID of the progress bar to update

  • options – Additional configuration parameters (currently unused).

Returns:

Promise.<void> – A promise that resolves (with no value) once all studies in the project have been processed.

StudySocket.saveStudyAsTemplate(data, options)

Socket Event: studySaveAsTemplate

Creates a new study template based on an existing study or directly from data. This operation is restricted to the owner of the original study or an administrator.

Arguments:
  • data (object) – The data object containing the identifier for the source study or template data.

  • data.id (number) – the ID of the study to save as template (required if onlyTemplate is false)

  • data.onlyTemplate (boolean) – if true, creates template directly from provided data without creating a study

  • data.templateData (object) – the template data when onlyTemplate is true

  • options (object) – Configuration for the database operation.

  • options.transaction (Object) – A Sequelize DB transaction object.

Throws:

Error – Throws an error if the user does not have permission to access the source study.

Returns:

Promise.<*> – A promise that resolves with the newly created study template object from the database.

User Socket Functions (user.js)

class UserSocket()

Handle user through websocket

UserSocket.bulkCreateUsers(data, users, roleMap)

Socket Event: userBulkCreate

Creates or updates a list of users in bulk. Each user is processed in an isolated database transaction. Errors for individual users are caught, logged, and added to an error array without halting the entire process. Progress is reported to the client.

Arguments:
  • data (Object) – The data object containing the users and role map

  • users – Users to be created or updated

  • roleMap (Object) – A role map that maps an external platform roles to CARE roles

Returns:

Promise.<{createdUsers: Array, errors: Array}> – An object containing the created users and errors

UserSocket.checkUsersExists(data, options)

Socket Event: userCheckExistsByMail

Check a list of users if they already exist in the database by email

Arguments:
  • data – The data object containing the users to check at least email key is required

  • options – The options object

Returns:

UserSocket.createUser(data, options)

Socket Event: userCreate

Adds a new user to the database. This operation is restricted to users with administrator privileges.

Arguments:
  • data – An object containing the properties for the new user record

  • options – Configuration for the database operation.

Throws:

Error – Throws an error if the requesting user is not an administrator.

Returns:

Promise.<*> – A promise that resolves with the newly created user object from the database.

UserSocket.getUsers(role)

Get users by their role.status === “duplicate”

Arguments:
  • role (string) – The role of the users to fetch. Possible values: “student”, “mentor”, “all”

Returns:

Promise.<(Array.<Object>|void)> – A promise that resolves with an array of user objects if successful, or with no value if the user lacks permission or an error occurs.

UserSocket.getUsersByRole(data, options)

Socket Event: userGetByRole

Get users by their role

Arguments:
  • data (Object) – The input data from the frontend

  • data.role (string) – The role of the user

  • options (Object) – Additional configuration parameter

  • options.transaction (Object) – Sequelize DB transaction options

Throws:

Error – If the user does not have right

Returns:

Promise.<void> – A promise that resolves (with no value) once the data or an error message has been emitted.

UserSocket.getUsersFromCourse(options)

Socket Event: userMoodleUserGetAll

Retrieves a list of users from a Moodle course via an RPC call. The returned user data is then augmented with an external ID and checked for local existence in the database. The external moodle API is called here through Moodle RPC

Arguments:
  • options (Object) – The data object containing the course ID, Moodle URL and the API token.

  • options.courseID (number) – The ID of the course to fetch users from.

  • options.options.apiKey (string) – The API token for the Moodle instance

  • options.options.apiUrl (string) – The URL of the Moodle instance.

Returns:

Promise.<*> – The response from the RPC service

UserSocket.minimalFields(user)

Filters a user object to include only a predefined set of essential fields. This is useful for sending minimal, non-sensitive user information to clients.

Arguments:
  • user (object) – The user object

Returns:

Object – A new object containing only the allowed fields (e.g., ‘id’, ‘userName’) from the original user object.

UserSocket.resetUserPwd(data, options)

Socket Event: userResetPwd

Reset user’s password A user can only reset their own password unless they are an administrator.

Arguments:
  • data (Object) – The input data from the frontend

  • data.userId (number) – The ID of the user

  • data.password (string) – The new password

  • options (Object) – Additional configuration parameter

  • options.transaction (Object) – Sequelize DB transaction options

Throws:

Error – If the user is not an admin or the user tries to reset other’s password

Returns:

Promise.<void> – A promise that resolves (with no value) once the password has been successfully reset.

UserSocket.updateCreatorName(data, key="userId", targetName="creator_name")

Adds the username as creator_name of a database entry with column creator

Accepts data as a list of objects or a single object Note: Always returns a list of objects

Arguments:
  • data (object|Array.<object>) – The data to update

  • key (string) – The key of the user ID field

  • targetName (string) – The name of the target field

Returns:

UserSocket.updateUserConsent(data, options)

Socket Event: userConsentUpdate

Update user’s consent data It first verifies the user exists before proceeding with the update.

Arguments:
  • data (Object) – The consent data object

  • data.acceptTerms (boolean) – Indicates whether the user has consented to the terms of service

  • data.acceptStats (boolean) – Indicates whether the user has agreed to tracking

  • data.acceptDataSharing (boolean) – Indicates whether the user has agreed to donate their annotation data

  • data.acceptedAt (string) – Time when the user made the consent

  • options (Object) – Sequelize transaction options

  • options.transaction (Object) – A Sequelize DB transaction object.

Throws:

Error – Throws an error if the current user cannot be found in the database.

Returns:

Promise.<Object> – A promise that resolves with the updated user object, excluding sensitive and timestamp fields.

UserSocket.userPublishMoodle(data)

Socket Event: userPublishMoodle

Uploads login data to a Moodle assignment as feedback comments through Moodle RPC.

Arguments:
  • data (Object) – The data required for uploading login data.

  • data.options (Object) – The options object containing the API key and URL of the Moodle instance.

  • data.options.courseID (number) – The ID of the course to fetch users from.

  • data.options.assignmentID (number) – The ID of the Moodle assignment.

  • data.options.apiKey (string) – The API token for the Moodle instance

  • data.options.apiUrl (string) – The URL of the Moodle instance.

  • data.users (Array.<Object>) – An array of objects containing the uploaded users.

Returns:

Promise.<Object> – A promise that resolves when the passwords have been uploaded.