Psyter Node.js WebSocket Server - Detailed Structure

Project: Real-time WebSocket Server (Node.js)
Technology: Node.js, WebSocket (ws), SQL Server, Firebase Cloud Messaging
Purpose: Real-time communication for video calls, messaging, and presence status
Last Updated: November 5, 2025


📁 Root Structure

NodeServer/
├── server.js                    # Main WebSocket server (2,968 lines)
├── package.json                 # NPM dependencies
├── package-lock.json            # Dependency lock file
├── config.json                  # Runtime configuration
├── restart.sh                   # Server restart script
├── ssl/                         # SSL certificates
│   ├── AuthKey_L7XHN784FM.p8   # Apple Push Notification key
│   ├── PSYTER_DEV/             # Dev environment certs
│   ├── PSYTER_LIVE/            # Production certs
│   └── QURAN_LMS/              # Other project certs
├── node_modules/                # NPM packages
├── .git/                        # Git repository
└── .vs/                         # Visual Studio files

🎯 Project Overview

Purpose

Real-time communication hub that powers:
- WebRTC Signaling: Video/audio call setup and management
- Presence Status: Online/offline/away status tracking
- Instant Messaging: Real-time chat with read receipts
- Push Notifications: FCM for Android, APNs for iOS
- Multi-platform Support: Web, Android, iOS clients

Architecture

WebSocket Server:
- Persistent Connections: Long-lived bidirectional connections
- Two Connection Modes:
- Presence Mode: Status tracking, messaging, notifications
- Collaboration Mode: Video/audio call signaling (WebRTC)
- Dual Authentication: User credentials or communication key
- Database Integration: SQL Server for persistence
- Push Notifications: Firebase Cloud Messaging + Apple Push Notification

Key Capabilities

Real-time Messaging: Instant message delivery with status tracking
Video Call Signaling: WebRTC SDP/ICE candidate exchange
User Presence: Online/offline/busy/away status
Multi-device Support: Same user on multiple devices
Network Monitoring: Poor connection detection
Call Recording: Session time tracking
Firebase Push: Offline user notifications
Heartbeat/Ping: Connection health monitoring


📦 Dependencies

package.json

{
  "name": "cn.signaling-server",
  "version": "0.0.0",
  "description": "CN.SignalingServer",
  "main": "server.js",
  "scripts": {
    "start": "node server.js --clientmode=2 &",
    "stop": "pkill -f QuranLive",
    "restart": "pkill -f QuranLive && node server.js --clientmode=2 &"
  },
  "dependencies": {
    "concurrently": "5.0.2",        // Run multiple processes
    "winston": "3.2.1",              // Logging
    "winston-daily-rotate-file": "4.4.1",  // Log rotation
    "ws": "7.2.1",                   // WebSocket server
    "mssql": "6.0.1",                // SQL Server client
    "firebase-admin": "8.9.1",       // Firebase push notifications
    "jsonfile": "5.0.0",             // JSON file operations
    "apn": "2.2.0",                  // Apple Push Notification
    "yargs": "15.1.0"                // Command-line args parsing
  }
}

Core Modules

WebSocket: ws@7.2.1
- Production-grade WebSocket server
- Connection management
- Heartbeat/ping support

Database: mssql@6.0.1
- SQL Server connectivity
- Stored procedure execution
- Connection pooling

Logging: winston@3.2.1
- Daily rotating log files
- Multiple log levels (INFO, ERROR)
- Console + file output

Push Notifications:
- firebase-admin@8.9.1 - Android/Web push
- apn@2.2.0 - iOS push (VoIP)


⚙️ Configuration

config.json

{
  "enableLog": "true"
}

Purpose: Runtime configuration (hot-reloadable every 10 seconds)

Settings:
- enableLog: Enable/disable logging without restart

Client Mode Selection

Command Line Argument: --clientmode=<number>

Available Modes:

QURAN_DEV: 1          // Port 2333
QURAN_LMS: 2          // Port 1443
PSYTER_DEV: 3         // Port 3333
PSYTER_LIVE: 4        // Port 3223
ONE2ONE: 5            // Port 4223
ASSESSMENTSYSTEM_*    // Ports 5223-5228
NARAAKUM_DEV: 12      // Port 6223

For Psyter:
- Development: --clientmode=3 (Port 3333)
- Production: --clientmode=4 (Port 3223)

