docpouch-client 0.8.10 → 0.8.12

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/dist/index.d.ts CHANGED
@@ -176,6 +176,7 @@ export interface I_StructureCreation {
176
176
  fields: I_StructureField[];
177
177
  }
178
178
  export interface I_StructureUpdate {
179
+ _id?: string;
179
180
  name?: string;
180
181
  description?: string;
181
182
  fields?: I_StructureField[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docpouch-client",
3
- "version": "0.8.10",
3
+ "version": "0.8.12",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "exports": {
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
- * 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();
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
- 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
- }
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
- // 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;
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
- // Reconnect websocket with new token if realtime sync is enabled
123
- if (this.realTimeSync) {
124
- this.initWebSocket();
125
- }
122
+ // Reconnect websocket with new token if realtime sync is enabled
123
+ if (this.realTimeSync) {
124
+ this.initWebSocket();
125
+ }
126
126
 
127
- return {token: response.token, isAdmin: response.isAdmin, userName: response.userName};
127
+ return {token: response.token, isAdmin: response.isAdmin, userName: response.userName};
128
+ }
129
+ return null;
128
130
  }
129
- return null;
130
- }
131
131
 
132
- async listUsers(): Promise<I_UserEntry[]> {
133
- return await this.request<I_UserEntry[]>('/users/list', 'GET');
134
- }
132
+ async listUsers(): Promise<I_UserEntry[]> {
133
+ return await this.request<I_UserEntry[]>('/users/list', 'GET');
134
+ }
135
135
 
