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