admindb 2.0.0 → 2.1.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 +2 -2
- package/dist/app.d.ts +1 -69
- package/dist/app.js +1 -257
- package/dist/auth/config.d.ts +1 -19
- package/dist/auth/config.js +1 -147
- package/dist/auth/crypto.d.ts +1 -27
- package/dist/auth/crypto.js +1 -133
- package/dist/auth/index.d.ts +1 -5
- package/dist/auth/index.js +1 -22
- package/dist/auth/middleware.d.ts +1 -14
- package/dist/auth/middleware.js +1 -100
- package/dist/auth/routes.d.ts +1 -7
- package/dist/auth/routes.js +1 -78
- package/dist/auth/types.d.ts +1 -35
- package/dist/auth/types.js +1 -11
- package/dist/cli/args.d.ts +1 -45
- package/dist/cli/args.js +1 -328
- package/dist/cli/config.d.ts +1 -56
- package/dist/cli/config.js +1 -133
- package/dist/cli/index.d.ts +1 -4
- package/dist/cli/index.js +1 -21
- package/dist/cli/runner.d.ts +1 -1
- package/dist/cli/runner.js +1 -222
- package/dist/cli.d.ts +1 -2
- package/dist/cli.js +1 -5
- package/dist/data/datasets.d.ts +1 -17
- package/dist/data/datasets.js +1 -133
- package/dist/data/detector.d.ts +1 -30
- package/dist/data/detector.js +1 -472
- package/dist/data/engine.d.ts +1 -10
- package/dist/data/engine.js +1 -211
- package/dist/data/index.d.ts +1 -5
- package/dist/data/index.js +1 -22
- package/dist/data/strategies.d.ts +1 -30
- package/dist/data/strategies.js +1 -343
- package/dist/data/types.d.ts +1 -73
- package/dist/data/types.js +1 -8
- package/dist/db/database.d.ts +1 -119
- package/dist/db/database.js +1 -471
- package/dist/db/export.d.ts +1 -12
- package/dist/db/export.js +1 -42
- package/dist/db/filters.d.ts +1 -22
- package/dist/db/filters.js +1 -158
- package/dist/db/index.d.ts +1 -8
- package/dist/db/index.js +1 -25
- package/dist/db/introspection.d.ts +1 -6
- package/dist/db/introspection.js +1 -172
- package/dist/db/manager.d.ts +1 -81
- package/dist/db/manager.js +1 -397
- package/dist/db/migrations.d.ts +1 -5
- package/dist/db/migrations.js +1 -110
- package/dist/db/postgres.d.ts +1 -85
- package/dist/db/postgres.js +1 -745
- package/dist/db/types.d.ts +1 -206
- package/dist/db/types.js +1 -7
- package/dist/index.d.ts +1 -23
- package/dist/index.js +1 -66
- 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/databases.js +1 -235
- package/dist/public/js/designer.js +1 -243
- package/dist/public/js/forms.js +1 -826
- package/dist/public/js/icons.js +1 -51
- package/dist/public/js/inspector.js +1 -638
- package/dist/public/js/query.js +1 -322
- package/dist/public/js/schema.js +1 -432
- package/dist/public/js/seed.js +1 -1090
- package/dist/routes/api/helpers.d.ts +1 -42
- package/dist/routes/api/helpers.js +1 -133
- package/dist/routes/api/import-export.d.ts +1 -3
- package/dist/routes/api/import-export.js +1 -85
- package/dist/routes/api/index.d.ts +1 -4
- package/dist/routes/api/index.js +1 -37
- package/dist/routes/api/query.d.ts +1 -3
- package/dist/routes/api/query.js +1 -76
- package/dist/routes/api/rows.d.ts +1 -3
- package/dist/routes/api/rows.js +1 -373
- package/dist/routes/api/seed.d.ts +1 -3
- package/dist/routes/api/seed.js +1 -82
- package/dist/routes/api/tables.d.ts +1 -3
- package/dist/routes/api/tables.js +1 -160
- package/dist/routes/databases.d.ts +1 -26
- package/dist/routes/databases.js +1 -230
- package/dist/routes/index.d.ts +1 -3
- package/dist/routes/index.js +1 -20
- package/dist/routes/pages.d.ts +1 -9
- package/dist/routes/pages.js +1 -382
- package/dist/serverless.d.ts +1 -40
- package/dist/serverless.js +1 -210
- package/dist/sql/classifier.d.ts +1 -19
- package/dist/sql/classifier.js +1 -37
- package/dist/sql/generator.d.ts +1 -78
- package/dist/sql/generator.js +1 -308
- package/dist/sql/index.d.ts +1 -2
- package/dist/sql/index.js +1 -19
- package/dist/types/api.d.ts +1 -412
- package/dist/types/api.js +1 -9
- package/dist/utils/colors.d.ts +1 -40
- package/dist/utils/colors.js +1 -64
- package/dist/utils/common.d.ts +1 -50
- package/dist/utils/common.js +1 -229
- package/dist/utils/csv.d.ts +1 -15
- package/dist/utils/csv.js +1 -80
- package/dist/utils/datatype.d.ts +1 -49
- package/dist/utils/datatype.js +1 -222
- package/dist/utils/icons.d.ts +1 -11
- package/dist/utils/icons.js +1 -72
- package/dist/utils/index.d.ts +1 -4
- package/dist/utils/index.js +1 -21
- package/dist/utils/logger.d.ts +1 -10
- package/dist/utils/logger.js +1 -40
- package/dist/views/layouts/main.hbs +8 -5
- package/dist/views/pages/databases.hbs +0 -18
- package/dist/views/pages/designer.hbs +22 -17
- package/dist/views/pages/error.hbs +0 -2
- package/dist/views/pages/form.hbs +0 -5
- package/dist/views/pages/home.hbs +0 -8
- package/dist/views/pages/login.hbs +0 -13
- package/dist/views/pages/query.hbs +0 -9
- package/dist/views/pages/schema.hbs +0 -20
- package/dist/views/pages/seed.hbs +0 -31
- 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 +0 -8
- package/docs/API.md +144 -0
- package/docs/EXAMPLES.md +576 -0
- package/docs/SECURITY.md +265 -0
- package/package.json +7 -2
- 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/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
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|

