Table of Contents
Introduction
We provide Sign In and Sign Up options, allowing users to create an account and access the TrustedCloud dashboard to submit onboarding requests.
Once a user is successfully authenticated, they are redirected to one of four dashboards based on their assigned role:
- Admin Dashboard
 - Applicant & Approver Dashboard
 - Applicant & Auditor Dashboard
 - Applicant Dashboard
 
Note:
Upon initial registration in TrustedCloud, users are assigned the default Applicant role. Administrators can later modify user roles and grant additional access privileges as needed.
Applicants can submit onboarding requests through the system. Approvers and Auditors are responsible for reviewing these requests. They can either approve or reject them and provide comments for clarification or feedback.
Role-Based Dashboard Access
1. Admin Dashboard
Can view requests in:
Pending, Audited, Approved, RejectedCan access the list of all approved requests that exist in the Service List
2. Approver Dashboard
Can view requests in:
Audited, ApprovedCan see only rejected requests that were rejected by Approvers, not those rejected by Auditors
3. Auditor Dashboard
- Can view requests in: 
Pending, Audited, Rejected 
4. Applicant Dashboard
Can view requests in: Pending, Audited, Approved, Rejected
Overview
our authentication system consists of:
A uibuilder front-end (Vue.js app) for handling Sign In, Sign Up, and Forgot Password screens.
A Node-RED backend flow, connected via uibuilder websockets, for verifying user credentials and managing database queries (in our case, MongoDB Users collection).
This setup enables real-time, stateful communication between the browser and Node-RED — no REST API layer is required because uibuilder handles the WebSocket channel.
1. UIBuilder Frontend Initialization
When the user opens the login page, your Vue app runs this block:
uibuilder.start();
This initializes a WebSocket connection between the browser and Node-RED.
uibuilder automatically assigns each client a unique clientId (and a corresponding uibuilder-client-id cookie).
This clientId allows Node-RED to respond directly to the same user session.
2. Cookie-Based Session Check
When the app starts (mounted() hook), it checks for an existing userToken cookie:
if (this.getCookie("userToken")) {
  uibuilder.send({
    auth: {
      userToken: this.getCookie("userToken"),
      clientId: this.getCookie("uibuilder-client-id"),
    },
  });
}
Purpose:
If the token exists, the backend verifies it and automatically logs the user in (session persistence).
If not, the backend treats it as a fresh session.
3. Sign Up Flow
High-level flow:
Frontend (Vue/React)
      ↓ uibuilder.send
Node-RED Flow
      ↓ MongoDB Users Collection
      ↓ uibuilder.onChange (response)
Frontend Redirect
When the user clicks Sign Up, this runs:
signupButton() {
    const clientId = uibuilder.get("clientId");
    uibuilder.send({
        auth: {
            clientId: clientId,
            fullName: this.fullName,
            email: this.email,
            password: this.password,
            confirmPassword: this.confirmPassword,
            type: "signup"
        }
    });
}
Sample request payload:
{
  "auth": {},
  "clientId": "oD6i_WpIes8pBvWjNXy81",
  "fullName": "hediyeh",
  "email": "h@gmail.com",
  "password": "111111",
  "confirmPassword": "111111",
  "type": "signup",
  "_socketId": "kmN2iMG_VAMQ7WSFAEhJ",
  "topic": "uibuilder",
  "_msgid": "96c8e068f17bef90"
}
Backend Flow:
| Node Name | Type | Responsibility | 
|---|---|---|
Validate Auth Payload | 
Function | Checks payload existence, validates email format, attaches socketId, prepares original payload. | 
User Query | 
Function | Queries Users collection for existing email. | 
Query Users Collection | 
MongoDB | Executes the email query. | 
Check Email Availability | 
Function | Determines if email exists; sets status failed or prepares data for insertion. | 
Evaluate Registration Status | 
Switch | Routes flow based on payload.status (success or failed). | 
Get Last User ID | 
Function | Fetches highest id from Users collection to increment. | 
Fetch Last User | 
MongoDB | Executes query for last user document. | 
Generate New User Data | 
Function | Hashes password, generates roles, constructs profile, prepares insertOne payload. | 
Insert New User | 
MongoDB | Inserts new user document into the Users collection. | 
Registration Success Response | 
Function | Returns success response with redirect URL. | 
set warning | 
Function | Returns failure response when email exists. | 
signin | 
Debug | Logs final response for monitoring. | 
- Node-RED receives the signup data.
 - Validates if the email already exists.
 - Hashes the password before saving to the Users collection.
 - Returns a success message with:
 
{
  "payload": {
    "action": "register",
    "status": "success",
    "URL": "/hediyeh/trustedcloud2"
  }
}
- The frontend receives it and redirects the user to the dashboard.
 
Sample Responses (uibuilder.onChange)
| Status | Response | 
|---|---|
| Success | { "action": "register", "status": "success", "message": "You have registered successfully!", "URL": "https://codenxa.leanea.com/hediyeh/signin" } | 
| Failure | { "action": "register", "status": "failed", "message": "Email already exists. Please use a different email." } | 
MongoDB Users Collection Schema
| Field | Type | Description | 
|---|---|---|
_id | 
ObjectId | Unique document ID | 
clientId | 
string | Unique client identifier | 
fullName | 
string | User full name | 
email | 
string | Unique email | 
password | 
string | Hashed password | 
confirmPassword | 
string | Hashed password (same as password) | 
roles | 
array | Assigned roles (e.g., ["Applicant","Auditor"]) | 
status | 
string | Registration status (not_found, success) | 
profile | 
object | User profile data (avatar, first/last name, contact info) | 
createdAt | 
Date | Timestamp of account creation | 
updatedAt | 
Date | Last update timestamp | 
Sample document:
{
  "_id": "68f830f9d6c4398ea5f5cbdf",
  "clientId": "GiHBKhnJvqNbA6dYRqWHF",
  "fullName": "David Kim",
  "email": "david.kim@brightwave.co",
  "password": "$2b$10$MiGhJqBXNIJhayVOnqG1C...",
  "roles": ["Applicant","Auditor"],
  "status": "not_found",
  "profile": {
    "firstName": "David",
    "lastName": "Kim",
    "avatarUrl": "data:image/jpeg;base64,..."
  },
  "createdAt": "2025-10-22T01:18:49.129Z",
  "updatedAt": "2025-10-28T02:20:04.260Z"
}
Security Considerations
- Passwords are hashed using bcrypt before storage.
 - Emails must be unique; duplicate entries are rejected.
 - The clientId is used to track frontend sessions securely.
 - Ensure HTTPS is enforced for all frontend/backend communications.
 
4. Sign In Flow
High-Level Flow:
Frontend (Vue/React)
      ↓ uibuilder.send
Node-RED Flow
      ↓ MongoDB Users & Tokens Collections
      ↓ uibuilder.onChange (response)
Frontend Redirect / Error Handling
Frontend Sign In Action
When the user clicks the Sign In button, the following function runs:
signinButton() {
    const isEmailValid = this.validateEmail();
    const isPasswordValid = this.validatePassword();
    if (!isEmailValid || !isPasswordValid) {
        return;
    }
    const clientId = uibuilder.get("clientId");
    uibuilder.send({
        auth: {
            clientId: clientId,
            email: this.email,
            password: this.password,
            rememberMe: this.rememberMe,
            type: "login"
        }
    });
}
Sample Request Payload:
{
  "auth": {},
  "clientId": "oD6i_WpIes8pBvWjNXy81",
  "email": "h@gmail.com",
  "password": "111111",
  "rememberMe": false,
  "type": "login",
  "_socketId": "70nLJLlIrgzsiFLpAEhQ",
  "topic": "uibuilder",
  "_msgid": "2e88832acfbcd402"
}
Backend Node-RED Flow
| Node Name | Type | Responsibility | 
|---|---|---|
Prepare Auth Data | 
Function | Validates auth payload, ensures email exists, attaches socketId and saves original payload | 
Prepare Filter for Email Check | 
Function | Creates MongoDB query payload to find user by email | 
Find User by Email | 
MongoDB | Queries Users collection for the given email | 
Check Email & Password | 
Function | Compares entered password with stored hashed password; generates JWT if successful | 
Check Login Result | 
Switch | Routes flow based on payload.status (success or invalidCredentials) | 
Sign JWT | 
JWT Sign | Signs a JWT token using HS256 with 1-hour expiration | 
Save JWT to DB | 
Function | Prepares messages for both MongoDB Tokens collection insert and frontend response | 
Prepare Filter for DB Insert | 
Function | Inserts token into Tokens collection | 
set warning | 
Function | Returns failure response when credentials are invalid | 
login | 
Debug | Logs final response for monitoring | 
Backend Flow Summary
- Node-RED receives login data from the frontend via 
uibuilder.send. - Validates payload and ensures 
emailis provided. - Queries the 
Userscollection for the given email. - If the email exists, compares the entered password against the stored hashed password using 
bcrypt. - If the password matches:
 - Generates a JWT token.
 - Prepares a 