Environment-Specific Configuration

Psyter Development:

dbConfig = {
    user: 'PsyterUser',
    password: 'PsyterPa$$w0Rd',
    server: 'db.innotech-sa.com',
    database: 'Psyter_v1'
};
port = 3333;
process.title = 'NODE_PSYTER_DEV';

Psyter Production:

dbConfig = {
    user: 'PsyterUser',
    password: 'Zo@mb!sPsyter',
    server: '51.89.234.59',
    database: 'Psyter'
};
port = 3223;
process.title = 'PsyterLive';

Firebase Configuration:

fcmAPISettings = {
    ProjectId: "psyterdev",
    ClientEmail: "firebase-adminsdk-uh0r6@psyterdev.iam.gserviceaccount.com",
    PrivateKey: "-----BEGIN PRIVATE KEY-----\n...",
    DatabaseURL: "https://psyterdev.firebaseio.com"
};


🔌 Connection Modes

1. Presence Mode (General Communication)

Purpose: User status, messaging, notifications

Connection URL:

wss://server:3333/?connectionMode=1&communicationKey=ABC123&deviceId=DEVICE123&clientPlatform=2

Parameters:
- connectionMode=1 - Presence mode
- communicationKey - User verification token
- deviceId - Unique device identifier
- clientPlatform - 1=Web, 2=Android, 3=iOS
- reConnect - 1 if reconnecting
- authenticateOnly - 1 for auth check only

Alternative Authentication:
- userName + password - Direct credentials
- clientKey - API client key

Features:
- User presence (online/offline/away/busy)
- Instant messaging
- File sharing
- Typing indicators
- Push notifications
- Friend management

2. Collaboration Mode (Video/Audio Calls)

Purpose: WebRTC signaling for video/audio calls

Connection URL:

wss://server:3333/?connectionMode=2&fromUserId=123&collaborationKey=KEY123&clientPlatform=2

Parameters:
- connectionMode=2 - Collaboration mode
- fromUserId - Calling user ID
- collaborationKey - Unique call session ID
- clientPlatform - Client type
- reConnect - 1 if reconnecting

Features:
- WebRTC SDP exchange
- ICE candidate exchange
- Call hold/unhold
- Camera on/off
- Audio mute/unmute
- Screen sharing
- Network quality monitoring
- Call time limits


📡 Command System

Command Enumerations

Total Commands: 66

Presence Commands (P_*):

P_AUTHENTICATE: 1              // Initial authentication
P_SIGNIN: 2                    // User signed in
P_SIGNOUT: 3                   // User signed out
P_CLOSE: 4                     // Close connection
P_CHAT: 5                      // Chat message
P_STATUS: 6                    // Status change
P_COLLABORATION: 9             // Collaboration request
P_SEND_MESSAGE: 55             // Send message
P_RECIEVE_MESSAGE: 56          // Receive message
P_GET_NEW_MESSAGE_COUNT: 57    // Get unread count
P_GET_MESSAGE_CONVERSATION_LIST: 58  // Get conversations
P_GET_CONVERSATION_MESSAGE_LIST: 59  // Get messages
P_UPDATE_MESSAGE_STATUS: 60    // Update read status
P_REFRESH_NOTIFICATION: 65     // Refresh notifications

Collaboration Commands (C_*):

C_INITIATE: 12                 // Start collaboration
C_CREATE_COLLABORATION: 13     // Create call
C_JOINED_COLLABORATION: 14     // User joined
C_SDP: 16                      // WebRTC SDP offer/answer
C_ICECANDIDATE: 17             // WebRTC ICE candidate
C_LEAVE_COLLABORATION: 18      // Leave call
C_END_COLLABORATION: 19        // End call
C_HOLD_COLLABORATION: 21       // Hold call
C_UNHOLD_COLLABORATION: 22     // Unhold call
C_CAMERA_ON: 23                // Enable camera
C_CAMERA_OFF: 24               // Disable camera
C_AUDIO_ON: 32                 // Unmute audio
C_AUDIO_OFF: 33                // Mute audio
C_NETWORK_POOR: 25             // Poor connection
C_NETWORK_OK: 26               // Good connection
C_TYPING_ON: 36                // Typing indicator on
C_TYPING_OFF: 37               // Typing indicator off
C_TIME_LIMIT: 50               // Set call time limit
C_UPDATE_COUNT: 51             // Update service count

