chatly-sdk 0.0.5 ā 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRIBUTING.md +658 -0
- package/IMPROVEMENTS.md +402 -0
- package/README.md +1538 -164
- package/dist/index.d.ts +430 -9
- package/dist/index.js +1420 -63
- package/examples/01-basic-chat/README.md +61 -0
- package/examples/01-basic-chat/index.js +58 -0
- package/examples/01-basic-chat/package.json +13 -0
- package/examples/02-group-chat/README.md +78 -0
- package/examples/02-group-chat/index.js +76 -0
- package/examples/02-group-chat/package.json +13 -0
- package/examples/03-offline-messaging/README.md +73 -0
- package/examples/03-offline-messaging/index.js +80 -0
- package/examples/03-offline-messaging/package.json +13 -0
- package/examples/04-live-chat/README.md +80 -0
- package/examples/04-live-chat/index.js +114 -0
- package/examples/04-live-chat/package.json +13 -0
- package/examples/05-hybrid-messaging/README.md +71 -0
- package/examples/05-hybrid-messaging/index.js +106 -0
- package/examples/05-hybrid-messaging/package.json +13 -0
- package/examples/06-postgresql-integration/README.md +101 -0
- package/examples/06-postgresql-integration/adapters/groupStore.js +73 -0
- package/examples/06-postgresql-integration/adapters/messageStore.js +47 -0
- package/examples/06-postgresql-integration/adapters/userStore.js +40 -0
- package/examples/06-postgresql-integration/index.js +92 -0
- package/examples/06-postgresql-integration/package.json +14 -0
- package/examples/06-postgresql-integration/schema.sql +58 -0
- package/examples/08-customer-support/README.md +70 -0
- package/examples/08-customer-support/index.js +104 -0
- package/examples/08-customer-support/package.json +13 -0
- package/examples/README.md +105 -0
- package/jest.config.cjs +28 -0
- package/package.json +12 -8
- package/src/chat/ChatSession.ts +81 -0
- package/src/chat/GroupSession.ts +79 -0
- package/src/constants.ts +61 -0
- package/src/crypto/e2e.ts +0 -20
- package/src/index.ts +525 -63
- package/src/models/mediaTypes.ts +58 -0
- package/src/models/message.ts +4 -1
- package/src/transport/adapters.ts +51 -1
- package/src/transport/memoryTransport.ts +75 -13
- package/src/transport/websocketClient.ts +269 -21
- package/src/transport/websocketServer.ts +26 -26
- package/src/utils/errors.ts +97 -0
- package/src/utils/logger.ts +96 -0
- package/src/utils/mediaUtils.ts +235 -0
- package/src/utils/messageQueue.ts +162 -0
- package/src/utils/validation.ts +99 -0
- package/test/crypto.test.ts +122 -35
- package/test/sdk.test.ts +276 -0
- package/test/validation.test.ts +64 -0
- package/tsconfig.json +11 -10
- package/tsconfig.test.json +11 -0
- package/src/ChatManager.ts +0 -103
- package/src/crypto/keyManager.ts +0 -28
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Hybrid Messaging Example
|
|
2
|
+
|
|
3
|
+
Combines real-time and offline messaging (Slack/Instagram-style).
|
|
4
|
+
|
|
5
|
+
## What You'll Learn
|
|
6
|
+
|
|
7
|
+
- Hybrid online/offline messaging
|
|
8
|
+
- Message queue with automatic retry
|
|
9
|
+
- Graceful degradation
|
|
10
|
+
- Best of both worlds
|
|
11
|
+
|
|
12
|
+
## Running the Example
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install
|
|
16
|
+
npm start
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## How It Works
|
|
20
|
+
|
|
21
|
+
This example demonstrates **hybrid messaging**:
|
|
22
|
+
- Real-time delivery when online
|
|
23
|
+
- Automatic queueing when offline
|
|
24
|
+
- Messages sent when reconnected
|
|
25
|
+
- Seamless user experience
|
|
26
|
+
|
|
27
|
+
## Output
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
š Chatly SDK - Hybrid Messaging Example
|
|
31
|
+
========================================
|
|
32
|
+
|
|
33
|
+
Initializing hybrid SDK...
|
|
34
|
+
ā
SDK ready (online + offline support)
|
|
35
|
+
|
|
36
|
+
Creating users...
|
|
37
|
+
ā
Alice and Bob created
|
|
38
|
+
|
|
39
|
+
Scenario 1: Online messaging (real-time)
|
|
40
|
+
š¢ Connected
|
|
41
|
+
š¤ Alice: Hey! (delivered instantly)
|
|
42
|
+
šØ Bob received: Hey!
|
|
43
|
+
|
|
44
|
+
Scenario 2: Offline messaging (queued)
|
|
45
|
+
š“ Simulating disconnect...
|
|
46
|
+
š¤ Alice: Are you there? (queued)
|
|
47
|
+
š¤ Alice: I'll wait for you (queued)
|
|
48
|
+
ā³ 2 messages in queue
|
|
49
|
+
|
|
50
|
+
Scenario 3: Reconnection (auto-send)
|
|
51
|
+
š¢ Reconnected!
|
|
52
|
+
ā
Sending queued messages...
|
|
53
|
+
šØ Bob received: Are you there?
|
|
54
|
+
šØ Bob received: I'll wait for you
|
|
55
|
+
|
|
56
|
+
ā
Hybrid messaging works perfectly!
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Key Features
|
|
60
|
+
|
|
61
|
+
- **Smart Delivery**: Real-time when online, queued when offline
|
|
62
|
+
- **Automatic Queue**: Messages queued during disconnection
|
|
63
|
+
- **Auto-Retry**: Failed messages retry automatically
|
|
64
|
+
- **Seamless UX**: Users don't need to worry about connection state
|
|
65
|
+
|
|
66
|
+
## Use Cases
|
|
67
|
+
|
|
68
|
+
- **Social Media DMs** (Instagram, Twitter)
|
|
69
|
+
- **Team Chat** (Slack, Microsoft Teams)
|
|
70
|
+
- **Mobile Apps** (intermittent connectivity)
|
|
71
|
+
- **Progressive Web Apps**
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ChatSDK,
|
|
3
|
+
InMemoryUserStore,
|
|
4
|
+
InMemoryMessageStore,
|
|
5
|
+
InMemoryGroupStore,
|
|
6
|
+
MemoryTransport,
|
|
7
|
+
EVENTS,
|
|
8
|
+
ConnectionState,
|
|
9
|
+
LogLevel
|
|
10
|
+
} from 'chatly-sdk';
|
|
11
|
+
|
|
12
|
+
async function main() {
|
|
13
|
+
console.log('š Chatly SDK - Hybrid Messaging Example');
|
|
14
|
+
console.log('========================================\n');
|
|
15
|
+
|
|
16
|
+
// Use MemoryTransport for demo (simulates WebSocket)
|
|
17
|
+
const transport = new MemoryTransport();
|
|
18
|
+
|
|
19
|
+
console.log('Initializing hybrid SDK...');
|
|
20
|
+
const sdk = new ChatSDK({
|
|
21
|
+
userStore: new InMemoryUserStore(),
|
|
22
|
+
messageStore: new InMemoryMessageStore(),
|
|
23
|
+
groupStore: new InMemoryGroupStore(),
|
|
24
|
+
transport, // Hybrid: works online AND offline
|
|
25
|
+
logLevel: LogLevel.NONE,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Track connection state
|
|
29
|
+
let connectionState = ConnectionState.DISCONNECTED;
|
|
30
|
+
sdk.on(EVENTS.CONNECTION_STATE_CHANGED, (state) => {
|
|
31
|
+
connectionState = state;
|
|
32
|
+
const emoji = state === ConnectionState.CONNECTED ? 'š¢' : 'š“';
|
|
33
|
+
console.log(`${emoji} Connection: ${state}`);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
console.log('ā
SDK ready (online + offline support)\n');
|
|
37
|
+
|
|
38
|
+
// Create users
|
|
39
|
+
console.log('Creating users...');
|
|
40
|
+
const alice = await sdk.createUser('alice');
|
|
41
|
+
const bob = await sdk.createUser('bob');
|
|
42
|
+
console.log('ā
Alice and Bob created\n');
|
|
43
|
+
|
|
44
|
+
const session = await sdk.startSession(alice, bob);
|
|
45
|
+
|
|
46
|
+
// Scenario 1: Online messaging (real-time)
|
|
47
|
+
console.log('Scenario 1: Online messaging (real-time)');
|
|
48
|
+
sdk.setCurrentUser(alice);
|
|
49
|
+
await sdk.setCurrentUser(alice); // This connects the transport
|
|
50
|
+
|
|
51
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
52
|
+
|
|
53
|
+
await sdk.sendMessage(session, 'Hey!');
|
|
54
|
+
console.log('š¤ Alice: Hey! (delivered instantly)');
|
|
55
|
+
|
|
56
|
+
sdk.setCurrentUser(bob);
|
|
57
|
+
const messages1 = await sdk.getMessagesForUser(bob.id);
|
|
58
|
+
const text1 = await sdk.decryptMessage(messages1[0], bob);
|
|
59
|
+
console.log(`šØ Bob received: ${text1}\n`);
|
|
60
|
+
|
|
61
|
+
// Scenario 2: Offline messaging (queued)
|
|
62
|
+
console.log('Scenario 2: Offline messaging (queued)');
|
|
63
|
+
console.log('š“ Simulating disconnect...');
|
|
64
|
+
await sdk.disconnect();
|
|
65
|
+
|
|
66
|
+
sdk.setCurrentUser(alice);
|
|
67
|
+
await sdk.sendMessage(session, 'Are you there?');
|
|
68
|
+
console.log('š¤ Alice: Are you there? (queued)');
|
|
69
|
+
|
|
70
|
+
await sdk.sendMessage(session, 'I\'ll wait for you');
|
|
71
|
+
console.log('š¤ Alice: I\'ll wait for you (queued)');
|
|
72
|
+
|
|
73
|
+
const queueStatus = sdk.getQueueStatus();
|
|
74
|
+
console.log(`ā³ ${queueStatus.pending} messages in queue\n`);
|
|
75
|
+
|
|
76
|
+
// Scenario 3: Reconnection (auto-send queued messages)
|
|
77
|
+
console.log('Scenario 3: Reconnection (auto-send)');
|
|
78
|
+
console.log('š¢ Reconnecting...');
|
|
79
|
+
|
|
80
|
+
await sdk.reconnect();
|
|
81
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
82
|
+
|
|
83
|
+
console.log('ā
Sending queued messages...');
|
|
84
|
+
|
|
85
|
+
sdk.setCurrentUser(bob);
|
|
86
|
+
const messages2 = await sdk.getMessagesForUser(bob.id);
|
|
87
|
+
|
|
88
|
+
// Get the last 2 messages
|
|
89
|
+
const recentMessages = messages2.slice(-2);
|
|
90
|
+
for (const msg of recentMessages) {
|
|
91
|
+
const text = await sdk.decryptMessage(msg, bob);
|
|
92
|
+
console.log(`šØ Bob received: ${text}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
console.log();
|
|
96
|
+
console.log('ā
Hybrid messaging works perfectly!');
|
|
97
|
+
console.log('\nš” Key Features:');
|
|
98
|
+
console.log(' - Real-time when online');
|
|
99
|
+
console.log(' - Queued when offline');
|
|
100
|
+
console.log(' - Auto-send on reconnect');
|
|
101
|
+
console.log(' - Seamless user experience');
|
|
102
|
+
|
|
103
|
+
await sdk.disconnect();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "chatly-sdk-hybrid-messaging-example",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Hybrid online/offline messaging example using Chatly SDK",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"start": "node index.js"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"chatly-sdk": "^0.0.5"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# PostgreSQL Integration Example
|
|
2
|
+
|
|
3
|
+
Complete example of using Chatly SDK with PostgreSQL database.
|
|
4
|
+
|
|
5
|
+
## What You'll Learn
|
|
6
|
+
|
|
7
|
+
- Implementing custom storage adapters
|
|
8
|
+
- PostgreSQL schema design
|
|
9
|
+
- Database transactions
|
|
10
|
+
- Connection pooling
|
|
11
|
+
- Production-ready setup
|
|
12
|
+
|
|
13
|
+
## Prerequisites
|
|
14
|
+
|
|
15
|
+
- PostgreSQL installed and running
|
|
16
|
+
- Database created: `chatly`
|
|
17
|
+
|
|
18
|
+
## Setup
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# Install dependencies
|
|
22
|
+
npm install
|
|
23
|
+
|
|
24
|
+
# Create database
|
|
25
|
+
createdb chatly
|
|
26
|
+
|
|
27
|
+
# Run schema migration
|
|
28
|
+
psql chatly < schema.sql
|
|
29
|
+
|
|
30
|
+
# Update database credentials in index.js
|
|
31
|
+
# Then run
|
|
32
|
+
npm start
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Database Schema
|
|
36
|
+
|
|
37
|
+
See [`schema.sql`](./schema.sql) for the complete schema:
|
|
38
|
+
- `users` table
|
|
39
|
+
- `messages` table
|
|
40
|
+
- `groups` table
|
|
41
|
+
- `group_members` table
|
|
42
|
+
- Indexes for performance
|
|
43
|
+
|
|
44
|
+
## Code Structure
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
06-postgresql-integration/
|
|
48
|
+
āāā README.md
|
|
49
|
+
āāā package.json
|
|
50
|
+
āāā schema.sql # Database schema
|
|
51
|
+
āāā adapters/
|
|
52
|
+
ā āāā userStore.js # User storage adapter
|
|
53
|
+
ā āāā messageStore.js # Message storage adapter
|
|
54
|
+
ā āāā groupStore.js # Group storage adapter
|
|
55
|
+
āāā index.js # Main example
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Output
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
šļø Chatly SDK - PostgreSQL Integration
|
|
62
|
+
======================================
|
|
63
|
+
|
|
64
|
+
Connecting to PostgreSQL...
|
|
65
|
+
ā
Connected to database: chatly
|
|
66
|
+
|
|
67
|
+
Creating users in database...
|
|
68
|
+
ā
Alice saved to PostgreSQL
|
|
69
|
+
ā
Bob saved to PostgreSQL
|
|
70
|
+
|
|
71
|
+
Sending encrypted messages...
|
|
72
|
+
š¤ Message 1 saved to database
|
|
73
|
+
š¤ Message 2 saved to database
|
|
74
|
+
š¤ Message 3 saved to database
|
|
75
|
+
|
|
76
|
+
Retrieving messages from database...
|
|
77
|
+
šØ Retrieved 3 messages from PostgreSQL
|
|
78
|
+
šØ Decrypted: Hello from PostgreSQL!
|
|
79
|
+
šØ Decrypted: Messages are persisted
|
|
80
|
+
šØ Decrypted: Even after restart!
|
|
81
|
+
|
|
82
|
+
ā
PostgreSQL integration works!
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Production Features
|
|
86
|
+
|
|
87
|
+
- ā
Connection pooling
|
|
88
|
+
- ā
Prepared statements (SQL injection protection)
|
|
89
|
+
- ā
Transactions for data integrity
|
|
90
|
+
- ā
Indexes for performance
|
|
91
|
+
- ā
Error handling
|
|
92
|
+
- ā
Graceful shutdown
|
|
93
|
+
|
|
94
|
+
## Adapting for Other Databases
|
|
95
|
+
|
|
96
|
+
This pattern works for any SQL database:
|
|
97
|
+
- **MySQL**: Change `pg` to `mysql2`
|
|
98
|
+
- **SQLite**: Use `better-sqlite3`
|
|
99
|
+
- **SQL Server**: Use `mssql`
|
|
100
|
+
|
|
101
|
+
Just implement the same adapter interfaces!
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
export class PostgreSQLGroupStore {
|
|
2
|
+
constructor(pool) {
|
|
3
|
+
this.pool = pool;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
async create(group) {
|
|
7
|
+
const client = await this.pool.connect();
|
|
8
|
+
try {
|
|
9
|
+
await client.query('BEGIN');
|
|
10
|
+
|
|
11
|
+
// Insert group
|
|
12
|
+
await client.query(
|
|
13
|
+
'INSERT INTO groups (id, name, shared_secret) VALUES ($1, $2, $3)',
|
|
14
|
+
[group.id, group.name, group.sharedSecret]
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
// Insert members
|
|
18
|
+
for (const userId of group.members) {
|
|
19
|
+
await client.query(
|
|
20
|
+
'INSERT INTO group_members (group_id, user_id) VALUES ($1, $2)',
|
|
21
|
+
[group.id, userId]
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
await client.query('COMMIT');
|
|
26
|
+
return group;
|
|
27
|
+
} catch (error) {
|
|
28
|
+
await client.query('ROLLBACK');
|
|
29
|
+
throw error;
|
|
30
|
+
} finally {
|
|
31
|
+
client.release();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async findById(id) {
|
|
36
|
+
const groupResult = await this.pool.query(
|
|
37
|
+
'SELECT id, name, shared_secret as "sharedSecret" FROM groups WHERE id = $1',
|
|
38
|
+
[id]
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
if (groupResult.rows.length === 0) return undefined;
|
|
42
|
+
|
|
43
|
+
const membersResult = await this.pool.query(
|
|
44
|
+
'SELECT user_id FROM group_members WHERE group_id = $1',
|
|
45
|
+
[id]
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
...groupResult.rows[0],
|
|
50
|
+
members: membersResult.rows.map(row => row.user_id)
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async list() {
|
|
55
|
+
const groupsResult = await this.pool.query(
|
|
56
|
+
'SELECT id, name, shared_secret as "sharedSecret" FROM groups'
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const groups = [];
|
|
60
|
+
for (const group of groupsResult.rows) {
|
|
61
|
+
const membersResult = await this.pool.query(
|
|
62
|
+
'SELECT user_id FROM group_members WHERE group_id = $1',
|
|
63
|
+
[group.id]
|
|
64
|
+
);
|
|
65
|
+
groups.push({
|
|
66
|
+
...group,
|
|
67
|
+
members: membersResult.rows.map(row => row.user_id)
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return groups;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export class PostgreSQLMessageStore {
|
|
2
|
+
constructor(pool) {
|
|
3
|
+
this.pool = pool;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
async create(message) {
|
|
7
|
+
await this.pool.query(
|
|
8
|
+
`INSERT INTO messages (id, sender_id, receiver_id, group_id, ciphertext, iv, timestamp, status)
|
|
9
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`,
|
|
10
|
+
[
|
|
11
|
+
message.id,
|
|
12
|
+
message.senderId,
|
|
13
|
+
message.receiverId || null,
|
|
14
|
+
message.groupId || null,
|
|
15
|
+
message.ciphertext,
|
|
16
|
+
message.iv,
|
|
17
|
+
message.timestamp,
|
|
18
|
+
message.status || 'pending'
|
|
19
|
+
]
|
|
20
|
+
);
|
|
21
|
+
return message;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async listByUser(userId) {
|
|
25
|
+
const result = await this.pool.query(
|
|
26
|
+
`SELECT id, sender_id as "senderId", receiver_id as "receiverId",
|
|
27
|
+
group_id as "groupId", ciphertext, iv, timestamp, status
|
|
28
|
+
FROM messages
|
|
29
|
+
WHERE receiver_id = $1 OR sender_id = $1
|
|
30
|
+
ORDER BY timestamp ASC`,
|
|
31
|
+
[userId]
|
|
32
|
+
);
|
|
33
|
+
return result.rows;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async listByGroup(groupId) {
|
|
37
|
+
const result = await this.pool.query(
|
|
38
|
+
`SELECT id, sender_id as "senderId", receiver_id as "receiverId",
|
|
39
|
+
group_id as "groupId", ciphertext, iv, timestamp, status
|
|
40
|
+
FROM messages
|
|
41
|
+
WHERE group_id = $1
|
|
42
|
+
ORDER BY timestamp ASC`,
|
|
43
|
+
[groupId]
|
|
44
|
+
);
|
|
45
|
+
return result.rows;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export class PostgreSQLUserStore {
|
|
2
|
+
constructor(pool) {
|
|
3
|
+
this.pool = pool;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
async create(user) {
|
|
7
|
+
await this.pool.query(
|
|
8
|
+
`INSERT INTO users (id, username, public_key, private_key)
|
|
9
|
+
VALUES ($1, $2, $3, $4)`,
|
|
10
|
+
[user.id, user.username, user.publicKey, user.privateKey]
|
|
11
|
+
);
|
|
12
|
+
return user;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async findById(id) {
|
|
16
|
+
const result = await this.pool.query(
|
|
17
|
+
`SELECT id, username, public_key as "publicKey", private_key as "privateKey"
|
|
18
|
+
FROM users WHERE id = $1`,
|
|
19
|
+
[id]
|
|
20
|
+
);
|
|
21
|
+
return result.rows[0];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async save(user) {
|
|
25
|
+
await this.pool.query(
|
|
26
|
+
`UPDATE users
|
|
27
|
+
SET username = $1, public_key = $2
|
|
28
|
+
WHERE id = $3`,
|
|
29
|
+
[user.username, user.publicKey, user.id]
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async list() {
|
|
34
|
+
const result = await this.pool.query(
|
|
35
|
+
`SELECT id, username, public_key as "publicKey", private_key as "privateKey"
|
|
36
|
+
FROM users`
|
|
37
|
+
);
|
|
38
|
+
return result.rows;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import pkg from 'pg';
|
|
2
|
+
const { Pool } = pkg;
|
|
3
|
+
import { ChatSDK, LogLevel } from 'chatly-sdk';
|
|
4
|
+
import { PostgreSQLUserStore } from './adapters/userStore.js';
|
|
5
|
+
import { PostgreSQLMessageStore } from './adapters/messageStore.js';
|
|
6
|
+
import { PostgreSQLGroupStore } from './adapters/groupStore.js';
|
|
7
|
+
|
|
8
|
+
async function main() {
|
|
9
|
+
console.log('šļø Chatly SDK - PostgreSQL Integration');
|
|
10
|
+
console.log('======================================\n');
|
|
11
|
+
|
|
12
|
+
// Create PostgreSQL connection pool
|
|
13
|
+
console.log('Connecting to PostgreSQL...');
|
|
14
|
+
const pool = new Pool({
|
|
15
|
+
host: 'localhost',
|
|
16
|
+
port: 5432,
|
|
17
|
+
database: 'chatly',
|
|
18
|
+
user: 'postgres', // Update with your credentials
|
|
19
|
+
password: 'password', // Update with your credentials
|
|
20
|
+
max: 20,
|
|
21
|
+
min: 5,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Test connection
|
|
25
|
+
try {
|
|
26
|
+
await pool.query('SELECT NOW()');
|
|
27
|
+
console.log('ā
Connected to database: chatly\n');
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.error('ā Database connection failed:', error.message);
|
|
30
|
+
console.log('\nš” Make sure PostgreSQL is running and database exists:');
|
|
31
|
+
console.log(' createdb chatly');
|
|
32
|
+
console.log(' psql chatly < schema.sql');
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Initialize SDK with PostgreSQL adapters
|
|
37
|
+
const sdk = new ChatSDK({
|
|
38
|
+
userStore: new PostgreSQLUserStore(pool),
|
|
39
|
+
messageStore: new PostgreSQLMessageStore(pool),
|
|
40
|
+
groupStore: new PostgreSQLGroupStore(pool),
|
|
41
|
+
logLevel: LogLevel.NONE,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Create users
|
|
45
|
+
console.log('Creating users in database...');
|
|
46
|
+
const alice = await sdk.createUser('alice-pg');
|
|
47
|
+
const bob = await sdk.createUser('bob-pg');
|
|
48
|
+
console.log('ā
Alice saved to PostgreSQL');
|
|
49
|
+
console.log('ā
Bob saved to PostgreSQL\n');
|
|
50
|
+
|
|
51
|
+
// Start session
|
|
52
|
+
const session = await sdk.startSession(alice, bob);
|
|
53
|
+
|
|
54
|
+
// Send messages
|
|
55
|
+
console.log('Sending encrypted messages...');
|
|
56
|
+
sdk.setCurrentUser(alice);
|
|
57
|
+
|
|
58
|
+
await sdk.sendMessage(session, 'Hello from PostgreSQL!');
|
|
59
|
+
console.log('š¤ Message 1 saved to database');
|
|
60
|
+
|
|
61
|
+
await sdk.sendMessage(session, 'Messages are persisted');
|
|
62
|
+
console.log('š¤ Message 2 saved to database');
|
|
63
|
+
|
|
64
|
+
await sdk.sendMessage(session, 'Even after restart!');
|
|
65
|
+
console.log('š¤ Message 3 saved to database\n');
|
|
66
|
+
|
|
67
|
+
// Retrieve messages
|
|
68
|
+
console.log('Retrieving messages from database...');
|
|
69
|
+
sdk.setCurrentUser(bob);
|
|
70
|
+
|
|
71
|
+
const messages = await sdk.getMessagesForUser(bob.id);
|
|
72
|
+
console.log(`šØ Retrieved ${messages.length} messages from PostgreSQL`);
|
|
73
|
+
|
|
74
|
+
for (const msg of messages) {
|
|
75
|
+
const plaintext = await sdk.decryptMessage(msg, bob);
|
|
76
|
+
console.log(`šØ Decrypted: ${plaintext}`);
|
|
77
|
+
}
|
|
78
|
+
console.log();
|
|
79
|
+
|
|
80
|
+
console.log('ā
PostgreSQL integration works!');
|
|
81
|
+
console.log('\nš” Key Features:');
|
|
82
|
+
console.log(' - Messages persisted in PostgreSQL');
|
|
83
|
+
console.log(' - Connection pooling for performance');
|
|
84
|
+
console.log(' - Transactions for data integrity');
|
|
85
|
+
console.log(' - Indexes for fast queries');
|
|
86
|
+
|
|
87
|
+
// Cleanup
|
|
88
|
+
await pool.end();
|
|
89
|
+
console.log('\nš Database connection closed');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "chatly-sdk-postgresql-integration",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "PostgreSQL database integration example for Chatly SDK",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"start": "node index.js"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"chatly-sdk": "^0.0.5",
|
|
12
|
+
"pg": "^8.11.3"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
-- Chatly SDK PostgreSQL Schema
|
|
2
|
+
|
|
3
|
+
-- Users table
|
|
4
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
5
|
+
id VARCHAR(255) PRIMARY KEY,
|
|
6
|
+
username VARCHAR(50) NOT NULL UNIQUE,
|
|
7
|
+
public_key TEXT NOT NULL,
|
|
8
|
+
private_key TEXT NOT NULL,
|
|
9
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
10
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
-- Messages table
|
|
14
|
+
CREATE TABLE IF NOT EXISTS messages (
|
|
15
|
+
id VARCHAR(255) PRIMARY KEY,
|
|
16
|
+
sender_id VARCHAR(255) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
17
|
+
receiver_id VARCHAR(255) REFERENCES users(id) ON DELETE CASCADE,
|
|
18
|
+
group_id VARCHAR(255),
|
|
19
|
+
ciphertext TEXT NOT NULL,
|
|
20
|
+
iv VARCHAR(255) NOT NULL,
|
|
21
|
+
timestamp BIGINT NOT NULL,
|
|
22
|
+
status VARCHAR(20) DEFAULT 'pending',
|
|
23
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
-- Groups table
|
|
27
|
+
CREATE TABLE IF NOT EXISTS groups (
|
|
28
|
+
id VARCHAR(255) PRIMARY KEY,
|
|
29
|
+
name VARCHAR(100) NOT NULL,
|
|
30
|
+
shared_secret TEXT NOT NULL,
|
|
31
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
-- Group members table
|
|
35
|
+
CREATE TABLE IF NOT EXISTS group_members (
|
|
36
|
+
group_id VARCHAR(255) NOT NULL REFERENCES groups(id) ON DELETE CASCADE,
|
|
37
|
+
user_id VARCHAR(255) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
38
|
+
joined_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
39
|
+
PRIMARY KEY (group_id, user_id)
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
-- Indexes for performance
|
|
43
|
+
CREATE INDEX IF NOT EXISTS idx_messages_receiver ON messages(receiver_id, timestamp);
|
|
44
|
+
CREATE INDEX IF NOT EXISTS idx_messages_group ON messages(group_id, timestamp);
|
|
45
|
+
CREATE INDEX IF NOT EXISTS idx_messages_sender ON messages(sender_id);
|
|
46
|
+
CREATE INDEX IF NOT EXISTS idx_users_username ON users(username);
|
|
47
|
+
|
|
48
|
+
-- Update timestamp trigger
|
|
49
|
+
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
|
50
|
+
RETURNS TRIGGER AS $$
|
|
51
|
+
BEGIN
|
|
52
|
+
NEW.updated_at = CURRENT_TIMESTAMP;
|
|
53
|
+
RETURN NEW;
|
|
54
|
+
END;
|
|
55
|
+
$$ language 'plpgsql';
|
|
56
|
+
|
|
57
|
+
CREATE TRIGGER update_users_updated_at BEFORE UPDATE ON users
|
|
58
|
+
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Customer Support Chat Example
|
|
2
|
+
|
|
3
|
+
Real-world customer support system with live chat and offline fallback.
|
|
4
|
+
|
|
5
|
+
## What You'll Learn
|
|
6
|
+
|
|
7
|
+
- Building a customer support chat system
|
|
8
|
+
- Agent availability management
|
|
9
|
+
- Offline message handling
|
|
10
|
+
- Queue management for support tickets
|
|
11
|
+
|
|
12
|
+
## Running the Example
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install
|
|
16
|
+
npm start
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## How It Works
|
|
20
|
+
|
|
21
|
+
This example simulates a **customer support system**:
|
|
22
|
+
- Customers can send messages anytime
|
|
23
|
+
- Messages queued if no agents online
|
|
24
|
+
- Agents see messages when they come online
|
|
25
|
+
- Real-time chat when both are online
|
|
26
|
+
|
|
27
|
+
## Output
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
š§ Chatly SDK - Customer Support Example
|
|
31
|
+
========================================
|
|
32
|
+
|
|
33
|
+
Setting up support system...
|
|
34
|
+
ā
Support agent created
|
|
35
|
+
ā
Customer created
|
|
36
|
+
|
|
37
|
+
Scenario 1: Customer sends message (agent offline)
|
|
38
|
+
š Customer: I need help with my order
|
|
39
|
+
ā³ Message queued (no agents available)
|
|
40
|
+
|
|
41
|
+
Scenario 2: Agent comes online
|
|
42
|
+
š¢ Agent online
|
|
43
|
+
š¬ Agent has 1 pending message
|
|
44
|
+
šØ Agent sees: I need help with my order
|
|
45
|
+
|
|
46
|
+
Scenario 3: Live chat (both online)
|
|
47
|
+
š¬ Real-time conversation started
|
|
48
|
+
š¤ Agent: Hi! How can I help?
|
|
49
|
+
šØ Customer received: Hi! How can I help?
|
|
50
|
+
š¤ Customer: My order #12345 is delayed
|
|
51
|
+
šØ Agent received: My order #12345 is delayed
|
|
52
|
+
š¤ Agent: Let me check that for you
|
|
53
|
+
šØ Customer received: Let me check that for you
|
|
54
|
+
|
|
55
|
+
ā
Support system works perfectly!
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Features
|
|
59
|
+
|
|
60
|
+
- **24/7 Availability**: Customers can message anytime
|
|
61
|
+
- **Offline Queue**: Messages queued when agents offline
|
|
62
|
+
- **Real-time**: Live chat when agents available
|
|
63
|
+
- **Message History**: Full conversation history
|
|
64
|
+
|
|
65
|
+
## Use Cases
|
|
66
|
+
|
|
67
|
+
- **Customer Support** (Zendesk, Intercom-style)
|
|
68
|
+
- **Help Desk Systems**
|
|
69
|
+
- **Live Chat Widgets**
|
|
70
|
+
- **Support Ticketing**
|