profileobject includinguserId,id,email,roles, andfullName. - Sends response to frontend and inserts token into Tokens collection.
 - If the 
emailorpasswordis invalid, returns a failure message with status invalidCredentials. 
Sample Responses (uibuilder.onChange)
| Status | Response | 
|---|---|
| Invalid Email | { "action": "login", "status": "invalidCredentials", "message": "Email not found." } | 
| Invalid Password | { "action": "login", "status": "invalidCredentials", "message": "Password does not match." } | 
| Success | { "action": "login", "status": "success", "message": "Logged In Successfully" } | 
Security Considerations
- Passwords are stored as bcrypt hashes.
 - JWT tokens are signed with a secret key and expire after 1 hour.
 - Tokens are saved in the Tokens collection for session tracking.
 - Always use HTTPS for communication between frontend and Node-RED.
 - Ensure JWT tokens are stored securely on the client (
cookies with Secure;SameSite=Strict). 
MongoDB Tokens Collection
| Field | Type | Description | 
|---|---|---|
_id | 
ObjectId | Unique document ID | 
email | 
string | User email | 
userId | 
string | MongoDB user _id
 | 
id | 
number | Internal user ID | 
token | 
string | JWT token | 
createdAt | 
Date | Token creation timestamp | 
expiresAt | 
Date | Token expiration timestamp (1 hour by default) | 
Sample Document
{
  "_id": "69024c1bd6c4398ea5f5cd17",
  "email": "david.kim@brightwave.co",
  "userId": "68f830f9d6c4398ea5f5cbdf",
  "id": 1,
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IiJ9...",
  "createdAt": "2025-10-29T17:17:15.326Z",
  "expiresAt": "2025-10-29T18:17:15.326Z"
}
5. Reset Password Flow
High-Level Flow:
[User clicks "Forgot Password"]
        │
        ▼
[Frontend: Sends reset request with email]
        │
        ▼
[Node-RED: Validate Reset Request Input]
        │
        ├── Invalid → Stop flow (missing auth/email)
        └── Valid → Attach socketId & forward for email verification
Frontend Reset Password Action
When a user submits their email in the Forgot Password form, the frontend sends a request via uibuilder:
resetPasswordRequest() {
  const clientId = uibuilder.get("clientId");
  uibuilder.send({
    auth: {
      email: this.email,
      type: "resetPassword"
    },
    _socketId: clientId
  });
}
Sample Request Payload:
{
  "auth": {
    "email": "user@example.com",
    "type": "resetPassword"
  },
  "_socketId": "TtK9mNkl_2Fh8Wx2K3DdW"
}
Backend Node-RED Flow
| Node Name | What It Does | 
|---|---|
| resetPassword(in) | Entry point for the reset password flow; receives incoming reset requests. | 
| Validate Reset Request Input | Checks that msg.auth and msg.auth.email exist, attaches socketId, saves original payload. | 
| Users Collection | Prepares MongoDB find operation on the Users collection using the email. | 
| Find User by Email | Queries the MongoDB Users collection to check if the email exists. | 
| Validate User Existence | Checks the query result, sets msg.payload.status to success or invalidEmail, prepares user data. | 
| Branch by User Status | Routes the flow based on user existence (success → generate code, invalidEmail → failure response). | 
| Generate and Store Reset Code | Generates a 6-digit reset code, inserts a record into PasswordResets collection, sets expiration. | 
| Insert Reset Code Record | Inserts the reset code record into MongoDB. | 
| Compose Reset Email Message | Prepares the email content with the reset code to be sent to the user. | 
| Prepare Success Response | Prepares the response message indicating that the reset code was sent. | 
| Prepare Failure Response | Prepares the response message indicating the email was not found. | 
| validEmail (debug) | Debug node to inspect successful reset responses in the sidebar. | 
| invalidEmail (debug) | Debug node to inspect failed reset responses in the sidebar. | 
Backend Flow Summary
Validate Reset Request Input
- Receives the reset request from the frontend via 
uibuilder.send. - Validates that the 
authobject andemailfield exist. - Attaches 
socketIdand preserves the original payload for tracking. - Stops the flow if validation fails.
 
Prepare Filter for Email Check
- Constructs a MongoDB query: 
{ email: msg.auth.email }. - Prepares the message for the next MongoDB node.
 
Find User by Email
- Queries the 
Userscollection in MongoDB. - Returns an array (
msg.payload) containing matching user(s). - If no user is found → triggers a generic response to prevent enumeration attacks.
 - If a user exists → forwards data to generate a reset token.
 
Generate Reset Token & Save (Next Step in Flow)
- Creates a unique 6-digit code or UUID token.
 - Stores token in 
ResetCodescollection with:userIdemailcreatedAtexpiresAtused: false
 - Prepares payload for sending email.
 
Send Reset Code / Email
- Sends an email to the user with the reset code.
 - Returns a generic success response to the frontend.
 
Sample Responses (uibuilder.onChange)
| status | Response | 
|---|---|
| Valid request | { "status": "ok", "message": "Email validation initiated." } | 
| Missing email | { "status": "error", "message": "Email is required." } | 
MongoDB PasswordResets Collection
| Field | Type | Description | 
|---|---|---|
_id | 
ObjectId | Unique document ID | 
email | 
string | User email | 
userId | 
string | MongoDB _id of the user | 
code | 
string | One-time 6-digit reset code | 
used | 
boolean | Whether code has been used (default false) | 
createdAt | 
Date | Timestamp of token creation | 
expiresAt | 
Date | Expiration timestamp (e.g., 2 minutes) | 
Sample Document
{
  "_id": "69078db8d6c4398ea5f5cd54",
  "code": "994015",
  "email": "li@gmail.com",
  "userId": "69076591d6c4398ea5f5cd48",
  "id": 15,
  "createdAt": "2025-11-02T16:58:32.299+00:00",
  "expiresAt": "2025-11-02T17:00:32.299+00:00",
  "used": false
}
6. sendResetCode Flow
High-Level Flow:
User clicks "Forgot Password" → Enter email
             ↓
       Vue.js Frontend
             ↓ uibuilder.send
      Node-RED Backend Flow
             ↓ MongoDB Users & ResetCodes
             ↓ uibuilder.onChange (response)
       Frontend Shows Verification Code Input
Frontend Reset Password Action
handleDigitInput method
handleDigitInput(index, event) {
    const value = event.target.value;
    if (value && index < 5) {
        this.verificationInputs[index + 1].focus();
    } else if (value && index === 5) {
        const allFilled = this.verificationDigits.every(digit => digit !== '');
        if (allFilled) {
            const code = this.verificationDigits.join('');
            uibuilder.send({
                auth: {
                    type: "sendResetCode",
                    email: this.resetEmail,
                    code: code
                }
            });
            this.showVerificationSuccess = true;
        }
    }
}
- 
verificationDigits.join('')ensures the code is concatenated properly. - 
authpayload structure matches your sample request payload. 
resendCode method
   resendCode() {
            this.verificationDigits = ['', '', '', '', '', ''];
            this.startTimer();
            uibuilder.send({
                auth:{
                    email: this.resetEmail,
                    type: "resendResetPassword",
                }
            })
        },