Reason Codes:

SUCCESS: 1                     // Operation successful
ERROR: 2                       // General error
INVALID_REQUEST: 3             // Invalid request
INVALID_USER: 4                // User not found
DATABASE_NOT_AVAILABLE: 5      // DB error
RECONNECT: 6                   // Reconnection needed
ALREADY_LOGGED_IN: 7           // User already logged in
REJECT_COLLABORATION: 8        // Call rejected
NO_ANSWER: 9                   // Call not answered
ALREADY_IN_COLLABORATION: 11   // Already in call
CLOSE_COLLABORATION: 13        // Call closed
TIMEOUT: 16                    // Session timeout


💬 Message Protocol

Message Structure

All WebSocket messages are JSON:

{
    "Message": "...",              // Command-specific data
    "CollaborationKey": "...",     // Call session ID (if applicable)
    "ConnectionMode": 1 or 2,      // Presence or Collaboration
    "Command": 5,                  // Command code
    "Reason": 1,                   // Reason code
    "FromUser": {...},             // Sender info
    "ToUserList": [{...}],         // Recipient(s)
    "ClientId": 123456789,         // Client connection ID
    "ClientInitiationTime": "...", // Client timestamp
    "ServerIntimationTime": "...", // Server timestamp
    "CollaborationMode": 3,        // Audio/Video mode
    "SessionTime": 300             // Call duration (seconds)
}


🔐 Authentication Flow

1. Connect with Communication Key

Client → Server:

wss://server:3333/?connectionMode=1&communicationKey=ABC123&deviceId=DEVICE123&clientPlatform=2

Server:
1. Execute stored procedure COB_Authenticate_Get_User_List
2. Validate communication key
3. Load user info and related users
4. Create WebSocket connection
5. Broadcast signin to all friends

Server → Client:

{
    "Command": 1,  // P_AUTHENTICATE
    "Reason": 1,   // SUCCESS
    "FromUser": {
        "Id": 123,
        "Fullname": "Dr. John Smith",
        "Status": 2,  // ONLINE
        "NetworkStatus": 7
    },
    "ToUserList": [...]  // Friends list
}

2. Connect with Username/Password

Client → Server:

wss://server:3333/?connectionMode=1&userName=john@example.com&password=pass123&deviceId=DEVICE123&clientPlatform=2

Server:
1. Execute COB_Authenticate_Get_User_List with username/password
2. Same flow as communication key

3. Reconnection

Client → Server:

wss://server:3333/?connectionMode=1&communicationKey=ABC123&reConnect=1&collaborationKey=CALL123

Server:
1. Re-authenticate user
2. If collaborationKey provided, rejoin call
3. Restore previous state


📞 Video Call Flow (WebRTC Signaling)

Complete Call Lifecycle

1. Initiator Starts Call:

// CLIENT → SERVER (Presence Mode)
{
    "ConnectionMode": 1,
    "Command": 13,  // C_CREATE_COLLABORATION
    "FromUser": {"Id": 123, "Fullname": "Dr. Smith"},
    "ToUserList": [{"Id": 456, "Fullname": "Patient John"}],
    "CollaborationMode": 3,  // VIDEO_AUDIO
    "ClientInitiationTime": "2025-11-05 10:30:00",
    "SessionTime": 1800,  // 30 minutes
    "Message": "{...additional data...}"
}

2. Server Creates Collaboration Session:

// Server generates unique collaborationKey
collaboration = {
    Key: "abc-123-def-456",
    Initiator: {Id: 123, ...},
    ParticipantList: [{Id: 456, ...}],
    BookingId: 789,
    IsScheduled: 1
};

3. Server Notifies Recipient (FCM/APNs):

// SERVER → FIREBASE/APNS
{
    "topic": "doctor_456~",
    "title": "Incoming call",
    "body": {
        "Command": 7,  // P_ACCEPT_COLLABORATION
        "FromUser": {"Id": 123, "Fullname": "Dr. Smith"},
        "CollaborationKey": "abc-123-def-456",
        "CollaborationMode": 3
    }
}

4. Recipient Connects (Collaboration Mode):

wss://server:3333/?connectionMode=2&fromUserId=456&collaborationKey=abc-123-def-456&clientPlatform=2