|
|
22
22
|
|
|
23
|
-
> 📸 **Visual Tour:** See [`docs/screenshots/`](docs/screenshots/) for screenshots of the Table Browser,
|
|
23
|
+
> 📸 **Visual Tour:** See [`docs/screenshots/`](docs/screenshots/) for screenshots of the Table Browser, Inline Grid Editor, Read-Only Mode, Query Workbench, Visual Schema Designer, Universal Data Inspector, Mock Data Seeder, and Multi-Database Manager.
|
|
24
24
|
|
|
25
25
|
---
|
|
26
26
|
|
|
@@ -73,7 +73,7 @@ admindb
|
|
|
73
73
|
AdminDB features a clean, colorized CLI startup banner and streamlined, low-noise runtime logging:
|
|
74
74
|
|
|
75
75
|
```text
|
|
76
|
-
⚡ AdminDB v2.
|
|
76
|
+
⚡ AdminDB v2.1.1
|
|
77
77
|
|
|
78
78
|
➜ Local: http://localhost:45531/
|
|
79
79
|
➜ Network: http://192.168.1.15:45531/
|
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
|
@@ -1,257 +1 @@
|
|
|
1
|
-
"use
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.createRouter = createRouter;
|
|
7
|
-
const express_1 = __importDefault(require("express"));
|
|
8
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
-
const express_handlebars_1 = require("express-handlebars");
|
|
10
|
-
const database_1 = require("./db/database");
|
|
11
|
-
const postgres_1 = require("./db/postgres");
|
|
12
|
-
const logger_1 = require("./utils/logger");
|
|
13
|
-
const pages_1 = require("./routes/pages");
|
|
14
|
-
const index_1 = require("./routes/api/index");
|
|
15
|
-
const databases_1 = require("./routes/databases");
|
|
16
|
-
const args_1 = require("./cli/args");
|
|
17
|
-
const common_1 = require("./utils/common");
|
|
18
|
-
const icons_1 = require("./utils/icons");
|
|
19
|
-
const serverless_1 = require("./serverless");
|
|
20
|
-
const auth_1 = require("./auth");
|
|
21
|
-
/** Installed package version, exposed to every template as `{{version}}`. */
|
|
22
|
-
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
|
-
function createRouter(options = {}) {
|
|
37
|
-
return options.manager ? createManagerApp(options) : createSingleDbApp(options);
|
|
38
|
-
}
|
|
39
|
-
function setupViewEngine(app, _logger) {
|
|
40
|
-
app.engine('hbs', (0, express_handlebars_1.engine)({
|
|
41
|
-
extname: '.hbs',
|
|
42
|
-
defaultLayout: 'main',
|
|
43
|
-
layoutsDir: node_path_1.default.join(__dirname, 'views', 'layouts'),
|
|
44
|
-
partialsDir: node_path_1.default.join(__dirname, 'views', 'partials'),
|
|
45
|
-
helpers: {
|
|
46
|
-
eq: (a, b) => a === b,
|
|
47
|
-
startsWith: (a, b) => String(a ?? '').startsWith(String(b ?? '')),
|
|
48
|
-
isNull: (v) => v === null || v === undefined,
|
|
49
|
-
truncate: (v, n) => {
|
|
50
|
-
const s = v == null ? '' : String(v);
|
|
51
|
-
return s.length > n ? `${s.slice(0, n)}…` : s;
|
|
52
|
-
},
|
|
53
|
-
json: (v) => (JSON.stringify(v ?? null) ?? 'null').replace(/</g, '\\u003c'),
|
|
54
|
-
add: (a, b) => Number(a) + Number(b),
|
|
55
|
-
sub: (a, b) => Number(a) - Number(b),
|
|
56
|
-
gt: (a, b) => Number(a) > Number(b),
|
|
57
|
-
lt: (a, b) => Number(a) < Number(b),
|
|
58
|
-
join: (arr, sep) => (Array.isArray(arr) ? arr.join(String(sep ?? ',')) : String(arr ?? '')),
|
|
59
|
-
icon: (name, options) => {
|
|
60
|
-
const customClass = (options && options.hash && options.hash.class) || undefined;
|
|
61
|
-
return (0, icons_1.renderIcon)(String(name ?? ''), customClass);
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
}));
|
|
65
|
-
app.set('view engine', 'hbs');
|
|
66
|
-
app.set('views', node_path_1.default.join(__dirname, 'views'));
|
|
67
|
-
}
|
|
68
|
-
function addErrorHandlers(app, logger) {
|
|
69
|
-
// 404 handler.
|
|
70
|
-
app.use((req, res) => {
|
|
71
|
-
if (req.path.startsWith('/api/')) {
|
|
72
|
-
return res.status(404).json({ success: false, error: 'Not found.' });
|
|
73
|
-
}
|
|
74
|
-
res.status(404).render('pages/error', { title: 'Not found', status: 404, error: 'The page you requested does not exist.' });
|
|
75
|
-
});
|
|
76
|
-
// Central error handler.
|
|
77
|
-
app.use((err, req, res, _next) => {
|
|
78
|
-
logger.error('Unhandled error', err);
|
|
79
|
-
if (req.path.startsWith('/api/')) {
|
|
80
|
-
return res.status(500).json({ success: false, error: (0, common_1.errorMessage)(err) });
|
|
81
|
-
}
|
|
82
|
-
res.status(500).render('pages/error', { title: 'Error', status: 500, error: (0, common_1.errorMessage)(err) });
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
/** Single-database app (also used as the per-database app in multi-db mode). */
|
|
86
|
-
function createSingleDbApp(options) {
|
|
87
|
-
const isServerless = options.serverless !== undefined ? options.serverless : (0, serverless_1.isServerlessEnvironment)();
|
|
88
|
-
const rawTarget = options.connection || options.dbPath || '';
|
|
89
|
-
const isPg = Boolean(options.pgOptions) ||
|
|
90
|
-
(typeof rawTarget === 'string' && (rawTarget.startsWith('postgres://') || rawTarget.startsWith('postgresql://'))) ||
|
|
91
|
-
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
|
-
const readonly = options.readonly !== undefined
|
|
95
|
-
? Boolean(options.readonly)
|
|
96
|
-
: (isServerless && !isPg);
|
|
97
|
-
const basePath = (options.basePath ?? '').replace(/\/+$/, '');
|
|
98
|
-
const logger = options.logger ?? (0, logger_1.createLogger)(options.logLevel ?? 'info', 'admindb');
|
|
99
|
-
let db;
|
|
100
|
-
if (options.db) {
|
|
101
|
-
db = options.db;
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
if (isPg) {
|
|
105
|
-
const conn = options.pgOptions || rawTarget;
|
|
106
|
-
db = new postgres_1.PostgresDatabase(conn, logger, { readonly });
|
|
107
|
-
}
|
|
108
|
-
else {
|
|
109
|
-
db = new database_1.SqliteDatabase(options.dbPath ?? 'admindb.db', logger, { readonly });
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
const databasesUrl = options.databasesUrl;
|
|
113
|
-
const authConfig = (0, auth_1.resolveAuthConfig)(options.auth);
|
|
114
|
-
const router = (0, express_1.default)();
|
|
115
|
-
router.disable('x-powered-by');
|
|
116
|
-
// Static assets (relative to the mount point, so a prefix "just works").
|
|
117
|
-
router.use(express_1.default.static(node_path_1.default.join(__dirname, 'public')));
|
|
118
|
-
setupViewEngine(router, logger);
|
|
119
|
-
router.use(express_1.default.json({ limit: '10mb' }));
|
|
120
|
-
router.use(express_1.default.urlencoded({ extended: false }));
|
|
121
|
-
// Register authentication endpoints (/login, /logout)
|
|
122
|
-
(0, auth_1.registerAuthRoutes)(router, authConfig, basePath, logger);
|
|
123
|
-
// Authentication gatekeeper middleware
|
|
124
|
-
router.use((0, auth_1.createAuthMiddleware)(authConfig, basePath, logger));
|
|
125
|
-
// Page locals (skipped for API + static paths).
|
|
126
|
-
router.use(async (req, res, next) => {
|
|
127
|
-
try {
|
|
128
|
-
res.locals.basePath = basePath;
|
|
129
|
-
res.locals.currentPath = req.path;
|
|
130
|
-
res.locals.currentTable = null;
|
|
131
|
-
res.locals.dbPath = db.path;
|
|
132
|
-
res.locals.dbId = options.dbId ?? (db.dialect === 'postgres' ? 'PostgreSQL' : (node_path_1.default.basename(db.path) || 'SQLite'));
|
|
133
|
-
res.locals.dialect = db.dialect;
|
|
134
|
-
res.locals.isPostgres = db.dialect === 'postgres';
|
|
135
|
-
res.locals.isSqlite = db.dialect === 'sqlite';
|
|
136
|
-
res.locals.dialectName = db.dialect === 'postgres' ? 'PostgreSQL' : 'SQLite';
|
|
137
|
-
res.locals.databasesUrl = databasesUrl ?? null;
|
|
138
|
-
res.locals.databasesMode = false;
|
|
139
|
-
res.locals.readonly = db.isReadOnly;
|
|
140
|
-
res.locals.serverless = isServerless;
|
|
141
|
-
res.locals.version = APP_VERSION;
|
|
142
|
-
res.locals.authEnabled = authConfig.enabled;
|
|
143
|
-
res.locals.isDefaultPassword = authConfig.isDefaultPassword;
|
|
144
|
-
if (!req.path.startsWith('/api/')) {
|
|
145
|
-
const all = await db.listTables();
|
|
146
|
-
const names = (all.data ?? []).map((t) => t.name);
|
|
147
|
-
res.locals.tables = names.filter((n) => !n.startsWith('_'));
|
|
148
|
-
res.locals.internalTables = names.filter((n) => n.startsWith('_'));
|
|
149
|
-
}
|
|
150
|
-
next();
|
|
151
|
-
}
|
|
152
|
-
catch (err) {
|
|
153
|
-
next(err);
|
|
154
|
-
}
|
|
155
|
-
});
|
|
156
|
-
(0, pages_1.registerPages)(router, { db, logger });
|
|
157
|
-
(0, index_1.registerApi)(router, { db, logger });
|
|
158
|
-
addErrorHandlers(router, logger);
|
|
159
|
-
return router;
|
|
160
|
-
}
|
|
161
|
-
/** Multi-database app: databases landing page + a per-database sub-app per file. */
|
|
162
|
-
function createManagerApp(options) {
|
|
163
|
-
const isServerless = options.serverless !== undefined ? options.serverless : (0, serverless_1.isServerlessEnvironment)();
|
|
164
|
-
const readonly = isServerless || Boolean(options.readonly);
|
|
165
|
-
const manager = options.manager;
|
|
166
|
-
const basePath = (options.basePath ?? '').replace(/\/+$/, '');
|
|
167
|
-
const logger = options.logger ?? (0, logger_1.createLogger)(options.logLevel ?? 'info', 'admindb');
|
|
168
|
-
const authConfig = (0, auth_1.resolveAuthConfig)(options.auth);
|
|
169
|
-
const app = (0, express_1.default)();
|
|
170
|
-
app.disable('x-powered-by');
|
|
171
|
-
app.use(express_1.default.static(node_path_1.default.join(__dirname, 'public')));
|
|
172
|
-
setupViewEngine(app, logger);
|
|
173
|
-
app.use(express_1.default.json({ limit: '10mb' }));
|
|
174
|
-
app.use(express_1.default.urlencoded({ extended: false }));
|
|
175
|
-
// Register authentication endpoints (/login, /logout)
|
|
176
|
-
(0, auth_1.registerAuthRoutes)(app, authConfig, basePath, logger);
|
|
177
|
-
// Authentication gatekeeper middleware
|
|
178
|
-
app.use((0, auth_1.createAuthMiddleware)(authConfig, basePath, logger));
|
|
179
|
-
// Locals for the databases landing page.
|
|
180
|
-
app.use(async (req, res, next) => {
|
|
181
|
-
try {
|
|
182
|
-
res.locals.basePath = basePath;
|
|
183
|
-
res.locals.currentPath = req.path;
|
|
184
|
-
res.locals.currentTable = null;
|
|
185
|
-
res.locals.dbPath = manager.directory;
|
|
186
|
-
res.locals.dbId = null;
|
|
187
|
-
res.locals.databasesUrl = null;
|
|
188
|
-
res.locals.databasesMode = true;
|
|
189
|
-
res.locals.readonly = readonly;
|
|
190
|
-
res.locals.serverless = isServerless;
|
|
191
|
-
res.locals.version = APP_VERSION;
|
|
192
|
-
res.locals.authEnabled = authConfig.enabled;
|
|
193
|
-
res.locals.isDefaultPassword = authConfig.isDefaultPassword;
|
|
194
|
-
if (!req.path.startsWith('/api/')) {
|
|
195
|
-
res.locals.tables = [];
|
|
196
|
-
res.locals.internalTables = [];
|
|
197
|
-
}
|
|
198
|
-
next();
|
|
199
|
-
}
|
|
200
|
-
catch (err) {
|
|
201
|
-
next(err);
|
|
202
|
-
}
|
|
203
|
-
});
|
|
204
|
-
// Per-database dispatch: mount a cached single-db app at /:dbId.
|
|
205
|
-
const subApps = new Map();
|
|
206
|
-
(0, databases_1.registerDatabasesRoutes)(app, {
|
|
207
|
-
manager,
|
|
208
|
-
logger,
|
|
209
|
-
basePath,
|
|
210
|
-
readonly,
|
|
211
|
-
allowBrowse: isServerless ? false : options.allowBrowse,
|
|
212
|
-
browseRoot: options.browseRoot,
|
|
213
|
-
invalidate: (id) => subApps.delete(id),
|
|
214
|
-
});
|
|
215
|
-
app.use('/:dbId', (req, res, next) => {
|
|
216
|
-
const dbId = String(req.params.dbId);
|
|
217
|
-
if (!manager.has(dbId)) {
|
|
218
|
-
if (req.path.startsWith('/api/')) {
|
|
219
|
-
return res.status(404).json({ success: false, error: `Database "${dbId}" does not exist.` });
|
|
220
|
-
}
|
|
221
|
-
return res.status(404).render('pages/error', {
|
|
222
|
-
title: 'Not found',
|
|
223
|
-
status: 404,
|
|
224
|
-
error: `Database "${dbId}" does not exist.`,
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
const rawRo = req.query.readonly;
|
|
228
|
-
if (rawRo !== undefined) {
|
|
229
|
-
const explicitRo = rawRo === '1' || rawRo === 'true';
|
|
230
|
-
if (!readonly || explicitRo) {
|
|
231
|
-
manager.setReadonly(dbId, explicitRo);
|
|
232
|
-
subApps.delete(dbId);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
let sub = subApps.get(dbId);
|
|
236
|
-
if (!sub) {
|
|
237
|
-
const subBase = `${basePath ? basePath : ''}/${encodeURIComponent(dbId)}`;
|
|
238
|
-
const effectiveRo = readonly || manager.isDbReadOnly(dbId);
|
|
239
|
-
sub = createSingleDbApp({
|
|
240
|
-
db: manager.open(dbId, effectiveRo),
|
|
241
|
-
basePath: subBase,
|
|
242
|
-
logger,
|
|
243
|
-
logLevel: options.logLevel,
|
|
244
|
-
dbId,
|
|
245
|
-
databasesUrl: `${basePath ? basePath : ''}/`,
|
|
246
|
-
readonly: effectiveRo,
|
|
247
|
-
serverless: isServerless,
|
|
248
|
-
auth: authConfig,
|
|
249
|
-
});
|
|
250
|
-
subApps.set(dbId, sub);
|
|
251
|
-
}
|
|
252
|
-
return sub(req, res, next);
|
|
253
|
-
});
|
|
254
|
-
addErrorHandlers(app, logger);
|
|
255
|
-
return app;
|
|
256
|
-
}
|
|
257
|
-
//# sourceMappingURL=app.js.map
|
|
1
|
+
var __importDefault=this&&this.__importDefault||function(mod){return mod&&mod.__esModule?mod:{default:mod}};Object.defineProperty(exports,"__esModule",{value:!0}),exports.createRouter=function(options={}){return options.manager?function(options){const isServerless=void 0!==options.serverless?options.serverless:(0,serverless_1.isServerlessEnvironment)(),readonly=isServerless||Boolean(options.readonly),manager=options.manager,basePath=(options.basePath??"").replace(/\/+$/,""),logger=options.logger??(0,logger_1.createLogger)(options.logLevel??"info","admindb"),authConfig=(0,auth_1.resolveAuthConfig)(options.auth),app=(0,express_1.default)();app.disable("x-powered-by"),app.use(express_1.default.static(node_path_1.default.join(__dirname,"public"))),setupViewEngine(app,logger),app.use(express_1.default.json({limit:"10mb"})),app.use(express_1.default.urlencoded({extended:!1})),(0,auth_1.registerAuthRoutes)(app,authConfig,basePath,logger),app.use((0,auth_1.createAuthMiddleware)(authConfig,basePath,logger)),app.use(async(req,res,next)=>{try{res.locals.basePath=basePath,res.locals.currentPath=req.path,res.locals.currentTable=null,res.locals.dbPath=manager.directory,res.locals.dbId=null,res.locals.databasesUrl=null,res.locals.databasesMode=!0,res.locals.readonly=readonly,res.locals.serverless=isServerless,res.locals.version=APP_VERSION,res.locals.authEnabled=authConfig.enabled,res.locals.isDefaultPassword=authConfig.isDefaultPassword,req.path.startsWith("/api/")||(res.locals.tables=[],res.locals.internalTables=[]),next()}catch(err){next(err)}});const subApps=new Map;return(0,databases_1.registerDatabasesRoutes)(app,{manager:manager,logger:logger,basePath:basePath,readonly:readonly,allowBrowse:!isServerless&&options.allowBrowse,browseRoot:options.browseRoot,invalidate:id=>subApps.delete(id)}),app.use("/:dbId",(req,res,next)=>{const dbId=String(req.params.dbId);if(!manager.has(dbId))return req.path.startsWith("/api/")?res.status(404).json({success:!1,error:`Database "${dbId}" does not exist.`}):res.status(404).render("pages/error",{title:"Not found",status:404,error:`Database "${dbId}" does not exist.`});const rawRo=req.query.readonly;if(void 0!==rawRo){const explicitRo="1"===rawRo||"true"===rawRo;readonly&&!explicitRo||(manager.setReadonly(dbId,explicitRo),subApps.delete(dbId))}let sub=subApps.get(dbId);if(!sub){const subBase=`${basePath||""}/${encodeURIComponent(dbId)}`,effectiveRo=readonly||manager.isDbReadOnly(dbId);sub=createSingleDbApp({db:manager.open(dbId,effectiveRo),basePath:subBase,logger:logger,logLevel:options.logLevel,dbId:dbId,databasesUrl:`${basePath||""}/`,readonly:effectiveRo,serverless:isServerless,auth:authConfig}),subApps.set(dbId,sub)}return sub(req,res,next)}),addErrorHandlers(app,logger),app}(options):createSingleDbApp(options)};const express_1=__importDefault(require("express")),node_path_1=__importDefault(require("node:path")),express_handlebars_1=require("express-handlebars"),database_1=require("./db/database"),postgres_1=require("./db/postgres"),logger_1=require("./utils/logger"),pages_1=require("./routes/pages"),index_1=require("./routes/api/index"),databases_1=require("./routes/databases"),args_1=require("./cli/args"),common_1=require("./utils/common"),icons_1=require("./utils/icons"),serverless_1=require("./serverless"),auth_1=require("./auth"),APP_VERSION=(0,args_1.getPackageVersion)();function setupViewEngine(app,_logger){app.engine("hbs",(0,express_handlebars_1.engine)({extname:".hbs",defaultLayout:"main",layoutsDir:node_path_1.default.join(__dirname,"views","layouts"),partialsDir:node_path_1.default.join(__dirname,"views","partials"),helpers:{eq:(a,b)=>a===b,startsWith:(a,b)=>String(a??"").startsWith(String(b??"")),isNull:v=>null==v,truncate:(v,n)=>{const s=null==v?"":String(v);return s.length>n?`${s.slice(0,n)}…`:s},json:v=>(JSON.stringify(v??null)??"null").replace(/</g,"\\u003c"),add:(a,b)=>Number(a)+Number(b),sub:(a,b)=>Number(a)-Number(b),gt:(a,b)=>Number(a)>Number(b),lt:(a,b)=>Number(a)<Number(b),join:(arr,sep)=>Array.isArray(arr)?arr.join(String(sep??",")):String(arr??""),icon:(name,options)=>{const customClass=options&&options.hash&&options.hash.class||void 0;return(0,icons_1.renderIcon)(String(name??""),customClass)}}})),app.set("view engine","hbs"),app.set("views",node_path_1.default.join(__dirname,"views"))}function addErrorHandlers(app,logger){app.use((req,res)=>{if(req.path.startsWith("/api/"))return res.status(404).json({success:!1,error:"Not found."});res.status(404).render("pages/error",{title:"Not found",status:404,error:"The page you requested does not exist."})}),app.use((err,req,res,_next)=>{if(logger.error("Unhandled error",err),req.path.startsWith("/api/"))return res.status(500).json({success:!1,error:(0,common_1.errorMessage)(err)});res.status(500).render("pages/error",{title:"Error",status:500,error:(0,common_1.errorMessage)(err)})})}function createSingleDbApp(options){const isServerless=void 0!==options.serverless?options.serverless:(0,serverless_1.isServerlessEnvironment)(),rawTarget=options.connection||options.dbPath||"",isPg=Boolean(options.pgOptions)||"string"==typeof rawTarget&&(rawTarget.startsWith("postgres://")||rawTarget.startsWith("postgresql://"))||"postgres"===options.db?.dialect,readonly=void 0!==options.readonly?Boolean(options.readonly):isServerless&&!isPg,basePath=(options.basePath??"").replace(/\/+$/,""),logger=options.logger??(0,logger_1.createLogger)(options.logLevel??"info","admindb");let db;if(options.db)db=options.db;else if(isPg){const conn=options.pgOptions||rawTarget;db=new postgres_1.PostgresDatabase(conn,logger,{readonly:readonly})}else db=new database_1.SqliteDatabase(options.dbPath??"admindb.db",logger,{readonly:readonly});const databasesUrl=options.databasesUrl,authConfig=(0,auth_1.resolveAuthConfig)(options.auth),router=(0,express_1.default)();return router.disable("x-powered-by"),router.use(express_1.default.static(node_path_1.default.join(__dirname,"public"))),setupViewEngine(router),router.use(express_1.default.json({limit:"10mb"})),router.use(express_1.default.urlencoded({extended:!1})),(0,auth_1.registerAuthRoutes)(router,authConfig,basePath,logger),router.use((0,auth_1.createAuthMiddleware)(authConfig,basePath,logger)),router.use(async(req,res,next)=>{try{if(res.locals.basePath=basePath,res.locals.currentPath=req.path,res.locals.currentTable=null,res.locals.dbPath=db.path,res.locals.dbId=options.dbId??("postgres"===db.dialect?"PostgreSQL":node_path_1.default.basename(db.path)||"SQLite"),res.locals.dialect=db.dialect,res.locals.isPostgres="postgres"===db.dialect,res.locals.isSqlite="sqlite"===db.dialect,res.locals.dialectName="postgres"===db.dialect?"PostgreSQL":"SQLite",res.locals.databasesUrl=databasesUrl??null,res.locals.databasesMode=!1,res.locals.readonly=db.isReadOnly,res.locals.serverless=isServerless,res.locals.version=APP_VERSION,res.locals.authEnabled=authConfig.enabled,res.locals.isDefaultPassword=authConfig.isDefaultPassword,!req.path.startsWith("/api/")){const names=((await db.listTables()).data??[]).map(t=>t.name);res.locals.tables=names.filter(n=>!n.startsWith("_")),res.locals.internalTables=names.filter(n=>n.startsWith("_"))}next()}catch(err){next(err)}}),(0,pages_1.registerPages)(router,{db:db,logger:logger}),(0,index_1.registerApi)(router,{db:db,logger:logger}),addErrorHandlers(router,logger),router}
|
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
|
@@ -1,147 +1 @@
|
|
|
1
|
-
"
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.resetPersistentSecretCache = resetPersistentSecretCache;
|
|
7
|
-
exports.getOrCreatePersistentSecret = getOrCreatePersistentSecret;
|
|
8
|
-
exports.resolveAuthConfig = resolveAuthConfig;
|
|
9
|
-
const node_crypto_1 = require("node:crypto");
|
|
10
|
-
const node_fs_1 = require("node:fs");
|
|
11
|
-
const node_os_1 = __importDefault(require("node:os"));
|
|
12
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
13
|
-
const types_1 = require("./types");
|
|
14
|
-
// In-memory cache of resolved secret to avoid repeated disk reads per process
|
|
15
|
-
let cachedFallbackSecret = null;
|
|
16
|
-
/**
|
|
17
|
-
* Clears the in-memory fallback secret cache (primarily for testing).
|
|
18
|
-
*/
|
|
19
|
-
function resetPersistentSecretCache() {
|
|
20
|
-
cachedFallbackSecret = null;
|
|
21
|
-
}
|
|
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
|
-
function getOrCreatePersistentSecret(env = process.env) {
|
|
32
|
-
const envSecret = env.ADMINDB_SECRET ?? env.SESSION_SECRET;
|
|
33
|
-
if (envSecret && String(envSecret).trim()) {
|
|
34
|
-
return String(envSecret).trim();
|
|
35
|
-
}
|
|
36
|
-
// If a custom secret file is specified, don't use default home cache
|
|
37
|
-
const customSecretFile = env.ADMINDB_SECRET_FILE ? String(env.ADMINDB_SECRET_FILE).trim() : undefined;
|
|
38
|
-
if (!customSecretFile && cachedFallbackSecret) {
|
|
39
|
-
return cachedFallbackSecret;
|
|
40
|
-
}
|
|
41
|
-
const candidatePaths = [];
|
|
42
|
-
if (customSecretFile) {
|
|
43
|
-
candidatePaths.push(node_path_1.default.resolve(customSecretFile));
|
|
44
|
-
}
|
|
45
|
-
else {
|
|
46
|
-
try {
|
|
47
|
-
const home = node_os_1.default.homedir();
|
|
48
|
-
if (home) {
|
|
49
|
-
candidatePaths.push(node_path_1.default.join(home, '.admindb-secret'));
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
catch {
|
|
53
|
-
/* ignore */
|
|
54
|
-
}
|
|
55
|
-
try {
|
|
56
|
-
const tmp = node_os_1.default.tmpdir();
|
|
57
|
-
if (tmp) {
|
|
58
|
-
candidatePaths.push(node_path_1.default.join(tmp, '.admindb-secret'));
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
catch {
|
|
62
|
-
/* ignore */
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
// 1. Try reading existing secret from candidate paths
|
|
66
|
-
for (const filePath of candidatePaths) {
|
|
67
|
-
try {
|
|
68
|
-
if ((0, node_fs_1.existsSync)(filePath)) {
|
|
69
|
-
const content = (0, node_fs_1.readFileSync)(filePath, 'utf8').trim();
|
|
70
|
-
if (content.length >= 16) {
|
|
71
|
-
if (!customSecretFile) {
|
|
72
|
-
cachedFallbackSecret = content;
|
|
73
|
-
}
|
|
74
|
-
return content;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
catch {
|
|
79
|
-
// Continue to next candidate or write attempt
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
// 2. Generate new secret and attempt writing to the first writable candidate path
|
|
83
|
-
const newSecret = (0, node_crypto_1.randomBytes)(32).toString('hex');
|
|
84
|
-
for (const filePath of candidatePaths) {
|
|
85
|
-
try {
|
|
86
|
-
const dir = node_path_1.default.dirname(filePath);
|
|
87
|
-
if (!(0, node_fs_1.existsSync)(dir)) {
|
|
88
|
-
(0, node_fs_1.mkdirSync)(dir, { recursive: true });
|
|
89
|
-
}
|
|
90
|
-
(0, node_fs_1.writeFileSync)(filePath, newSecret, { encoding: 'utf8', mode: 0o600 });
|
|
91
|
-
if (!customSecretFile) {
|
|
92
|
-
cachedFallbackSecret = newSecret;
|
|
93
|
-
}
|
|
94
|
-
return newSecret;
|
|
95
|
-
}
|
|
96
|
-
catch {
|
|
97
|
-
// Continue to next candidate if write fails
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
// 3. Last-resort fallback to in-memory secret if disk is not writable
|
|
101
|
-
if (!customSecretFile) {
|
|
102
|
-
cachedFallbackSecret = newSecret;
|
|
103
|
-
}
|
|
104
|
-
return newSecret;
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Resolves the authentication options against environment variables.
|
|
108
|
-
*/
|
|
109
|
-
function resolveAuthConfig(optionsAuth, env = process.env) {
|
|
110
|
-
// Check if explicitly disabled via options or environment variable
|
|
111
|
-
const envDisabled = ['0', 'false', 'no', 'off'].includes(String(env.ADMINDB_AUTH ?? '').trim().toLowerCase()) ||
|
|
112
|
-
['1', 'true', 'yes'].includes(String(env.ADMINDB_NO_AUTH ?? env.ADMINDB_DISABLE_AUTH ?? '').trim().toLowerCase());
|
|
113
|
-
let enabled = true;
|
|
114
|
-
let username = (env.ADMINDB_USERNAME ?? env.ADMINDB_USER) ? String(env.ADMINDB_USERNAME ?? env.ADMINDB_USER).trim() : types_1.DEFAULT_USERNAME;
|
|
115
|
-
let password = (env.ADMINDB_PASSWORD ?? env.ADMINDB_PASS) ? String(env.ADMINDB_PASSWORD ?? env.ADMINDB_PASS).trim() : types_1.DEFAULT_PASSWORD_HASH;
|
|
116
|
-
let secret = (env.ADMINDB_SECRET ?? env.SESSION_SECRET)
|
|
117
|
-
? String(env.ADMINDB_SECRET ?? env.SESSION_SECRET).trim()
|
|
118
|
-
: getOrCreatePersistentSecret(env);
|
|
119
|
-
let sessionDurationMs = types_1.DEFAULT_SESSION_DURATION_MS;
|
|
120
|
-
if (optionsAuth === false || envDisabled) {
|
|
121
|
-
enabled = false;
|
|
122
|
-
}
|
|
123
|
-
else if (typeof optionsAuth === 'object' && optionsAuth !== null) {
|
|
124
|
-
if (optionsAuth.enabled === false)
|
|
125
|
-
enabled = false;
|
|
126
|
-
if (optionsAuth.username !== undefined)
|
|
127
|
-
username = String(optionsAuth.username).trim();
|
|
128
|
-
if (optionsAuth.password !== undefined)
|
|
129
|
-
password = String(optionsAuth.password).trim();
|
|
130
|
-
if (optionsAuth.secret !== undefined && String(optionsAuth.secret).trim()) {
|
|
131
|
-
secret = String(optionsAuth.secret).trim();
|
|
132
|
-
}
|
|
133
|
-
if (optionsAuth.sessionDurationMs !== undefined)
|
|
134
|
-
sessionDurationMs = optionsAuth.sessionDurationMs;
|
|
135
|
-
}
|
|
136
|
-
const isDefaultPassword = username === types_1.DEFAULT_USERNAME &&
|
|
137
|
-
(password === types_1.DEFAULT_PASSWORD_HASH || password === 'admin');
|
|
138
|
-
return {
|
|
139
|
-
enabled,
|
|
140
|
-
username,
|
|
141
|
-
password,
|
|
142
|
-
secret,
|
|
143
|
-
sessionDurationMs,
|
|
144
|
-
isDefaultPassword,
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
//# sourceMappingURL=config.js.map
|
|
1
|
+
var __importDefault=this&&this.__importDefault||function(mod){return mod&&mod.__esModule?mod:{default:mod}};Object.defineProperty(exports,"__esModule",{value:!0}),exports.resetPersistentSecretCache=function(){cachedFallbackSecret=null},exports.getOrCreatePersistentSecret=getOrCreatePersistentSecret,exports.resolveAuthConfig=function(optionsAuth,env=process.env){const envDisabled=["0","false","no","off"].includes(String(env.ADMINDB_AUTH??"").trim().toLowerCase())||["1","true","yes"].includes(String(env.ADMINDB_NO_AUTH??env.ADMINDB_DISABLE_AUTH??"").trim().toLowerCase());let enabled=!0,username=env.ADMINDB_USERNAME??env.ADMINDB_USER?String(env.ADMINDB_USERNAME??env.ADMINDB_USER).trim():types_1.DEFAULT_USERNAME,password=env.ADMINDB_PASSWORD??env.ADMINDB_PASS?String(env.ADMINDB_PASSWORD??env.ADMINDB_PASS).trim():types_1.DEFAULT_PASSWORD_HASH,secret=env.ADMINDB_SECRET??env.SESSION_SECRET?String(env.ADMINDB_SECRET??env.SESSION_SECRET).trim():getOrCreatePersistentSecret(env),sessionDurationMs=types_1.DEFAULT_SESSION_DURATION_MS;!1===optionsAuth||envDisabled?enabled=!1:"object"==typeof optionsAuth&&null!==optionsAuth&&(!1===optionsAuth.enabled&&(enabled=!1),void 0!==optionsAuth.username&&(username=String(optionsAuth.username).trim()),void 0!==optionsAuth.password&&(password=String(optionsAuth.password).trim()),void 0!==optionsAuth.secret&&String(optionsAuth.secret).trim()&&(secret=String(optionsAuth.secret).trim()),void 0!==optionsAuth.sessionDurationMs&&(sessionDurationMs=optionsAuth.sessionDurationMs));const isDefaultPassword=username===types_1.DEFAULT_USERNAME&&(password===types_1.DEFAULT_PASSWORD_HASH||"admin"===password);return{enabled:enabled,username:username,password:password,secret:secret,sessionDurationMs:sessionDurationMs,isDefaultPassword:isDefaultPassword}};const node_crypto_1=require("node:crypto"),node_fs_1=require("node:fs"),node_os_1=__importDefault(require("node:os")),node_path_1=__importDefault(require("node:path")),types_1=require("./types");let cachedFallbackSecret=null;function getOrCreatePersistentSecret(env=process.env){const envSecret=env.ADMINDB_SECRET??env.SESSION_SECRET;if(envSecret&&String(envSecret).trim())return String(envSecret).trim();const customSecretFile=env.ADMINDB_SECRET_FILE?String(env.ADMINDB_SECRET_FILE).trim():void 0;if(!customSecretFile&&cachedFallbackSecret)return cachedFallbackSecret;const candidatePaths=[];if(customSecretFile)candidatePaths.push(node_path_1.default.resolve(customSecretFile));else{try{const home=node_os_1.default.homedir();home&&candidatePaths.push(node_path_1.default.join(home,".admindb-secret"))}catch{}try{const tmp=node_os_1.default.tmpdir();tmp&&candidatePaths.push(node_path_1.default.join(tmp,".admindb-secret"))}catch{}}for(const filePath of candidatePaths)try{if((0,node_fs_1.existsSync)(filePath)){const content=(0,node_fs_1.readFileSync)(filePath,"utf8").trim();if(content.length>=16)return customSecretFile||(cachedFallbackSecret=content),content}}catch{}const newSecret=(0,node_crypto_1.randomBytes)(32).toString("hex");for(const filePath of candidatePaths)try{const dir=node_path_1.default.dirname(filePath);return(0,node_fs_1.existsSync)(dir)||(0,node_fs_1.mkdirSync)(dir,{recursive:!0}),(0,node_fs_1.writeFileSync)(filePath,newSecret,{encoding:"utf8",mode:384}),customSecretFile||(cachedFallbackSecret=newSecret),newSecret}catch{}return customSecretFile||(cachedFallbackSecret=newSecret),newSecret}
|
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;
|