136
- async updateUser(userID: string, userData: I_UserUpdate): Promise<void> {
137
- await this.request<void>(`/users/update/${userID}`, 'PATCH', userData);
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
- async createUser(userData: I_UserCreation): Promise<I_UserDisplay> {
141
- return await this.request<I_UserDisplay>('/users/create', 'POST', userData);
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
- async removeUser(userID: string): Promise<void> {
145
- await this.request<void>(`/users/remove/${userID}`, 'DELETE');
146
- }
144
+ async removeUser(userID: string): Promise<void> {
145
+ await this.request<void>(`/users/remove/${userID}`, 'DELETE');
146
+ }
147
147
 
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
- }
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
- async listDocuments(): Promise<I_DocumentEntry[]> {
154
- return await this.request<I_DocumentEntry[]>('/docs/list', 'GET');
155
- }
153
+ async listDocuments(): Promise<I_DocumentEntry[]> {
154
+ return await this.request<I_DocumentEntry[]>('/docs/list', 'GET');
155
+ }
156
156
 
157
- async fetchDocument(queryObject: I_DocumentQuery): Promise<I_DocumentEntry[]> {
158
- return await this.request<I_DocumentEntry[]>(`/docs/fetch/`, 'POST', queryObject);
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
- async updateDocument(documentID: string, documentData: I_DocumentEntry): Promise<void> {
162
- await this.request<void>(`/docs/update/${documentID}`, 'PATCH', documentData);
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
- async removeDocument(documentID: string): Promise<void> {
166
- await this.request<void>(`/docs/remove/${documentID}`, 'DELETE');
167
- }
165
+ async removeDocument(documentID: string): Promise<void> {
166
+ await this.request<void>(`/docs/remove/${documentID}`, 'DELETE');
167
+ }
168
168
 
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
- }
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
- async getStructures(): Promise<I_DataStructure[]> {
175
- return await this.request<I_DataStructure[]>('/structures/list', 'GET');
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
- async updateStructure(structureID: string, structureData: I_DataStructure): Promise<void> {
179
- await this.request<void>(`/structures/update/${structureID}`, 'PATCH', structureData);
180
- }
182
+ async removeStructure(structureID: string): Promise<void> {
183
+ await this.request<void>(`/structures/remove/${structureID}`, 'DELETE');
184
+ }
181
185
 
182
- async removeStructure(structureID: string): Promise<void> {
183
- await this.request<void>(`/structures/remove/${structureID}`, 'DELETE');
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
- // Data Type Endpoints
187
- async createType(type: I_DocumentType): Promise<I_DocumentType> {
188
- return await this.request<I_DocumentType>('/types/write', 'POST', type);
189
- }
191
+ async removeType(typeID: string) {
192
+ return await this.request<void>(`/types/remove/${typeID}`, 'DELETE');
193
+ }
190
194
 
191
- async removeType(typeID: string) {
192
- return await this.request<void>(`/types/remove/${typeID}`, 'DELETE');
193
- }
195
+ async getTypes(): Promise<I_DocumentType[]> {
196
+ return await this.request<I_DocumentType[]>('/types/list', 'GET');
197
+ }
194
198
 
195
- async getTypes(): Promise<I_DocumentType[]> {
196
- return await this.request<I_DocumentType[]>('/types/list', 'GET');
197
- }
199
+ async updateType(updatedType: I_DocumentType): Promise<void> {
200
+ await this.request<void>(`/types/write`, 'POST', updatedType);
201
+ }
198
202
 
199
- async updateType(updatedType: I_DocumentType): Promise<void> {
200
- await this.request<void>(`/types/write`, 'POST', updatedType);
201
- }
203
+ setToken(token: string | null): void {
204
+ console.log("Setting token to:", token ? "***token***" : "null");
202
205
 
203
- setToken(token: string | null): void {
204
- console.log("Setting token to:", token ? "***token***" : "null");
206
+ const tokenChanged = this.authToken !== token;
207
+ this.authToken = token;
205
208
 
206
- const tokenChanged = this.authToken !== token;
207
- this.authToken = token;
209
+ if (!tokenChanged) {
210
+ console.log("Token unchanged, no need to reconnect");
211
+ return;
212
+ }
208
213
 
209
- if (!tokenChanged) {
210
- console.log("Token unchanged, no need to reconnect");
211
- return;
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
- // 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
- }
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
- if (this.connectionInProgress) {
302
- console.log("Connection already in progress, skipping initialization");
303
- return;
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
- if (this.socket.connected) {
307
- console.log("Socket already connected with ID:", this.socket.id);
308
- return;
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
- this.connectionInProgress = true;
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
- try {
314
- console.log("Setting up WebSocket connection with token");
301
+ if (this.connectionInProgress) {
302
+ console.log("Connection already in progress, skipping initialization");
303
+ return;
304
+ }
315
305
 
316
- // Update the auth token
317
- this.socket.auth = {token: this.authToken};
306
+ if (this.socket.connected) {
307
+ console.log("Socket already connected with ID:", this.socket.id);
308
+ return;
309
+ }
318
310
 
319
- // Remove any dynamic event listeners that might have been added
320
- this.socket.offAny();
311
+ this.connectionInProgress = true;
321
312
 
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
- });
313
+ try {
314
+ console.log("Setting up WebSocket connection with token");
331
315
 
332
- // Connect to the server
333
- console.log("Connecting socket with auth token");
334
- this.socket.connect();
316
+ // Update the auth token
317
+ this.socket.auth = {token: this.authToken};
335
318
 
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;
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
- // 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'];
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
- return await response.json() as T;
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
- _id: string;
392
+ _id: string;
393
393
  }
394
394
 
395
395
  export interface I_UserLogin {
396
- name: string;
397
- password: string;
396
+ name: string;
397
+ password: string;
398
398
  }
399
399
 
400
400
  export interface I_UserCreation {
401
- name: string;
402
- password: string;
403
- email?: string;
404
- department: string;
405
- group: string;
406
- isAdmin: boolean;
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
- name?: string;
411
- password?: string;
412
- email?: string;
413
- department?: string;
414
- group?: string;
415
- isAdmin?: boolean;
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
- _id: string;
420
- username: string;
421
- department: string;
422
- group: string;
423
- email?: string;
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
- token: string;
428
- isAdmin: boolean;
429
- userName: string;
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
- _id: string;
434
+ _id: string;
435
435
  }
436
436
 
437
437
  export interface I_DocumentCreation {
438
- title: string;
439
- description?: string;
440
- type: number;
441
- subType: number;
442
- content: any;
443
- shareWithGroup: boolean;
444
- shareWithDepartment: boolean;
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
- owner: string;
449
+ owner: string;
450
450
  }
451
451
 
452
452
  export interface I_DocumentUpdate extends I_DocumentQuery {
453
- content?: any;
454
- description?: string;
453
+ content?: any;
454
+ description?: string;
455
455
  }
456
456
 
457
457
  export interface I_DocumentQuery {
458
- _id?: string;
459
- owner?: string;
460
- title?: string;
461
- type?: number;
462
- subType?: number;
463
- shareWithGroup?: boolean;
464
- shareWithDepartment?: boolean;
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
- _id?: string | undefined;
471
- name: string;
472
- description: string;
473
- fields: I_StructureField[];
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
- name: string;
478
- type: string;
479
- items?: string;
477
+ name: string;
478
+ type: string;
479
+ items?: string;
480
480
  }
481
481
 
482
482
  export interface I_StructureEntry {
483
- _id?: string;
484
- name: string;
485
- description: string;
486
- fields: I_StructureField[];
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
- name: string;
492
- description?: string;
493
- fields: I_StructureField[];
491
+ name: string;
492
+ description?: string;
493
+ fields: I_StructureField[];
494
494
  }
495
495
 
496
496
  export interface I_StructureUpdate {
497
- name?: string;
498
- description?: string;
499
- fields?: I_StructureField[];
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
- _id?: string;
505
- type: number;
506
- subType: number;
507
- name: string;
508
- description?: string;
509
- defaultStructureID?: string;
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
- newDocument?: I_DocumentEntry;
519
- newStructure?: I_StructureEntry;
520
- newUser?: I_UserEntry;
521
- removedID?: string;
522
- changedDocument?: I_DocumentUpdate;
523
- changedStructure?: I_StructureUpdate;
524
- changedUser?: I_UserUpdate;
525
- confirmSubscription?: boolean;
526
- confirmUnsubscription?: boolean;
527
- heartbeatPing?: number;
528
- heartbeatPong?: number;
529
- newType?: I_DocumentType;
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
  }