5. Server Confirms Connection:

// SERVER → RECIPIENT
{
    "Command": 12,  // C_INITIATE
    "Reason": 1,
    "CollaborationKey": "abc-123-def-456"
}

// SERVER → INITIATOR
{
    "Command": 14,  // C_JOINED_COLLABORATION
    "FromUser": {"Id": 456, "Fullname": "Patient John"}
}

6. WebRTC SDP Exchange:

// INITIATOR → SERVER
{
    "Command": 16,  // C_SDP
    "Message": "{\"type\":\"offer\",\"sdp\":\"...\"}",
    "CollaborationKey": "abc-123-def-456"
}

// SERVER → RECIPIENT (broadcasts to other party)
{
    "Command": 16,
    "Message": "{\"type\":\"offer\",\"sdp\":\"...\"}",
    "FromUser": {"Id": 123}
}

// RECIPIENT → SERVER
{
    "Command": 16,
    "Message": "{\"type\":\"answer\",\"sdp\":\"...\"}",
    "CollaborationKey": "abc-123-def-456"
}

// SERVER → INITIATOR
{
    "Command": 16,
    "Message": "{\"type\":\"answer\",\"sdp\":\"...\"}",
    "FromUser": {"Id": 456}
}

7. ICE Candidate Exchange:

// EITHER PARTY → SERVER
{
    "Command": 17,  // C_ICECANDIDATE
    "Message": "{\"candidate\":\"...\"}",
    "CollaborationKey": "abc-123-def-456"
}

// SERVER → OTHER PARTY (broadcasts)
{
    "Command": 17,
    "Message": "{\"candidate\":\"...\"}",
    "FromUser": {...}
}

8. Call Controls:

// Mute Audio
{
    "Command": 33,  // C_AUDIO_OFF
    "CollaborationKey": "abc-123-def-456"
}

// Turn Off Camera
{
    "Command": 24,  // C_CAMERA_OFF
    "CollaborationKey": "abc-123-def-456"
}

// Hold Call
{
    "Command": 21,  // C_HOLD_COLLABORATION
    "CollaborationKey": "abc-123-def-456"
}

9. End Call:

// EITHER PARTY → SERVER
{
    "Command": 19,  // C_END_COLLABORATION
    "CollaborationKey": "abc-123-def-456"
}

// SERVER → OTHER PARTY
{
    "Command": 19,
    "Reason": 1,
    "FromUser": {...}
}

// Server updates call consumption in database


💬 Messaging Flow

Send Message

Client → Server:

{
    "ConnectionMode": 1,
    "Command": 55,  // P_SEND_MESSAGE
    "FromUser": {"Id": 123, "Fullname": "Dr. Smith"},
    "ToUserList": [{"Id": 456}],
    "Message": "{
        \"Text\": \"Hello, how are you?\",
        \"FilePath\": null,
        \"CatFileTypeId\": 1
    }",
    "ClientInitiationTime": "2025-11-05 10:30:00"
}

Server Processing:
1. Execute Message_InsertMessage stored procedure
2. Store message in database
3. Send to recipient if online
4. Send FCM/APNs if offline
5. Confirm to sender

Server → Recipient:

{
    "Command": 56,  // P_RECIEVE_MESSAGE
    "Reason": 1,
    "FromUser": {"Id": 123, "Fullname": "Dr. Smith"},
    "Message": "[{
        \"MessageId\": 789,
        \"SenderId\": 123,
        \"RecieverId\": 456,
        \"Text\": \"Hello, how are you?\",
        \"CreatedDate\": \"2025-11-05 10:30:00\",
        \"CatMessageStatus\": 1
    }]"
}

Server → Sender (Confirmation):

{
    "Command": 55,
    "Reason": 1,
    "Message": "[{\"MessageId\": 789, ...}]"
}

Get New Message Count

Client → Server:

{
    "Command": 57,  // P_GET_NEW_MESSAGE_COUNT
    "FromUser": {"Id": 123}
}

Server → Client:

{
    "Command": 57,
    "Reason": 1,
    "Message": "[{\"NewMessagesCount\": 5}]"
}

Get Conversations List

Client → Server:

{
    "Command": 58,  // P_GET_MESSAGE_CONVERSATION_LIST
    "FromUser": {"Id": 123},
    "Message": "{
        \"PageNumber\": 0,
        \"PageSize\": 20,
        \"DeviceIdentityToken\": \"token123\"
    }"
}