Sample Request Payload
{
  "auth": {},
  "type": "sendResetCode",
  "code": "373600",
  "email": "h@gmail.com",
  "_socketId": "TQQZkMEJW3yr8AXbAEhi",
  "topic": "uibuilder",
  "_msgid": "563ae2fa2bd25a81"
}
Backend Node-RED Flow
| Node Name | Type | Responsibility / Description | 
|---|---|---|
sendResetCode(in) | 
Link In | Entry point for the send reset code flow; triggers when a reset code request comes in. | 
Validate Auth & Prepare Payload | 
Function | Validates that auth object and email exist; attaches socketId and stores original payload. | 
Prepare DB Query | 
Function | Prepares a MongoDB query to check if a reset code record exists for the email. | 
Find Password Reset Record | 
MongoDB4 | Queries the PasswordResets collection in MongoDB. Returns matching records as an array. | 
Check Reset Record Existence | 
Function | Checks if a record exists; sets status to success if found, invalidEmail if not. Prepares _data for next node. | 
Branch by Status | 
Switch | Routes the flow based on payload.status: success → process code, invalidEmail → failure response. | 
Handle Invalid Email | 
Function | Prepares payload for invalid email or missing code scenarios. | 
link out 48 | 
Link Out | Connects the invalid email path to the main response flow. | 
Prepare DB Query (for code) | 
Function | Prepares MongoDB query to find the specific reset code that is not used and matches the email/code. | 
Find Password Reset Record | 
MongoDB4 | Queries PasswordResets collection to verify the entered code. | 
Check Reset Record Existence | 
Function | Validates if code exists, is unused, and not expired. Sets success, invalidCode, or expired. | 
Branch by Status (code) | 
Switch | Routes flow based on code verification status: success → update used flag, invalidCode or expired → fail. | 
Compare Code & Update Used Flag | 
Function | Prepares updateOne operation to mark the reset code as used in MongoDB. | 
Update Used Flag | 
MongoDB4 | Executes the update operation to mark the reset code as used. | 
Prepare Success Response | 
Function | Prepares the success payload (validCode) for frontend consumption. | 
validCode | 
Debug | Displays success message and payload in the Node-RED sidebar. | 
invalidEmail | 
Debug | Displays invalid email or missing code payload in the sidebar. | 
link out 49/50/51 | 
Link Out | Connects the flow outputs to the main response flow to send back to frontend. | 
Backend Flow Summary
User submits email
- Validated by 
Validate Auth & Prepare Payload. 
Check for existing reset record
- Nodes: 
Prepare DB Query+Find Password Reset Record. 
Record exists?
- Checked via 
Check Reset Record Existence. - Branch by status:
- If invalid email → handled by 
Handle Invalid Email. - If code entered → verify via 
Find Password Reset Record+Check Reset Record Existence. 
 - If invalid email → handled by 
 
Update used flag if valid
- Nodes: 
Compare Code & Update Used Flag+Update Used Flag. 
Send response back to frontend
- Nodes: 
Prepare Success Responseand debug nodes. 
Response Examples (uibuilder.onChange)
| Status | Response | 
|---|---|
| invalidCode | { "action": "resetPassword", "status": "invalidCode", "message": "code not found" } | 
| expired | { "action": "resetPassword", "status": "expired", "message": "your code has been expired" } | 
| validCode | { "action": "resetPassword", "status": "validCode", "message": "Code matched successfully" } | 
MongoDB PasswordResets Collection
| Field | Type | Description | 
|---|---|---|
_id | 
ObjectId | Unique document ID | 
email | 
string | User email | 
userId | 
string | MongoDB _id of the user | 
code | 
string | One-time 6-digit reset code | 
used | 
boolean | Whether code has been used (default false) | 
createdAt | 
Date | Timestamp of token creation | 
expiresAt | 
Date | Expiration timestamp (e.g., 2 minutes) | 
Sample Document
{
  "_id": "69078db8d6c4398ea5f5cd54",
  "code": "994015",
  "email": "li@gmail.com",
  "userId": "69076591d6c4398ea5f5cd48",
  "id": 15,
  "createdAt": "2025-11-02T16:58:32.299+00:00",
  "expiresAt": "2025-11-02T17:00:32.299+00:00",
  "used": false
}
7. setNewPassword Flow
High-Level Flow:
User enters email & new password
         ↓
      Frontend (Vue.js)
         ↓ uibuilder.send
   Node-RED: setNewPassword flow
         ↓
  Check email in MongoDB
         ↓
 ┌───────────────┐
 │ Email exists  │ → Update password → Success response
 └───────────────┘
 ┌───────────────┐
 │ Email invalid │ → Error response
 └───────────────┘
         ↓
      Frontend shows result
Frontend setNewPassword Action
submitNewPassword() {
    if (!this.newPassword) {
        alert('Please enter a new password');
        return;
    }
    if (this.newPassword !== this.confirmNewPassword) {
        alert('Passwords do not match');
        return;
    }
    // Send new password to backend via uibuilder
    uibuilder.send({
        auth: {
            type: "setNewPassword",
            email: this.resetEmail,
            password: this.newPassword
        }
    });
    console.log('New password set for:', this.resetEmail);
    // Close modal and reset fields
    this.closeSetNewPasswordModal();
}
- Validates that both new password and confirmation are entered and match.
 - Sends the 
setNewPasswordaction along with the email and new password to the backend viauibuilder.send(). - Closes the modal and clears fields.
 
Sample Request Payload
{
  "auth": {},
  "type": "setNewPassword",
  "email": "h@gmail.com",
  "password": "123456",
  "_socketId": "3hHNyKWUI-Khbn9cAEh0",
  "topic": "TrustedCloud Password Reset Code – Action Required",
  "_msgid": "4a86446e49c05025"
}
Backend Node-RED Flow
| Node Name | Type | Responsibility / Description | 
|---|---|---|
setNewPassword(in) | 
Link In | Entry point of the flow; receives the request to set a new password. | 
Validate Auth & Prepare Payload | 
Function | Checks if msg.auth exists and has an email. Adds socketId and preserves the original payload. | 
Check User by Email | 
Function | Prepares a MongoDB query to find the user in the Users collection by email. | 
Find User Record | 
MongoDB | Executes the query against the Users collection and returns the matching user document(s). | 
Check if Email Exists in DB | 
Function | Checks if the user exists. Prepares a response: success if email exists, invalidEmail if not. Also stores user data for further processing. | 
Branch by Email Status | 
Switch | Routes the flow depending on whether the email exists (success) or not (invalidEmail). | 
Update User Password | 
Function | Hashes the new password using bcrypt, prepares the MongoDB update operation, and stores a success response. | 
Update User Password | 
MongoDB | Updates the user’s password in the Users collection. | 
Branch by Update Result | 
Switch | Checks the result of the update operation; routes to success or invalid user. | 
Prepare Success Response | 
Function | Prepares the final success payload: "Password updated successfully" with status success. | 
Handle Invalid Email | 
Function | Prepares a response if the email was not found: "Email address not found" with status invalidEmail. | 
Handle Invalid User | 
Function | Prepares a response if the user is invalid: "User not found" with status invalidUser. | 
invalidEmail | 
Debug | Outputs invalid email response for debugging. | 
success | 
Debug | Outputs success response for debugging. | 
invalidUser | 
Debug | Outputs invalid user response for debugging. | 
Backend Flow Summary
- 
Validation & Payload Preparation (
Validate Auth & Prepare Payload)- Validates the request contains a valid 
authobject and email. - Adds 
socketIdand preserves the original payload for tracking. 
 - Validates the request contains a valid 
 Check User Existence (
Check User by Email+Find User Record)
Queries theUserscollection in MongoDB to verify whether the provided email exists.- 
Decision Branching (
Check if Email Exists in DB+Branch by Email Status)- If the email exists: proceeds to update the password.
 - If not: sends an 
invalidEmailresponse. 
 - 
Password Update (
Update User PasswordFunction + MongoDB node)- Hashes the new password using bcrypt.
 - Updates it in the database.
 - Prepares a success response.
 
 - 
Post-Update Decision (
Branch by Update Result)- If update is successful: sends 
successresponse. - If the user is invalid or not found: sends 
invalidUserresponse. 
 - If update is successful: sends 
 Response Handling (
Prepare Success Response,Handle Invalid Email,Handle Invalid User)
Response Examples (uibuilder.onChange)
| Status | Response | 
|---|---|
| invalidEmail | { "action": "sendResetCode", "status": "invalidEmail", "message": "Email address not found." } | 
| invalidUser | { "action": "setNewPassword", "status": "invalidUser", "message": "User not found." } | 
| success | { "action": "setNewPassword", "status": "success", "message": "Password updated successfully." } | 
MongoDB Users Collection Schema
| Field | Type | Description | 
|---|---|---|
_id | 
ObjectId | Unique document ID | 
clientId | 
string | Unique client identifier | 
fullName | 
string | User full name | 
email | 
string | Unique email | 
password | 
string | Hashed password | 
confirmPassword | 
string | Hashed password (same as password) | 
roles | 
array | Assigned roles (e.g., ["Applicant","Auditor"]) | 
status | 
string | Registration status (not_found, success) | 
profile | 
object | User profile data (avatar, first/last name, contact info) | 
createdAt | 
Date | Timestamp of account creation | 
updatedAt | 
Date | Last update timestamp | 
Sample document:
{
  "_id": "68f830f9d6c4398ea5f5cbdf",
  "clientId": "GiHBKhnJvqNbA6dYRqWHF",
  "fullName": "David Kim",
  "email": "david.kim@brightwave.co",
  "password": "$2b$10$MiGhJqBXNIJhayVOnqG1C...",
  "roles": ["Applicant","Auditor"],
  "status": "not_found",
  "profile": {
    "firstName": "David",
    "lastName": "Kim",
    "avatarUrl": "data:image/jpeg;base64,..."
  },
  "createdAt": "2025-10-22T01:18:49.129Z",
  "updatedAt": "2025-10-28T02:20:04.260Z"
}
1. getDashboard (Applicant) Flow
High-Level Flow:
User logs in
     ↓
