@hivedev/hivesdk 1.0.37 → 1.0.41
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/README.md +188 -151
- package/hive-server.cjs +65 -31
- package/hive-server.js +65 -31
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ Every service in the Hive ecosystem (NoteHive, and any future service) is built
|
|
|
10
10
|
|
|
11
11
|
- [Concepts](#concepts)
|
|
12
12
|
- [Installation](#installation)
|
|
13
|
+
- [ESM vs CJS — Which to Use](#esm-vs-cjs--which-to-use)
|
|
13
14
|
- [Server SDK](#server-sdk)
|
|
14
15
|
- [1. Initialising the Server](#1-initialising-the-server)
|
|
15
16
|
- [2. Registering the Service](#2-registering-the-service)
|
|
@@ -54,9 +55,47 @@ npm install @hivedev/hivesdk
|
|
|
54
55
|
```
|
|
55
56
|
|
|
56
57
|
The SDK ships three pre-built files:
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
|
|
59
|
+
| File | Format | For |
|
|
60
|
+
|------|--------|-----|
|
|
61
|
+
| `hive-server.js` | ESM | Node.js backends with `"type": "module"` in `package.json` |
|
|
62
|
+
| `hive-server.cjs` | CJS | Node.js backends without `"type": "module"` |
|
|
63
|
+
| `hive-client.js` | ESM | Browser frontends only |
|
|
64
|
+
|
|
65
|
+
> **Peer dependencies:** `nodemailer` and `mongodb` are not bundled into the SDK. Install them in your service:
|
|
66
|
+
> ```bash
|
|
67
|
+
> npm install nodemailer mongodb
|
|
68
|
+
> ```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## ESM vs CJS — Which to Use
|
|
73
|
+
|
|
74
|
+
The SDK provides both an ESM and a CJS build for the server. **You use one or the other depending on your own application's module system — never both.**
|
|
75
|
+
|
|
76
|
+
**If your `package.json` has `"type": "module"`**, your project is ESM. Use `import`:
|
|
77
|
+
|
|
78
|
+
```js
|
|
79
|
+
import { initServer } from "@hivedev/hivesdk/server";
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**If your `package.json` does not have `"type": "module"`** (or explicitly has `"type": "commonjs"`), your project is CJS. Use `require`:
|
|
83
|
+
|
|
84
|
+
```js
|
|
85
|
+
const { initServer } = require("@hivedev/hivesdk/server");
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Mixing them will fail. Using `require()` in an ESM project throws:
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
ReferenceError: require is not defined in ES module scope
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Using `import` in a CJS project throws a syntax error. Check your `package.json` once and use the matching syntax everywhere.
|
|
95
|
+
|
|
96
|
+
All examples below show both variants. Use whichever matches your project.
|
|
97
|
+
|
|
98
|
+
> **Note:** CJS does not support top-level `await`. All async SDK calls in CJS must be wrapped in an `async` function or an async IIFE — `(async () => { ... })()`. The examples below show this where needed.
|
|
60
99
|
|
|
61
100
|
---
|
|
62
101
|
|
|
@@ -71,10 +110,10 @@ Call `initServer` once at the very start of your service, before anything else.
|
|
|
71
110
|
import { initServer } from "@hivedev/hivesdk/server";
|
|
72
111
|
|
|
73
112
|
await initServer({
|
|
74
|
-
serviceName: "NoteHive",
|
|
75
|
-
servicePort: 49161,
|
|
76
|
-
remoteUrl: "",
|
|
77
|
-
serverPassword: ""
|
|
113
|
+
serviceName: "NoteHive", // Must match the name registered in HivePortal
|
|
114
|
+
servicePort: 49161, // The port this service listens on
|
|
115
|
+
remoteUrl: "", // Public-facing URL if accessible from outside LAN, empty if LAN-only
|
|
116
|
+
serverPassword: "" // Optional — see password resolution below
|
|
78
117
|
});
|
|
79
118
|
```
|
|
80
119
|
|
|
@@ -82,19 +121,22 @@ await initServer({
|
|
|
82
121
|
// CJS
|
|
83
122
|
const { initServer } = require("@hivedev/hivesdk/server");
|
|
84
123
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
124
|
+
(async () =>
|
|
125
|
+
{
|
|
126
|
+
await initServer({
|
|
127
|
+
serviceName: "NoteHive",
|
|
128
|
+
servicePort: 49161,
|
|
129
|
+
remoteUrl: "",
|
|
130
|
+
serverPassword: ""
|
|
131
|
+
});
|
|
132
|
+
})();
|
|
91
133
|
```
|
|
92
134
|
|
|
93
135
|
**Password resolution order:**
|
|
94
136
|
|
|
95
137
|
1. The `serverPassword` argument, if provided.
|
|
96
138
|
2. The `SERVER_PASSWORD` environment variable, if set.
|
|
97
|
-
3. An interactive terminal prompt
|
|
139
|
+
3. An interactive terminal prompt — asks the user to create a password on first run, or enter their existing password on subsequent runs.
|
|
98
140
|
|
|
99
141
|
The password is used as the encryption key for all `.dat` configuration files. Losing it means losing access to the stored configuration.
|
|
100
142
|
|
|
@@ -117,34 +159,33 @@ await registerService();
|
|
|
117
159
|
// CJS
|
|
118
160
|
const { registerService } = require("@hivedev/hivesdk/server");
|
|
119
161
|
|
|
120
|
-
await registerService();
|
|
162
|
+
await registerService(); // inside an async function or IIFE
|
|
121
163
|
```
|
|
122
164
|
|
|
123
165
|
What this does internally:
|
|
124
166
|
|
|
125
167
|
1. Sets the service's local URL (`http://<hostIp>:<servicePort>`) and remote URL in the internal registry.
|
|
126
168
|
2. POSTs to HivePortal's `/RegisterService` endpoint.
|
|
127
|
-
3.
|
|
128
|
-
4. Once an administrator approves the service in HivePortal, the configuration payload is received, decrypted, and saved to disk
|
|
169
|
+
3. Begins polling HivePortal's `/Configuration` endpoint every 2 seconds.
|
|
170
|
+
4. Once an administrator approves the service in HivePortal, the configuration payload is received, decrypted, and saved to disk.
|
|
129
171
|
|
|
130
172
|
**On first run**, the terminal will show:
|
|
131
173
|
|
|
132
174
|
```
|
|
133
175
|
Registering service...
|
|
134
176
|
Service registration hasn't been approved yet. Please contact administrators!
|
|
135
|
-
Service registration hasn't been approved yet. Please contact administrators!
|
|
136
177
|
...
|
|
137
178
|
Configuration received.
|
|
138
179
|
Saving configuration...
|
|
139
180
|
Service registered.
|
|
140
181
|
```
|
|
141
182
|
|
|
142
|
-
Once registered and approved, subsequent startups load configuration from disk directly
|
|
183
|
+
Once registered and approved, subsequent startups load configuration from disk directly and do not need to poll again.
|
|
143
184
|
|
|
144
185
|
> In a clustered setup, call `registerService` only from the primary process, after a short delay to allow workers to start up first.
|
|
145
186
|
|
|
146
187
|
```js
|
|
147
|
-
// ESM
|
|
188
|
+
// ESM — clustered
|
|
148
189
|
import cluster from "cluster";
|
|
149
190
|
import os from "os";
|
|
150
191
|
import { initServer, registerService } from "@hivedev/hivesdk/server";
|
|
@@ -153,46 +194,29 @@ if (cluster.isPrimary)
|
|
|
153
194
|
{
|
|
154
195
|
await initServer({ serviceName: "NoteHive", servicePort: 49161, remoteUrl: "" });
|
|
155
196
|
|
|
156
|
-
setTimeout(async () =>
|
|
157
|
-
{
|
|
158
|
-
await registerService();
|
|
159
|
-
}, 3000);
|
|
197
|
+
setTimeout(async () => { await registerService(); }, 3000);
|
|
160
198
|
|
|
161
|
-
for (let i = 0; i < os.cpus().length; i++)
|
|
162
|
-
{
|
|
163
|
-
cluster.fork({ ...process.env });
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
else
|
|
167
|
-
{
|
|
168
|
-
// Start your HTTP server in workers
|
|
199
|
+
for (let i = 0; i < os.cpus().length; i++) cluster.fork({ ...process.env });
|
|
169
200
|
}
|
|
170
201
|
```
|
|
171
202
|
|
|
172
203
|
```js
|
|
173
|
-
// CJS
|
|
204
|
+
// CJS — clustered
|
|
174
205
|
const cluster = require("cluster");
|
|
175
206
|
const os = require("os");
|
|
176
207
|
const { initServer, registerService } = require("@hivedev/hivesdk/server");
|
|
177
208
|
|
|
178
|
-
|
|
209
|
+
(async () =>
|
|
179
210
|
{
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
setTimeout(async () =>
|
|
211
|
+
if (cluster.isPrimary)
|
|
183
212
|
{
|
|
184
|
-
await
|
|
185
|
-
}, 3000);
|
|
213
|
+
await initServer({ serviceName: "NoteHive", servicePort: 49161, remoteUrl: "" });
|
|
186
214
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
cluster.fork({ ...process.env });
|
|
215
|
+
setTimeout(async () => { await registerService(); }, 3000);
|
|
216
|
+
|
|
217
|
+
for (let i = 0; i < os.cpus().length; i++) cluster.fork({ ...process.env });
|
|
190
218
|
}
|
|
191
|
-
}
|
|
192
|
-
else
|
|
193
|
-
{
|
|
194
|
-
// Start your HTTP server in workers
|
|
195
|
-
}
|
|
219
|
+
})();
|
|
196
220
|
```
|
|
197
221
|
|
|
198
222
|
---
|
|
@@ -212,8 +236,6 @@ const app = express();
|
|
|
212
236
|
app.use(express.json());
|
|
213
237
|
app.use(cookieParser());
|
|
214
238
|
app.use(handleHiveRequests); // Must come before your own routes
|
|
215
|
-
|
|
216
|
-
app.get("/my-route", (request, response) => { ... });
|
|
217
239
|
```
|
|
218
240
|
|
|
219
241
|
```js
|
|
@@ -227,22 +249,20 @@ const app = express();
|
|
|
227
249
|
app.use(express.json());
|
|
228
250
|
app.use(cookieParser());
|
|
229
251
|
app.use(handleHiveRequests);
|
|
230
|
-
|
|
231
|
-
app.get("/my-route", (request, response) => { ... });
|
|
232
252
|
```
|
|
233
253
|
|
|
234
254
|
**Routes handled automatically by this middleware:**
|
|
235
255
|
|
|
236
256
|
| Method | Route | What it does |
|
|
237
257
|
|--------|-------|--------------|
|
|
238
|
-
| `POST` | `/Login` | Proxies
|
|
239
|
-
| `POST` | `/Logout` | Proxies logout to HivePortal
|
|
258
|
+
| `POST` | `/Login` | Proxies credentials to HivePortal, sets session cookie and/or `x-session-token` header |
|
|
259
|
+
| `POST` | `/Logout` | Proxies logout to HivePortal |
|
|
240
260
|
| `GET` | `/ServiceUrls` | Returns local or remote URL for a named service |
|
|
241
261
|
| `POST` | `/IsLoggedIn` | Checks if the current session is valid with HivePortal |
|
|
242
|
-
| `POST` | `/IsLoggedInWithPermission` | Checks session validity and a
|
|
262
|
+
| `POST` | `/IsLoggedInWithPermission` | Checks session validity and a named permission |
|
|
243
263
|
| `POST` | `/SaveUserFilter` | Saves a user-defined data filter (requires `FILTER_OPERATIONS` permission) |
|
|
244
264
|
| `POST` | `/PushNotificationToken` | Registers a push notification token for the current device |
|
|
245
|
-
| `GET` | `/hive-client.js` | Serves the built client-side SDK bundle
|
|
265
|
+
| `GET` | `/hive-client.js` | Serves the built client-side SDK bundle |
|
|
246
266
|
|
|
247
267
|
You do not need to implement any of these routes yourself.
|
|
248
268
|
|
|
@@ -263,7 +283,7 @@ await loadConfiguration();
|
|
|
263
283
|
// CJS
|
|
264
284
|
const { loadConfiguration } = require("@hivedev/hivesdk/server");
|
|
265
285
|
|
|
266
|
-
await loadConfiguration();
|
|
286
|
+
await loadConfiguration(); // inside an async function or IIFE
|
|
267
287
|
```
|
|
268
288
|
|
|
269
289
|
This populates the following environment variables (only those with saved `.dat` files on disk are loaded):
|
|
@@ -274,22 +294,33 @@ This populates the following environment variables (only those with saved `.dat`
|
|
|
274
294
|
| `DATABASE_CREDENTIALS` | JSON string of database credentials, keyed by service name |
|
|
275
295
|
| `PERMISSIONS` | JSON string of permission definitions |
|
|
276
296
|
| `SMTP_CREDENTIALS` | JSON string of SMTP credentials |
|
|
277
|
-
| `SMTP_PASSWORD` | Extracted SMTP password, ready for
|
|
297
|
+
| `SMTP_PASSWORD` | Extracted SMTP password, ready for nodemailer |
|
|
278
298
|
|
|
279
|
-
It also calls `DatabaseConnector.setCredentials` internally using the credentials
|
|
299
|
+
It also calls `DatabaseConnector.setCredentials` internally using the credentials for your service name, so the database is ready to connect after this call.
|
|
280
300
|
|
|
281
301
|
If a `.dat` file does not exist yet (e.g. first run before registration is approved), that credential is silently skipped.
|
|
282
302
|
|
|
283
|
-
> Call `loadConfiguration` before `registerService`. On first run the files won't exist yet and it will skip gracefully. Once
|
|
303
|
+
> Call `loadConfiguration` before `registerService`. On first run the files won't exist yet and it will skip gracefully. Once the administrator approves the service and `registerService` saves the config, it will be fully loaded on the next startup.
|
|
284
304
|
|
|
285
305
|
---
|
|
286
306
|
|
|
287
307
|
### 5. Connecting to the Database
|
|
288
308
|
|
|
289
|
-
|
|
309
|
+
`DatabaseConnector` supports two connection modes. **You use one or the other — not both at the same time.**
|
|
310
|
+
|
|
311
|
+
#### Option A — MongoDB Atlas
|
|
312
|
+
|
|
313
|
+
Set the `MONGODB_ATLAS_URL` environment variable to your Atlas connection string. When this variable is present, `connect()` uses it directly and skips the local credentials system entirely — no `setCredentials` call, no `loadConfiguration` needed for the database.
|
|
314
|
+
|
|
315
|
+
The database name is read from the URL path if present (e.g. the `mydb` in `...mongodb.net/mydb`). If the URL has no path, the name falls back to whatever was set via `setCredentials`, or `"untitled"` if neither was called.
|
|
290
316
|
|
|
291
317
|
```js
|
|
292
|
-
//
|
|
318
|
+
// Set in your environment before connect() is called
|
|
319
|
+
process.env.MONGODB_ATLAS_URL = "mongodb+srv://user:pass@cluster.mongodb.net/mydb";
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
```js
|
|
323
|
+
// ESM
|
|
293
324
|
import DatabaseConnector from "./node_modules/@hivedev/hivesdk/DatabaseConnector.js";
|
|
294
325
|
|
|
295
326
|
const connected = await DatabaseConnector.connect();
|
|
@@ -300,14 +331,35 @@ if (!connected)
|
|
|
300
331
|
process.exit(1);
|
|
301
332
|
}
|
|
302
333
|
|
|
303
|
-
|
|
304
|
-
const collection = DatabaseConnector.databaseObject.collection("my_collection");
|
|
334
|
+
const collection = DatabaseConnector.getDatabase().collection("my_collection");
|
|
305
335
|
```
|
|
306
336
|
|
|
307
337
|
```js
|
|
308
338
|
// CJS
|
|
309
339
|
const DatabaseConnector = require("./node_modules/@hivedev/hivesdk/DatabaseConnector.js");
|
|
310
340
|
|
|
341
|
+
(async () =>
|
|
342
|
+
{
|
|
343
|
+
const connected = await DatabaseConnector.connect();
|
|
344
|
+
|
|
345
|
+
if (!connected)
|
|
346
|
+
{
|
|
347
|
+
console.error("Failed to connect to database.");
|
|
348
|
+
process.exit(1);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const collection = DatabaseConnector.getDatabase().collection("my_collection");
|
|
352
|
+
})();
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
#### Option B — Self-hosted / Local MongoDB
|
|
356
|
+
|
|
357
|
+
If `MONGODB_ATLAS_URL` is not set, `connect()` falls back to the local credentials flow. Credentials are set automatically by `loadConfiguration` using the service-name-keyed entry in the `DATABASE_CREDENTIALS` env var. If credentials have not been configured when `connect()` is called, it will attempt to call `loadConfiguration` itself.
|
|
358
|
+
|
|
359
|
+
```js
|
|
360
|
+
// ESM — credentials are loaded automatically via loadConfiguration()
|
|
361
|
+
import DatabaseConnector from "./node_modules/@hivedev/hivesdk/DatabaseConnector.js";
|
|
362
|
+
|
|
311
363
|
const connected = await DatabaseConnector.connect();
|
|
312
364
|
|
|
313
365
|
if (!connected)
|
|
@@ -316,10 +368,38 @@ if (!connected)
|
|
|
316
368
|
process.exit(1);
|
|
317
369
|
}
|
|
318
370
|
|
|
319
|
-
const collection = DatabaseConnector.
|
|
371
|
+
const collection = DatabaseConnector.getDatabase().collection("my_collection");
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
```js
|
|
375
|
+
// CJS
|
|
376
|
+
const DatabaseConnector = require("./node_modules/@hivedev/hivesdk/DatabaseConnector.js");
|
|
377
|
+
|
|
378
|
+
(async () =>
|
|
379
|
+
{
|
|
380
|
+
const connected = await DatabaseConnector.connect();
|
|
381
|
+
|
|
382
|
+
if (!connected)
|
|
383
|
+
{
|
|
384
|
+
console.error("Failed to connect to database.");
|
|
385
|
+
process.exit(1);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const collection = DatabaseConnector.getDatabase().collection("my_collection");
|
|
389
|
+
})();
|
|
320
390
|
```
|
|
321
391
|
|
|
322
|
-
|
|
392
|
+
#### Accessing the Database Object
|
|
393
|
+
|
|
394
|
+
Two equivalent ways to get the active database instance after a successful `connect()`:
|
|
395
|
+
|
|
396
|
+
```js
|
|
397
|
+
// Recommended — throws a clear error if not connected
|
|
398
|
+
const db = DatabaseConnector.getDatabase();
|
|
399
|
+
|
|
400
|
+
// Also works — direct field access, returns null if not connected
|
|
401
|
+
const db = DatabaseConnector.databaseObject;
|
|
402
|
+
```
|
|
323
403
|
|
|
324
404
|
**Disconnecting:**
|
|
325
405
|
|
|
@@ -335,11 +415,11 @@ Use `isLoggedIn` and `isLoggedInWithPermission` to protect your own route handle
|
|
|
335
415
|
|
|
336
416
|
When `bSendResponse` is `false`, the function returns a boolean and leaves the response untouched — use this to guard logic inside your own handlers.
|
|
337
417
|
|
|
338
|
-
When `bSendResponse` is `true`, the function writes the result directly to the response — used internally by `handleHiveRequests
|
|
418
|
+
When `bSendResponse` is `true`, the function writes the result directly to the response — used internally by `handleHiveRequests`.
|
|
339
419
|
|
|
340
|
-
Both functions handle **session refresh transparently** — if HivePortal signals that the
|
|
420
|
+
Both functions handle **session refresh transparently** — if HivePortal signals that the token should be refreshed, a new cookie and/or `x-session-token` header is set automatically before your handler continues.
|
|
341
421
|
|
|
342
|
-
**
|
|
422
|
+
**Login check:**
|
|
343
423
|
|
|
344
424
|
```js
|
|
345
425
|
// ESM
|
|
@@ -379,7 +459,7 @@ app.get("/my-protected-route", async (request, response) =>
|
|
|
379
459
|
});
|
|
380
460
|
```
|
|
381
461
|
|
|
382
|
-
**
|
|
462
|
+
**Permission check:**
|
|
383
463
|
|
|
384
464
|
The permission name must be set on `request.body.permissionName` before calling `isLoggedInWithPermission`.
|
|
385
465
|
|
|
@@ -436,8 +516,8 @@ import { sendMail } from "@hivedev/hivesdk/server";
|
|
|
436
516
|
await sendMail(
|
|
437
517
|
"sender@yourdomain.com",
|
|
438
518
|
"recipient@example.com",
|
|
439
|
-
"Subject line
|
|
440
|
-
"<p>HTML body
|
|
519
|
+
"Subject line",
|
|
520
|
+
"<p>HTML body</p>"
|
|
441
521
|
);
|
|
442
522
|
```
|
|
443
523
|
|
|
@@ -448,137 +528,110 @@ const { sendMail } = require("@hivedev/hivesdk/server");
|
|
|
448
528
|
await sendMail(
|
|
449
529
|
"sender@yourdomain.com",
|
|
450
530
|
"recipient@example.com",
|
|
451
|
-
"Subject line
|
|
452
|
-
"<p>HTML body
|
|
531
|
+
"Subject line",
|
|
532
|
+
"<p>HTML body</p>"
|
|
453
533
|
);
|
|
454
534
|
```
|
|
455
535
|
|
|
456
|
-
Uses Gmail SMTP via nodemailer. The SMTP password is read from `process.env.SMTP_PASSWORD`,
|
|
536
|
+
Uses Gmail SMTP via nodemailer. The SMTP password is read from `process.env.SMTP_PASSWORD`, populated automatically by `loadConfiguration`. The sender Gmail address is hardcoded in `SendMail.js` — edit it in the SDK source if needed.
|
|
457
537
|
|
|
458
538
|
---
|
|
459
539
|
|
|
460
540
|
### 8. Utility Functions
|
|
461
541
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
**`getAppDataDirectory()`**
|
|
465
|
-
|
|
466
|
-
Returns the platform-appropriate data directory for your service, creating it if it doesn't exist. On Windows this is `%APPDATA%\<ServiceName>`, on macOS `~/Library/Application Support/<ServiceName>`, on Linux `~/.config/<ServiceName>`.
|
|
542
|
+
**`getAppDataDirectory()`** — Platform-appropriate data directory for your service. On Windows: `%APPDATA%\<ServiceName>`, macOS: `~/Library/Application Support/<ServiceName>`, Linux: `~/.config/<ServiceName>`.
|
|
467
543
|
|
|
468
544
|
```js
|
|
469
545
|
// ESM
|
|
470
546
|
import { getAppDataDirectory } from "@hivedev/hivesdk/server";
|
|
471
|
-
const dataDir = getAppDataDirectory();
|
|
472
|
-
// e.g. "/home/user/.config/NoteHive"
|
|
547
|
+
const dataDir = getAppDataDirectory(); // e.g. "/home/user/.config/NoteHive"
|
|
473
548
|
```
|
|
474
|
-
|
|
475
549
|
```js
|
|
476
550
|
// CJS
|
|
477
551
|
const { getAppDataDirectory } = require("@hivedev/hivesdk/server");
|
|
478
552
|
const dataDir = getAppDataDirectory();
|
|
479
553
|
```
|
|
480
554
|
|
|
481
|
-
|
|
555
|
+
---
|
|
482
556
|
|
|
483
|
-
|
|
557
|
+
**`getHostIp()`** — First non-loopback IPv4 address of the host machine.
|
|
484
558
|
|
|
485
559
|
```js
|
|
486
560
|
// ESM
|
|
487
561
|
import { getHostIp } from "@hivedev/hivesdk/server";
|
|
488
562
|
const ip = getHostIp(); // e.g. "192.168.1.42"
|
|
489
563
|
```
|
|
490
|
-
|
|
491
564
|
```js
|
|
492
565
|
// CJS
|
|
493
566
|
const { getHostIp } = require("@hivedev/hivesdk/server");
|
|
494
567
|
const ip = getHostIp();
|
|
495
568
|
```
|
|
496
569
|
|
|
497
|
-
|
|
570
|
+
---
|
|
498
571
|
|
|
499
|
-
Discovers another Hive service on the LAN via UDP to port 49153. Returns `{ name, urls: { local, remote } }` or `null
|
|
572
|
+
**`findService(serviceName)`** — Discovers another Hive service on the LAN via UDP to port 49153. Returns `{ name, urls: { local, remote } }` or `null`. Requires `HIVE_PORTAL_LAN_IP` to be set.
|
|
500
573
|
|
|
501
574
|
```js
|
|
502
575
|
// ESM
|
|
503
576
|
import { findService } from "@hivedev/hivesdk/server";
|
|
504
|
-
|
|
505
577
|
const service = await findService("NoteHive");
|
|
506
|
-
|
|
507
|
-
if (service)
|
|
508
|
-
{
|
|
509
|
-
console.log(service.urls.local); // "http://192.168.1.42:49161"
|
|
510
|
-
}
|
|
578
|
+
if (service) console.log(service.urls.local); // "http://192.168.1.42:49161"
|
|
511
579
|
```
|
|
512
|
-
|
|
513
580
|
```js
|
|
514
581
|
// CJS
|
|
515
582
|
const { findService } = require("@hivedev/hivesdk/server");
|
|
516
|
-
|
|
517
583
|
const service = await findService("NoteHive");
|
|
518
|
-
|
|
519
|
-
if (service)
|
|
520
|
-
{
|
|
521
|
-
console.log(service.urls.local);
|
|
522
|
-
}
|
|
584
|
+
if (service) console.log(service.urls.local);
|
|
523
585
|
```
|
|
524
586
|
|
|
525
|
-
|
|
587
|
+
---
|
|
526
588
|
|
|
527
|
-
Parses `process.env` and returns the
|
|
589
|
+
**`extractConfigurationForService()`** — Parses `process.env` and returns the four credential sets as objects. Database credentials are extracted for the current service name specifically.
|
|
528
590
|
|
|
529
591
|
```js
|
|
530
592
|
// ESM
|
|
531
593
|
import { extractConfigurationForService } from "@hivedev/hivesdk/server";
|
|
532
|
-
|
|
533
594
|
const { firebaseAdminCredentials, databaseCredentials, permissions, smtpCredentials } = extractConfigurationForService();
|
|
534
595
|
```
|
|
535
|
-
|
|
536
596
|
```js
|
|
537
597
|
// CJS
|
|
538
598
|
const { extractConfigurationForService } = require("@hivedev/hivesdk/server");
|
|
539
|
-
|
|
540
599
|
const { firebaseAdminCredentials, databaseCredentials, permissions, smtpCredentials } = extractConfigurationForService();
|
|
541
600
|
```
|
|
542
601
|
|
|
543
|
-
|
|
602
|
+
---
|
|
544
603
|
|
|
545
|
-
Encrypts a string or Buffer with AES-256-CBC and writes it to disk. Creates the directory if it doesn't exist.
|
|
604
|
+
**`encryptAndStoreFile(data, fullPath, password)`** — Encrypts a string or Buffer with AES-256-CBC and writes it to disk. Creates the directory if it doesn't exist.
|
|
546
605
|
|
|
547
606
|
```js
|
|
548
607
|
// ESM
|
|
549
608
|
import { encryptAndStoreFile } from "@hivedev/hivesdk/server";
|
|
550
|
-
|
|
551
|
-
await encryptAndStoreFile("my secret data", "/path/to/file.dat", process.env.SERVER_PASSWORD);
|
|
609
|
+
await encryptAndStoreFile("secret", "/path/to/file.dat", process.env.SERVER_PASSWORD);
|
|
552
610
|
```
|
|
553
|
-
|
|
554
611
|
```js
|
|
555
612
|
// CJS
|
|
556
613
|
const { encryptAndStoreFile } = require("@hivedev/hivesdk/server");
|
|
557
|
-
|
|
558
|
-
await encryptAndStoreFile("my secret data", "/path/to/file.dat", process.env.SERVER_PASSWORD);
|
|
614
|
+
await encryptAndStoreFile("secret", "/path/to/file.dat", process.env.SERVER_PASSWORD);
|
|
559
615
|
```
|
|
560
616
|
|
|
561
|
-
|
|
617
|
+
---
|
|
562
618
|
|
|
563
|
-
Reads and decrypts an encrypted `.dat` file. Returns the decrypted string, or `null` on failure.
|
|
619
|
+
**`decryptFileWithPassword(fullPath, password)`** — Reads and decrypts an encrypted `.dat` file. Returns the decrypted string, or `null` on failure.
|
|
564
620
|
|
|
565
621
|
```js
|
|
566
622
|
// ESM
|
|
567
623
|
import { decryptFileWithPassword } from "@hivedev/hivesdk/server";
|
|
568
|
-
|
|
569
624
|
const contents = await decryptFileWithPassword("/path/to/file.dat", process.env.SERVER_PASSWORD);
|
|
570
625
|
```
|
|
571
|
-
|
|
572
626
|
```js
|
|
573
627
|
// CJS
|
|
574
628
|
const { decryptFileWithPassword } = require("@hivedev/hivesdk/server");
|
|
575
|
-
|
|
576
629
|
const contents = await decryptFileWithPassword("/path/to/file.dat", process.env.SERVER_PASSWORD);
|
|
577
630
|
```
|
|
578
631
|
|
|
579
|
-
|
|
632
|
+
---
|
|
580
633
|
|
|
581
|
-
|
|
634
|
+
**`saveConfiguration(configurationObject)` / `loadConfiguration()`** — Normally called automatically. `saveConfiguration` only writes keys present in the object — missing keys do not overwrite existing files.
|
|
582
635
|
|
|
583
636
|
```js
|
|
584
637
|
// ESM
|
|
@@ -591,7 +644,6 @@ await saveConfiguration({
|
|
|
591
644
|
|
|
592
645
|
await loadConfiguration();
|
|
593
646
|
```
|
|
594
|
-
|
|
595
647
|
```js
|
|
596
648
|
// CJS
|
|
597
649
|
const { saveConfiguration, loadConfiguration } = require("@hivedev/hivesdk/server");
|
|
@@ -608,8 +660,6 @@ await loadConfiguration();
|
|
|
608
660
|
|
|
609
661
|
### Full Server Bootstrap Example
|
|
610
662
|
|
|
611
|
-
This is the recommended startup sequence for any Hive microservice.
|
|
612
|
-
|
|
613
663
|
```js
|
|
614
664
|
// ESM
|
|
615
665
|
import express from "express";
|
|
@@ -731,13 +781,13 @@ app.get("/", (request, response) => response.redirect("/Client/Pages/HomePage.ht
|
|
|
731
781
|
|
|
732
782
|
## Client SDK
|
|
733
783
|
|
|
734
|
-
The client SDK is a browser-only ESM bundle
|
|
784
|
+
The client SDK is a browser-only ESM bundle. There is no CJS variant — it runs exclusively in the browser.
|
|
735
785
|
|
|
736
786
|
### 1. Loading the Client Bundle
|
|
737
787
|
|
|
738
|
-
The `handleHiveRequests` middleware automatically serves the client bundle at `/hive-client.js` by reading it from `node_modules/@hivedev/hivesdk/hive-client.js`. **This works as long as your service's Node.js process is started from the root of the package** (
|
|
788
|
+
The `handleHiveRequests` middleware automatically serves the client bundle at `/hive-client.js` by reading it from `node_modules/@hivedev/hivesdk/hive-client.js`. **This works as long as your service's Node.js process is started from the root of the package** (the directory containing `node_modules`), which is the recommended setup.
|
|
739
789
|
|
|
740
|
-
If your service is started from a different working directory, the automatic route will fail to locate the file. In that case, serve it
|
|
790
|
+
If your service is started from a different working directory, the automatic route will fail to locate the file. In that case, serve it manually:
|
|
741
791
|
|
|
742
792
|
```js
|
|
743
793
|
import path from "path";
|
|
@@ -745,26 +795,18 @@ import { createReadStream } from "fs";
|
|
|
745
795
|
|
|
746
796
|
app.get("/hive-client.js", (request, response) =>
|
|
747
797
|
{
|
|
748
|
-
const filePath =
|
|
798
|
+
const filePath = "/absolute/path/to/node_modules/@hivedev/hivesdk/hive-client.js";
|
|
749
799
|
response.setHeader("Content-Type", "application/javascript");
|
|
750
800
|
createReadStream(filePath).pipe(response);
|
|
751
801
|
});
|
|
752
802
|
```
|
|
753
803
|
|
|
754
|
-
Include it in your HTML pages
|
|
804
|
+
Include it in your HTML pages:
|
|
755
805
|
|
|
756
806
|
```html
|
|
757
807
|
<script type="module" src="/hive-client.js"></script>
|
|
758
808
|
```
|
|
759
809
|
|
|
760
|
-
Or import from it in another module script on the page:
|
|
761
|
-
|
|
762
|
-
```html
|
|
763
|
-
<script type="module">
|
|
764
|
-
import { login } from "/hive-client.js";
|
|
765
|
-
</script>
|
|
766
|
-
```
|
|
767
|
-
|
|
768
810
|
---
|
|
769
811
|
|
|
770
812
|
### 2. Initialising the Client
|
|
@@ -777,8 +819,6 @@ import { initClient } from "/hive-client.js";
|
|
|
777
819
|
await initClient({});
|
|
778
820
|
```
|
|
779
821
|
|
|
780
|
-
`initClient` accepts one optional parameter:
|
|
781
|
-
|
|
782
822
|
| Parameter | Type | Default | Description |
|
|
783
823
|
|-----------|------|---------|-------------|
|
|
784
824
|
| `loginTokenStorageMethod` | `sessionStorageMethod` enum | `HTTP_ONLY_COOKIE` | How session tokens are stored on the client |
|
|
@@ -787,7 +827,7 @@ await initClient({});
|
|
|
787
827
|
|
|
788
828
|
### 3. Login
|
|
789
829
|
|
|
790
|
-
Call `login()` to open the HivePortal login page. The SDK handles the entire flow — the popup opens, the user enters their credentials, and on success the page
|
|
830
|
+
Call `login()` to open the HivePortal login page. The SDK handles the entire flow — the popup opens, the user enters their credentials, and on success the page redirects to `/`.
|
|
791
831
|
|
|
792
832
|
```js
|
|
793
833
|
import { login } from "/hive-client.js";
|
|
@@ -795,11 +835,9 @@ import { login } from "/hive-client.js";
|
|
|
795
835
|
login();
|
|
796
836
|
```
|
|
797
837
|
|
|
798
|
-
|
|
838
|
+
Wire it to any element:
|
|
799
839
|
|
|
800
840
|
```js
|
|
801
|
-
import { login } from "/hive-client.js";
|
|
802
|
-
|
|
803
841
|
document.getElementById("login-button").addEventListener("click", login);
|
|
804
842
|
```
|
|
805
843
|
|
|
@@ -809,7 +847,7 @@ When running inside the native mobile app (detected automatically), the login po
|
|
|
809
847
|
|
|
810
848
|
### 4. Checking Login State
|
|
811
849
|
|
|
812
|
-
`initClient`
|
|
850
|
+
`initClient` starts a periodic login check (every 30 seconds, clamped between 10 and 60 seconds). Each check fires a `login-state-changed` custom event on `window`.
|
|
813
851
|
|
|
814
852
|
```js
|
|
815
853
|
window.addEventListener("login-state-changed", (event) =>
|
|
@@ -821,7 +859,7 @@ window.addEventListener("login-state-changed", (event) =>
|
|
|
821
859
|
});
|
|
822
860
|
```
|
|
823
861
|
|
|
824
|
-
|
|
862
|
+
Current login state is also available synchronously at any time:
|
|
825
863
|
|
|
826
864
|
```js
|
|
827
865
|
if (window.IS_LOGGED_IN)
|
|
@@ -874,8 +912,6 @@ if (window.IS_LOGGED_IN)
|
|
|
874
912
|
|
|
875
913
|
## Enumerations
|
|
876
914
|
|
|
877
|
-
The SDK exports several enumerations used throughout the Hive ecosystem.
|
|
878
|
-
|
|
879
915
|
**`userPrivileges`** — Numeric privilege levels assigned to users.
|
|
880
916
|
|
|
881
917
|
| Key | Value |
|
|
@@ -921,7 +957,7 @@ The SDK exports several enumerations used throughout the Hive ecosystem.
|
|
|
921
957
|
| `USER_REGISTERATION` | 0 |
|
|
922
958
|
| `SERVICE_REGISTERATION` | 1 |
|
|
923
959
|
|
|
924
|
-
**`filterTypes`**, **`filterOperators`**, **`userDataFetchFilterComparision`**, **`userFieldTypes`**, **`userFieldFormats`**, **`fileTreeNodeTypes`** — Domain-specific enumerations
|
|
960
|
+
**`filterTypes`**, **`filterOperators`**, **`userDataFetchFilterComparision`**, **`userFieldTypes`**, **`userFieldFormats`**, **`fileTreeNodeTypes`** — Domain-specific enumerations for data querying, user field definitions, and file tree structures. Refer to their respective source files for values.
|
|
925
961
|
|
|
926
962
|
---
|
|
927
963
|
|
|
@@ -934,6 +970,7 @@ The SDK exports several enumerations used throughout the Hive ecosystem.
|
|
|
934
970
|
| `SERVICE_PORT` | `initServer` | Port this service listens on |
|
|
935
971
|
| `REMOTE_URL` | `initServer` | Public-facing URL of this service (empty if LAN-only) |
|
|
936
972
|
| `HIVE_PORTAL_LAN_IP` | External | LAN IP of HivePortal, used by `findService` for UDP discovery |
|
|
973
|
+
| `MONGODB_ATLAS_URL` | External | Atlas connection string — if set, bypasses local DB credentials entirely |
|
|
937
974
|
| `FIREBASE_ADMIN_CREDENTIALS` | `loadConfiguration` | JSON string of Firebase Admin credentials |
|
|
938
975
|
| `DATABASE_CREDENTIALS` | `loadConfiguration` | JSON string of DB credentials keyed by service name |
|
|
939
976
|
| `PERMISSIONS` | `loadConfiguration` | JSON string of permission definitions |
|
|
@@ -980,8 +1017,8 @@ Every authentication check a service performs is a proxied call to HivePortal. H
|
|
|
980
1017
|
|
|
981
1018
|
## Known Limitations
|
|
982
1019
|
|
|
983
|
-
- **`ScheduleServiceUrlUpdate`** —
|
|
984
|
-
- **`loginTokenStorageMethod`** —
|
|
985
|
-
- **SMTP sender address** —
|
|
986
|
-
- **MongoDB only** — `DatabaseConnector` is built for MongoDB
|
|
987
|
-
- **MongoDB port hardcoded** —
|
|
1020
|
+
- **`ScheduleServiceUrlUpdate`** — Currently a stub and does nothing. Intended for future use to re-broadcast service URLs if the host IP changes.
|
|
1021
|
+
- **`loginTokenStorageMethod`** — `LOCAL_STORAGE` and `SESSION_STORAGE` are defined in the enum but not yet implemented. Only `HTTP_ONLY_COOKIE` (with the `x-session-token` header fallback for native app clients) is fully functional.
|
|
1022
|
+
- **SMTP sender address** — Hardcoded in `SendMail.js`. Edit it in the SDK source if your service needs a different address.
|
|
1023
|
+
- **MongoDB only** — `DatabaseConnector` is built for MongoDB only.
|
|
1024
|
+
- **MongoDB port hardcoded** — For local connections, the port is fixed at `27017`. The host is configurable via credentials but the port is not. Use Atlas (`MONGODB_ATLAS_URL`) if you need a non-standard port.
|
package/hive-server.cjs
CHANGED
|
@@ -326,29 +326,52 @@ var DatabaseConnector = class _DatabaseConnector {
|
|
|
326
326
|
static databaseObject = null;
|
|
327
327
|
/**
|
|
328
328
|
* Connects to the MongoDB database.
|
|
329
|
-
*
|
|
330
|
-
* If the
|
|
331
|
-
*
|
|
332
|
-
*
|
|
333
|
-
*
|
|
334
|
-
*
|
|
335
|
-
*
|
|
329
|
+
*
|
|
330
|
+
* If the MONGODB_ATLAS_URL environment variable is set, it is used directly as the
|
|
331
|
+
* connection string and the local credentials configuration is bypassed entirely.
|
|
332
|
+
* The database name is parsed from the URL path if present (e.g. the "mydb" in
|
|
333
|
+
* "mongodb+srv://user:pass@cluster.mongodb.net/mydb"), otherwise the value set via
|
|
334
|
+
* setCredentials is used.
|
|
335
|
+
*
|
|
336
|
+
* If MONGODB_ATLAS_URL is not set, the normal local credentials flow applies:
|
|
337
|
+
* if credentials have not been configured, loadConfiguration() is called first.
|
|
338
|
+
*
|
|
339
|
+
* @returns {Promise<boolean>} True if the connection is successful, false otherwise.
|
|
336
340
|
*/
|
|
337
341
|
static async connect() {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
if (!_DatabaseConnector.#
|
|
341
|
-
|
|
342
|
-
|
|
342
|
+
const atlasUrl = process.env.MONGODB_ATLAS_URL;
|
|
343
|
+
if (atlasUrl) {
|
|
344
|
+
if (!_DatabaseConnector.#mongoClient) {
|
|
345
|
+
let databaseName = _DatabaseConnector.#databaseName;
|
|
346
|
+
try {
|
|
347
|
+
const parsedUrl = new URL(atlasUrl);
|
|
348
|
+
const nameFromUrl = parsedUrl.pathname.replace("/", "").trim();
|
|
349
|
+
if (nameFromUrl) {
|
|
350
|
+
databaseName = nameFromUrl;
|
|
351
|
+
}
|
|
352
|
+
} catch (error) {
|
|
353
|
+
console.error("DatabaseConnector: Failed to parse MONGODB_ATLAS_URL.", error);
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
_DatabaseConnector.#mongoClient = new import_mongodb.MongoClient(atlasUrl);
|
|
357
|
+
_DatabaseConnector.#databaseName = databaseName;
|
|
343
358
|
}
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
_DatabaseConnector.#
|
|
348
|
-
|
|
349
|
-
|
|
359
|
+
} else {
|
|
360
|
+
if (!_DatabaseConnector.#bConfigured) {
|
|
361
|
+
await loadConfiguration();
|
|
362
|
+
if (!_DatabaseConnector.#bConfigured) {
|
|
363
|
+
console.log("Database configuration is not set! Please provide database credentials first.");
|
|
364
|
+
return false;
|
|
350
365
|
}
|
|
351
|
-
|
|
366
|
+
}
|
|
367
|
+
if (!_DatabaseConnector.#mongoClient) {
|
|
368
|
+
_DatabaseConnector.#mongoClient = new import_mongodb.MongoClient(
|
|
369
|
+
_DatabaseConnector.#databaseUrl,
|
|
370
|
+
{
|
|
371
|
+
auth: { username: _DatabaseConnector.#username, password: _DatabaseConnector.#password }
|
|
372
|
+
}
|
|
373
|
+
);
|
|
374
|
+
}
|
|
352
375
|
}
|
|
353
376
|
try {
|
|
354
377
|
await _DatabaseConnector.#mongoClient.connect();
|
|
@@ -375,7 +398,8 @@ var DatabaseConnector = class _DatabaseConnector {
|
|
|
375
398
|
console.log("Disconnected from database");
|
|
376
399
|
}
|
|
377
400
|
/**
|
|
378
|
-
* Sets the database credentials for the DatabaseConnector
|
|
401
|
+
* Sets the database credentials for the DatabaseConnector.
|
|
402
|
+
* Used for local/self-hosted MongoDB instances. Not required when using MONGODB_ATLAS_URL.
|
|
379
403
|
* @param {Object} obj - contains the database host, username, password, and name
|
|
380
404
|
* @param {string} obj.host - The host of the database
|
|
381
405
|
* @param {string} obj.username - The username of the database
|
|
@@ -394,6 +418,17 @@ var DatabaseConnector = class _DatabaseConnector {
|
|
|
394
418
|
else throw new Error("Database name not set!");
|
|
395
419
|
_DatabaseConnector.#bConfigured = true;
|
|
396
420
|
}
|
|
421
|
+
/**
|
|
422
|
+
* Returns the active MongoDB database object.
|
|
423
|
+
* @returns {import("mongodb").Db} The active database object.
|
|
424
|
+
* @throws {Error} If connect() has not been called or the connection failed.
|
|
425
|
+
*/
|
|
426
|
+
static getDatabase() {
|
|
427
|
+
if (!_DatabaseConnector.databaseObject) {
|
|
428
|
+
throw new Error("DatabaseConnector: No active database connection. Call connect() first.");
|
|
429
|
+
}
|
|
430
|
+
return _DatabaseConnector.databaseObject;
|
|
431
|
+
}
|
|
397
432
|
};
|
|
398
433
|
var DatabaseConnector_default = DatabaseConnector;
|
|
399
434
|
|
|
@@ -788,9 +823,6 @@ async function handleLogin(request, response) {
|
|
|
788
823
|
var import_path6 = __toESM(require("path"), 1);
|
|
789
824
|
var import_fs6 = __toESM(require("fs"), 1);
|
|
790
825
|
|
|
791
|
-
// Server/Authentication/IsLoggedIn.js
|
|
792
|
-
var import_cookie2 = __toESM(require_dist(), 1);
|
|
793
|
-
|
|
794
826
|
// Server/Utility/ParseRequestCookies.js
|
|
795
827
|
async function parseRequestCookies(request) {
|
|
796
828
|
if (typeof request.cookies === "object") {
|
|
@@ -815,6 +847,7 @@ async function parseRequestCookies(request) {
|
|
|
815
847
|
}
|
|
816
848
|
|
|
817
849
|
// Server/Authentication/IsLoggedIn.js
|
|
850
|
+
var import_cookie2 = __toESM(require_dist(), 1);
|
|
818
851
|
async function isLoggedIn(request, response, bSendResponse = false) {
|
|
819
852
|
await parseRequestCookies(request);
|
|
820
853
|
const cookies = request.cookies;
|
|
@@ -1036,29 +1069,30 @@ async function pushNotificationToken(request, response) {
|
|
|
1036
1069
|
// Server/HandleHiveRequests.js
|
|
1037
1070
|
async function handleHiveRequests(request, response, next) {
|
|
1038
1071
|
const { url, method } = request;
|
|
1072
|
+
const pathname = url.split("?")[0];
|
|
1039
1073
|
try {
|
|
1040
|
-
if (
|
|
1074
|
+
if (pathname === "/Login" && method === "POST") {
|
|
1041
1075
|
await handleLogin(request, response);
|
|
1042
1076
|
return;
|
|
1043
|
-
} else if (
|
|
1077
|
+
} else if (pathname === "/Logout" && method === "POST") {
|
|
1044
1078
|
await handleLogout(request, response);
|
|
1045
1079
|
return;
|
|
1046
|
-
} else if (
|
|
1080
|
+
} else if (pathname === "/ServiceUrls" && method === "GET") {
|
|
1047
1081
|
await getServiceUrls(request, response);
|
|
1048
1082
|
return;
|
|
1049
|
-
} else if (
|
|
1083
|
+
} else if (pathname === "/IsLoggedIn" && method === "POST") {
|
|
1050
1084
|
await isLoggedIn(request, response, true);
|
|
1051
1085
|
return;
|
|
1052
|
-
} else if (
|
|
1086
|
+
} else if (pathname === "/IsLoggedInWithPermission" && method === "POST") {
|
|
1053
1087
|
await isLoggedInWithPermission(request, response, true);
|
|
1054
1088
|
return;
|
|
1055
|
-
} else if (
|
|
1089
|
+
} else if (pathname === "/SaveUserFilter" && method === "POST") {
|
|
1056
1090
|
await saveUserFilter(request, response);
|
|
1057
1091
|
return;
|
|
1058
|
-
} else if (
|
|
1092
|
+
} else if (pathname === "/PushNotificationToken" && method === "POST") {
|
|
1059
1093
|
pushNotificationToken(request, response);
|
|
1060
1094
|
return;
|
|
1061
|
-
} else if (
|
|
1095
|
+
} else if (pathname === "/hive-client.js") {
|
|
1062
1096
|
const filePath = import_path6.default.join(process.cwd(), "node_modules/@hivedev/hivesdk/hive-client.js");
|
|
1063
1097
|
const stream = import_fs6.default.createReadStream(filePath);
|
|
1064
1098
|
stream.on("open", () => {
|
package/hive-server.js
CHANGED
|
@@ -299,29 +299,52 @@ var DatabaseConnector = class _DatabaseConnector {
|
|
|
299
299
|
static databaseObject = null;
|
|
300
300
|
/**
|
|
301
301
|
* Connects to the MongoDB database.
|
|
302
|
-
*
|
|
303
|
-
* If the
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
302
|
+
*
|
|
303
|
+
* If the MONGODB_ATLAS_URL environment variable is set, it is used directly as the
|
|
304
|
+
* connection string and the local credentials configuration is bypassed entirely.
|
|
305
|
+
* The database name is parsed from the URL path if present (e.g. the "mydb" in
|
|
306
|
+
* "mongodb+srv://user:pass@cluster.mongodb.net/mydb"), otherwise the value set via
|
|
307
|
+
* setCredentials is used.
|
|
308
|
+
*
|
|
309
|
+
* If MONGODB_ATLAS_URL is not set, the normal local credentials flow applies:
|
|
310
|
+
* if credentials have not been configured, loadConfiguration() is called first.
|
|
311
|
+
*
|
|
312
|
+
* @returns {Promise<boolean>} True if the connection is successful, false otherwise.
|
|
309
313
|
*/
|
|
310
314
|
static async connect() {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
if (!_DatabaseConnector.#
|
|
314
|
-
|
|
315
|
-
|
|
315
|
+
const atlasUrl = process.env.MONGODB_ATLAS_URL;
|
|
316
|
+
if (atlasUrl) {
|
|
317
|
+
if (!_DatabaseConnector.#mongoClient) {
|
|
318
|
+
let databaseName = _DatabaseConnector.#databaseName;
|
|
319
|
+
try {
|
|
320
|
+
const parsedUrl = new URL(atlasUrl);
|
|
321
|
+
const nameFromUrl = parsedUrl.pathname.replace("/", "").trim();
|
|
322
|
+
if (nameFromUrl) {
|
|
323
|
+
databaseName = nameFromUrl;
|
|
324
|
+
}
|
|
325
|
+
} catch (error) {
|
|
326
|
+
console.error("DatabaseConnector: Failed to parse MONGODB_ATLAS_URL.", error);
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
_DatabaseConnector.#mongoClient = new MongoClient(atlasUrl);
|
|
330
|
+
_DatabaseConnector.#databaseName = databaseName;
|
|
316
331
|
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
_DatabaseConnector.#
|
|
321
|
-
|
|
322
|
-
|
|
332
|
+
} else {
|
|
333
|
+
if (!_DatabaseConnector.#bConfigured) {
|
|
334
|
+
await loadConfiguration();
|
|
335
|
+
if (!_DatabaseConnector.#bConfigured) {
|
|
336
|
+
console.log("Database configuration is not set! Please provide database credentials first.");
|
|
337
|
+
return false;
|
|
323
338
|
}
|
|
324
|
-
|
|
339
|
+
}
|
|
340
|
+
if (!_DatabaseConnector.#mongoClient) {
|
|
341
|
+
_DatabaseConnector.#mongoClient = new MongoClient(
|
|
342
|
+
_DatabaseConnector.#databaseUrl,
|
|
343
|
+
{
|
|
344
|
+
auth: { username: _DatabaseConnector.#username, password: _DatabaseConnector.#password }
|
|
345
|
+
}
|
|
346
|
+
);
|
|
347
|
+
}
|
|
325
348
|
}
|
|
326
349
|
try {
|
|
327
350
|
await _DatabaseConnector.#mongoClient.connect();
|
|
@@ -348,7 +371,8 @@ var DatabaseConnector = class _DatabaseConnector {
|
|
|
348
371
|
console.log("Disconnected from database");
|
|
349
372
|
}
|
|
350
373
|
/**
|
|
351
|
-
* Sets the database credentials for the DatabaseConnector
|
|
374
|
+
* Sets the database credentials for the DatabaseConnector.
|
|
375
|
+
* Used for local/self-hosted MongoDB instances. Not required when using MONGODB_ATLAS_URL.
|
|
352
376
|
* @param {Object} obj - contains the database host, username, password, and name
|
|
353
377
|
* @param {string} obj.host - The host of the database
|
|
354
378
|
* @param {string} obj.username - The username of the database
|
|
@@ -367,6 +391,17 @@ var DatabaseConnector = class _DatabaseConnector {
|
|
|
367
391
|
else throw new Error("Database name not set!");
|
|
368
392
|
_DatabaseConnector.#bConfigured = true;
|
|
369
393
|
}
|
|
394
|
+
/**
|
|
395
|
+
* Returns the active MongoDB database object.
|
|
396
|
+
* @returns {import("mongodb").Db} The active database object.
|
|
397
|
+
* @throws {Error} If connect() has not been called or the connection failed.
|
|
398
|
+
*/
|
|
399
|
+
static getDatabase() {
|
|
400
|
+
if (!_DatabaseConnector.databaseObject) {
|
|
401
|
+
throw new Error("DatabaseConnector: No active database connection. Call connect() first.");
|
|
402
|
+
}
|
|
403
|
+
return _DatabaseConnector.databaseObject;
|
|
404
|
+
}
|
|
370
405
|
};
|
|
371
406
|
var DatabaseConnector_default = DatabaseConnector;
|
|
372
407
|
|
|
@@ -761,9 +796,6 @@ async function handleLogin(request, response) {
|
|
|
761
796
|
import path6, { parse } from "path";
|
|
762
797
|
import fs6 from "fs";
|
|
763
798
|
|
|
764
|
-
// Server/Authentication/IsLoggedIn.js
|
|
765
|
-
var import_cookie2 = __toESM(require_dist(), 1);
|
|
766
|
-
|
|
767
799
|
// Server/Utility/ParseRequestCookies.js
|
|
768
800
|
async function parseRequestCookies(request) {
|
|
769
801
|
if (typeof request.cookies === "object") {
|
|
@@ -788,6 +820,7 @@ async function parseRequestCookies(request) {
|
|
|
788
820
|
}
|
|
789
821
|
|
|
790
822
|
// Server/Authentication/IsLoggedIn.js
|
|
823
|
+
var import_cookie2 = __toESM(require_dist(), 1);
|
|
791
824
|
async function isLoggedIn(request, response, bSendResponse = false) {
|
|
792
825
|
await parseRequestCookies(request);
|
|
793
826
|
const cookies = request.cookies;
|
|
@@ -1009,29 +1042,30 @@ async function pushNotificationToken(request, response) {
|
|
|
1009
1042
|
// Server/HandleHiveRequests.js
|
|
1010
1043
|
async function handleHiveRequests(request, response, next) {
|
|
1011
1044
|
const { url, method } = request;
|
|
1045
|
+
const pathname = url.split("?")[0];
|
|
1012
1046
|
try {
|
|
1013
|
-
if (
|
|
1047
|
+
if (pathname === "/Login" && method === "POST") {
|
|
1014
1048
|
await handleLogin(request, response);
|
|
1015
1049
|
return;
|
|
1016
|
-
} else if (
|
|
1050
|
+
} else if (pathname === "/Logout" && method === "POST") {
|
|
1017
1051
|
await handleLogout(request, response);
|
|
1018
1052
|
return;
|
|
1019
|
-
} else if (
|
|
1053
|
+
} else if (pathname === "/ServiceUrls" && method === "GET") {
|
|
1020
1054
|
await getServiceUrls(request, response);
|
|
1021
1055
|
return;
|
|
1022
|
-
} else if (
|
|
1056
|
+
} else if (pathname === "/IsLoggedIn" && method === "POST") {
|
|
1023
1057
|
await isLoggedIn(request, response, true);
|
|
1024
1058
|
return;
|
|
1025
|
-
} else if (
|
|
1059
|
+
} else if (pathname === "/IsLoggedInWithPermission" && method === "POST") {
|
|
1026
1060
|
await isLoggedInWithPermission(request, response, true);
|
|
1027
1061
|
return;
|
|
1028
|
-
} else if (
|
|
1062
|
+
} else if (pathname === "/SaveUserFilter" && method === "POST") {
|
|
1029
1063
|
await saveUserFilter(request, response);
|
|
1030
1064
|
return;
|
|
1031
|
-
} else if (
|
|
1065
|
+
} else if (pathname === "/PushNotificationToken" && method === "POST") {
|
|
1032
1066
|
pushNotificationToken(request, response);
|
|
1033
1067
|
return;
|
|
1034
|
-
} else if (
|
|
1068
|
+
} else if (pathname === "/hive-client.js") {
|
|
1035
1069
|
const filePath = path6.join(process.cwd(), "node_modules/@hivedev/hivesdk/hive-client.js");
|
|
1036
1070
|
const stream = fs6.createReadStream(filePath);
|
|
1037
1071
|
stream.on("open", () => {
|