Server → Client:

{
    "Command": 58,
    "Reason": 1,
    "Message": "{
        \"ConversationsList\": [...],
        \"Statistics\": {\"TotalCount\": 15},
        \"DeviceIdentityToken\": \"token123\"
    }"
}

Get Conversation Messages

Client → Server:

{
    "Command": 59,  // P_GET_CONVERSATION_MESSAGE_LIST
    "FromUser": {"Id": 123},
    "ToUserList": [{"Id": 456}],
    "Message": "{\"PageNumber\": 0, \"PageSize\": 50}"
}

Server → Client:

{
    "Command": 59,
    "Reason": 1,
    "Message": "{
        \"MessagesList\": [...],
        \"Statistics\": {\"TotalCount\": 100}
    }"
}

Update Message Status (Read Receipts)

Client → Server:

{
    "Command": 60,  // P_UPDATE_MESSAGE_STATUS
    "FromUser": {"Id": 123},
    "ToUserList": [{"Id": 456}],
    "Message": "{\"MessageStatus\": 3}"  // 3 = Read
}

Server:
1. Update all messages from sender(s) to read
2. Notify sender(s) that messages were read

Server → Sender:

{
    "Command": 60,
    "Reason": 1,
    "FromUser": {"Id": 123},
    "Message": "{\"UpdatedCount\": 5}"
}


🔔 Push Notifications

Firebase Cloud Messaging (Android/Web)

Topic Structure:

// For patients
topic = "patient_" + patientId + "~";

// For providers
topic = "doctor_" + serviceProviderId + "~";

Call Notification:

message = {
    condition: "'doctor_456~' in topics",
    data: {
        title: "P_COLLABORATION",
        body: JSON.stringify({
            FromUser: {Id: 123, Fullname: "Dr. Smith"},
            CollaborationKey: "abc-123",
            CollaborationMode: 3,
            Command: 7
        })
    },
    android: { priority: "high" },
    apns: {
        payload: {
            aps: {
                alert: {
                    title: "Incoming call",
                    body: "Dr. Smith"
                },
                sound: "ringtone.mp3"
            },
            collaborationData: "..."
        }
    }
};

Message Notification:

message = {
    topic: "patient_456~",
    data: { title: fromUser.Fullname, body: messageText },
    android: { priority: "high" },
    apns: {
        payload: {
            aps: {
                alert: {
                    title: "Dr. Smith",
                    body: "Hello, how are you?"
                }
            },
            messageData: "..."
        }
    }
};

Apple Push Notification (iOS VoIP)

Configuration:

apnOptions = {
    token: {
        key: "ssl/AuthKey_L7XHN784FM.p8",
        keyId: "L7XHN784FM",
        teamId: "EV53AQE262"
    },
    production: false
};

VoIP Notification:

note.payload = {
    aps: {
        alert: {
            title: "Incoming call",
            body: "Dr. Smith"
        },
        sound: "ringtone.mp3"
    },
    collaborationData: JSON.stringify(callData)
};
note.topic = "com.innotech-sa.Psyter.voip";


🗄️ Database Integration

Stored Procedures Used

Authentication:
- COB_Authenticate_Get_User_List - Authenticate user
- COB_Authenticate - Validate client key

User Management:
- COB_Get_Related_User_List - Get user’s contacts
- COB_Get_User_List - Get all users
- COB_Manage_User_Friend - Add/remove friend
- COB_Update_Password - Change password
- COB_Create_User - Register new user

Messaging:
- Message_InsertMessage - Save message
- Message_GetNewMessagesCount - Get unread count
- Message_GetUserConversationsList - Get conversations
- Message_GetUserConversationMessages - Get messages
- Message_UpdateMessageStatus - Update read status

Call Management:
- Mobile_Package_ConsumptionDetail_Update - Track call usage
- SP_ManageCareProvidersServiceCount - Update service count
- SP_GetBookingDetailsById - Get booking info

Data Structures

User Object:

{
    Id: 123,
    UserInfo: {
        Id: 123,
        Fullname: "Dr. John Smith",
        Email: "john@example.com",
        Status: 2,  // ONLINE
        NetworkStatus: 7,  // NETWORK_OK
        CollaborationStatus: 1,  // AVAILABLE
        IsPatient: false,
        ServiceProviderId: 456,
        PicPath: "/path/to/pic.jpg"
    },
    UserList: [...]  // Related users
}