Node-RED: Determine User Roles
     ↓
Switch routes by role:
     ├─ Applicant → Fetch user's own requests
     │        ↓
     │   MongoDB: Requests collection
     │        ↓
     │   Dashboard Payload → uibuilder (frontend)
Frontend getDashboard (Applicant)
   uibuilder.start();
    this.isLoading = true; 
    var vueApp = this;
    // Send dashboard request
    uibuilder.send({
        type: 'getDashboard',
        auth: {
            userToken: this.getCookie('userToken'),
            clientId: this.getCookie('uibuilder-client-id')
        }
    });
- 
uibuilder.start()starts the communication channel with Node-RED via WebSocket. - It sets 
isLoading = trueto show a loading indicator (e.g., spinner). - Then it sends a message to Node-RED requesting the dashboard data.
 - The request message looks like:
 
Sample Request Payload
{
  "type": "getDashboard",
  "auth": {},
  "userToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IiJ9.eyJzdGF0dXMiOiJzdWNjZXNzIiwidXNlclRva2VuIjoiU09NRV9SQU5ET01fVE9LRU5fMTc2MTU5OTExNzkxNCIsInByb2ZpbGUiOnsidXNlcklkIjoiNjhmZmNjMjFkNmM0Mzk4ZWE1ZjVjY2Y5IiwiaWQiOjEwLCJlbWFpbCI6ImhAZ21haWwuY29tIiwicm9sZXMiOlsiYXBwbGljYW50Il0sImZ1bGxOYW1lIjoiaGVkaXllaCJ9LCJhY3Rpb24iOiJsb2dpbiIsImlhdCI6MTc2MTU5OTExNywiZXhwIjoxNzYxNjAyNzE3fQ.mkvYqaZX1Y8m8gJdS3VAlQvDJ7zj3amk-Rc59rKkR8k",
  "clientId": "DucPqZtrHFQNgOnyiGV02",
  "_socketId": "X6ZKjzzFvV-ibjhwAEiI",
  "_msgid": "8bd0ae08cdfe06a9"
}
Backend Node-RED Flow
| Node Name | Type | Responsibility | 
|---|---|---|
| Determine User Roles | Function | Reads user object from msg.user or msg.payload. Checks user roles (Applicant, Auditor, Approver) and sets msg.payload to one of: Applicant, ApplicantandAuditor, ApplicantandApprover, or Admin. Attaches full user info in msg.user. | 
| Route by Role Type | Switch | Routes flow based on msg.payload role type. Sends messages to different paths for each role type. | 
| Fetch Applicant Requests | Function | Prepares MongoDB query for Requests collection. Filters requests where createdBy.id matches user ID. Sets msg.collection and msg.payload for MongoDB node. | 
| Applicant Requests | MongoDB4 | Executes query to fetch requests created by the applicant. Returns results as an array in msg.payload. | 
| Dashboard Payload | Function | Builds the final dashboard payload: type, state, applicantRequests, requests. Adds profile info from msg.user or msg.userData including user ID, email, name, roles, avatar, and contact info. | 
| Applicant (Debug) | Debug | Logs the final payload for the applicant to the Node-RED debug sidebar. Useful for monitoring and troubleshooting. | 
Backend Flow Summary
Determine User Roles
- Purpose: Identify the user’s role(s) based on data from MongoDB.
 - 
Logic:
- Checks the 
rolesarray in the user object. - Maps combinations to one of four categories:
 ApplicantApplicantandAuditorApplicantandApproverAdmin
 - Checks the 
 - 
Output: Sets 
msg.payloadto role type and attaches full user info inmsg.user. 
Route by Role Type
- Purpose: Directs the flow depending on the role determined.
 - Logic: Uses a Switch node to route messages to different downstream nodes for Applicants, Approvers, Auditors, or Admins.
 - Outcome: Ensures role-specific logic is applied to dashboard requests.
 
Fetch Requests (Role-Based)
- Purpose: Retrieve relevant requests from MongoDB for the user.
 - 
Logic:
- For Applicants: query 
Requestscollection wherecreatedBy.id = userId. 
 - For Applicants: query 
 
Note:
Applicant Dashboard
Can view requests in: Pending, Audited, Approved, Rejected
Prepare Dashboard Payload
- Purpose: Format data for front-end consumption.
 - 
