admindb 2.1.0 → 2.2.1
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 +41 -12
- package/dist/app.d.ts +1 -69
- package/dist/app.js +3 -30
- package/dist/auth/config.d.ts +1 -19
- package/dist/auth/config.js +0 -26
- package/dist/auth/crypto.d.ts +1 -27
- package/dist/auth/crypto.js +1 -26
- package/dist/auth/index.d.ts +1 -5
- package/dist/auth/index.js +0 -1
- package/dist/auth/middleware.d.ts +1 -14
- package/dist/auth/middleware.js +0 -15
- package/dist/auth/routes.d.ts +1 -7
- package/dist/auth/routes.js +0 -8
- package/dist/auth/types.d.ts +1 -35
- package/dist/auth/types.js +1 -5
- package/dist/cli/args.d.ts +1 -45
- package/dist/cli/args.js +3 -11
- package/dist/cli/config.d.ts +1 -56
- package/dist/cli/config.js +0 -5
- package/dist/cli/index.d.ts +1 -4
- package/dist/cli/index.js +0 -1
- package/dist/cli/runner.d.ts +1 -1
- package/dist/cli/runner.js +6 -17
- package/dist/cli.d.ts +1 -2
- package/dist/cli.js +0 -1
- package/dist/data/chain.d.ts +1 -0
- package/dist/data/chain.js +707 -0
- package/dist/data/datasets.d.ts +1 -17
- package/dist/data/datasets.js +0 -4
- package/dist/data/detector.d.ts +1 -30
- package/dist/data/detector.js +53 -14
- package/dist/data/engine.d.ts +1 -10
- package/dist/data/engine.js +82 -48
- package/dist/data/index.d.ts +1 -5
- package/dist/data/index.js +1 -1
- package/dist/data/strategies.d.ts +1 -30
- package/dist/data/strategies.js +4 -8
- package/dist/data/types.d.ts +1 -73
- package/dist/data/types.js +0 -3
- package/dist/db/database.d.ts +1 -119
- package/dist/db/database.js +20 -14
- package/dist/db/export.d.ts +1 -12
- package/dist/db/export.js +54 -6
- package/dist/db/filters.d.ts +1 -22
- package/dist/db/filters.js +0 -16
- package/dist/db/index.d.ts +1 -8
- package/dist/db/index.js +0 -1
- package/dist/db/introspection.d.ts +1 -6
- package/dist/db/introspection.js +0 -5
- package/dist/db/manager.d.ts +1 -81
- package/dist/db/manager.js +13 -59
- package/dist/db/migrations.d.ts +1 -5
- package/dist/db/migrations.js +2 -5
- package/dist/db/postgres.d.ts +1 -85
- package/dist/db/postgres.js +21 -31
- package/dist/db/types.d.ts +1 -206
- package/dist/db/types.js +0 -1
- package/dist/index.d.ts +1 -23
- package/dist/index.js +0 -14
- package/dist/public/css/app.css +1 -1
- package/dist/public/js/api.js +1 -47
- package/dist/public/js/app.js +1 -262
- package/dist/public/js/browse.js +1 -1371
- package/dist/public/js/common.js +1 -0
- package/dist/public/js/databases.js +1 -235
- package/dist/public/js/designer.js +1 -258
- package/dist/public/js/forms.js +1 -826
- package/dist/public/js/home.js +1 -0
- package/dist/public/js/inspector.js +1 -638
- package/dist/public/js/query.js +1 -322
- package/dist/public/js/schema.js +1 -447
- package/dist/public/js/seed.js +1 -1090
- package/dist/routes/api/erd.d.ts +1 -0
- package/dist/routes/api/erd.js +50 -0
- package/dist/routes/api/helpers.d.ts +1 -42
- package/dist/routes/api/helpers.js +33 -3
- package/dist/routes/api/import-export.d.ts +1 -3
- package/dist/routes/api/import-export.js +0 -2
- package/dist/routes/api/index.d.ts +1 -4
- package/dist/routes/api/index.js +15 -4
- package/dist/routes/api/query.d.ts +1 -3
- package/dist/routes/api/query.js +13 -11
- package/dist/routes/api/rows.d.ts +1 -3
- package/dist/routes/api/rows.js +0 -11
- package/dist/routes/api/seed.d.ts +1 -3
- package/dist/routes/api/seed.js +79 -8
- package/dist/routes/api/tables.d.ts +1 -3
- package/dist/routes/api/tables.js +77 -6
- package/dist/routes/databases.d.ts +1 -26
- package/dist/routes/databases.js +1 -3
- package/dist/routes/index.d.ts +1 -3
- package/dist/routes/index.js +0 -1
- package/dist/routes/pages.d.ts +1 -9
- package/dist/routes/pages.js +18 -11
- package/dist/serverless.d.ts +1 -40
- package/dist/serverless.js +3 -39
- package/dist/sql/classifier.d.ts +1 -19
- package/dist/sql/classifier.js +0 -13
- package/dist/sql/error-analyzer.d.ts +1 -0
- package/dist/sql/error-analyzer.js +550 -0
- package/dist/sql/generator.d.ts +1 -81
- package/dist/sql/generator.js +0 -29
- package/dist/sql/index.d.ts +1 -2
- package/dist/sql/index.js +0 -1
- package/dist/types/api.d.ts +1 -412
- package/dist/types/api.js +0 -7
- package/dist/utils/colors.d.ts +1 -40
- package/dist/utils/colors.js +0 -10
- package/dist/utils/common.d.ts +1 -50
- package/dist/utils/common.js +19 -23
- package/dist/utils/csv.d.ts +1 -15
- package/dist/utils/csv.js +0 -13
- package/dist/utils/datatype.d.ts +1 -49
- package/dist/utils/datatype.js +0 -41
- package/dist/utils/icons.d.ts +1 -11
- package/dist/utils/icons.js +25 -16
- package/dist/utils/index.d.ts +1 -4
- package/dist/utils/index.js +0 -1
- package/dist/utils/logger.d.ts +1 -10
- package/dist/utils/logger.js +0 -4
- package/dist/views/layouts/main.hbs +14 -5
- package/dist/views/pages/databases.hbs +0 -18
- package/dist/views/pages/designer.hbs +0 -10
- package/dist/views/pages/erd.hbs +1241 -0
- package/dist/views/pages/error.hbs +0 -2
- package/dist/views/pages/form.hbs +0 -5
- package/dist/views/pages/home.hbs +98 -10
- package/dist/views/pages/info.hbs +107 -0
- package/dist/views/pages/login.hbs +0 -13
- package/dist/views/pages/query.hbs +54 -21
- package/dist/views/pages/schema.hbs +0 -20
- package/dist/views/pages/seed.hbs +435 -202
- package/dist/views/pages/table.hbs +0 -14
- package/dist/views/partials/icon.hbs +0 -1
- package/dist/views/partials/navbar.hbs +0 -6
- package/dist/views/partials/sidebar.hbs +12 -8
- package/docs/API.md +6 -1
- package/docs/EXAMPLES.md +3 -0
- package/package.json +7 -5
- package/dist/app.js.map +0 -1
- package/dist/auth/config.js.map +0 -1
- package/dist/auth/crypto.js.map +0 -1
- package/dist/auth/index.js.map +0 -1
- package/dist/auth/middleware.js.map +0 -1
- package/dist/auth/routes.js.map +0 -1
- package/dist/auth/types.js.map +0 -1
- package/dist/cli/args.js.map +0 -1
- package/dist/cli/config.js.map +0 -1
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/runner.js.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/data/datasets.js.map +0 -1
- package/dist/data/detector.js.map +0 -1
- package/dist/data/engine.js.map +0 -1
- package/dist/data/index.js.map +0 -1
- package/dist/data/strategies.js.map +0 -1
- package/dist/data/types.js.map +0 -1
- package/dist/db/database.js.map +0 -1
- package/dist/db/export.js.map +0 -1
- package/dist/db/filters.js.map +0 -1
- package/dist/db/index.js.map +0 -1
- package/dist/db/introspection.js.map +0 -1
- package/dist/db/manager.js.map +0 -1
- package/dist/db/migrations.js.map +0 -1
- package/dist/db/postgres.js.map +0 -1
- package/dist/db/types.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/public/css/input.css +0 -302
- package/dist/public/js/icons.js +0 -51
- package/dist/routes/api/helpers.js.map +0 -1
- package/dist/routes/api/import-export.js.map +0 -1
- package/dist/routes/api/index.js.map +0 -1
- package/dist/routes/api/query.js.map +0 -1
- package/dist/routes/api/rows.js.map +0 -1
- package/dist/routes/api/seed.js.map +0 -1
- package/dist/routes/api/tables.js.map +0 -1
- package/dist/routes/databases.js.map +0 -1
- package/dist/routes/index.js.map +0 -1
- package/dist/routes/pages.js.map +0 -1
- package/dist/serverless.js.map +0 -1
- package/dist/sql/classifier.js.map +0 -1
- package/dist/sql/generator.js.map +0 -1
- package/dist/sql/index.js.map +0 -1
- package/dist/types/api.js.map +0 -1
- package/dist/utils/colors.js.map +0 -1
- package/dist/utils/common.js.map +0 -1
- package/dist/utils/csv.js.map +0 -1
- package/dist/utils/datatype.js.map +0 -1
- package/dist/utils/icons.js.map +0 -1
- package/dist/utils/index.js.map +0 -1
- package/dist/utils/logger.js.map +0 -1
package/README.md
CHANGED
|
@@ -10,10 +10,12 @@
|
|
|
10
10
|
|
|
11
11
|
- **SQLite & PostgreSQL Multi-Engine:** Seamlessly manage local SQLite files, remote PostgreSQL connections, or multi-database environments with distinct engine badges and credentials protection.
|
|
12
12
|
- **Secure JSON Config Files:** Pass database credentials securely in `.json` files (`name.postgres.json`) without exposing secrets on the CLI.
|
|
13
|
-
- **Zero frontend build step:** Server-rendered Handlebars UI + vanilla JS +
|
|
13
|
+
- **Zero frontend build step:** Server-rendered Handlebars UI + vanilla JS + heavily refined DaisyUI/TailwindCSS styling; a single lightweight Express process serves pages, static assets, and the REST API.
|
|
14
14
|
- **Standalone CLI or embeddable library:** Run instantly via `npx admindb` or mount it directly into your existing Express application under any subpath.
|
|
15
|
+
- **Intelligent SQL Error Analyzer:** Intercepts raw SQL errors and provides human-readable explanations and suggested fixes for syntax errors, missing columns, and constraint violations.
|
|
15
16
|
- **Modern terminal experience:** Clean, colorized startup banner with auto-detected local/network URLs and streamlined runtime logs.
|
|
16
17
|
- **Rich Data Types & Calendar Controls:** In-place calendar pickers with presets (`Now`, `Yesterday`, `Tomorrow`, `+7 Days`, `+30 Days`), PostgreSQL Array chip managers, JSON modal inspector, UUID generators, and byte dump inspector.
|
|
18
|
+
- **Interactive ER Diagram & Relationship Graph:** Interactive SVG schema visualizer with 3 layout modes (Hierarchy, Force, Circular), relationship filtering, hover edge inspectors, and clean SVG export.
|
|
17
19
|
- **Safe SQL by construction:** Quoted identifiers, escaped literals, parameterized queries, and non-executing SQL preview modes.
|
|
18
20
|
|
|
19
21
|
---
|
|
@@ -73,7 +75,7 @@ admindb
|
|
|
73
75
|
AdminDB features a clean, colorized CLI startup banner and streamlined, low-noise runtime logging:
|
|
74
76
|
|
|
75
77
|
```text
|
|
76
|
-
⚡ AdminDB v2.1
|
|
78
|
+
⚡ AdminDB v2.2.1
|
|
77
79
|
|
|
78
80
|
➜ Local: http://localhost:45531/
|
|
79
81
|
➜ Network: http://192.168.1.15:45531/
|
|
@@ -126,16 +128,28 @@ Runtime operations produce crisp, color-coded status logs:
|
|
|
126
128
|
* **Full Database Dump:** Download the entire database as a standard SQL file (`CREATE TABLE` + `INSERT` statements).
|
|
127
129
|
|
|
128
130
|
### 🗂️ Visual Schema Designer & Indexes
|
|
131
|
+
* **Database Info & Settings:** View active database configuration parameters (e.g. `journal_mode`, `max_connections`) and a complete database-wide syntax-highlighted Schema DDL extraction with an easy 1-click copy tool.
|
|
129
132
|
* **Visual Table Designer:** Create tables interactively with column types (including `UUID`, `JSONB`, `TIMESTAMP`, `TIMESTAMPTZ`, `INTERVAL`, `BYTEA`, `INET`, `SERIAL`, `BIGINT`), primary keys, autoincrement, nullable/unique constraints, default values, and foreign keys.
|
|
130
133
|
* **Relationship-Safe Schema Editor:** Rename tables, add columns, modify column types, rename columns, and drop columns/tables with safety checks to protect active foreign keys and unique constraints.
|
|
131
134
|
* **Index Manager:** Create single or multi-column indexes (plain or unique) with live SQL previews, and drop existing indexes safely.
|
|
132
135
|
|
|
136
|
+
### 📊 Interactive ER Diagram & Relationship Visualization
|
|
137
|
+
* **3 Layout Modes:**
|
|
138
|
+
* **Hierarchy (default):** Layered topological layout organizing master/root tables on the left with dependencies progressing to the right, barycenter-sorted to minimize edge crossings.
|
|
139
|
+
* **Force-Directed:** Organic physics simulation balancing node repulsion and edge spring tension.
|
|
140
|
+
* **Circular:** Clean circular arrangement for connected tables with isolated/unreferenced tables placed cleanly in a side column.
|
|
141
|
+
* **Interactive Canvas & Controls:** Smooth zoom & pan, node dragging, minimap overview, live table search filter with auto-pan, compact mode toggle, and keyboard shortcuts (<kbd>F</kbd> fit, <kbd>L</kbd> cycle layout, <kbd>C</kbd> compact).
|
|
142
|
+
* **Relationship Highlighting & Inspector:** Select any table to highlight its incoming & outgoing foreign-key connections with dimmed backdrop, hover relationship curves to inspect column mappings, and access direct Schema / Browse shortcuts from the sliding side panel or right-click context menu.
|
|
143
|
+
* **Standalone SVG Export:** Export clean, production-ready SVG diagrams with inlined styling and proper viewBox bounds for documentation and architecture reviews.
|
|
144
|
+
|
|
133
145
|
### 🔄 Import, Export & Seed Data Generation
|
|
134
146
|
* **CSV Import:** Upload or paste CSV files with column matching, executed transactionally.
|
|
135
147
|
* **Data Export:** Download table data or arbitrary SQL query results as CSV or JSON.
|
|
136
148
|
* **Intelligent Seed Generator:** Populate tables with up to 5,000 realistic rows using intelligent heuristic strategy detection (names, emails, phones, addresses, dates, UUIDs, custom templates, or sampled foreign keys). Includes live table preview before execution.
|
|
149
|
+
* **Cross-Table Relational Chain Seeder:** Automatically detects and resolves foreign key dependencies, allowing you to seed an entire branch of related tables in topological order in a single click.
|
|
137
150
|
|
|
138
|
-
### 📁 Multi-Database Manager
|
|
151
|
+
### 📁 Multi-Database Manager & Schema Operations
|
|
152
|
+
* **Bulk Table Operations:** Select multiple tables from the home dashboard to bulk drop or bulk truncate them simultaneously, with force cascade options that temporarily disable and bypass foreign key checks.
|
|
139
153
|
* Manage directories of SQLite files, explicit file lists, or named JSON connections.
|
|
140
154
|
* Dedicated landing page with engine badges (`PostgreSQL` / `SQLite`), table counts, connection paths, and seamless database switching.
|
|
141
155
|
|
|
@@ -150,33 +164,42 @@ Runtime operations produce crisp, color-coded status logs:
|
|
|
150
164
|
|
|
151
165
|
## 🚀 Embed AdminDB in Express
|
|
152
166
|
|
|
153
|
-
AdminDB can
|
|
167
|
+
AdminDB is built to work natively with both **CommonJS (`require`)** and **ES Modules (`import`)**. You can mount it directly into any existing Express application under any subpath:
|
|
154
168
|
|
|
169
|
+
### ES Modules (ESM) or TypeScript
|
|
155
170
|
```ts
|
|
156
171
|
import express from 'express';
|
|
157
172
|
import { createRouter } from 'admindb';
|
|
158
173
|
|
|
159
174
|
const app = express();
|
|
160
175
|
|
|
161
|
-
app.get('/', (_req, res) => res.send('Main App'));
|
|
162
|
-
|
|
163
176
|
// Mount AdminDB for SQLite
|
|
164
177
|
app.use('/admin', createRouter({
|
|
165
178
|
dbPath: './data/app.db',
|
|
166
179
|
basePath: '/admin',
|
|
167
180
|
}));
|
|
168
181
|
|
|
169
|
-
|
|
170
|
-
|
|
182
|
+
app.listen(45531);
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### CommonJS (CJS)
|
|
186
|
+
```js
|
|
187
|
+
const express = require('express');
|
|
188
|
+
const { createRouter } = require('admindb');
|
|
189
|
+
|
|
190
|
+
const app = express();
|
|
191
|
+
|
|
192
|
+
app.use('/admin', createRouter({
|
|
171
193
|
connection: 'postgresql://postgres:secret@localhost:5432/mydb',
|
|
172
|
-
basePath: '/admin
|
|
194
|
+
basePath: '/admin',
|
|
173
195
|
}));
|
|
174
196
|
|
|
175
|
-
app.listen(45531
|
|
176
|
-
console.log('App running on http://localhost:45531 (Admin: http://localhost:45531/admin)');
|
|
177
|
-
});
|
|
197
|
+
app.listen(45531);
|
|
178
198
|
```
|
|
179
199
|
|
|
200
|
+
> **💡 Try the Embedded Test App:**
|
|
201
|
+
> You can see a complete, working example of an embedded host application by running `npm run testapp` from the project root. This spins up a standalone Express server that imports and mounts AdminDB.
|
|
202
|
+
|
|
180
203
|
> 📖 **Full Options & Advanced Embedding Recipes:**
|
|
181
204
|
> See [**`docs/EXAMPLES.md`**](docs/EXAMPLES.md#-part-2-programmatic-code-examples-express--typescript) for the complete `createRouter` options reference, multi-database management (`DbManager`), custom loggers, read-only mode, and custom authentication configurations.
|
|
182
205
|
|
|
@@ -330,6 +353,12 @@ npm test # Run comprehensive unit test suite
|
|
|
330
353
|
|
|
331
354
|
---
|
|
332
355
|
|
|
356
|
+
## 🙌 Credits
|
|
357
|
+
|
|
358
|
+
AdminDB is built and maintained by **[Muhammad Bin Khalid](https://github.com/MIbnEKhalid)** under the umbrella of **[MBKTech.org](https://mbktech.org)**.
|
|
359
|
+
|
|
360
|
+
---
|
|
361
|
+
|
|
333
362
|
## 📄 License
|
|
334
363
|
|
|
335
364
|
[MIT](./LICENSE) © MIbnEKhalid
|
package/dist/app.d.ts
CHANGED
|
@@ -1,69 +1 @@
|
|
|
1
|
-
import express from 'express';
|
|
2
|
-
import type { IDatabase, PostgresOptions } from './db/types';
|
|
3
|
-
import { DbManager } from './db/manager';
|
|
4
|
-
import { type Logger, type LogLevel } from './utils/logger';
|
|
5
|
-
import { type AuthConfig } from './auth';
|
|
6
|
-
export interface AppOptions {
|
|
7
|
-
/** Path to the SQLite file (single-db mode) or connection string. Defaults to `admindb.db`. */
|
|
8
|
-
dbPath?: string;
|
|
9
|
-
/** PostgreSQL or database connection string (e.g. `postgresql://user:pass@host:5432/db`). */
|
|
10
|
-
connection?: string;
|
|
11
|
-
/** Explicit PostgreSQL connection options. */
|
|
12
|
-
pgOptions?: PostgresOptions;
|
|
13
|
-
/** URL prefix used by templates/static assets (e.g. `/admin`). Defaults to `''`. */
|
|
14
|
-
basePath?: string;
|
|
15
|
-
logger?: Logger;
|
|
16
|
-
logLevel?: LogLevel;
|
|
17
|
-
/** Provide an existing database instance (single-db embedding). */
|
|
18
|
-
db?: IDatabase;
|
|
19
|
-
/** Enable multi-database mode with a manager over a directory of `.db` files. */
|
|
20
|
-
manager?: DbManager;
|
|
21
|
-
/** Open the database read-only — all write routes are rejected with 403. */
|
|
22
|
-
readonly?: boolean;
|
|
23
|
-
/**
|
|
24
|
-
* Serverless mode flag.
|
|
25
|
-
* When enabled (or auto-detected in Vercel/AWS Lambda/Netlify/etc.),
|
|
26
|
-
* all write operations, mutations, schema alterations, and seeder generators
|
|
27
|
-
* are disabled, enforcing strict read-only safety for ephemeral environments.
|
|
28
|
-
*
|
|
29
|
-
* Defaults to `undefined` (auto-detected via environment).
|
|
30
|
-
*/
|
|
31
|
-
serverless?: boolean;
|
|
32
|
-
/**
|
|
33
|
-
* Authentication configuration.
|
|
34
|
-
* - `true` (default): authentication enabled with default or env credentials.
|
|
35
|
-
* - `false`: completely disables authentication (no protection / developer's own auth).
|
|
36
|
-
* - `{ username, password, secret, ... }`: custom credentials and options.
|
|
37
|
-
*/
|
|
38
|
-
auth?: boolean | AuthConfig;
|
|
39
|
-
/**
|
|
40
|
-
* Manager mode: show the filesystem file-browser on the databases landing
|
|
41
|
-
* page. Defaults to `true`. Set to `false` to disable browsing (e.g. when the
|
|
42
|
-
* server was started with specific database files only).
|
|
43
|
-
*/
|
|
44
|
-
allowBrowse?: boolean;
|
|
45
|
-
/**
|
|
46
|
-
* Manager mode: restrict the file-browser to this folder (absolute path).
|
|
47
|
-
* When set, the browser cannot navigate above it and only databases inside it
|
|
48
|
-
* can be opened.
|
|
49
|
-
*/
|
|
50
|
-
browseRoot?: string;
|
|
51
|
-
/** Internal: URL of the databases list (used by per-db apps to link "switch database"). */
|
|
52
|
-
databasesUrl?: string;
|
|
53
|
-
/** Internal: current database id (for locals/display). */
|
|
54
|
-
dbId?: string;
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Build a fully configured, mountable Express app (pages + JSON API + static
|
|
58
|
-
* assets + view engine).
|
|
59
|
-
*
|
|
60
|
-
* - Single database: pass `dbPath` (or `db`). Mount under your own prefix with
|
|
61
|
-
* `basePath`. This is also the mode used per-database in multi-db mode.
|
|
62
|
-
* - Multiple databases: pass a `manager`. The app then shows a "Databases"
|
|
63
|
-
* landing page and scopes every database under `/{dbFile}/…` (e.g.
|
|
64
|
-
* `/{base}/app.db/tables/users`, `/{base}/app.db/api/tables`).
|
|
65
|
-
*
|
|
66
|
-
* An Express app is valid middleware, so `app.use('/admin', createRouter({...}))`
|
|
67
|
-
* works and shares the same port.
|
|
68
|
-
*/
|
|
69
|
-
export declare function createRouter(options?: AppOptions): express.Express;
|
|
1
|
+
import express from 'express';import type {IDatabase,PostgresOptions} from './db/types';import{DbManager} from './db/manager';import{type Logger,type LogLevel} from './utils/logger';import{type AuthConfig} from './auth';export interface AppOptions{dbPath?:string;connection?:string;pgOptions?:PostgresOptions;basePath?:string;logger?:Logger;logLevel?:LogLevel;db?:IDatabase;manager?:DbManager;readonly ?:boolean;serverless?:boolean;auth?:boolean|AuthConfig;allowBrowse?:boolean;browseRoot?:string;databasesUrl?:string;dbId?:string;}export declare function createRouter(options?:AppOptions):express.Express;
|
package/dist/app.js
CHANGED
|
@@ -18,21 +18,7 @@ const common_1 = require("./utils/common");
|
|
|
18
18
|
const icons_1 = require("./utils/icons");
|
|
19
19
|
const serverless_1 = require("./serverless");
|
|
20
20
|
const auth_1 = require("./auth");
|
|
21
|
-
/** Installed package version, exposed to every template as `{{version}}`. */
|
|
22
21
|
const APP_VERSION = (0, args_1.getPackageVersion)();
|
|
23
|
-
/**
|
|
24
|
-
* Build a fully configured, mountable Express app (pages + JSON API + static
|
|
25
|
-
* assets + view engine).
|
|
26
|
-
*
|
|
27
|
-
* - Single database: pass `dbPath` (or `db`). Mount under your own prefix with
|
|
28
|
-
* `basePath`. This is also the mode used per-database in multi-db mode.
|
|
29
|
-
* - Multiple databases: pass a `manager`. The app then shows a "Databases"
|
|
30
|
-
* landing page and scopes every database under `/{dbFile}/…` (e.g.
|
|
31
|
-
* `/{base}/app.db/tables/users`, `/{base}/app.db/api/tables`).
|
|
32
|
-
*
|
|
33
|
-
* An Express app is valid middleware, so `app.use('/admin', createRouter({...}))`
|
|
34
|
-
* works and shares the same port.
|
|
35
|
-
*/
|
|
36
22
|
function createRouter(options = {}) {
|
|
37
23
|
return options.manager ? createManagerApp(options) : createSingleDbApp(options);
|
|
38
24
|
}
|
|
@@ -66,14 +52,12 @@ function setupViewEngine(app, _logger) {
|
|
|
66
52
|
app.set('views', node_path_1.default.join(__dirname, 'views'));
|
|
67
53
|
}
|
|
68
54
|
function addErrorHandlers(app, logger) {
|
|
69
|
-
// 404 handler.
|
|
70
55
|
app.use((req, res) => {
|
|
71
56
|
if (req.path.startsWith('/api/')) {
|
|
72
57
|
return res.status(404).json({ success: false, error: 'Not found.' });
|
|
73
58
|
}
|
|
74
59
|
res.status(404).render('pages/error', { title: 'Not found', status: 404, error: 'The page you requested does not exist.' });
|
|
75
60
|
});
|
|
76
|
-
// Central error handler.
|
|
77
61
|
app.use((err, req, res, _next) => {
|
|
78
62
|
logger.error('Unhandled error', err);
|
|
79
63
|
if (req.path.startsWith('/api/')) {
|
|
@@ -82,15 +66,12 @@ function addErrorHandlers(app, logger) {
|
|
|
82
66
|
res.status(500).render('pages/error', { title: 'Error', status: 500, error: (0, common_1.errorMessage)(err) });
|
|
83
67
|
});
|
|
84
68
|
}
|
|
85
|
-
/** Single-database app (also used as the per-database app in multi-db mode). */
|
|
86
69
|
function createSingleDbApp(options) {
|
|
87
70
|
const isServerless = options.serverless !== undefined ? options.serverless : (0, serverless_1.isServerlessEnvironment)();
|
|
88
71
|
const rawTarget = options.connection || options.dbPath || '';
|
|
89
72
|
const isPg = Boolean(options.pgOptions) ||
|
|
90
|
-
(
|
|
73
|
+
(0, common_1.isPostgresConnectionString)(rawTarget) ||
|
|
91
74
|
options.db?.dialect === 'postgres';
|
|
92
|
-
// In serverless environments, SQLite local files are ephemeral/read-only,
|
|
93
|
-
// whereas PostgreSQL connects to a remote networked database and is fully editable.
|
|
94
75
|
const readonly = options.readonly !== undefined
|
|
95
76
|
? Boolean(options.readonly)
|
|
96
77
|
: (isServerless && !isPg);
|
|
@@ -113,16 +94,12 @@ function createSingleDbApp(options) {
|
|
|
113
94
|
const authConfig = (0, auth_1.resolveAuthConfig)(options.auth);
|
|
114
95
|
const router = (0, express_1.default)();
|
|
115
96
|
router.disable('x-powered-by');
|
|
116
|
-
// Static assets (relative to the mount point, so a prefix "just works").
|
|
117
97
|
router.use(express_1.default.static(node_path_1.default.join(__dirname, 'public')));
|
|
118
98
|
setupViewEngine(router, logger);
|
|
119
99
|
router.use(express_1.default.json({ limit: '10mb' }));
|
|
120
100
|
router.use(express_1.default.urlencoded({ extended: false }));
|
|
121
|
-
// Register authentication endpoints (/login, /logout)
|
|
122
101
|
(0, auth_1.registerAuthRoutes)(router, authConfig, basePath, logger);
|
|
123
|
-
// Authentication gatekeeper middleware
|
|
124
102
|
router.use((0, auth_1.createAuthMiddleware)(authConfig, basePath, logger));
|
|
125
|
-
// Page locals (skipped for API + static paths).
|
|
126
103
|
router.use(async (req, res, next) => {
|
|
127
104
|
try {
|
|
128
105
|
res.locals.basePath = basePath;
|
|
@@ -141,6 +118,7 @@ function createSingleDbApp(options) {
|
|
|
141
118
|
res.locals.version = APP_VERSION;
|
|
142
119
|
res.locals.authEnabled = authConfig.enabled;
|
|
143
120
|
res.locals.isDefaultPassword = authConfig.isDefaultPassword;
|
|
121
|
+
res.locals.icons = icons_1.ICONS;
|
|
144
122
|
if (!req.path.startsWith('/api/')) {
|
|
145
123
|
const all = await db.listTables();
|
|
146
124
|
const names = (all.data ?? []).map((t) => t.name);
|
|
@@ -158,7 +136,6 @@ function createSingleDbApp(options) {
|
|
|
158
136
|
addErrorHandlers(router, logger);
|
|
159
137
|
return router;
|
|
160
138
|
}
|
|
161
|
-
/** Multi-database app: databases landing page + a per-database sub-app per file. */
|
|
162
139
|
function createManagerApp(options) {
|
|
163
140
|
const isServerless = options.serverless !== undefined ? options.serverless : (0, serverless_1.isServerlessEnvironment)();
|
|
164
141
|
const readonly = isServerless || Boolean(options.readonly);
|
|
@@ -172,11 +149,8 @@ function createManagerApp(options) {
|
|
|
172
149
|
setupViewEngine(app, logger);
|
|
173
150
|
app.use(express_1.default.json({ limit: '10mb' }));
|
|
174
151
|
app.use(express_1.default.urlencoded({ extended: false }));
|
|
175
|
-
// Register authentication endpoints (/login, /logout)
|
|
176
152
|
(0, auth_1.registerAuthRoutes)(app, authConfig, basePath, logger);
|
|
177
|
-
// Authentication gatekeeper middleware
|
|
178
153
|
app.use((0, auth_1.createAuthMiddleware)(authConfig, basePath, logger));
|
|
179
|
-
// Locals for the databases landing page.
|
|
180
154
|
app.use(async (req, res, next) => {
|
|
181
155
|
try {
|
|
182
156
|
res.locals.basePath = basePath;
|
|
@@ -191,6 +165,7 @@ function createManagerApp(options) {
|
|
|
191
165
|
res.locals.version = APP_VERSION;
|
|
192
166
|
res.locals.authEnabled = authConfig.enabled;
|
|
193
167
|
res.locals.isDefaultPassword = authConfig.isDefaultPassword;
|
|
168
|
+
res.locals.icons = icons_1.ICONS;
|
|
194
169
|
if (!req.path.startsWith('/api/')) {
|
|
195
170
|
res.locals.tables = [];
|
|
196
171
|
res.locals.internalTables = [];
|
|
@@ -201,7 +176,6 @@ function createManagerApp(options) {
|
|
|
201
176
|
next(err);
|
|
202
177
|
}
|
|
203
178
|
});
|
|
204
|
-
// Per-database dispatch: mount a cached single-db app at /:dbId.
|
|
205
179
|
const subApps = new Map();
|
|
206
180
|
(0, databases_1.registerDatabasesRoutes)(app, {
|
|
207
181
|
manager,
|
|
@@ -254,4 +228,3 @@ function createManagerApp(options) {
|
|
|
254
228
|
addErrorHandlers(app, logger);
|
|
255
229
|
return app;
|
|
256
230
|
}
|
|
257
|
-
//# sourceMappingURL=app.js.map
|
package/dist/auth/config.d.ts
CHANGED
|
@@ -1,19 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
/**
|
|
3
|
-
* Clears the in-memory fallback secret cache (primarily for testing).
|
|
4
|
-
*/
|
|
5
|
-
export declare function resetPersistentSecretCache(): void;
|
|
6
|
-
/**
|
|
7
|
-
* Returns a persistent secret key for signing session cookies.
|
|
8
|
-
*
|
|
9
|
-
* Priority order:
|
|
10
|
-
* 1. `ADMINDB_SECRET` or `SESSION_SECRET` environment variables.
|
|
11
|
-
* 2. `ADMINDB_SECRET_FILE` path if specified in environment.
|
|
12
|
-
* 3. User home directory `.admindb-secret` (or temp directory if home is not writable).
|
|
13
|
-
* 4. In-memory random fallback if filesystem is completely read-only.
|
|
14
|
-
*/
|
|
15
|
-
export declare function getOrCreatePersistentSecret(env?: NodeJS.ProcessEnv): string;
|
|
16
|
-
/**
|
|
17
|
-
* Resolves the authentication options against environment variables.
|
|
18
|
-
*/
|
|
19
|
-
export declare function resolveAuthConfig(optionsAuth?: boolean | AuthConfig, env?: NodeJS.ProcessEnv): ResolvedAuthConfig;
|
|
1
|
+
import{type AuthConfig,type ResolvedAuthConfig} from './types';export declare function resetPersistentSecretCache():void;export declare function getOrCreatePersistentSecret(env?:NodeJS.ProcessEnv):string;export declare function resolveAuthConfig(optionsAuth?:boolean|AuthConfig,env?:NodeJS.ProcessEnv):ResolvedAuthConfig;
|
package/dist/auth/config.js
CHANGED
|
@@ -11,29 +11,15 @@ const node_fs_1 = require("node:fs");
|
|
|
11
11
|
const node_os_1 = __importDefault(require("node:os"));
|
|
12
12
|
const node_path_1 = __importDefault(require("node:path"));
|
|
13
13
|
const types_1 = require("./types");
|
|
14
|
-
// In-memory cache of resolved secret to avoid repeated disk reads per process
|
|
15
14
|
let cachedFallbackSecret = null;
|
|
16
|
-
/**
|
|
17
|
-
* Clears the in-memory fallback secret cache (primarily for testing).
|
|
18
|
-
*/
|
|
19
15
|
function resetPersistentSecretCache() {
|
|
20
16
|
cachedFallbackSecret = null;
|
|
21
17
|
}
|
|
22
|
-
/**
|
|
23
|
-
* Returns a persistent secret key for signing session cookies.
|
|
24
|
-
*
|
|
25
|
-
* Priority order:
|
|
26
|
-
* 1. `ADMINDB_SECRET` or `SESSION_SECRET` environment variables.
|
|
27
|
-
* 2. `ADMINDB_SECRET_FILE` path if specified in environment.
|
|
28
|
-
* 3. User home directory `.admindb-secret` (or temp directory if home is not writable).
|
|
29
|
-
* 4. In-memory random fallback if filesystem is completely read-only.
|
|
30
|
-
*/
|
|
31
18
|
function getOrCreatePersistentSecret(env = process.env) {
|
|
32
19
|
const envSecret = env.ADMINDB_SECRET ?? env.SESSION_SECRET;
|
|
33
20
|
if (envSecret && String(envSecret).trim()) {
|
|
34
21
|
return String(envSecret).trim();
|
|
35
22
|
}
|
|
36
|
-
// If a custom secret file is specified, don't use default home cache
|
|
37
23
|
const customSecretFile = env.ADMINDB_SECRET_FILE ? String(env.ADMINDB_SECRET_FILE).trim() : undefined;
|
|
38
24
|
if (!customSecretFile && cachedFallbackSecret) {
|
|
39
25
|
return cachedFallbackSecret;
|
|
@@ -50,7 +36,6 @@ function getOrCreatePersistentSecret(env = process.env) {
|
|
|
50
36
|
}
|
|
51
37
|
}
|
|
52
38
|
catch {
|
|
53
|
-
/* ignore */
|
|
54
39
|
}
|
|
55
40
|
try {
|
|
56
41
|
const tmp = node_os_1.default.tmpdir();
|
|
@@ -59,10 +44,8 @@ function getOrCreatePersistentSecret(env = process.env) {
|
|
|
59
44
|
}
|
|
60
45
|
}
|
|
61
46
|
catch {
|
|
62
|
-
/* ignore */
|
|
63
47
|
}
|
|
64
48
|
}
|
|
65
|
-
// 1. Try reading existing secret from candidate paths
|
|
66
49
|
for (const filePath of candidatePaths) {
|
|
67
50
|
try {
|
|
68
51
|
if ((0, node_fs_1.existsSync)(filePath)) {
|
|
@@ -76,10 +59,8 @@ function getOrCreatePersistentSecret(env = process.env) {
|
|
|
76
59
|
}
|
|
77
60
|
}
|
|
78
61
|
catch {
|
|
79
|
-
// Continue to next candidate or write attempt
|
|
80
62
|
}
|
|
81
63
|
}
|
|
82
|
-
// 2. Generate new secret and attempt writing to the first writable candidate path
|
|
83
64
|
const newSecret = (0, node_crypto_1.randomBytes)(32).toString('hex');
|
|
84
65
|
for (const filePath of candidatePaths) {
|
|
85
66
|
try {
|
|
@@ -94,20 +75,14 @@ function getOrCreatePersistentSecret(env = process.env) {
|
|
|
94
75
|
return newSecret;
|
|
95
76
|
}
|
|
96
77
|
catch {
|
|
97
|
-
// Continue to next candidate if write fails
|
|
98
78
|
}
|
|
99
79
|
}
|
|
100
|
-
// 3. Last-resort fallback to in-memory secret if disk is not writable
|
|
101
80
|
if (!customSecretFile) {
|
|
102
81
|
cachedFallbackSecret = newSecret;
|
|
103
82
|
}
|
|
104
83
|
return newSecret;
|
|
105
84
|
}
|
|
106
|
-
/**
|
|
107
|
-
* Resolves the authentication options against environment variables.
|
|
108
|
-
*/
|
|
109
85
|
function resolveAuthConfig(optionsAuth, env = process.env) {
|
|
110
|
-
// Check if explicitly disabled via options or environment variable
|
|
111
86
|
const envDisabled = ['0', 'false', 'no', 'off'].includes(String(env.ADMINDB_AUTH ?? '').trim().toLowerCase()) ||
|
|
112
87
|
['1', 'true', 'yes'].includes(String(env.ADMINDB_NO_AUTH ?? env.ADMINDB_DISABLE_AUTH ?? '').trim().toLowerCase());
|
|
113
88
|
let enabled = true;
|
|
@@ -144,4 +119,3 @@ function resolveAuthConfig(optionsAuth, env = process.env) {
|
|
|
144
119
|
isDefaultPassword,
|
|
145
120
|
};
|
|
146
121
|
}
|
|
147
|
-
//# sourceMappingURL=config.js.map
|
package/dist/auth/crypto.d.ts
CHANGED
|
@@ -1,27 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* Generates a secure scrypt password hash with a random or provided salt.
|
|
3
|
-
* Format: `scrypt:<saltHex>:<derivedKeyHex>`
|
|
4
|
-
*/
|
|
5
|
-
export declare function hashPassword(password: string, salt?: string): string;
|
|
6
|
-
/**
|
|
7
|
-
* Constant-time comparison between two strings to prevent timing attacks.
|
|
8
|
-
*/
|
|
9
|
-
export declare function constantTimeCompare(a: string, b: string): boolean;
|
|
10
|
-
/**
|
|
11
|
-
* Verifies a candidate plaintext password against a stored password or scrypt hash.
|
|
12
|
-
*/
|
|
13
|
-
export declare function verifyPassword(candidatePassword: string, storedPasswordOrHash: string): boolean;
|
|
14
|
-
/**
|
|
15
|
-
* Parses raw Cookie header string into key-value map.
|
|
16
|
-
*/
|
|
17
|
-
export declare function parseCookies(cookieHeader?: string): Record<string, string>;
|
|
18
|
-
/**
|
|
19
|
-
* Creates a tamper-proof signed session token using HMAC-SHA256.
|
|
20
|
-
* Format: `<base64url(username)>.<expiresAtTimestamp>.<hmacSignature>`
|
|
21
|
-
*/
|
|
22
|
-
export declare function createSessionToken(username: string, secret: string, durationMs?: number): string;
|
|
23
|
-
/**
|
|
24
|
-
* Verifies a signed session token in constant time.
|
|
25
|
-
* Returns the authenticated username if valid and non-expired, or null otherwise.
|
|
26
|
-
*/
|
|
27
|
-
export declare function verifySessionToken(token: string, secret: string, expectedUser?: string): string | null;
|
|
1
|
+
export declare function hashPassword(password:string,salt?:string):string;export declare function constantTimeCompare(a:string,b:string):boolean;export declare function verifyPassword(candidatePassword:string,storedPasswordOrHash:string):boolean;export declare function parseCookies(cookieHeader?:string):Record<string,string>;export declare function createSessionToken(username:string,secret:string,durationMs?:number):string;export declare function verifySessionToken(token:string,secret:string,expectedUser?:string):string|null;
|
package/dist/auth/crypto.js
CHANGED
|
@@ -8,24 +8,16 @@ exports.createSessionToken = createSessionToken;
|
|
|
8
8
|
exports.verifySessionToken = verifySessionToken;
|
|
9
9
|
const node_crypto_1 = require("node:crypto");
|
|
10
10
|
const types_1 = require("./types");
|
|
11
|
-
/**
|
|
12
|
-
* Generates a secure scrypt password hash with a random or provided salt.
|
|
13
|
-
* Format: `scrypt:<saltHex>:<derivedKeyHex>`
|
|
14
|
-
*/
|
|
15
11
|
function hashPassword(password, salt) {
|
|
16
12
|
const saltHex = salt || (0, node_crypto_1.randomBytes)(16).toString('hex');
|
|
17
13
|
const derivedKey = (0, node_crypto_1.scryptSync)(password, saltHex, 64).toString('hex');
|
|
18
14
|
return `scrypt:${saltHex}:${derivedKey}`;
|
|
19
15
|
}
|
|
20
|
-
/**
|
|
21
|
-
* Constant-time comparison between two strings to prevent timing attacks.
|
|
22
|
-
*/
|
|
23
16
|
function constantTimeCompare(a, b) {
|
|
24
17
|
try {
|
|
25
18
|
const bufA = Buffer.from(String(a), 'utf8');
|
|
26
19
|
const bufB = Buffer.from(String(b), 'utf8');
|
|
27
20
|
if (bufA.length !== bufB.length) {
|
|
28
|
-
// Dummy comparison to prevent timing leak on length mismatch
|
|
29
21
|
(0, node_crypto_1.timingSafeEqual)(bufA, bufA);
|
|
30
22
|
return false;
|
|
31
23
|
}
|
|
@@ -35,14 +27,10 @@ function constantTimeCompare(a, b) {
|
|
|
35
27
|
return false;
|
|
36
28
|
}
|
|
37
29
|
}
|
|
38
|
-
/**
|
|
39
|
-
* Verifies a candidate plaintext password against a stored password or scrypt hash.
|
|
40
|
-
*/
|
|
41
30
|
function verifyPassword(candidatePassword, storedPasswordOrHash) {
|
|
42
31
|
if (typeof candidatePassword !== 'string' || typeof storedPasswordOrHash !== 'string') {
|
|
43
32
|
return false;
|
|
44
33
|
}
|
|
45
|
-
// Handle scrypt formatted hash: `scrypt:<salt>:<hash>`
|
|
46
34
|
if (storedPasswordOrHash.startsWith('scrypt:')) {
|
|
47
35
|
const parts = storedPasswordOrHash.split(':');
|
|
48
36
|
if (parts.length === 3 && parts[1] && parts[2]) {
|
|
@@ -57,12 +45,8 @@ function verifyPassword(candidatePassword, storedPasswordOrHash) {
|
|
|
57
45
|
}
|
|
58
46
|
}
|
|
59
47
|
}
|
|
60
|
-
// Fallback for plaintext passwords (legacy or plain string configs)
|
|
61
48
|
return constantTimeCompare(candidatePassword, storedPasswordOrHash);
|
|
62
49
|
}
|
|
63
|
-
/**
|
|
64
|
-
* Parses raw Cookie header string into key-value map.
|
|
65
|
-
*/
|
|
66
50
|
function parseCookies(cookieHeader) {
|
|
67
51
|
const cookies = {};
|
|
68
52
|
if (!cookieHeader)
|
|
@@ -87,10 +71,6 @@ function parseCookies(cookieHeader) {
|
|
|
87
71
|
}
|
|
88
72
|
return cookies;
|
|
89
73
|
}
|
|
90
|
-
/**
|
|
91
|
-
* Creates a tamper-proof signed session token using HMAC-SHA256.
|
|
92
|
-
* Format: `<base64url(username)>.<expiresAtTimestamp>.<hmacSignature>`
|
|
93
|
-
*/
|
|
94
74
|
function createSessionToken(username, secret, durationMs = types_1.DEFAULT_SESSION_DURATION_MS) {
|
|
95
75
|
const expiresAt = Date.now() + durationMs;
|
|
96
76
|
const userPayload = Buffer.from(username, 'utf8').toString('base64url');
|
|
@@ -98,10 +78,6 @@ function createSessionToken(username, secret, durationMs = types_1.DEFAULT_SESSI
|
|
|
98
78
|
const signature = (0, node_crypto_1.createHmac)('sha256', secret).update(data).digest('base64url');
|
|
99
79
|
return `${data}.${signature}`;
|
|
100
80
|
}
|
|
101
|
-
/**
|
|
102
|
-
* Verifies a signed session token in constant time.
|
|
103
|
-
* Returns the authenticated username if valid and non-expired, or null otherwise.
|
|
104
|
-
*/
|
|
105
81
|
function verifySessionToken(token, secret, expectedUser) {
|
|
106
82
|
if (!token || typeof token !== 'string')
|
|
107
83
|
return null;
|
|
@@ -116,7 +92,7 @@ function verifySessionToken(token, secret, expectedUser) {
|
|
|
116
92
|
}
|
|
117
93
|
const expiresAt = Number.parseInt(expiresStr, 10);
|
|
118
94
|
if (!Number.isFinite(expiresAt) || Date.now() > expiresAt) {
|
|
119
|
-
return null;
|
|
95
|
+
return null;
|
|
120
96
|
}
|
|
121
97
|
let username;
|
|
122
98
|
try {
|
|
@@ -130,4 +106,3 @@ function verifySessionToken(token, secret, expectedUser) {
|
|
|
130
106
|
}
|
|
131
107
|
return username;
|
|
132
108
|
}
|
|
133
|
-
//# sourceMappingURL=crypto.js.map
|
package/dist/auth/index.d.ts
CHANGED
package/dist/auth/index.js
CHANGED
|
@@ -1,14 +1 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { Logger } from '../utils/logger';
|
|
3
|
-
import { type ResolvedAuthConfig } from './types';
|
|
4
|
-
/**
|
|
5
|
-
* Checks incoming request headers and cookies for valid authentication.
|
|
6
|
-
*/
|
|
7
|
-
export declare function authenticateRequest(req: Request, config: ResolvedAuthConfig): {
|
|
8
|
-
authenticated: boolean;
|
|
9
|
-
user?: string;
|
|
10
|
-
};
|
|
11
|
-
/**
|
|
12
|
-
* Express middleware to protect routes.
|
|
13
|
-
*/
|
|
14
|
-
export declare function createAuthMiddleware(config: ResolvedAuthConfig, basePath: string, _logger: Logger): (req: Request, res: Response, next: NextFunction) => void;
|
|
1
|
+
import type {Request,Response,NextFunction} from 'express';import type {Logger} from '../utils/logger';import{type ResolvedAuthConfig} from './types';export declare function authenticateRequest(req:Request,config:ResolvedAuthConfig):{authenticated:boolean;user?:string;};export declare function createAuthMiddleware(config:ResolvedAuthConfig,basePath:string,_logger:Logger):(req:Request,res:Response,next:NextFunction)=>void;
|
package/dist/auth/middleware.js
CHANGED
|
@@ -4,14 +4,10 @@ exports.authenticateRequest = authenticateRequest;
|
|
|
4
4
|
exports.createAuthMiddleware = createAuthMiddleware;
|
|
5
5
|
const types_1 = require("./types");
|
|
6
6
|
const crypto_1 = require("./crypto");
|
|
7
|
-
/**
|
|
8
|
-
* Checks incoming request headers and cookies for valid authentication.
|
|
9
|
-
*/
|
|
10
7
|
function authenticateRequest(req, config) {
|
|
11
8
|
if (!config.enabled) {
|
|
12
9
|
return { authenticated: true, user: 'anonymous' };
|
|
13
10
|
}
|
|
14
|
-
// 1. Check HTTP Authorization header (Basic or Bearer)
|
|
15
11
|
const authHeader = req.headers.authorization;
|
|
16
12
|
if (authHeader) {
|
|
17
13
|
const [scheme, credentials] = authHeader.split(/\s+/, 2);
|
|
@@ -29,7 +25,6 @@ function authenticateRequest(req, config) {
|
|
|
29
25
|
}
|
|
30
26
|
}
|
|
31
27
|
catch {
|
|
32
|
-
// Ignore parse errors
|
|
33
28
|
}
|
|
34
29
|
}
|
|
35
30
|
else if (scheme.toLowerCase() === 'bearer') {
|
|
@@ -37,14 +32,12 @@ function authenticateRequest(req, config) {
|
|
|
37
32
|
if (user) {
|
|
38
33
|
return { authenticated: true, user };
|
|
39
34
|
}
|
|
40
|
-
// Direct password comparison as bearer token
|
|
41
35
|
if ((0, crypto_1.verifyPassword)(credentials, config.password)) {
|
|
42
36
|
return { authenticated: true, user: config.username };
|
|
43
37
|
}
|
|
44
38
|
}
|
|
45
39
|
}
|
|
46
40
|
}
|
|
47
|
-
// 2. Check Cookie session
|
|
48
41
|
const cookies = (0, crypto_1.parseCookies)(req.headers.cookie);
|
|
49
42
|
const sessionCookie = cookies[types_1.SESSION_COOKIE_NAME];
|
|
50
43
|
if (sessionCookie) {
|
|
@@ -55,12 +48,8 @@ function authenticateRequest(req, config) {
|
|
|
55
48
|
}
|
|
56
49
|
return { authenticated: false };
|
|
57
50
|
}
|
|
58
|
-
/**
|
|
59
|
-
* Express middleware to protect routes.
|
|
60
|
-
*/
|
|
61
51
|
function createAuthMiddleware(config, basePath, _logger) {
|
|
62
52
|
return (req, res, next) => {
|
|
63
|
-
// Expose auth state to templates
|
|
64
53
|
res.locals.authEnabled = config.enabled;
|
|
65
54
|
res.locals.isDefaultPassword = config.isDefaultPassword;
|
|
66
55
|
if (!config.enabled) {
|
|
@@ -68,7 +57,6 @@ function createAuthMiddleware(config, basePath, _logger) {
|
|
|
68
57
|
return next();
|
|
69
58
|
}
|
|
70
59
|
const p = req.path;
|
|
71
|
-
// Public static assets and auth endpoints bypass authentication
|
|
72
60
|
if (p.startsWith('/css/') ||
|
|
73
61
|
p.startsWith('/js/') ||
|
|
74
62
|
p === '/favicon.ico' ||
|
|
@@ -82,7 +70,6 @@ function createAuthMiddleware(config, basePath, _logger) {
|
|
|
82
70
|
req.user = auth.user;
|
|
83
71
|
return next();
|
|
84
72
|
}
|
|
85
|
-
// Unauthenticated: API requests get 401 JSON
|
|
86
73
|
if (p.startsWith('/api/') || req.xhr || (req.headers.accept && req.headers.accept.includes('application/json'))) {
|
|
87
74
|
res.setHeader('WWW-Authenticate', 'Basic realm="AdminDB", Bearer');
|
|
88
75
|
res.status(401).json({
|
|
@@ -91,10 +78,8 @@ function createAuthMiddleware(config, basePath, _logger) {
|
|
|
91
78
|
});
|
|
92
79
|
return;
|
|
93
80
|
}
|
|
94
|
-
// Web UI: Redirect to login with return path
|
|
95
81
|
const returnUrl = req.originalUrl || `${basePath}${req.url}`;
|
|
96
82
|
const loginUrl = `${basePath}/login${returnUrl && returnUrl !== `${basePath}/` ? `?next=${encodeURIComponent(returnUrl)}` : ''}`;
|
|
97
83
|
res.redirect(loginUrl);
|
|
98
84
|
};
|
|
99
85
|
}
|
|
100
|
-
//# sourceMappingURL=middleware.js.map
|
package/dist/auth/routes.d.ts
CHANGED
|
@@ -1,7 +1 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { Logger } from '../utils/logger';
|
|
3
|
-
import { type ResolvedAuthConfig } from './types';
|
|
4
|
-
/**
|
|
5
|
-
* Registers /login and /logout routes.
|
|
6
|
-
*/
|
|
7
|
-
export declare function registerAuthRoutes(router: Router, config: ResolvedAuthConfig, basePath: string, logger: Logger): void;
|
|
1
|
+
import type {Router} from 'express';import type {Logger} from '../utils/logger';import{type ResolvedAuthConfig} from './types';export declare function registerAuthRoutes(router:Router,config:ResolvedAuthConfig,basePath:string,logger:Logger):void;
|