Collaboration Object:

{
    Key: "abc-123-def-456",
    Initiator: {Id: 123, Fullname: "Dr. Smith", ...},
    ParticipantList: [{Id: 456, ...}],
    BookingId: 789,
    IsScheduled: 1,
    StartTime: "2025-11-05 10:30:00",
    TimeLimit: 1800,
    CountdownTimer: intervalId
}


🔍 Connection Management

Connection Lists

Presence Connections:

pUserConnectionList = [
    {
        Id: 123,  // User ID
        wsClientList: [
            {
                Id: 1635940123456,  // Client ID (timestamp)
                deviceId: "DEVICE123",
                clientPlatform: 2,  // Android
                wsClient: <WebSocket>
            }
        ]
    }
];

Collaboration Connections:

cUserConnectionList = [
    {
        Id: 123,  // User ID
        wsClientList: [
            {
                Id: 1635940123456,
                wsClient: <WebSocket>
            }
        ]
    }
];

User List:

userList = [
    {
        Id: 123,
        UserInfo: {...},
        UserList: [...]  // Friends
    }
];

Collaboration List:

collaborationList = [
    {
        Key: "abc-123",
        Initiator: {...},
        ParticipantList: [...]
    }
];

Multi-Device Support

Same User on Multiple Devices:
- Each device gets unique clientId
- All devices in wsClientList array
- Status changes broadcast to all devices
- Messages delivered to all devices

Example:

{
    Id: 123,
    wsClientList: [
        {Id: 1111, deviceId: "PHONE", clientPlatform: 2, wsClient: ws1},
        {Id: 2222, deviceId: "TABLET", clientPlatform: 2, wsClient: ws2},
        {Id: 3333, deviceId: "WEB", clientPlatform: 1, wsClient: ws3}
    ]
}


🔥 Health Monitoring

Heartbeat/Ping

Interval: Every 30 minutes

Process:

setInterval(function ping() {
    wss.clients.forEach(function each(ws) {
        if (ws.isAlive === false) {
            // Connection dead, clean up
            OnUnExpectedConnectionClose(ws);
            CleanSocketConnectionList();
            return ws.terminate();
        }

        ws.isAlive = false;

        if (ws.readyState === ws.OPEN) {
            ws.ping(noop);  // Send ping
        }
    });
}, (30 * 60 * 1000));

Client Response:
- Client must respond with pong
- Sets ws.isAlive = true
- Failure to respond = dead connection

Network Quality Monitoring

Commands:
- C_NETWORK_POOR / P_NETWORK_POOR - Bad connection
- C_NETWORK_OK / P_NETWORK_OK - Connection restored

Broadcast:
- Notify all participants in call
- Update user status
- Client can show warning UI


📊 Logging

Winston Logger

Configuration:

const logDir = '/var/www/html/node/pro/log/PSYTER_DEV/';

logger = createLogger({
    level: 'info',
    format: format.combine(
        format.colorize(),
        format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
        format.printf(info => `${info.timestamp}: ${info.message}`)
    ),
    transports: [
        new transports.Console(),
        new transports.DailyRotateFile({
            filename: `${logDir}/PSYTER_DEV_%DATE%.txt`,
            datePattern: 'YYYY-MM-DD'
        })
    ]
});

Log Files:
- Location: /var/www/html/node/pro/log/PSYTER_DEV/
- Rotation: Daily (one file per day)
- Format: PSYTER_DEV_2025-11-05.txt

What’s Logged:

-#----- Active Connections ( 15 ) -----#-
INFO  -->  CLIENT  -->  cCommand.P_SEND_MESSAGE  -->  Sender = Dr. Smith (123)
INFO  -->  SERVER  -->  cCommand.P_RECIEVE_MESSAGE, cReason.SUCCESS  -->  Sender = Dr. Smith (123), Receiver = Patient John (456)

Error Handling

Global Error Handlers:

process
    .on('unhandledRejection', (reason, p) => {
        logInfo("process  -->  unhandledRejection  -->  reason = " + reason, logType.ERROR);
    })
    .on('uncaughtException', (err) => {
        logInfo("process  -->  uncaughtException  -->  err = " + err, logType.ERROR);
    });