Logic:
- Combines request data (
applicantRequests,requests) with user profile info (id,email,name,roles,avatar,contact info). - Ensures front-end receives a standardized object regardless of role.
 
 - Combines request data (
 - 
Output: 
msg.payloadready to send to UI via uibuilder. 
Sending Data to Front-End
- 
Link/Debug Nodes:
- 
Link Outnode passes data to the front-end flow. - 
Debugnodes monitor output for verification and troubleshooting. 
 - 
 
Response Examples (uibuilder.onChange)
{
  "type": "getDashboard",
  "auth": {},
  "userToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IiJ9.eyJzdGF0dXMiOiJzdWNjZXNzIiwidXNlclRva2VuIjoiU09NRV9SQU5ET01fVE9LRU5fMTc2MTU5OTkyNTMyNCIsInByb2ZpbGUiOnsidXNlcklkIjoiNjhmZmUxOTZkNmM0Mzk4ZWE1ZjVjZDA2IiwiaWQiOjksImVtYWlsIjoibEBnbWFpbC5jb20iLCJyb2xlcyI6WyJBcHBsaWNhbnQiXSwiZnVsbE5hbWUiOiJsaWxpYSJ9LCJhY3Rpb24iOiJsb2dpbiIsImlhdCI6MTc2MTU5OTkyNSwiZXhwIjoxNzYxNjAzNTI1fQ.SJLPhRoJOVnLvUQLsk5ngvdw4d2MgFw28p5yG6Uh4wc",
  "clientId": "DucPqZtrHFQNgOnyiGV02",
  "_socketId": "2xxN12-ysdStG4sBAEj5",
  "_msgid": "09d2712e6aea20b0",
  "_archiveRequest": null,
  "_editCompanyDetails": null,
  "_waitingForResponse": null,
  "_editProfile": null,
  "_approveRequest": null,
  "collection": "Requests",
  "operation": "find",
  "payload": {
    "type": "getDashboard",
    "state": "requestsFetched",
    "applicantRequests": [
      {
        "_id": "68ffe1e7d6c4398ea5f5cd08",
        "id": 18,
        "createdBy": {},
        "companyDetails": {},
        "priority": "low",
        "status": "Pending",
        "archive": false,
        "timeEstimation": "10 business day",
        "assignedTo": "",
        "auditInfo": {},
        "approveInfo": {},
        "createdAt": "2025-10-27T21:19:35.315Z",
        "profile": {
          "userId": "68ffe196d6c4398ea5f5cd06",
          "email": "l@gmail.com",
          "name": "",
          "role": "",
          "roles": ["Applicant"],
          "avatarUrl": "./images/sara-profile.png",
          "firstName": "lilia",
          "lastName": "",
          "contactNumber": "",
          "address": "",
          "country": "",
          "stateOrProvince": "",
          "postalCode": "",
          "options": {},
          "output": "toArray"
        }
      }
    ]
  }
}
  
  
  MongoDB Requests Collection Schema
| Field | Type | Description | 
|---|---|---|
| _id | String | Unique MongoDB document identifier | 
| id | Number | Internal record ID | 
| createdBy.userId | String | ID of the user who created the record | 
| createdBy.id | Number | Creator’s internal ID | 
| createdBy.fullName | String | Name of the creator (e.g., David Kim) | 
| createdBy.email | String | Creator’s email address | 
| createdBy.avatarUrl | String | Base64-encoded profile image URL | 
| companyDetails.priority | String | Task or request priority (e.g., low) | 
| companyDetails.status | String | Current status (e.g., Approved) | 
| companyDetails.archive | Boolean | Indicates if the record is archived | 
| companyDetails.timeEstimation | String | Estimated completion time | 
| companyDetails.assignedTo | String | Assigned user (empty if none) | 
| auditInfo | Object | Audit tracking data (timestamps, etc.) | 
| approveInfo | Object | Approval metadata or history | 
| createdAt | DateTime | Record creation timestamp | 
| updatedAt | DateTime | Last update timestamp | 
Sample document:
{
  "_id": "6907636ed6c4398ea5f5cd42",
  "id": 2,
  "createdBy": {
    "userId": "68f830f9d6c4398ea5f5cbdf",
    "id": 1,
    "fullName": "David Kim",
    "email": "david.kim@brightwave.co",
    "avatarUrl": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBxIQEhUSEhI…"
  },
  "companyDetails": {
    "priority": "low",
    "status": "Approved",
    "archive": false,
    "timeEstimation": "10 business day",
    "assignedTo": ""
  },
  "auditInfo": {},
  "approveInfo": {},
  "createdAt": "2025-11-02T13:58:06.411+00:00",
  "updatedAt": "2025-11-02T14:05:05.471+00:00"
}
2. getDashboard(Applicant and Auditor) Flow
High-Level Flow:
User logs in
     ↓
Node-RED: Determine User Roles
     ↓
Switch routes by role:
     ├─ Applicant and Auditor → Fetch user's own requests
     │        ↓
     │   MongoDB: Requests collection
     │        ↓
     │   Dashboard Payload → uibuilder (frontend)
Frontend getDashboard (Applicant and Auditor)
   uibuilder.start();
    this.isLoading = true; 
    var vueApp = this;
    // Send dashboard request
    uibuilder.send({
        type: 'getDashboard',
        auth: {
            userToken: this.getCookie('userToken'),
            clientId: this.getCookie('uibuilder-client-id')
        }
    });
- 
uibuilder.start()starts the communication channel with Node-RED via WebSocket. - It sets 
isLoading = trueto show a loading indicator (e.g., spinner). - Then it sends a message to Node-RED requesting the dashboard data.
 - The request message looks like:
 
Sample Request Payload
{
  "type": "getDashboard",
  "auth": {},
  "userToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IiJ9.eyJzdGF0dXMiOiJzdWNjZXNzIiwidXNlclRva2VuIjoiU09NRV9SQU5ET01fVE9LRU5fMTc2MTU5OTExNzkxNCIsInByb2ZpbGUiOnsidXNlcklkIjoiNjhmZmNjMjFkNmM0Mzk4ZWE1ZjVjY2Y5IiwiaWQiOjEwLCJlbWFpbCI6ImhAZ21haWwuY29tIiwicm9sZXMiOlsiYXBwbGljYW50Il0sImZ1bGxOYW1lIjoiaGVkaXllaCJ9LCJhY3Rpb24iOiJsb2dpbiIsImlhdCI6MTc2MTU5OTExNywiZXhwIjoxNzYxNjAyNzE3fQ.mkvYqaZX1Y8m8gJdS3VAlQvDJ7zj3amk-Rc59rKkR8k",
  "clientId": "DucPqZtrHFQNgOnyiGV02",
  "_socketId": "X6ZKjzzFvV-ibjhwAEiI",
  "_msgid": "8bd0ae08cdfe06a9"
}
Backend Node-RED Flow
| Node Name | Type | Description | 
|---|---|---|
| Determine User Roles | Function | Identifies the user’s roles from the MongoDB user object and categorizes them into one of four types: Applicant, ApplicantandAuditor, ApplicantandApprover, or Admin. Sets msg.payload to the detected role and attaches user details to msg.user. | 
| Route by Role Type | Switch | Routes the message to the appropriate flow branch based on msg.payload (role type). For example, users with the ApplicantandAuditor role are routed to nodes that handle both applicant and auditor data retrieval. | 
| Fetch Applicant Requests | Function | Builds a MongoDB query to fetch all requests created by the logged-in user (createdBy.id = userId). Adds _phase: "Applicant" for tracking. | 
| Applicant Requests | MongoDB4 | Executes the database query built in the previous function and retrieves all applicant-related requests. Passes the data to the next node. | 
| Fetch Auditor Requests | Function | Executes only after applicant requests are fetched. Builds a new MongoDB query to find all requests requiring auditor attention (status: Pending, Audited, or Rejected). Sets _phase: "Auditor". | 
| Auditor Requests | MongoDB4 | Runs the MongoDB query for auditor-related requests and returns matching records as an array in msg.payload. | 
| Normalize Auditor Requests | Function | Cleans and enriches the auditor requests array by adding user metadata (name, email, avatar). Ensures consistent structure for UI rendering. | 
| Dashboard Payload | Function | Combines applicantRequests, auditorRequests, and full user profile data into a single msg.payload formatted for front-end use (type: "getDashboard"). | 
| Link Out | Link Out | Sends the final formatted dashboard payload to the front-end flow handled by uibuilder. | 
| ApplicantandAuditor (Debug) | Debug | Displays the final output payload in Node-RED’s debug sidebar for inspection and troubleshooting. | 
Backend Flow Summary
Purpose
This backend flow dynamically builds the Applicant and Auditor dashboard by retrieving and combining user-specific and role-specific data from MongoDB.
Process Overview
Identify User Role:
   Extracts and interprets the user’s roles to determine access type (Applicant, Auditor, etc.).
Route by Role:
   Directs the flow to the appropriate logic branch depending on the identified role.
Retrieve Requests:
- Applicant Requests: Queries records created by the user.
 - Auditor Requests: Fetches items pending review or action based on role permissions.
 
Note:
Auditor Dashboard
Can view requests in: Pending, Audited, Rejected
Applicant Dashboard
Can view requests in: Pending, Audited, Approved, Rejected
Normalize Data:
   Standardizes each record’s format for the front-end, enriching it with user and request metadata.
Prepare Final Dashboard Payload:
   Merges both Applicant and Auditor data streams with the user’s full profile to produce a unified, front-end-ready response.
Send to Front-End (uibuilder):
   The formatted payload is sent through a Link Out node, then displayed in the user’s dashboard interface.
Response Examples (uibuilder.onChange)
{
{
  "type": "getDashboard",
  "auth": {},
  "_socketId": "0KAO_QB8U2vpv5xDAElI",
  "_msgid": "b668b84c0ec154e7",
  "_archiveRequest": null,
  "_editCompanyDetails": null,
  "_waitingForResponse": null,
  "_editProfile": null,
  "_approveRequest": null,
  "collection": "Requests",
  "operation": "find",
  "payload": {
    "options": {},
    "userId": "68f830f9d6c4398ea5f5cbdf",
    "user": {},
    "_phase": "Auditor",
    "applicantRequests": [
      /* 13 request objects */
    ],
    "auditorRequests": [
      /* 16 request objects */
    ]
  }
}
  
  
  MongoDB Requests Collection Schema
| Field | Type | Description | 
|---|---|---|
| _id | String | Unique MongoDB document identifier | 
| id | Number | Internal record ID | 
| createdBy.userId | String | ID of the user who created the record | 
| createdBy.id | Number | Creator’s internal ID | 
| createdBy.fullName | String | Name of the creator (e.g., David Kim) | 
| createdBy.email | String | Creator’s email address | 
| createdBy.avatarUrl | String | Base64-encoded profile image URL | 
| companyDetails.priority | String | Task or request priority (e.g., low) | 
| companyDetails.status | String | Current status (e.g., Approved) | 
| companyDetails.archive | Boolean | Indicates if the record is archived | 
| companyDetails.timeEstimation | String | Estimated completion time | 
| companyDetails.assignedTo | String | Assigned user (empty if none) | 
| auditInfo | Object | Audit tracking data (timestamps, etc.) | 
| approveInfo | Object | Approval metadata or history | 
| createdAt | DateTime | Record creation timestamp | 
| updatedAt | DateTime | Last update timestamp | 
Sample document:
{
  "_id": "6907636ed6c4398ea5f5cd42",
  "id": 2,
  "createdBy": {
    "userId": "68f830f9d6c4398ea5f5cbdf",
    "id": 1,
    "fullName": "David Kim",
    "email": "david.kim@brightwave.co",
    "avatarUrl": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBxIQEhUSEhI…"
  },
  "companyDetails": {
    "priority": "low",
    "status": "Approved",
    "archive": false,
    "timeEstimation": "10 business day",
    "assignedTo": ""
  },
  "auditInfo": {},
  "approveInfo": {},
  "createdAt": "2025-11-02T13:58:06.411+00:00",
  "updatedAt": "2025-11-02T14:05:05.471+00:00"
}
3. getDashboard(Applicant and Approver) Flow
High-Level Flow:
User logs in
     ↓
Node-RED: Determine User Roles
     ↓
Switch routes by role:
     ├─ Applicant and Approver→ Fetch user's own requests
     │        ↓
     │   MongoDB: Requests collection
     │        ↓
     │   Dashboard Payload → uibuilder (frontend)
Frontend getDashboard (Applicant and Approver)
   uibuilder.start();
    this.isLoading = true; 
    var vueApp = this;
    // Send dashboard request
    uibuilder.send({
        type: 'getDashboard',
        auth: {
            userToken: this.getCookie('userToken'),
            clientId: this.getCookie('uibuilder-client-id')
        }
    });
- 
uibuilder.start()starts the communication channel with Node-RED via WebSocket. - It sets 
isLoading = trueto show a loading indicator (e.g., spinner). - Then it sends a message to Node-RED requesting the dashboard data.
 - The request message looks like:
 
Sample Request Payload
{
  "type": "getDashboard",
  "auth": {},
  "userToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IiJ9.eyJzdGF0dXMiOiJzdWNjZXNzIiwidXNlclRva2VuIjoiU09NRV9SQU5ET01fVE9LRU5fMTc2MTU5OTExNzkxNCIsInByb2ZpbGUiOnsidXNlcklkIjoiNjhmZmNjMjFkNmM0Mzk4ZWE1ZjVjY2Y5IiwiaWQiOjEwLCJlbWFpbCI6ImhAZ21haWwuY29tIiwicm9sZXMiOlsiYXBwbGljYW50Il0sImZ1bGxOYW1lIjoiaGVkaXllaCJ9LCJhY3Rpb24iOiJsb2dpbiIsImlhdCI6MTc2MTU5OTExNywiZXhwIjoxNzYxNjAyNzE3fQ.mkvYqaZX1Y8m8gJdS3VAlQvDJ7zj3amk-Rc59rKkR8k",
  "clientId": "DucPqZtrHFQNgOnyiGV02",
  "_socketId": "X6ZKjzzFvV-ibjhwAEiI",
  "_msgid": "8bd0ae08cdfe06a9"
}
Backend Node-RED Flow
| Node Name | Type | Description | 
|---|---|---|
| Determine User Roles | Function | Determines the user’s role(s) from MongoDB and categorizes into Applicant, ApplicantandApprover, ApplicantandAuditor, or Admin. Sets msg.payload to the role and attaches the full user object in msg.user. | 
| Route by Role Type | Switch | Routes messages based on msg.payload (role type) to the correct downstream flow for Applicants, Approvers, or Admins. | 
| Fetch Applicant Requests | Function | Prepares MongoDB query to fetch requests created by the logged-in user (createdBy.id = userId). Adds _phase: "Applicant" to track workflow. | 
| Applicant Requests (MongoDB) | MongoDB4 | Executes the applicant request query and returns results in msg.payload array. | 
| Fetch Approver Requests | Function | Prepares MongoDB query for requests the approver needs to act on (status: Audited, Approved, Rejected). Sets _phase: "Approver" for tracking. | 
| Approver Requests (MongoDB) | MongoDB4 | Executes the approver request query and returns matching requests as an array in msg.payload. | 
| Normalize Approver Requests | Function | Standardizes the structure of approver requests. Adds creator metadata (name, email, avatar) for UI display. | 
| Dashboard Payload | Function | Combines applicantRequests and approverRequests with the user’s profile info into a single object formatted for the front-end (msg.payload.type = "getDashboard"). | 
| Link Out | Link Out | Sends the final dashboard payload to the front-end via uibuilder or other linked flows. | 
| ApplicantandApprover (Debug) | Debug | Shows final combined dashboard payload for testing and verification. | 
Backend Flow Summary
Purpose
The backend flow dynamically generates the Applicant and Approver dashboard by fetching and combining requests based on user role from MongoDB.
Workflow
Role Detection:
   Extracts the user’s roles and categorizes them into one of four types. Sets msg.payload and msg.user.
Role-Based Routing:
   Uses a switch node to send messages to different flow branches depending on the user role.
Fetch Requests:
Applicant Requests: Fetches all requests created by the logged-in user.
Approver Requests: Fetches requests with:
- Status Audited or Approved.
 - Rejected requests, but only those rejected by Approvers, excluding any rejected by Auditors.
 
Normalize Data:
   Formats requests to include essential user metadata for consistent front-end rendering.
Prepare Dashboard Payload:
   Combines applicant and approver requests with full user profile to generate msg.payload for the front-end.
Send to Front-End:
   Final payload is passed through a Link Out node to UI components (e.g., uibuilder).
Response Examples (uibuilder.onChange)
{
  ""type"": ""getDashboard"",
  ""auth"": {
    ""userToken"": ""eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IiJ9.eyJzdGF0dXMiOiJzdWNjZXNzIiwidXNlclRva2VuIjoiU09NRV9SQU5ET01fVE9LRU5fMTc2MTYxODg3NTczNSIsInByb2ZpbGUiOnsidXNlcklkIjoiNjhmODM2MzhkNmM0Mzk4ZWE1ZjVjYmU2IiwiaWQiOjIsImVtYWlsIjoibGVpbGEuaGFzc2FuQHNreW5ldC5zeXN0ZW1zIiwicm9sZXMiOlsiQXBwbGljYW50IiwiQXBwcm92ZXIiXSwiZnVsbE5hbWUiOiJMZWlsYSBIYXNzYW4ifSwiYWN0aW9uIjoibG9naW4iLCJpYXQiOjE3NjE2MTg4NzUsImV4cCI6MTc2MTYyMjQ3NX0.yC6fr76smTVPPr0kCrmTARcJQ5qiuGqYKvl8_16rcts""
  },
  ""clientId"": ""DucPqZtrHFQNgOnyiGV02"",
  ""_socketId"": ""l9Mu_3V_ZjsFAHcfAElj"",
  ""_msgid"": ""235919a3a70d095a"",
  ""_archiveRequest"": null,
  ""_editCompanyDetails"": null,
  ""_waitingForResponse"": null,
  ""_editProfile"": null,
  ""_approveRequest"": null,
  ""collection"": ""Requests"",
  ""operation"": ""find"",
  ""payload"": {},
  ""options"": {},
  ""userId"": ""68f83638d6c4398ea5f5cbe6"",
  ""user"": {},
  ""_phase"": ""Approver"",
  ""applicantRequests"": [],
  ""approverRequests"": [
    {},
    {},
    {},
    {}
  ]
}
  
  
  MongoDB Requests Collection Schema
| Field | Type | Description | 
|---|---|---|
| _id | String | Unique MongoDB document identifier | 
| id | Number | Internal record ID | 
| createdBy.userId | String | ID of the user who created the record | 
| createdBy.id | Number | Creator’s internal ID | 
| createdBy.fullName | String | Name of the creator (e.g., David Kim) | 
| createdBy.email | String | Creator’s email address | 
| createdBy.avatarUrl | String | Base64-encoded profile image URL | 
| companyDetails.priority | String | Task or request priority (e.g., low) | 
| companyDetails.status | String | Current status (e.g., Approved) | 
| companyDetails.archive | Boolean | Indicates if the record is archived | 
| companyDetails.timeEstimation | String | Estimated completion time | 
| companyDetails.assignedTo | String | Assigned user (empty if none) | 
| auditInfo | Object | Audit tracking data (timestamps, etc.) | 
| approveInfo | Object | Approval metadata or history | 
| createdAt | DateTime | Record creation timestamp | 
| updatedAt | DateTime | Last update timestamp | 
Sample document:
{
  "_id": "6907636ed6c4398ea5f5cd42",
  "id": 2,
  "createdBy": {
    "userId": "68f830f9d6c4398ea5f5cbdf",
    "id": 1,
    "fullName": "David Kim",
    "email": "david.kim@brightwave.co",
    "avatarUrl": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBxIQEhUSEhI…"
  },
  "companyDetails": {
    "priority": "low",
    "status": "Approved",
    "archive": false,
    "timeEstimation": "10 business day",
    "assignedTo": ""
  },
  "auditInfo": {},
  "approveInfo": {},
  "createdAt": "2025-11-02T13:58:06.411+00:00",
  "updatedAt": "2025-11-02T14:05:05.471+00:00"
}
3. getDashboard(Admin) Flow
High-Level Flow:
User logs in
     ↓
Node-RED: Determine User Roles
     ↓
Switch routes by role:
     ├─ Admin→ Fetch user's own requests
     │        ↓
     │   MongoDB: Requests collection
     │        ↓
     │   Dashboard Payload → uibuilder (frontend)
Frontend getDashboard (Admin)
   uibuilder.start();
    this.isLoading = true; 
    var vueApp = this;
    // Send dashboard request
    uibuilder.send({
        type: 'getDashboard',
        auth: {
            userToken: this.getCookie('userToken'),
            clientId: this.getCookie('uibuilder-client-id')
        }
    });
- 
uibuilder.start()starts the communication channel with Node-RED via WebSocket. - It sets 
isLoading = trueto show a loading indicator (e.g., spinner). - Then it sends a message to Node-RED requesting the dashboard data.
 - The request message looks like:
 
Sample Request Payload
{
  "type": "getDashboard",
  "auth": {},
  "userToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IiJ9.eyJzdGF0dXMiOiJzdWNjZXNzIiwidXNlclRva2VuIjoiU09NRV9SQU5ET01fVE9LRU5fMTc2MTU5OTExNzkxNCIsInByb2ZpbGUiOnsidXNlcklkIjoiNjhmZmNjMjFkNmM0Mzk4ZWE1ZjVjY2Y5IiwiaWQiOjEwLCJlbWFpbCI6ImhAZ21haWwuY29tIiwicm9sZXMiOlsiYXBwbGljYW50Il0sImZ1bGxOYW1lIjoiaGVkaXllaCJ9LCJhY3Rpb24iOiJsb2dpbiIsImlhdCI6MTc2MTU5OTExNywiZXhwIjoxNzYxNjAyNzE3fQ.mkvYqaZX1Y8m8gJdS3VAlQvDJ7zj3amk-Rc59rKkR8k",
  "clientId": "DucPqZtrHFQNgOnyiGV02",
  "_socketId": "X6ZKjzzFvV-ibjhwAEiI",
  "_msgid": "8bd0ae08cdfe06a9"
}
Backend Node-RED Flow
| Node Name | Type | Description | 
|---|---|---|
| Route by Role Type | Switch | Routes messages based on msg.payload (role type) to the correct downstream flow. Here, the Admin role is handled separately. | 
| Prepare Requests Query | Function | Prepares a generic MongoDB query for fetching all requests (no filter) for the Admin role. Sets collection to Requests and output format to toArray. | 
| Fetch Requests | MongoDB4 | Executes the Admin requests query and returns all requests in msg.payload array. | 
| Dashboard Payload | Function | Combines fetched requests with the Admin user’s profile into a structured object for the front-end (msg.payload.type = "getDashboard"). | 
| Link Out 16 | Link Out | Sends the final Admin dashboard payload to the front-end or linked flows. | 
| Admin | Debug | Displays the Admin dashboard payload for testing and verification. | 
Backend Flow Summary
- The flow identifies the user role as Admin via the Route by Role Type switch.
 - Prepare Requests Query constructs a MongoDB query with no restrictions, allowing Admins to see all requests.
 - Fetch Requests node executes the query on the Requests collection in MongoDB.
 - Dashboard Payload formats the data together with Admin profile information (email, name, roles, avatar, contact info, etc.) for front-end consumption.
 - Link Out 16 sends the formatted payload to the UI, while the Admin Debug node allows developers to verify the output.
 
Response Examples (uibuilder.onChange)
{
"{
  ""type"": ""getDashboard"",
  ""auth"": {
    ""userToken"": ""eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IiJ9.eyJzdGF0dXMiOiJzdWNjZXNzIiwidXNlclRva2VuIjoiU09NRV9SQU5ET01fVE9LRU5fMTc2MTYxOTI0MjQ4MiIsInByb2ZpbGUiOnsidXNlcklkIjoiNjhmODQ0NDlkNmM0Mzk4ZWE1ZjVjYmVlIiwiaWQiOjQsImVtYWlsIjoiZmFyaWQubW9yYWRpQHF1YW50dW1lZGdlLmlvIiwicm9sZXMiOlsiQWRtaW4iXSwiZnVsbE5hbWUiOiJGYXJpZCBNb3JhZGkifSwiYWN0aW9uIjoibG9naW4iLCJpYXQiOjE3NjE2MTkyNDIsImV4cCI6MTc2MTYyMjg0Mn0.GhkUkVsHVYPN3HtrJ8dgnCggAdXGXP0KSQIJYWtPx_w""
  },
  ""clientId"": ""DucPqZtrHFQNgOnyiGV02"",
  ""_socketId"": ""pP_KxEJNyY96NbnHAEl1"",
  ""_msgid"": ""1f420fa785af463d"",
  ""_archiveRequest"": null,
  ""_editCompanyDetails"": null,
  ""_waitingForResponse"": null,
  ""_editProfile"": null,
  ""_approveRequest"": null,
  ""collection"": ""Requests"",
  ""operation"": ""find"",
  ""payload"": {
    ""type"": ""getDashboard"",
    ""requests"": [
      {},
    ],
    ""profile"": {},
    ""options"": {}
  },
  ""userId"": ""68f84449d6c4398ea5f5c""
}
"
  
  
  MongoDB Requests Collection Schema
| Field | Type | Description | 
|---|---|---|
| _id | String | Unique MongoDB document identifier | 
| id | Number | Internal record ID | 
| createdBy.userId | String | ID of the user who created the record | 
| createdBy.id | Number | Creator’s internal ID | 
| createdBy.fullName | String | Name of the creator (e.g., David Kim) | 
| createdBy.email | String | Creator’s email address | 
| createdBy.avatarUrl | String | Base64-encoded profile image URL | 
| companyDetails.priority | String | Task or request priority (e.g., low) | 
| companyDetails.status | String | Current status (e.g., Approved) | 
| companyDetails.archive | Boolean | Indicates if the record is archived | 
| companyDetails.timeEstimation | String | Estimated completion time | 
| companyDetails.assignedTo | String | Assigned user (empty if none) | 
| auditInfo | Object | Audit tracking data (timestamps, etc.) | 
| approveInfo | Object | Approval metadata or history | 
| createdAt | DateTime | Record creation timestamp | 
| updatedAt | DateTime | Last update timestamp | 
Sample document:
{
  "_id": "6907636ed6c4398ea5f5cd42",
  "id": 2,
  "createdBy": {
    "userId": "68f830f9d6c4398ea5f5cbdf",
    "id": 1,
    "fullName": "David Kim",
    "email": "david.kim@brightwave.co",
    "avatarUrl": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBxIQEhUSEhI…"
  },
  "companyDetails": {
    "priority": "low",
    "status": "Approved",
    "archive": false,
    "timeEstimation": "10 business day",
    "assignedTo": ""
  },
  "auditInfo": {},
  "approveInfo": {},
  "createdAt": "2025-11-02T13:58:06.411+00:00",
  "updatedAt": "2025-11-02T14:05:05.471+00:00"
}
Collections Overview
| Collection | Collection Overview | Contains / Description | 
|---|---|---|
| PasswordResets | The Password Reset Collection is used to manage and validate password reset requests in the system. | Fields such as _id, code, email, userId, id, createdAt, expiresAt, and used — used to track temporary password reset codes, their status, and validity period. | 
| Requests | Stores user-submitted requests (e.g., access, approvals, or application actions). | Fields like requestId, userId, status, companyDetails, and timestamps — representing workflow requests and their approval states. | 
| Tokens | Handles session and authentication tokens for secure user access. | Contains token, userId, issuedAt, expiresAt — used to authenticate users and manage session expiration. | 
| Users | Maintains registered user account information. | Includes fields such as userId, fullName, email, password, roles, and profile metadata. Roles define access and permissions in the system and may include: Admin, Applicant, Applicant & Approver, or Applicant & Auditor. | 
Request and Response
| Type | Action Overview | Flow Overview | uibuilder.send (request) | uibuilder.onChange (response) | 
|---|---|---|---|---|
| signup | Handles new user registration and account creation. | Users — A new record is added with details such as fullName, email, password, and role. Validation ensures the email is unique before saving. | {"auth": {},"clientId": "oD6i_WpIes8pBvWjNXy81","fullName": "hediyeh","email": "h@gmail.com","password": "111111","confirmPassword": "111111","type": "signup","_socketId": "kmN2iMG_VAMQ7WSFAEhJ","topic": "uibuilder","_msgid": "96c8e068f17bef90"} | 
// Failure Response {"action": "register","status": "failed","message": "Email already exists. Please use a different email."} // Success Response {"action": "register","status": "success","message": "You have registered successfully!","URL": "https://codenxa.leanea.com/hediyeh/signin"} | 
| login | Authenticates existing users and establishes a secure session. | Users — Validates credentials (email, password). Tokens — Generates a new authentication token to manage the session and track expiration. | {"auth": {},"clientId": "oD6i_WpIes8pBvWjNXy81","email": "h@gmail.com","password": "111111","rememberMe": false,"type": "login","_socketId": "70nLJLlIrgzsiFLpAEhQ","topic": "uibuilder","_msgid": "2e88832acfbcd402"} | 
// Failure Response 1: Email not found {"action": "login","status": "invalidCredentials","message": "Email not found."} // Failure Response 2: Password does not match {"action": "login","status": "invalidCredentials","message": "Password does not match."} // Success Response: Logged in successfully {"action": "login","status": "success","message": "Logged In Successfully"} | 
| logOut | Ends the user session and removes authentication. | Tokens — Invalidates the user token. | {"type": "logOut","auth": {},"userToken": "<userToken>","clientId": "DucPqZtrHFQNgOnyiGV02","_socketId": "0TlVsj-P27gZSW9TAEhb","topic": "uibuilder","_msgid": "9cfd70eb231eb96c"} | 
N/A | 
| sendResetCode | Initiates the password recovery process. | PasswordResets — Creates a record with a verification code, userId, email, createdAt, and expiresAt. | {"auth": {},"type": "resetPassword","email": "h@gmail.com","_socketId": "TQQZkMEJW3yr8AXbAEhi","topic": "uibuilder","_msgid": "563ae2fa2bd25a81"} | 
// Failure Response 1 {"action": "resetPassword","status": "invalidCode","message": "code not found"} // Failure Response 2 {"action": "resetPassword","status": "expired","message": "your code has been expired"} // Success Response {"action": "resetPassword","status": "validCode","message": "Code matched successfully"} | 
| resetPassword | Finalizes password change after validation. | Users — Commits the new password. PasswordResets — Confirms the reset process is completed and the code is invalidated. | {"auth": {},"type": "resetPassword","email": "h@gmail.com","_socketId": "TQQZkMEJW3yr8AXbAEhi","topic": "uibuilder","_msgid": "563ae2fa2bd25a81"} | 
// Failure Response {"action": "resetPassword","status": "invalidEmail","message": "Email address not found."} // Success Response {"action": "resetPassword","status": "validEmail","message": "Reset code sent to your email"} | 
| resendResetPassword | Resends a password reset code. | PasswordResets — Updates or regenerates a reset code and resends it. | {"auth": {},"email": "h@gmail.com","type": "resendResetPassword","_socketId": "TQQZkMEJW3yr8AXbAEhi","topic": "TrustedCloud Password Reset Code – Action Required","_msgid": "a8539873adf09c69"} | 
// Failure Response {"action": "resetPassword","status": "invalidEmail","message": "Email address not found."} // Success Response {"action": "resetPassword","status": "validEmail","message": "Reset code sent to your email"} | 
| setNewPassword | Updates the password after reset code verification. | Users — Updates the password field for the corresponding account. PasswordResets — Marks the code as “used.” | {"auth": {},"type": "setNewPassword","email": "h@gmail.com","password": "123456","_socketId": "3hHNyKWUI-Khbn9cAEh0","topic": "TrustedCloud Password Reset Code – Action Required","_msgid": "4a86446e49c05025"} | 
// Success Response {"action": "setNewPassword","status": "success","message": "Password updated successfully."} | 
| archiveRequest | Moves completed or inactive requests to an archived state. | Requests — Sets the archive flag to true for historical tracking. | {"type": "archiveRequest","auth": {"userToken": "<userToken>"},"clientId": "DucPqZtrHFQNgOnyiGV02","requestID": 13,"_socketId": "0KAO_QB8U2vpv5xDAElI","topic": "uibuilder","_msgid": "7c5fc73c0374d772"} | 
N/A | 
| editCompanyDetails | Allows users to update company info in a request. | Requests — Updates companyDetails for a specific requestId. | {"type": "editCompanyDetails","auth": {"userToken": "<userToken>"},"clientId": "DucPqZtrHFQNgOnyiGV02","companyDetails": {},"_socketId": "0KAO_QB8U2vpv5xDAElI","topic": "uibuilder","_msgid": "318f5048ec2d"} | 
N/A | 
| editProfile | Modifies personal user information. | Users — Updates profile fields like name, email, and avatar. | {"type": "editProfile","auth": {"userToken": "<userToken>"},"clientId": "DucPqZtrHFQNgOnyiGV02","profile": {"userId": "68f830f9d6c4398ea5f5cbdf","email": "david.kim@brightwave.co","role": "Applicant","roles": ["Applicant","Auditor"]}} | 
N/A | 
| auditRequest | Marks a request as “Audited” by an Auditor. | Requests — Updates status to “Audited” and logs auditor info. | {"type": "auditRequest","auth": {"userToken": "<userToken>"},"clientId": "DucPqZtrHFQNgOnyiGV02","userId": "68f830f9d6c4398ea5f5cbdf","requestId": 18,"auditDecision": {"status": "Audited","auditDate": "2025-10-28","comment": "yes"},"_socketId": "0KAO_QB8U2vpv5xDAElI","topic": "uibuilder","_msgid": "196c7d70d5cc4"} | 
N/A | 
| approveRequest | Approvers mark a request as “Approved.” | Requests — Updates status to “Approved” and logs approver info. | {"type": "approveRequest","auth": {"userToken": "<userToken>"},"clientId": "DucPqZtrHFQNgOnyiGV02","userId": "68f83638d6c4398ea5f5cbe6","requestId": 18,"approverDecision": {},"_socketId": "fVUzLOHQeYT96ZrqAEla","topic": "uibuilder","_msgid": "b26bc97d28188"} | 
N/A | 
| createRequest (Applicant) | Initiates a new request submission. | Requests — Creates a record with companyDetails, userId, status, and timestamps. | {"type": "createRequest","auth": {"userToken": "<userToken>"},"clientId": "DucPqZtrHFQNgOnyiGV02","companyDetails": {},"_socketId": "0KAO_QB8U2vpv5xDAElI","topic": "uibuilder","_msgid": "46c1c3b28576"} | 
{"status": "success","action": "createRequest","message": "Request created successfully."} | 
| getDashboard (Applicant & Auditor) | Retrieves user-specific dashboard data. | Requests — Fetches data based on user role and status. Users — Determines role to tailor dashboard view. | {"type": "getDashboard","auth": {},"userToken": "<userToken>","clientId": "DucPqZtrHFQNgOnyiGV02","_socketId": "X6ZKjzzFvV-ibjhwAEiI","_msgid": "8bd0ae08cdfe06a9"} | 
{"type": "getDashboard","payload": {"applicantRequests": [/*...*/],"auditorRequests": [/*...*/]}} | 
| getDashboard (Applicant & Approver) | Retrieves user-specific dashboard data. | Requests — Fetches data based on role. Users — Determines role to tailor dashboard view. | {"type": "getDashboard","auth": {},"userToken": "<userToken>","clientId": "DucPqZtrHFQNgOnyiGV02","_socketId": "X6ZKjzzFvV-ibjhwAEiI","_msgid": "8bd0ae08cdfe06a9"} | 
{"type": "getDashboard","payload": {"applicantRequests": [],"approverRequests": [{},{},{},{}]}} | 
| getDashboard (Admin) | Retrieves dashboard for Admin users. | Requests — Fetches all requests. Users — Admin role gets full access. | {"type": "getDashboard","auth": {},"userToken": "<userToken>","clientId": "DucPqZtrHFQNgOnyiGV02","_socketId": "X6ZKjzzFvV-ibjhwAEiI","_msgid": "8bd0ae08cdfe06a9"} | 
{"type": "getDashboard","payload": {"requests": [{}],"profile": {}}} | 
| getDashboard (Applicant) | Retrieves dashboard for Applicant. | Requests — Fetches only the applicant's requests. Users — Determines role to tailor view. | {"type": "getDashboard","auth": {},"userToken": "<userToken>","clientId": "DucPqZtrHFQNgOnyiGV02","_socketId": "X6ZKjzzFvV-ibjhwAEiI","_msgid": "8bd0ae08cdfe06a9"} | 
{"type": "getDashboard","payload": {"applicantRequests": [{"_id": "68ffe1e7d6c4398ea5f5cd08","status": "Pending","profile": {"userId": "68ffe196d6c4398ea5f5cd06","roles": ["Applicant"]}}]}} | 






    
Top comments (0)