nicefox-graphdb 0.1.0

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.
Files changed (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +417 -0
  3. package/package.json +78 -0
  4. package/packages/nicefox-graphdb/LICENSE +21 -0
  5. package/packages/nicefox-graphdb/README.md +417 -0
  6. package/packages/nicefox-graphdb/dist/auth.d.ts +66 -0
  7. package/packages/nicefox-graphdb/dist/auth.d.ts.map +1 -0
  8. package/packages/nicefox-graphdb/dist/auth.js +148 -0
  9. package/packages/nicefox-graphdb/dist/auth.js.map +1 -0
  10. package/packages/nicefox-graphdb/dist/backup.d.ts +51 -0
  11. package/packages/nicefox-graphdb/dist/backup.d.ts.map +1 -0
  12. package/packages/nicefox-graphdb/dist/backup.js +201 -0
  13. package/packages/nicefox-graphdb/dist/backup.js.map +1 -0
  14. package/packages/nicefox-graphdb/dist/cli-helpers.d.ts +17 -0
  15. package/packages/nicefox-graphdb/dist/cli-helpers.d.ts.map +1 -0
  16. package/packages/nicefox-graphdb/dist/cli-helpers.js +121 -0
  17. package/packages/nicefox-graphdb/dist/cli-helpers.js.map +1 -0
  18. package/packages/nicefox-graphdb/dist/cli.d.ts +3 -0
  19. package/packages/nicefox-graphdb/dist/cli.d.ts.map +1 -0
  20. package/packages/nicefox-graphdb/dist/cli.js +660 -0
  21. package/packages/nicefox-graphdb/dist/cli.js.map +1 -0
  22. package/packages/nicefox-graphdb/dist/db.d.ts +118 -0
  23. package/packages/nicefox-graphdb/dist/db.d.ts.map +1 -0
  24. package/packages/nicefox-graphdb/dist/db.js +245 -0
  25. package/packages/nicefox-graphdb/dist/db.js.map +1 -0
  26. package/packages/nicefox-graphdb/dist/executor.d.ts +272 -0
  27. package/packages/nicefox-graphdb/dist/executor.d.ts.map +1 -0
  28. package/packages/nicefox-graphdb/dist/executor.js +3579 -0
  29. package/packages/nicefox-graphdb/dist/executor.js.map +1 -0
  30. package/packages/nicefox-graphdb/dist/index.d.ts +54 -0
  31. package/packages/nicefox-graphdb/dist/index.d.ts.map +1 -0
  32. package/packages/nicefox-graphdb/dist/index.js +74 -0
  33. package/packages/nicefox-graphdb/dist/index.js.map +1 -0
  34. package/packages/nicefox-graphdb/dist/local.d.ts +7 -0
  35. package/packages/nicefox-graphdb/dist/local.d.ts.map +1 -0
  36. package/packages/nicefox-graphdb/dist/local.js +115 -0
  37. package/packages/nicefox-graphdb/dist/local.js.map +1 -0
  38. package/packages/nicefox-graphdb/dist/parser.d.ts +300 -0
  39. package/packages/nicefox-graphdb/dist/parser.d.ts.map +1 -0
  40. package/packages/nicefox-graphdb/dist/parser.js +1891 -0
  41. package/packages/nicefox-graphdb/dist/parser.js.map +1 -0
  42. package/packages/nicefox-graphdb/dist/remote.d.ts +6 -0
  43. package/packages/nicefox-graphdb/dist/remote.d.ts.map +1 -0
  44. package/packages/nicefox-graphdb/dist/remote.js +87 -0
  45. package/packages/nicefox-graphdb/dist/remote.js.map +1 -0
  46. package/packages/nicefox-graphdb/dist/routes.d.ts +31 -0
  47. package/packages/nicefox-graphdb/dist/routes.d.ts.map +1 -0
  48. package/packages/nicefox-graphdb/dist/routes.js +202 -0
  49. package/packages/nicefox-graphdb/dist/routes.js.map +1 -0
  50. package/packages/nicefox-graphdb/dist/translator.d.ts +136 -0
  51. package/packages/nicefox-graphdb/dist/translator.d.ts.map +1 -0
  52. package/packages/nicefox-graphdb/dist/translator.js +4849 -0
  53. package/packages/nicefox-graphdb/dist/translator.js.map +1 -0
  54. package/packages/nicefox-graphdb/dist/types.d.ts +133 -0
  55. package/packages/nicefox-graphdb/dist/types.d.ts.map +1 -0
  56. package/packages/nicefox-graphdb/dist/types.js +21 -0
  57. package/packages/nicefox-graphdb/dist/types.js.map +1 -0
@@ -0,0 +1,417 @@
1
+ # NiceFox GraphDB
2
+
3
+ [![npm version](https://img.shields.io/npm/v/nicefox-graphdb.svg)](https://www.npmjs.com/package/nicefox-graphdb)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ A lightweight graph database with Cypher query support, powered by SQLite.
7
+
8
+ ## Installation
9
+
10
+ ```bash
11
+ npm install nicefox-graphdb
12
+ ```
13
+
14
+ ## Quick Start
15
+
16
+ ```typescript
17
+ import { GraphDB } from 'nicefox-graphdb';
18
+
19
+ const db = await GraphDB({
20
+ url: 'https://my-graphdb.example.com',
21
+ project: 'myapp',
22
+ apiKey: process.env.GRAPHDB_API_KEY,
23
+ });
24
+
25
+ // Create nodes and relationships
26
+ await db.execute(`
27
+ CREATE (alice:User {name: 'Alice'})-[:FOLLOWS]->(bob:User {name: 'Bob'})
28
+ `);
29
+
30
+ // Query the graph
31
+ const users = await db.query('MATCH (u:User) RETURN u.name AS name');
32
+ console.log(users); // [{ name: 'Alice' }, { name: 'Bob' }]
33
+
34
+ db.close();
35
+ ```
36
+
37
+ ## Development vs Production Mode
38
+
39
+ NiceFox GraphDB automatically adapts based on the `NODE_ENV` environment variable:
40
+
41
+ | Mode | `NODE_ENV` | Behavior |
42
+ |------|-----------|----------|
43
+ | **Development** | `development` | Uses local SQLite database. `url` and `apiKey` are ignored. |
44
+ | **Production** | `production` (or unset) | Connects to remote server via HTTP. `url` and `apiKey` are required. |
45
+
46
+ This means you can use the **exact same code** in both environments:
47
+
48
+ ```typescript
49
+ // Works in both development and production!
50
+ const db = await GraphDB({
51
+ url: 'https://my-graphdb.example.com',
52
+ project: 'myapp',
53
+ apiKey: process.env.GRAPHDB_API_KEY,
54
+ });
55
+ ```
56
+
57
+ ### Development Mode
58
+
59
+ When `NODE_ENV=development`:
60
+ - A local SQLite database is created automatically
61
+ - No server setup required
62
+ - `url` and `apiKey` parameters are ignored
63
+ - Data persists at `./data/{env}/{project}.db` by default
64
+
65
+ ```bash
66
+ # Run your app in development mode
67
+ NODE_ENV=development node app.js
68
+ ```
69
+
70
+ ### Production Mode
71
+
72
+ When `NODE_ENV=production` (or unset):
73
+ - Connects to a remote GraphDB server via HTTP
74
+ - `url` and `apiKey` are required
75
+
76
+ ```bash
77
+ # Run your app in production mode
78
+ NODE_ENV=production GRAPHDB_API_KEY=xxx node app.js
79
+ ```
80
+
81
+ ## Configuration Options
82
+
83
+ | Option | Type | Required | Default | Description |
84
+ |--------|------|----------|---------|-------------|
85
+ | `url` | `string` | Yes | - | Base URL of the GraphDB server (used in production) |
86
+ | `project` | `string` | Yes | - | Project name |
87
+ | `apiKey` | `string` | No | - | API key for authentication (used in production) |
88
+ | `env` | `'production' \| 'test'` | No | `'production'` | Environment (determines database isolation) |
89
+ | `dataPath` | `string` | No | `'./data'` | Path for local data storage (development only). Use `':memory:'` for in-memory database |
90
+
91
+ ### Examples
92
+
93
+ ```typescript
94
+ // Production: connect to remote server
95
+ const db = await GraphDB({
96
+ url: 'https://graphdb.example.com',
97
+ project: 'myapp',
98
+ apiKey: 'your-api-key',
99
+ env: 'production',
100
+ });
101
+
102
+ // Development: use local SQLite (url/apiKey ignored)
103
+ // NODE_ENV=development
104
+ const db = await GraphDB({
105
+ url: 'https://graphdb.example.com', // ignored
106
+ project: 'myapp',
107
+ apiKey: 'your-api-key', // ignored
108
+ dataPath: './local-data', // custom data directory
109
+ });
110
+
111
+ // Testing: use in-memory database
112
+ // NODE_ENV=development
113
+ const db = await GraphDB({
114
+ url: 'https://graphdb.example.com',
115
+ project: 'test-project',
116
+ dataPath: ':memory:', // resets on each run
117
+ });
118
+ ```
119
+
120
+ ## API Reference
121
+
122
+ ### `GraphDB(options): Promise<GraphDBClient>`
123
+
124
+ Create a new GraphDB client. Returns a promise that resolves to a client instance.
125
+
126
+ ### `db.query<T>(cypher, params?): Promise<T[]>`
127
+
128
+ Execute a Cypher query and return results as an array.
129
+
130
+ ```typescript
131
+ const users = await db.query<{ name: string; age: number }>(
132
+ 'MATCH (u:User) WHERE u.age > $minAge RETURN u.name AS name, u.age AS age',
133
+ { minAge: 21 }
134
+ );
135
+ // users = [{ name: 'Alice', age: 25 }, { name: 'Bob', age: 30 }]
136
+ ```
137
+
138
+ ### `db.execute(cypher, params?): Promise<void>`
139
+
140
+ Execute a mutating query (CREATE, SET, DELETE, MERGE) without expecting return data.
141
+
142
+ ```typescript
143
+ await db.execute('CREATE (n:User {name: $name, email: $email})', {
144
+ name: 'Alice',
145
+ email: 'alice@example.com'
146
+ });
147
+ ```
148
+
149
+ ### `db.queryRaw<T>(cypher, params?): Promise<QueryResponse<T>>`
150
+
151
+ Execute a query and return the full response including metadata.
152
+
153
+ ```typescript
154
+ const response = await db.queryRaw('MATCH (n) RETURN n LIMIT 10');
155
+ console.log(response.meta.count); // Number of rows
156
+ console.log(response.meta.time_ms); // Query execution time in ms
157
+ console.log(response.data); // Array of results
158
+ ```
159
+
160
+ ### `db.createNode(label, properties?): Promise<string>`
161
+
162
+ Create a node and return its ID.
163
+
164
+ ```typescript
165
+ const userId = await db.createNode('User', { name: 'Alice', email: 'alice@example.com' });
166
+ ```
167
+
168
+ ### `db.getNode(label, filter): Promise<Record<string, unknown> | null>`
169
+
170
+ Find a node by label and properties.
171
+
172
+ ```typescript
173
+ const user = await db.getNode('User', { email: 'alice@example.com' });
174
+ if (user) {
175
+ console.log(user.name); // 'Alice'
176
+ }
177
+ ```
178
+
179
+ ### `db.updateNode(id, properties): Promise<void>`
180
+
181
+ Update properties on a node.
182
+
183
+ ```typescript
184
+ await db.updateNode(userId, { name: 'Alice Smith', verified: true });
185
+ ```
186
+
187
+ ### `db.deleteNode(id): Promise<void>`
188
+
189
+ Delete a node and all its relationships (DETACH DELETE).
190
+
191
+ ```typescript
192
+ await db.deleteNode(userId);
193
+ ```
194
+
195
+ ### `db.createEdge(sourceId, type, targetId, properties?): Promise<void>`
196
+
197
+ Create a relationship between two nodes.
198
+
199
+ ```typescript
200
+ await db.createEdge(aliceId, 'FOLLOWS', bobId, { since: '2024-01-01' });
201
+ ```
202
+
203
+ ### `db.health(): Promise<{ status: string; timestamp: string }>`
204
+
205
+ Check server health. In development mode, always returns `{ status: 'ok', ... }`.
206
+
207
+ ### `db.close(): void`
208
+
209
+ Close the client and release resources. **Always call this when done.**
210
+
211
+ ```typescript
212
+ const db = await GraphDB({ ... });
213
+ try {
214
+ // ... use db
215
+ } finally {
216
+ db.close();
217
+ }
218
+ ```
219
+
220
+ ## Cypher Quick Reference
221
+
222
+ ### Supported Clauses
223
+
224
+ | Clause | Example |
225
+ |--------|---------|
226
+ | `CREATE` | `CREATE (n:User {name: 'Alice'})` |
227
+ | `MATCH` | `MATCH (n:User) RETURN n` |
228
+ | `OPTIONAL MATCH` | `OPTIONAL MATCH (n)-[:KNOWS]->(m) RETURN m` |
229
+ | `MERGE` | `MERGE (n:User {email: $email})` |
230
+ | `WHERE` | `WHERE n.age > 21 AND n.active = true` |
231
+ | `SET` | `SET n.name = 'Bob', n.updated = true` |
232
+ | `DELETE` | `DELETE n` |
233
+ | `DETACH DELETE` | `DETACH DELETE n` |
234
+ | `RETURN` | `RETURN n.name AS name, count(*) AS total` |
235
+ | `WITH` | `WITH n, count(*) AS cnt WHERE cnt > 1` |
236
+ | `UNWIND` | `UNWIND $list AS item CREATE (n {value: item})` |
237
+ | `UNION / UNION ALL` | `MATCH (n:A) RETURN n UNION MATCH (m:B) RETURN m` |
238
+ | `ORDER BY` | `ORDER BY n.name DESC` |
239
+ | `SKIP / LIMIT` | `SKIP 10 LIMIT 5` |
240
+ | `DISTINCT` | `RETURN DISTINCT n.category` |
241
+ | `CASE/WHEN` | `RETURN CASE WHEN n.age > 18 THEN 'adult' ELSE 'minor' END` |
242
+ | `CALL` | `CALL db.labels() YIELD label RETURN label` |
243
+
244
+ ### Operators
245
+
246
+ | Category | Operators |
247
+ |----------|-----------|
248
+ | Comparison | `=`, `<>`, `<`, `>`, `<=`, `>=` |
249
+ | Logical | `AND`, `OR`, `NOT` |
250
+ | String | `CONTAINS`, `STARTS WITH`, `ENDS WITH` |
251
+ | List | `IN` |
252
+ | Null | `IS NULL`, `IS NOT NULL` |
253
+ | Pattern | `EXISTS` |
254
+ | Arithmetic | `+`, `-`, `*`, `/`, `%` |
255
+
256
+ ### Functions
257
+
258
+ **Aggregation:** `COUNT`, `SUM`, `AVG`, `MIN`, `MAX`, `COLLECT`
259
+
260
+ **Scalar:** `ID`, `coalesce`
261
+
262
+ **String:** `toUpper`, `toLower`, `trim`, `substring`, `replace`, `toString`, `split`
263
+
264
+ **List:** `size`, `head`, `last`, `tail`, `keys`, `range`
265
+
266
+ **Node/Relationship:** `labels`, `type`, `properties`
267
+
268
+ **Math:** `abs`, `ceil`, `floor`, `round`, `rand`, `sqrt`
269
+
270
+ **Date/Time:** `date`, `datetime`, `timestamp`
271
+
272
+ ### Variable-Length Paths
273
+
274
+ ```cypher
275
+ -- Find friends of friends (1 to 3 hops)
276
+ MATCH (a:User {name: 'Alice'})-[:KNOWS*1..3]->(b:User)
277
+ RETURN DISTINCT b.name
278
+ ```
279
+
280
+ ### Procedures
281
+
282
+ ```cypher
283
+ -- List all labels
284
+ CALL db.labels() YIELD label RETURN label
285
+
286
+ -- List all relationship types
287
+ CALL db.relationshipTypes() YIELD type RETURN type
288
+
289
+ -- List all property keys
290
+ CALL db.propertyKeys() YIELD key RETURN key
291
+ ```
292
+
293
+ ## Running the Server (Production)
294
+
295
+ For production deployments, run a dedicated server:
296
+
297
+ ```bash
298
+ # Start the server
299
+ npx nicefox-graphdb serve --port 3000 --data ./data
300
+
301
+ # Or with custom host binding
302
+ npx nicefox-graphdb serve --port 3000 --host 0.0.0.0 --data ./data
303
+ ```
304
+
305
+ ### Creating Projects
306
+
307
+ ```bash
308
+ # Create a new project (generates API keys)
309
+ npx nicefox-graphdb create myapp --data ./data
310
+
311
+ # Output:
312
+ # [created] production/myapp.db
313
+ # [created] test/myapp.db
314
+ # API Keys:
315
+ # production: nfx_abc123...
316
+ # test: nfx_def456...
317
+ ```
318
+
319
+ ### CLI Reference
320
+
321
+ ```bash
322
+ # Server
323
+ nicefox-graphdb serve [options]
324
+ -p, --port <port> Port to listen on (default: 3000)
325
+ -d, --data <path> Data directory (default: /var/data/nicefox-graphdb)
326
+ -H, --host <host> Host to bind to (default: localhost)
327
+ -b, --backup <path> Backup directory (enables backup endpoints)
328
+
329
+ # Project management
330
+ nicefox-graphdb create <project> Create new project with API keys
331
+ nicefox-graphdb delete <project> Delete project (use --force)
332
+ nicefox-graphdb list List all projects
333
+
334
+ # Environment management
335
+ nicefox-graphdb clone <project> Copy production to test
336
+ nicefox-graphdb wipe <project> Clear test database
337
+
338
+ # Direct queries
339
+ nicefox-graphdb query <env> <project> "CYPHER"
340
+
341
+ # Backup
342
+ nicefox-graphdb backup [options]
343
+ -o, --output <path> Backup directory
344
+ -p, --project <name> Backup specific project
345
+ --status Show backup status
346
+
347
+ # API keys
348
+ nicefox-graphdb apikey add <project>
349
+ nicefox-graphdb apikey list
350
+ nicefox-graphdb apikey remove <prefix>
351
+ ```
352
+
353
+ ## Why NiceFox?
354
+
355
+ | Feature | NiceFox GraphDB | Neo4j |
356
+ |---------|-----------------|-------|
357
+ | **Deployment** | Single package, zero config | Complex setup, JVM required |
358
+ | **Development** | Local SQLite, no server needed | Server required |
359
+ | **Backup** | Just copy the SQLite file | Enterprise license required |
360
+ | **Resource usage** | ~50MB RAM | 1GB+ RAM minimum |
361
+ | **Cypher support** | Core subset | Full |
362
+ | **Cost** | Free, MIT license | Free tier limited |
363
+
364
+ NiceFox is ideal for:
365
+ - Applications needing graph queries without ops burden
366
+ - Projects that outgrow JSON but don't need a full graph database
367
+ - Self-hosted deployments where simplicity matters
368
+ - Development and testing with instant local databases
369
+
370
+ ## Advanced Usage
371
+
372
+ ### Direct Database Access
373
+
374
+ For advanced use cases, you can access the underlying components:
375
+
376
+ ```typescript
377
+ import { GraphDatabase, Executor, parse, translate } from 'nicefox-graphdb';
378
+
379
+ // Direct database access
380
+ const db = new GraphDatabase('./my-database.db');
381
+ db.initialize();
382
+
383
+ const executor = new Executor(db);
384
+ const result = executor.execute('MATCH (n) RETURN n LIMIT 10');
385
+
386
+ db.close();
387
+
388
+ // Parse Cypher to AST
389
+ const parseResult = parse('MATCH (n:User) RETURN n');
390
+ if (parseResult.success) {
391
+ console.log(parseResult.query);
392
+ }
393
+
394
+ // Translate AST to SQL
395
+ const translation = translate(parseResult.query, {});
396
+ console.log(translation.statements);
397
+ ```
398
+
399
+ ### Running a Custom Server
400
+
401
+ ```typescript
402
+ import { createServer } from 'nicefox-graphdb';
403
+ import { serve } from '@hono/node-server';
404
+
405
+ const { app, dbManager } = createServer({
406
+ dataPath: './data',
407
+ apiKeys: {
408
+ 'my-api-key': { project: 'myapp', env: 'production' }
409
+ }
410
+ });
411
+
412
+ serve({ fetch: app.fetch, port: 3000 });
413
+ ```
414
+
415
+ ## License
416
+
417
+ [MIT](https://github.com/co-l/nicefox-graphdb/blob/main/LICENSE) - Conrad Lelubre
@@ -0,0 +1,66 @@
1
+ import { Context, Next } from "hono";
2
+ export interface ApiKeyConfig {
3
+ project?: string;
4
+ env?: string;
5
+ admin?: boolean;
6
+ }
7
+ export interface ValidationResult {
8
+ valid: boolean;
9
+ project?: string;
10
+ env?: string;
11
+ admin?: boolean;
12
+ }
13
+ export interface KeyInfo {
14
+ prefix: string;
15
+ project?: string;
16
+ env?: string;
17
+ admin?: boolean;
18
+ }
19
+ export declare class ApiKeyStore {
20
+ private keys;
21
+ /**
22
+ * Add an API key with its configuration.
23
+ */
24
+ addKey(key: string, config: ApiKeyConfig): void;
25
+ /**
26
+ * Remove an API key.
27
+ */
28
+ removeKey(key: string): void;
29
+ /**
30
+ * Validate an API key and return its permissions.
31
+ */
32
+ validate(key: string): ValidationResult;
33
+ /**
34
+ * List all keys (with prefixes only for security).
35
+ */
36
+ listKeys(): KeyInfo[];
37
+ /**
38
+ * Check if any keys are configured.
39
+ */
40
+ hasKeys(): boolean;
41
+ /**
42
+ * Load keys from an object (for initialization from config).
43
+ */
44
+ loadKeys(keys: Record<string, ApiKeyConfig>): void;
45
+ }
46
+ /**
47
+ * Hono middleware for API key authentication.
48
+ *
49
+ * - Skips authentication for /health endpoint
50
+ * - Requires Bearer token in Authorization header
51
+ * - Checks project/env restrictions for /query endpoints
52
+ * - Requires admin flag for /admin endpoints
53
+ */
54
+ export declare function authMiddleware(store: ApiKeyStore): (c: Context, next: Next) => Promise<void | (Response & import("hono").TypedResponse<{
55
+ success: false;
56
+ error: {
57
+ message: string;
58
+ };
59
+ }, 401, "json">) | (Response & import("hono").TypedResponse<{
60
+ success: false;
61
+ error: {
62
+ message: string;
63
+ };
64
+ }, 403, "json">)>;
65
+ export declare function generateApiKey(): string;
66
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAMrC,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAMD,qBAAa,WAAW;IACtB,OAAO,CAAC,IAAI,CAAwC;IAEpD;;OAEG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI;IAI/C;;OAEG;IACH,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAI5B;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB;IAevC;;OAEG;IACH,QAAQ,IAAI,OAAO,EAAE;IAerB;;OAEG;IACH,OAAO,IAAI,OAAO;IAIlB;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GAAG,IAAI;CAKnD;AAMD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,IACjC,GAAG,OAAO,EAAE,MAAM,IAAI;;;;;;;;;;kBA0FrC;AAMD,wBAAgB,cAAc,IAAI,MAAM,CASvC"}
@@ -0,0 +1,148 @@
1
+ // API Key Authentication for NiceFox GraphDB
2
+ // ============================================================================
3
+ // ApiKeyStore
4
+ // ============================================================================
5
+ export class ApiKeyStore {
6
+ keys = new Map();
7
+ /**
8
+ * Add an API key with its configuration.
9
+ */
10
+ addKey(key, config) {
11
+ this.keys.set(key, config);
12
+ }
13
+ /**
14
+ * Remove an API key.
15
+ */
16
+ removeKey(key) {
17
+ this.keys.delete(key);
18
+ }
19
+ /**
20
+ * Validate an API key and return its permissions.
21
+ */
22
+ validate(key) {
23
+ const config = this.keys.get(key);
24
+ if (!config) {
25
+ return { valid: false };
26
+ }
27
+ return {
28
+ valid: true,
29
+ project: config.project,
30
+ env: config.env,
31
+ admin: config.admin,
32
+ };
33
+ }
34
+ /**
35
+ * List all keys (with prefixes only for security).
36
+ */
37
+ listKeys() {
38
+ const result = [];
39
+ for (const [key, config] of this.keys) {
40
+ result.push({
41
+ prefix: key.slice(0, 4) + "...",
42
+ project: config.project,
43
+ env: config.env,
44
+ admin: config.admin,
45
+ });
46
+ }
47
+ return result;
48
+ }
49
+ /**
50
+ * Check if any keys are configured.
51
+ */
52
+ hasKeys() {
53
+ return this.keys.size > 0;
54
+ }
55
+ /**
56
+ * Load keys from an object (for initialization from config).
57
+ */
58
+ loadKeys(keys) {
59
+ for (const [key, config] of Object.entries(keys)) {
60
+ this.addKey(key, config);
61
+ }
62
+ }
63
+ }
64
+ // ============================================================================
65
+ // Auth Middleware
66
+ // ============================================================================
67
+ /**
68
+ * Hono middleware for API key authentication.
69
+ *
70
+ * - Skips authentication for /health endpoint
71
+ * - Requires Bearer token in Authorization header
72
+ * - Checks project/env restrictions for /query endpoints
73
+ * - Requires admin flag for /admin endpoints
74
+ */
75
+ export function authMiddleware(store) {
76
+ return async (c, next) => {
77
+ const path = c.req.path;
78
+ // Skip auth for health endpoint
79
+ if (path === "/health") {
80
+ return next();
81
+ }
82
+ // Get authorization header
83
+ const authHeader = c.req.header("Authorization");
84
+ if (!authHeader) {
85
+ return c.json({
86
+ success: false,
87
+ error: { message: "Missing Authorization header" },
88
+ }, 401);
89
+ }
90
+ // Check Bearer format
91
+ if (!authHeader.startsWith("Bearer ")) {
92
+ return c.json({
93
+ success: false,
94
+ error: { message: "Authorization header must use Bearer scheme" },
95
+ }, 401);
96
+ }
97
+ const apiKey = authHeader.slice(7); // Remove "Bearer "
98
+ const validation = store.validate(apiKey);
99
+ if (!validation.valid) {
100
+ return c.json({
101
+ success: false,
102
+ error: { message: "Invalid API key" },
103
+ }, 401);
104
+ }
105
+ // Check admin access for /admin endpoints
106
+ if (path.startsWith("/admin") && !validation.admin) {
107
+ return c.json({
108
+ success: false,
109
+ error: { message: "Admin access required for this endpoint" },
110
+ }, 403);
111
+ }
112
+ // Check project/env restrictions for query endpoints
113
+ if (path.startsWith("/query/")) {
114
+ const parts = path.split("/");
115
+ const env = parts[2];
116
+ const project = parts[3];
117
+ // Check project restriction
118
+ if (validation.project && validation.project !== project) {
119
+ return c.json({
120
+ success: false,
121
+ error: { message: `Access denied for project: ${project}` },
122
+ }, 403);
123
+ }
124
+ // Check environment restriction
125
+ if (validation.env && validation.env !== env) {
126
+ return c.json({
127
+ success: false,
128
+ error: { message: `Access denied for environment: ${env}` },
129
+ }, 403);
130
+ }
131
+ }
132
+ // Store validation result in context for later use
133
+ c.set("auth", validation);
134
+ return next();
135
+ };
136
+ }
137
+ // ============================================================================
138
+ // Helper: Generate a random API key
139
+ // ============================================================================
140
+ export function generateApiKey() {
141
+ const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
142
+ let key = "";
143
+ for (let i = 0; i < 32; i++) {
144
+ key += chars.charAt(Math.floor(Math.random() * chars.length));
145
+ }
146
+ return key;
147
+ }
148
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,6CAA6C;AA4B7C,+EAA+E;AAC/E,cAAc;AACd,+EAA+E;AAE/E,MAAM,OAAO,WAAW;IACd,IAAI,GAA8B,IAAI,GAAG,EAAE,CAAC;IAEpD;;OAEG;IACH,MAAM,CAAC,GAAW,EAAE,MAAoB;QACtC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAC7B,CAAC;IAED;;OAEG;IACH,SAAS,CAAC,GAAW;QACnB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC;IAED;;OAEG;IACH,QAAQ,CAAC,GAAW;QAClB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAElC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QAC1B,CAAC;QAED,OAAO;YACL,KAAK,EAAE,IAAI;YACX,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,GAAG,EAAE,MAAM,CAAC,GAAG;YACf,KAAK,EAAE,MAAM,CAAC,KAAK;SACpB,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,QAAQ;QACN,MAAM,MAAM,GAAc,EAAE,CAAC;QAE7B,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC;gBACV,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK;gBAC/B,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,GAAG,EAAE,MAAM,CAAC,GAAG;gBACf,KAAK,EAAE,MAAM,CAAC,KAAK;aACpB,CAAC,CAAC;QACL,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;OAEG;IACH,OAAO;QACL,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;IAC5B,CAAC;IAED;;OAEG;IACH,QAAQ,CAAC,IAAkC;QACzC,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACjD,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;CACF;AAED,+EAA+E;AAC/E,kBAAkB;AAClB,+EAA+E;AAE/E;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,KAAkB;IAC/C,OAAO,KAAK,EAAE,CAAU,EAAE,IAAU,EAAE,EAAE;QACtC,MAAM,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC;QAExB,gCAAgC;QAChC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,OAAO,IAAI,EAAE,CAAC;QAChB,CAAC;QAED,2BAA2B;QAC3B,MAAM,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;QAEjD,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,CAAC,CAAC,IAAI,CACX;gBACE,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,EAAE,OAAO,EAAE,8BAA8B,EAAE;aACnD,EACD,GAAG,CACJ,CAAC;QACJ,CAAC;QAED,sBAAsB;QACtB,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YACtC,OAAO,CAAC,CAAC,IAAI,CACX;gBACE,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,EAAE,OAAO,EAAE,6CAA6C,EAAE;aAClE,EACD,GAAG,CACJ,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,mBAAmB;QACvD,MAAM,UAAU,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAE1C,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;YACtB,OAAO,CAAC,CAAC,IAAI,CACX;gBACE,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,EAAE,OAAO,EAAE,iBAAiB,EAAE;aACtC,EACD,GAAG,CACJ,CAAC;QACJ,CAAC;QAED,0CAA0C;QAC1C,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;YACnD,OAAO,CAAC,CAAC,IAAI,CACX;gBACE,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,EAAE,OAAO,EAAE,yCAAyC,EAAE;aAC9D,EACD,GAAG,CACJ,CAAC;QACJ,CAAC;QAED,qDAAqD;QACrD,IAAI,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC9B,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACrB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YAEzB,4BAA4B;YAC5B,IAAI,UAAU,CAAC,OAAO,IAAI,UAAU,CAAC,OAAO,KAAK,OAAO,EAAE,CAAC;gBACzD,OAAO,CAAC,CAAC,IAAI,CACX;oBACE,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,EAAE,OAAO,EAAE,8BAA8B,OAAO,EAAE,EAAE;iBAC5D,EACD,GAAG,CACJ,CAAC;YACJ,CAAC;YAED,gCAAgC;YAChC,IAAI,UAAU,CAAC,GAAG,IAAI,UAAU,CAAC,GAAG,KAAK,GAAG,EAAE,CAAC;gBAC7C,OAAO,CAAC,CAAC,IAAI,CACX;oBACE,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,EAAE,OAAO,EAAE,kCAAkC,GAAG,EAAE,EAAE;iBAC5D,EACD,GAAG,CACJ,CAAC;YACJ,CAAC;QACH,CAAC;QAED,mDAAmD;QACnD,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QAE1B,OAAO,IAAI,EAAE,CAAC;IAChB,CAAC,CAAC;AACJ,CAAC;AAED,+EAA+E;AAC/E,oCAAoC;AACpC,+EAA+E;AAE/E,MAAM,UAAU,cAAc;IAC5B,MAAM,KAAK,GAAG,gEAAgE,CAAC;IAC/E,IAAI,GAAG,GAAG,EAAE,CAAC;IAEb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5B,GAAG,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAChE,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,51 @@
1
+ export interface BackupResult {
2
+ success: boolean;
3
+ project: string;
4
+ sourcePath: string;
5
+ backupPath?: string;
6
+ error?: string;
7
+ durationMs?: number;
8
+ sizeBytes?: number;
9
+ }
10
+ export interface BackupStatus {
11
+ totalBackups: number;
12
+ totalSizeBytes: number;
13
+ projects: string[];
14
+ oldestBackup?: string;
15
+ newestBackup?: string;
16
+ }
17
+ export interface BackupAllOptions {
18
+ includeTest?: boolean;
19
+ }
20
+ export declare class BackupManager {
21
+ private backupDir;
22
+ constructor(backupDir: string);
23
+ /**
24
+ * Create a backup of a single database file using SQLite's backup API.
25
+ * This is a "hot" backup - it works even if the database is open and in use.
26
+ */
27
+ backupDatabase(sourcePath: string, project: string): Promise<BackupResult>;
28
+ /**
29
+ * Backup all databases in a data directory.
30
+ * By default, only backs up production databases.
31
+ */
32
+ backupAll(dataDir: string, options?: BackupAllOptions): Promise<BackupResult[]>;
33
+ /**
34
+ * List all backups for a specific project, sorted by date (newest first).
35
+ */
36
+ listBackups(project: string): string[];
37
+ /**
38
+ * Delete old backups, keeping only the specified number of recent ones.
39
+ * Returns the number of deleted backups.
40
+ */
41
+ cleanOldBackups(project: string, keepCount: number): number;
42
+ /**
43
+ * Get overall backup status and statistics.
44
+ */
45
+ getBackupStatus(): BackupStatus;
46
+ /**
47
+ * Restore a backup to a target path.
48
+ */
49
+ restoreBackup(backupFilename: string, targetPath: string): BackupResult;
50
+ }
51
+ //# sourceMappingURL=backup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup.d.ts","sourceRoot":"","sources":["../src/backup.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAMD,qBAAa,aAAa;IACxB,OAAO,CAAC,SAAS,CAAS;gBAEd,SAAS,EAAE,MAAM;IAI7B;;;OAGG;IACG,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAsDhF;;;OAGG;IACG,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAwBzF;;OAEG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE;IAYtC;;;OAGG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;IAuB3D;;OAEG;IACH,eAAe,IAAI,YAAY;IA4C/B;;OAEG;IACH,aAAa,CAAC,cAAc,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,YAAY;CAqCxE"}