SQL Errors:

sql.on('error', err => {
    logInfo("sql  -->  on(error)  -->  err = " + err, logType.ERROR);
});


🚀 Deployment

Manual Deployment

1. Install Dependencies:

cd /presense/pro/psyter/
npm install

2. Start Server (Dev):

node server.js --clientmode=3

3. Start Server (Production):

node server.js --clientmode=4

4. Background Mode:

nohup node server.js --clientmode=4 > output.txt 2>&1 &

Using NPM Scripts

Start:

npm start  # Uses clientmode=2 (QURAN_LMS) by default

Stop:

npm stop  # Kills QuranLive process

Restart:

npm run restart

restart.sh Script

#!/bin/sh
sudo pkill -f QuranLive && cd /presense/pro/quran/ && sudo nohup npm start > output.txt 2>&1 &
exit 0

Usage:

./restart.sh

Process Management

Check Running Process:

ps aux | grep NODE_PSYTER_DEV

Kill Process:

pkill -f NODE_PSYTER_DEV

View Logs:

tail -f /var/www/html/node/pro/log/PSYTER_DEV/PSYTER_DEV_2025-11-05.txt


🔒 SSL Configuration

Certificate Structure

ssl/
├── AuthKey_L7XHN784FM.p8     # Apple Push Notification key
├── PSYTER_DEV/
│   ├── key_live.pem           # Private key
│   └── cert_live.pem          # Certificate
├── PSYTER_LIVE/
│   ├── key_live.pem
│   └── cert_live.pem
└── QURAN_LMS/
    ├── key_live.pem
    └── cert_live.pem

HTTPS Server

var pkey = fs.readFileSync(`ssl/PSYTER_DEV/key_live.pem`);
var pcert = fs.readFileSync(`ssl/PSYTER_DEV/cert_live.pem`);
var options = {
    key: pkey,
    cert: pcert,
    passphrase: '123456789'
};

sslSrv = new https.createServer(options).listen(port);
wss = new ws.Server({ server: sslSrv });

WebSocket URLs:
- Dev: wss://server:3333
- Prod: wss://server:3223


🎯 Key Features Explained

1. Multi-Device Support

Same user on phone + tablet + web:

// User 123 connects from phone
pUserConnectionList[123].wsClientList.push({
    Id: 1111,
    deviceId: "PHONE_ABC",
    clientPlatform: 2,
    wsClient: ws1
});

// Same user connects from web
pUserConnectionList[123].wsClientList.push({
    Id: 2222,
    deviceId: "WEB_XYZ",
    clientPlatform: 1,
    wsClient: ws2
});

// Message sent to user 123 → delivered to both devices

2. Presence Broadcast

When user comes online:

// User 123 signs in
PBroadCast(123, clientId, P_SIGNIN, SUCCESS, ONLINE);

// Broadcasts to all friends:
user123.UserList.forEach(friend => {
    if (friend is online) {
        Send P_SIGNIN to friend;
    }
});

3. Call Time Limits

Set time limit on call:

// Client sends time limit
{
    "Command": 50,  // C_TIME_LIMIT
    "Message": "{\"TimeLimit\": 1800, \"BookingId\": 789}"
}

// Server starts countdown
collaborationObj.CountdownTimer = setInterval(() => {
    CConsumptionInsert(collaborationKey, true);
}, 1800 * 1000);

// After 30 minutes, server sends timeout
{
    "Command": 54,  // C_TIMEOUT_COLLABORATION
    "Reason": 16    // TIMEOUT
}

4. Offline User Handling

Message to offline user:

// Try to send message to user 456
if (pUserConnection for 456 not found) {
    // Send FCM/APNs push notification
    SendFCMNotification("patient_456~", "Dr. Smith", messageData);
}

5. Call Recording

Track call consumption:

// On call start
collaborationObj.StartTime = getCurrentUTCDateTime();

// On call end
endDateTime = getCurrentUTCDateTime();
Mobile_Package_ConsumptionDetail_Update(
    studentId,
    staffId,
    startDateTime,
    endDateTime,
    bookingId
);


📈 Performance Considerations

Connection Limits

Current Stats:
- Active Connections: Logged on each message
- No Hard Limit: Node.js can handle 10,000+ connections
- Memory Usage: ~1 MB per connection

Optimization Strategies

