docpouch-client 0.8.10 → 0.8.11
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/package.json +1 -1
- package/src/index.ts +409 -408
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -5,508 +5,509 @@ import packetJson from '../package.json'
|
|
|
5
5
|
* Client for interacting with docPouch API.
|
|
6
6
|
*/
|
|
7
7
|
export default class docPouchClient {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Sets the real-time synchronization status.
|
|
83
|
-
*
|
|
84
|
-
* @param {boolean} newRealTimeSync - The new real-time sync setting (true/false).
|
|
85
|
-
*/
|
|
86
|
-
setRealTimeSync(newRealTimeSync: boolean) {
|
|
87
|
-
console.log(`Setting realtime sync to: ${newRealTimeSync}. Current setting: ${this.realTimeSync}`);
|
|
88
|
-
|
|
89
|
-
// Skip if the setting isn't changing
|
|
90
|
-
if (newRealTimeSync === this.realTimeSync) {
|
|
91
|
-
console.log("Realtime sync setting unchanged, skipping");
|
|
92
|
-
return;
|
|
8
|
+
/**
|
|
9
|
+
* The base URL of the server.
|
|
10
|
+
*
|
|
11
|
+
* @type {string}
|
|
12
|
+
*/
|
|
13
|
+
baseUrl: string;
|
|
14
|
+
/**
|
|
15
|
+
* Socket.IO socket instance for real-time communication with the server.
|
|
16
|
+
*
|
|
17
|
+
* @type {Socket}
|
|
18
|
+
*/
|
|
19
|
+
socket: Socket;
|
|
20
|
+
/**
|
|
21
|
+
* Callback function to handle socket events.
|
|
22
|
+
*
|
|
23
|
+
* @type {(event: I_EventString, data: I_WsMessage) => void}
|
|
24
|
+
*/
|
|
25
|
+
callbackFunction;
|
|
26
|
+
/**
|
|
27
|
+
* Flag indicating whether real-time synchronization is enabled.
|
|
28
|
+
*
|
|
29
|
+
* @type {boolean}
|
|
30
|
+
*/
|
|
31
|
+
realTimeSync: boolean = false;
|
|
32
|
+
/**
|
|
33
|
+
* Authentication token used to authorize requests.
|
|
34
|
+
*
|
|
35
|
+
* @private
|
|
36
|
+
* @type {string | null}
|
|
37
|
+
*/
|
|
38
|
+
private authToken: string | null = null;
|
|
39
|
+
/**
|
|
40
|
+
* Flag indicating whether a connection attempt is in progress.
|
|
41
|
+
*
|
|
42
|
+
* @private
|
|
43
|
+
* @type {boolean}
|
|
44
|
+
*/
|
|
45
|
+
private connectionInProgress = false;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Creates an instance of docPouchClient.
|
|
49
|
+
*
|
|
50
|
+
* @param {string} host - The base URL for the server.
|
|
51
|
+
* @param {number} [port=80] - The port number to connect to (default is 80).
|
|
52
|
+
* @param {(event: I_EventString, data: I_WsMessage) => void} [callback] - Optional callback function for socket events.
|
|
53
|
+
*/
|
|
54
|
+
constructor(host: string, port: number = 80, callback?: (event: I_EventString, data: I_WsMessage) => void) {
|
|
55
|
+
this.baseUrl = host;
|
|
56
|
+
const socketUrl = host.includes('://') ? host : `https://${host}`;
|
|
57
|
+
const socketUrlWithPort = socketUrl.includes(':') && !socketUrl.endsWith(':')
|
|
58
|
+
? socketUrl
|
|
59
|
+
: `${socketUrl}:${port}`;
|
|
60
|
+
|
|
61
|
+
console.log(`Initializing Socket.IO with URL: ${socketUrlWithPort}, path: /socket.io`);
|
|
62
|
+
|
|
63
|
+
this.socket = io(`${socketUrlWithPort}`, {
|
|
64
|
+
autoConnect: false,
|
|
65
|
+
transports: ['websocket'], // Try websocket only first
|
|
66
|
+
reconnection: true,
|
|
67
|
+
reconnectionAttempts: 5,
|
|
68
|
+
reconnectionDelay: 1000,
|
|
69
|
+
forceNew: true, // Force a new connection
|
|
70
|
+
auth: {
|
|
71
|
+
token: null // Will be set later
|
|
72
|
+
},
|
|
73
|
+
path: '/socket.io'
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
this.callbackFunction = callback;
|
|
77
|
+
|
|
78
|
+
this.setupPermanentSocketListeners();
|
|
93
79
|
}
|
|
94
80
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
this.
|
|
112
|
-
|
|
81
|
+
/**
|
|
82
|
+
* Sets the real-time synchronization status.
|
|
83
|
+
*
|
|
84
|
+
* @param {boolean} newRealTimeSync - The new real-time sync setting (true/false).
|
|
85
|
+
*/
|
|
86
|
+
setRealTimeSync(newRealTimeSync: boolean) {
|
|
87
|
+
console.log(`Setting realtime sync to: ${newRealTimeSync}. Current setting: ${this.realTimeSync}`);
|
|
88
|
+
|
|
89
|
+
// Skip if the setting isn't changing
|
|
90
|
+
if (newRealTimeSync === this.realTimeSync) {
|
|
91
|
+
console.log("Realtime sync setting unchanged, skipping");
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
this.realTimeSync = newRealTimeSync;
|
|
96
|
+
|
|
97
|
+
if (newRealTimeSync && this.authToken) {
|
|
98
|
+
console.log("Activating realtime updates");
|
|
99
|
+
|
|
100
|
+
// Ensure we're not in the middle of another connection attempt
|
|
101
|
+
if (this.connectionInProgress) {
|
|
102
|
+
console.log("Connection already in progress, waiting before initializing");
|
|
103
|
+
setTimeout(() => this.initWebSocket(), 500);
|
|
104
|
+
} else {
|
|
105
|
+
this.initWebSocket();
|
|
106
|
+
}
|
|
107
|
+
} else if (!newRealTimeSync) {
|
|
108
|
+
console.log("Deactivating realtime updates");
|
|
109
|
+
if (this.socket.connected) {
|
|
110
|
+
console.log("Disconnecting socket");
|
|
111
|
+
this.socket.disconnect();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
113
114
|
}
|
|
114
|
-
}
|
|
115
115
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
116
|
+
// User Administration Endpoints
|
|
117
|
+
async login(credentials: I_UserLogin): Promise<I_LoginResponse | null> {
|
|
118
|
+
const response = await this.request<I_LoginResponse>('/users/login', 'POST', credentials, false);
|
|
119
|
+
if (response.token) {
|
|
120
|
+
this.authToken = response.token;
|
|
121
121
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
122
|
+
// Reconnect websocket with new token if realtime sync is enabled
|
|
123
|
+
if (this.realTimeSync) {
|
|
124
|
+
this.initWebSocket();
|
|
125
|
+
}
|
|
126
126
|
|
|
127
|
-
|
|
127
|
+
return {token: response.token, isAdmin: response.isAdmin, userName: response.userName};
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
128
130
|
}
|
|
129
|
-
return null;
|
|
130
|
-
}
|
|
131
131
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
132
|
+
async listUsers(): Promise<I_UserEntry[]> {
|
|
133
|
+
return await this.request<I_UserEntry[]>('/users/list', 'GET');
|
|
134
|
+
}
|
|
135
135
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
136
|
+
async updateUser(userID: string, userData: I_UserUpdate): Promise<void> {
|
|
137
|
+
await this.request<void>(`/users/update/${userID}`, 'PATCH', userData);
|
|
138
|
+
}
|
|
139
139
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
async createUser(userData: I_UserCreation): Promise<I_UserDisplay> {
|
|
141
|
+
return await this.request<I_UserDisplay>('/users/create', 'POST', userData);
|
|
142
|
+
}
|
|
143
143
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
144
|
+
async removeUser(userID: string): Promise<void> {
|
|
145
|
+
await this.request<void>(`/users/remove/${userID}`, 'DELETE');
|
|
146
|
+
}
|
|
147
147
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
148
|
+
// Document Management Endpoints
|
|
149
|
+
async createDocument(document: I_DocumentEntry): Promise<I_DocumentEntry> {
|
|
150
|
+
return await this.request<I_DocumentEntry>('/docs/create', 'POST', document);
|
|
151
|
+
}
|
|
152
152
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
153
|
+
async listDocuments(): Promise<I_DocumentEntry[]> {
|
|
154
|
+
return await this.request<I_DocumentEntry[]>('/docs/list', 'GET');
|
|
155
|
+
}
|
|
156
156
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
157
|
+
async fetchDocument(queryObject: I_DocumentQuery): Promise<I_DocumentEntry[]> {
|
|
158
|
+
return await this.request<I_DocumentEntry[]>(`/docs/fetch/`, 'POST', queryObject);
|
|
159
|
+
}
|
|
160
160
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
161
|
+
async updateDocument(documentID: string, documentData: I_DocumentEntry): Promise<void> {
|
|
162
|
+
await this.request<void>(`/docs/update/${documentID}`, 'PATCH', documentData);
|
|
163
|
+
}
|
|
164
164
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
165
|
+
async removeDocument(documentID: string): Promise<void> {
|
|
166
|
+
await this.request<void>(`/docs/remove/${documentID}`, 'DELETE');
|
|
167
|
+
}
|
|
168
168
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
169
|
+
// Data Structure Endpoints
|
|
170
|
+
async createStructure(structure: I_StructureCreation): Promise<I_DataStructure> {
|
|
171
|
+
return await this.request<I_DataStructure>('/structures/create', 'POST', structure);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async getStructures(): Promise<I_DataStructure[]> {
|
|
175
|
+
return await this.request<I_DataStructure[]>('/structures/list', 'GET');
|
|
176
|
+
}
|
|
173
177
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
178
|
+
async updateStructure(structureID: string, structureData: I_DataStructure): Promise<void> {
|
|
179
|
+
await this.request<void>(`/structures/update/${structureID}`, 'PATCH', structureData);
|
|
180
|
+
}
|
|
177
181
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
182
|
+
async removeStructure(structureID: string): Promise<void> {
|
|
183
|
+
await this.request<void>(`/structures/remove/${structureID}`, 'DELETE');
|
|
184
|
+
}
|
|
181
185
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
186
|
+
// Data Type Endpoints
|
|
187
|
+
async createType(type: I_DocumentType): Promise<I_DocumentType> {
|
|
188
|
+
return await this.request<I_DocumentType>('/types/write', 'POST', type);
|
|
189
|
+
}
|
|
185
190
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
191
|
+
async removeType(typeID: string) {
|
|
192
|
+
return await this.request<void>(`/types/remove/${typeID}`, 'DELETE');
|
|
193
|
+
}
|
|
190
194
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
195
|
+
async getTypes(): Promise<I_DocumentType[]> {
|
|
196
|
+
return await this.request<I_DocumentType[]>('/types/list', 'GET');
|
|
197
|
+
}
|
|
194
198
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
199
|
+
async updateType(updatedType: I_DocumentType): Promise<void> {
|
|
200
|
+
await this.request<void>(`/types/write`, 'POST', updatedType);
|
|
201
|
+
}
|
|
198
202
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
}
|
|
203
|
+
setToken(token: string | null): void {
|
|
204
|
+
console.log("Setting token to:", token ? "***token***" : "null");
|
|
202
205
|
|
|
203
|
-
|
|
204
|
-
|
|
206
|
+
const tokenChanged = this.authToken !== token;
|
|
207
|
+
this.authToken = token;
|
|
205
208
|
|
|
206
|
-
|
|
207
|
-
|
|
209
|
+
if (!tokenChanged) {
|
|
210
|
+
console.log("Token unchanged, no need to reconnect");
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
208
213
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
214
|
+
// If we have a new token and realtime sync is enabled
|
|
215
|
+
if (token && this.realTimeSync) {
|
|
216
|
+
console.log("New token set, will initialize WebSocket");
|
|
217
|
+
|
|
218
|
+
// Ensure any existing connection is closed first
|
|
219
|
+
if (this.socket.connected) {
|
|
220
|
+
console.log("Disconnecting existing socket before reconnecting with new token");
|
|
221
|
+
this.socket.disconnect();
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Wait a moment for the disconnect to complete
|
|
225
|
+
setTimeout(() => {
|
|
226
|
+
console.log("Initializing WebSocket with new token");
|
|
227
|
+
this.initWebSocket();
|
|
228
|
+
}, 300);
|
|
229
|
+
}
|
|
230
|
+
// If token was cleared or realtime sync is disabled
|
|
231
|
+
else if (this.socket.connected) {
|
|
232
|
+
console.log("Token cleared or realtime sync disabled, disconnecting");
|
|
233
|
+
this.socket.disconnect();
|
|
234
|
+
}
|
|
212
235
|
}
|
|
213
236
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
console.log("New token set, will initialize WebSocket");
|
|
217
|
-
|
|
218
|
-
// Ensure any existing connection is closed first
|
|
219
|
-
if (this.socket.connected) {
|
|
220
|
-
console.log("Disconnecting existing socket before reconnecting with new token");
|
|
221
|
-
this.socket.disconnect();
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// Wait a moment for the disconnect to complete
|
|
225
|
-
setTimeout(() => {
|
|
226
|
-
console.log("Initializing WebSocket with new token");
|
|
227
|
-
this.initWebSocket();
|
|
228
|
-
}, 300);
|
|
229
|
-
}
|
|
230
|
-
// If token was cleared or realtime sync is disabled
|
|
231
|
-
else if (this.socket.connected) {
|
|
232
|
-
console.log("Token cleared or realtime sync disabled, disconnecting");
|
|
233
|
-
this.socket.disconnect();
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
getVersion() {
|
|
238
|
-
return packetJson.version;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
debugSocketConnection(): void {
|
|
242
|
-
console.log("Socket connection debug info:");
|
|
243
|
-
console.log("- Connected:", this.socket.connected);
|
|
244
|
-
console.log("- Socket ID:", this.socket.id);
|
|
245
|
-
console.log("- Auth token present:", !!this.authToken);
|
|
246
|
-
console.log("- Connection in progress:", this.connectionInProgress);
|
|
247
|
-
console.log("- Realtime sync enabled:", this.realTimeSync);
|
|
248
|
-
console.log("- Socket options:", this.socket.io.opts);
|
|
249
|
-
|
|
250
|
-
// Try to force reconnection
|
|
251
|
-
if (!this.socket.connected && this.authToken && this.realTimeSync) {
|
|
252
|
-
console.log("Attempting to force reconnection...");
|
|
253
|
-
this.socket.auth = {token: this.authToken};
|
|
254
|
-
this.socket.connect();
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* Sets up permanent socket listeners for the client.
|
|
260
|
-
*
|
|
261
|
-
* @private
|
|
262
|
-
*/
|
|
263
|
-
private setupPermanentSocketListeners() {
|
|
264
|
-
// These are permanent listeners that won't be removed
|
|
265
|
-
this.socket.on('connect_error', (error) => {
|
|
266
|
-
console.error('Socket connection error:', error.message);
|
|
267
|
-
this.connectionInProgress = false;
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
this.socket.on('connect', () => {
|
|
271
|
-
console.log('Socket connected successfully with ID:', this.socket.id);
|
|
272
|
-
this.connectionInProgress = false;
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
this.socket.on('disconnect', (reason) => {
|
|
276
|
-
console.log('Socket disconnected. Reason:', reason);
|
|
277
|
-
this.connectionInProgress = false;
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
this.socket.on('error', (error) => {
|
|
281
|
-
console.error('Socket error:', error);
|
|
282
|
-
this.connectionInProgress = false;
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* Initializes the WebSocket connection with the server.
|
|
288
|
-
*
|
|
289
|
-
* @private
|
|
290
|
-
*/
|
|
291
|
-
private initWebSocket() {
|
|
292
|
-
console.log("initWebSocket called. Auth token present:", !!this.authToken,
|
|
293
|
-
"Connection in progress:", this.connectionInProgress,
|
|
294
|
-
"Socket connected:", this.socket.connected);
|
|
295
|
-
|
|
296
|
-
if (!this.authToken) {
|
|
297
|
-
console.log("Skipping WebSocket initialization: No auth token");
|
|
298
|
-
return;
|
|
237
|
+
getVersion() {
|
|
238
|
+
return packetJson.version;
|
|
299
239
|
}
|
|
300
240
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
241
|
+
debugSocketConnection(): void {
|
|
242
|
+
console.log("Socket connection debug info:");
|
|
243
|
+
console.log("- Connected:", this.socket.connected);
|
|
244
|
+
console.log("- Socket ID:", this.socket.id);
|
|
245
|
+
console.log("- Auth token present:", !!this.authToken);
|
|
246
|
+
console.log("- Connection in progress:", this.connectionInProgress);
|
|
247
|
+
console.log("- Realtime sync enabled:", this.realTimeSync);
|
|
248
|
+
console.log("- Socket options:", this.socket.io.opts);
|
|
249
|
+
|
|
250
|
+
// Try to force reconnection
|
|
251
|
+
if (!this.socket.connected && this.authToken && this.realTimeSync) {
|
|
252
|
+
console.log("Attempting to force reconnection...");
|
|
253
|
+
this.socket.auth = {token: this.authToken};
|
|
254
|
+
this.socket.connect();
|
|
255
|
+
}
|
|
304
256
|
}
|
|
305
257
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
258
|
+
/**
|
|
259
|
+
* Sets up permanent socket listeners for the client.
|
|
260
|
+
*
|
|
261
|
+
* @private
|
|
262
|
+
*/
|
|
263
|
+
private setupPermanentSocketListeners() {
|
|
264
|
+
// These are permanent listeners that won't be removed
|
|
265
|
+
this.socket.on('connect_error', (error) => {
|
|
266
|
+
console.error('Socket connection error:', error.message);
|
|
267
|
+
this.connectionInProgress = false;
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
this.socket.on('connect', () => {
|
|
271
|
+
console.log('Socket connected successfully with ID:', this.socket.id);
|
|
272
|
+
this.connectionInProgress = false;
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
this.socket.on('disconnect', (reason) => {
|
|
276
|
+
console.log('Socket disconnected. Reason:', reason);
|
|
277
|
+
this.connectionInProgress = false;
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
this.socket.on('error', (error) => {
|
|
281
|
+
console.error('Socket error:', error);
|
|
282
|
+
this.connectionInProgress = false;
|
|
283
|
+
});
|
|
309
284
|
}
|
|
310
285
|
|
|
311
|
-
|
|
286
|
+
/**
|
|
287
|
+
* Initializes the WebSocket connection with the server.
|
|
288
|
+
*
|
|
289
|
+
* @private
|
|
290
|
+
*/
|
|
291
|
+
private initWebSocket() {
|
|
292
|
+
console.log("initWebSocket called. Auth token present:", !!this.authToken,
|
|
293
|
+
"Connection in progress:", this.connectionInProgress,
|
|
294
|
+
"Socket connected:", this.socket.connected);
|
|
295
|
+
|
|
296
|
+
if (!this.authToken) {
|
|
297
|
+
console.log("Skipping WebSocket initialization: No auth token");
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
312
300
|
|
|
313
|
-
|
|
314
|
-
|
|
301
|
+
if (this.connectionInProgress) {
|
|
302
|
+
console.log("Connection already in progress, skipping initialization");
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
315
305
|
|
|
316
|
-
|
|
317
|
-
|
|
306
|
+
if (this.socket.connected) {
|
|
307
|
+
console.log("Socket already connected with ID:", this.socket.id);
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
318
310
|
|
|
319
|
-
|
|
320
|
-
this.socket.offAny();
|
|
311
|
+
this.connectionInProgress = true;
|
|
321
312
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
if (event === "heartbeatPing") {
|
|
325
|
-
console.log("Ping event received:", data);
|
|
326
|
-
this.socket.emit("heartbeatPong", Date.now());
|
|
327
|
-
} else if (this.callbackFunction) {
|
|
328
|
-
this.callbackFunction(event, data);
|
|
329
|
-
}
|
|
330
|
-
});
|
|
313
|
+
try {
|
|
314
|
+
console.log("Setting up WebSocket connection with token");
|
|
331
315
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
this.socket.connect();
|
|
316
|
+
// Update the auth token
|
|
317
|
+
this.socket.auth = {token: this.authToken};
|
|
335
318
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
319
|
+
// Remove any dynamic event listeners that might have been added
|
|
320
|
+
this.socket.offAny();
|
|
321
|
+
|
|
322
|
+
// Set up event handler for application events
|
|
323
|
+
this.socket.onAny((event: I_EventString, data: I_WsMessage) => {
|
|
324
|
+
if (event === "heartbeatPing") {
|
|
325
|
+
console.log("Ping event received:", data);
|
|
326
|
+
this.socket.emit("heartbeatPong", Date.now());
|
|
327
|
+
} else if (this.callbackFunction) {
|
|
328
|
+
this.callbackFunction(event, data);
|
|
329
|
+
}
|
|
330
|
+
});
|
|
341
331
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
console.log("Retrying connection with polling transport");
|
|
345
|
-
this.socket.io.opts.transports = ['polling', 'websocket'];
|
|
332
|
+
// Connect to the server
|
|
333
|
+
console.log("Connecting socket with auth token");
|
|
346
334
|
this.socket.connect();
|
|
347
|
-
|
|
335
|
+
|
|
336
|
+
// Add a timeout to detect if connection is taking too long
|
|
337
|
+
setTimeout(() => {
|
|
338
|
+
if (this.connectionInProgress) {
|
|
339
|
+
console.warn("Socket connection attempt timed out after 5 seconds");
|
|
340
|
+
this.connectionInProgress = false;
|
|
341
|
+
|
|
342
|
+
// If we're still not connected after the timeout, try again with polling
|
|
343
|
+
if (!this.socket.connected) {
|
|
344
|
+
console.log("Retrying connection with polling transport");
|
|
345
|
+
this.socket.io.opts.transports = ['polling', 'websocket'];
|
|
346
|
+
this.socket.connect();
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}, 5000);
|
|
350
|
+
} catch (error) {
|
|
351
|
+
console.error('Error in initWebSocket:', error);
|
|
352
|
+
this.connectionInProgress = false;
|
|
348
353
|
}
|
|
349
|
-
}, 5000);
|
|
350
|
-
} catch (error) {
|
|
351
|
-
console.error('Error in initWebSocket:', error);
|
|
352
|
-
this.connectionInProgress = false;
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
private async request<T>(endpoint: string, method: string, body?: any, requiresAuth: boolean = true): Promise<T> {
|
|
357
|
-
const headers: HeadersInit = {
|
|
358
|
-
'Content-Type': 'application/json',
|
|
359
|
-
};
|
|
360
|
-
|
|
361
|
-
if (requiresAuth && this.authToken)
|
|
362
|
-
headers['Authorization'] = `Bearer ${this.authToken}`;
|
|
363
|
-
if (this.socket.id)
|
|
364
|
-
headers['X-Socket-ID'] = this.socket.id;
|
|
365
|
-
|
|
366
|
-
const options: RequestInit = {
|
|
367
|
-
method,
|
|
368
|
-
headers,
|
|
369
|
-
body: body ? JSON.stringify(body) : undefined
|
|
370
|
-
};
|
|
371
|
-
|
|
372
|
-
const normalizedEndpoint = endpoint.startsWith('/') ? endpoint : `/${endpoint}`;
|
|
373
|
-
const normalizedBaseUrl = this.baseUrl.endsWith('/') ? this.baseUrl.slice(0, -1) : this.baseUrl;
|
|
374
|
-
const url = `${normalizedBaseUrl}${normalizedEndpoint}`;
|
|
375
|
-
const response = await fetch(url, options);
|
|
376
|
-
|
|
377
|
-
if (!response.ok) {
|
|
378
|
-
if (response.status === 401 || response.status === 403) {
|
|
379
|
-
this.authToken = null;
|
|
380
|
-
}
|
|
381
|
-
throw new Error(`API error: ${response.status} ${response.statusText}`);
|
|
382
354
|
}
|
|
383
355
|
|
|
384
|
-
|
|
385
|
-
|
|
356
|
+
private async request<T>(endpoint: string, method: string, body?: any, requiresAuth: boolean = true): Promise<T> {
|
|
357
|
+
const headers: HeadersInit = {
|
|
358
|
+
'Content-Type': 'application/json',
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
if (requiresAuth && this.authToken)
|
|
362
|
+
headers['Authorization'] = `Bearer ${this.authToken}`;
|
|
363
|
+
if (this.socket.id)
|
|
364
|
+
headers['X-Socket-ID'] = this.socket.id;
|
|
365
|
+
|
|
366
|
+
const options: RequestInit = {
|
|
367
|
+
method,
|
|
368
|
+
headers,
|
|
369
|
+
body: body ? JSON.stringify(body) : undefined
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
const normalizedEndpoint = endpoint.startsWith('/') ? endpoint : `/${endpoint}`;
|
|
373
|
+
const normalizedBaseUrl = this.baseUrl.endsWith('/') ? this.baseUrl.slice(0, -1) : this.baseUrl;
|
|
374
|
+
const url = `${normalizedBaseUrl}${normalizedEndpoint}`;
|
|
375
|
+
const response = await fetch(url, options);
|
|
376
|
+
|
|
377
|
+
if (!response.ok) {
|
|
378
|
+
if (response.status === 401 || response.status === 403) {
|
|
379
|
+
this.authToken = null;
|
|
380
|
+
}
|
|
381
|
+
throw new Error(`API error: ${response.status} ${response.statusText}`);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return await response.json() as T;
|
|
385
|
+
}
|
|
386
386
|
}
|
|
387
387
|
|
|
388
388
|
// Common type definitions for both frontend and backend
|
|
389
389
|
|
|
390
390
|
// User related types
|
|
391
391
|
export interface I_UserEntry extends I_UserCreation {
|
|
392
|
-
|
|
392
|
+
_id: string;
|
|
393
393
|
}
|
|
394
394
|
|
|
395
395
|
export interface I_UserLogin {
|
|
396
|
-
|
|
397
|
-
|
|
396
|
+
name: string;
|
|
397
|
+
password: string;
|
|
398
398
|
}
|
|
399
399
|
|
|
400
400
|
export interface I_UserCreation {
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
401
|
+
name: string;
|
|
402
|
+
password: string;
|
|
403
|
+
email?: string;
|
|
404
|
+
department: string;
|
|
405
|
+
group: string;
|
|
406
|
+
isAdmin: boolean;
|
|
407
407
|
}
|
|
408
408
|
|
|
409
409
|
export interface I_UserUpdate {
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
410
|
+
name?: string;
|
|
411
|
+
password?: string;
|
|
412
|
+
email?: string;
|
|
413
|
+
department?: string;
|
|
414
|
+
group?: string;
|
|
415
|
+
isAdmin?: boolean;
|
|
416
416
|
}
|
|
417
417
|
|
|
418
418
|
export interface I_UserDisplay {
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
419
|
+
_id: string;
|
|
420
|
+
username: string;
|
|
421
|
+
department: string;
|
|
422
|
+
group: string;
|
|
423
|
+
email?: string;
|
|
424
424
|
}
|
|
425
425
|
|
|
426
426
|
export interface I_LoginResponse {
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
427
|
+
token: string;
|
|
428
|
+
isAdmin: boolean;
|
|
429
|
+
userName: string;
|
|
430
430
|
}
|
|
431
431
|
|
|
432
432
|
// Document related types
|
|
433
433
|
export interface I_DocumentEntry extends I_DocumentCreationOwned {
|
|
434
|
-
|
|
434
|
+
_id: string;
|
|
435
435
|
}
|
|
436
436
|
|
|
437
437
|
export interface I_DocumentCreation {
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
438
|
+
title: string;
|
|
439
|
+
description?: string;
|
|
440
|
+
type: number;
|
|
441
|
+
subType: number;
|
|
442
|
+
content: any;
|
|
443
|
+
shareWithGroup: boolean;
|
|
444
|
+
shareWithDepartment: boolean;
|
|
445
445
|
}
|
|
446
446
|
|
|
447
447
|
|
|
448
448
|
export interface I_DocumentCreationOwned extends I_DocumentCreation {
|
|
449
|
-
|
|
449
|
+
owner: string;
|
|
450
450
|
}
|
|
451
451
|
|
|
452
452
|
export interface I_DocumentUpdate extends I_DocumentQuery {
|
|
453
|
-
|
|
454
|
-
|
|
453
|
+
content?: any;
|
|
454
|
+
description?: string;
|
|
455
455
|
}
|
|
456
456
|
|
|
457
457
|
export interface I_DocumentQuery {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
458
|
+
_id?: string;
|
|
459
|
+
owner?: string;
|
|
460
|
+
title?: string;
|
|
461
|
+
type?: number;
|
|
462
|
+
subType?: number;
|
|
463
|
+
shareWithGroup?: boolean;
|
|
464
|
+
shareWithDepartment?: boolean;
|
|
465
465
|
}
|
|
466
466
|
|
|
467
467
|
|
|
468
468
|
// Structure related types
|
|
469
469
|
export interface I_DataStructure {
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
470
|
+
_id?: string | undefined;
|
|
471
|
+
name: string;
|
|
472
|
+
description: string;
|
|
473
|
+
fields: I_StructureField[];
|
|
474
474
|
}
|
|
475
475
|
|
|
476
476
|
export interface I_StructureField {
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
477
|
+
name: string;
|
|
478
|
+
type: string;
|
|
479
|
+
items?: string;
|
|
480
480
|
}
|
|
481
481
|
|
|
482
482
|
export interface I_StructureEntry {
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
483
|
+
_id?: string;
|
|
484
|
+
name: string;
|
|
485
|
+
description: string;
|
|
486
|
+
fields: I_StructureField[];
|
|
487
487
|
}
|
|
488
488
|
|
|
489
489
|
|
|
490
490
|
export interface I_StructureCreation {
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
491
|
+
name: string;
|
|
492
|
+
description?: string;
|
|
493
|
+
fields: I_StructureField[];
|
|
494
494
|
}
|
|
495
495
|
|
|
496
496
|
export interface I_StructureUpdate {
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
497
|
+
_id?: string
|
|
498
|
+
name?: string;
|
|
499
|
+
description?: string;
|
|
500
|
+
fields?: I_StructureField[];
|
|
500
501
|
}
|
|
501
502
|
|
|
502
503
|
// Document type related types
|
|
503
504
|
export interface I_DocumentType {
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
505
|
+
_id?: string;
|
|
506
|
+
type: number;
|
|
507
|
+
subType: number;
|
|
508
|
+
name: string;
|
|
509
|
+
description?: string;
|
|
510
|
+
defaultStructureID?: string;
|
|
510
511
|
}
|
|
511
512
|
|
|
512
513
|
// WebSocket-related types
|
|
@@ -515,16 +516,16 @@ export type I_EventString = 'heartbeatPong' | "heartbeatPing" | "newDocument" |
|
|
|
515
516
|
"removedUser" | "removedStructure" | "removedDocument" | "removedType";
|
|
516
517
|
|
|
517
518
|
export interface I_WsMessage {
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
519
|
+
newDocument?: I_DocumentEntry;
|
|
520
|
+
newStructure?: I_StructureEntry;
|
|
521
|
+
newUser?: I_UserEntry;
|
|
522
|
+
removedID?: string;
|
|
523
|
+
changedDocument?: I_DocumentUpdate;
|
|
524
|
+
changedStructure?: I_StructureUpdate;
|
|
525
|
+
changedUser?: I_UserUpdate;
|
|
526
|
+
confirmSubscription?: boolean;
|
|
527
|
+
confirmUnsubscription?: boolean;
|
|
528
|
+
heartbeatPing?: number;
|
|
529
|
+
heartbeatPong?: number;
|
|
530
|
+
newType?: I_DocumentType;
|
|
530
531
|
}
|