1. Efficient Broadcasting:

// Only send to online users
userTempList = userTempList.filter(p => isOnline(p.Id));

2. Connection Cleanup:

// Every 30 minutes
CleanSocketConnectionList();  // Remove dead connections
CleanOtherUserList();         // Remove unused data

3. Database Connection Pooling:

// Single shared connection
connection = sql.connect(dbConfig);

4. Conditional Logging:

// Can disable via config.json
if (enableLog) {
    logger.info(message);
}


🔧 Maintenance Tasks

Regular Tasks

1. Monitor Logs:

tail -f /var/www/html/node/pro/log/PSYTER_DEV/*.txt

2. Check Process:

ps aux | grep NODE_PSYTER

3. Restart Server:

./restart.sh

4. Clean Old Logs:

find /var/www/html/node/pro/log/PSYTER_DEV/ -name "*.txt" -mtime +30 -delete

Troubleshooting

Connection Issues:
1. Check SSL certificates not expired
2. Verify port not blocked by firewall
3. Check database connectivity
4. Review error logs

High Memory Usage:
1. Check connection count
2. Review collaboration list size
3. Restart server if needed

Database Errors:
1. Check connection string
2. Verify stored procedures exist
3. Check database permissions
4. Review SQL error logs


🔄 Integration with Other Systems

With Main API

Flow:
1. User authenticates via Main API
2. Main API generates communicationKey
3. Client connects to WebSocket with key
4. WebSocket validates key with database
5. Real-time features enabled

With Mobile Apps

Android Client:
- WebSocket connection on app start
- FCM for push notifications
- Device ID tracking
- Auto-reconnect on network change

iOS Client:
- WebSocket connection (CFNetwork)
- APNs for VoIP push
- Background connection management
- Call continuity

With Web Portal

Browser Client:
- WebSocket API (new WebSocket(...))
- No push notifications (uses polling fallback)
- Tab synchronization
- Connection recovery


📚 API Summary

Connection Endpoints

Presence:

wss://server:3333/?connectionMode=1&communicationKey=ABC&deviceId=DEV&clientPlatform=2

Collaboration:

wss://server:3333/?connectionMode=2&fromUserId=123&collaborationKey=KEY&clientPlatform=2

Key Commands

Authentication:
- P_AUTHENTICATE - Initial auth
- P_SIGNIN - User signed in
- P_SIGNOUT - User signed out

Messaging:
- P_SEND_MESSAGE - Send message
- P_RECIEVE_MESSAGE - Receive message
- P_GET_NEW_MESSAGE_COUNT - Get unread
- P_GET_MESSAGE_CONVERSATION_LIST - Get conversations
- P_GET_CONVERSATION_MESSAGE_LIST - Get messages
- P_UPDATE_MESSAGE_STATUS - Mark as read

Calls:
- C_CREATE_COLLABORATION - Start call
- C_JOINED_COLLABORATION - User joined
- C_SDP - WebRTC offer/answer
- C_ICECANDIDATE - WebRTC ICE
- C_LEAVE_COLLABORATION - Leave call
- C_END_COLLABORATION - End call

Call Controls:
- C_CAMERA_ON/OFF - Camera control
- C_AUDIO_ON/OFF - Audio control
- C_HOLD/UNHOLD_COLLABORATION - Hold control


🎉 Key Benefits

Real-time Communication

Instant messaging with read receipts
Video/audio calls with WebRTC
Presence status for all users
Multi-device sync for seamless experience

Scalability

Handles 1000+ concurrent connections
Efficient broadcasting to online users only
Connection pooling for database
Automatic cleanup of dead connections

Reliability

Heartbeat monitoring every 30 minutes
Auto-reconnect support
Error recovery mechanisms
Comprehensive logging

Integration

Firebase push for Android
APNs push for iOS
Database persistence for messages
REST API compatibility


END OF NODESERVER DOCUMENTATION


Completion Status:
- ✅ Android Client (COMPLETED)
- ✅ AndroidCareProvider (COMPLETED)
- ✅ APIs - Part 1 (COMPLETED)
- ✅ APIs - Part 2 (COMPLETED)
- ✅ Media API (COMPLETED)
- ✅ NodeServer (COMPLETED)
- ⏭️ Tahoon_API (NEXT)
- Pending: Web, WindowsService, IOSCareProvider