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.
Files changed (191) hide show
  1. package/README.md +41 -12
  2. package/dist/app.d.ts +1 -69
  3. package/dist/app.js +3 -30
  4. package/dist/auth/config.d.ts +1 -19
  5. package/dist/auth/config.js +0 -26
  6. package/dist/auth/crypto.d.ts +1 -27
  7. package/dist/auth/crypto.js +1 -26
  8. package/dist/auth/index.d.ts +1 -5
  9. package/dist/auth/index.js +0 -1
  10. package/dist/auth/middleware.d.ts +1 -14
  11. package/dist/auth/middleware.js +0 -15
  12. package/dist/auth/routes.d.ts +1 -7
  13. package/dist/auth/routes.js +0 -8
  14. package/dist/auth/types.d.ts +1 -35
  15. package/dist/auth/types.js +1 -5
  16. package/dist/cli/args.d.ts +1 -45
  17. package/dist/cli/args.js +3 -11
  18. package/dist/cli/config.d.ts +1 -56
  19. package/dist/cli/config.js +0 -5
  20. package/dist/cli/index.d.ts +1 -4
  21. package/dist/cli/index.js +0 -1
  22. package/dist/cli/runner.d.ts +1 -1
  23. package/dist/cli/runner.js +6 -17
  24. package/dist/cli.d.ts +1 -2
  25. package/dist/cli.js +0 -1
  26. package/dist/data/chain.d.ts +1 -0
  27. package/dist/data/chain.js +707 -0
  28. package/dist/data/datasets.d.ts +1 -17
  29. package/dist/data/datasets.js +0 -4
  30. package/dist/data/detector.d.ts +1 -30
  31. package/dist/data/detector.js +53 -14
  32. package/dist/data/engine.d.ts +1 -10
  33. package/dist/data/engine.js +82 -48
  34. package/dist/data/index.d.ts +1 -5
  35. package/dist/data/index.js +1 -1
  36. package/dist/data/strategies.d.ts +1 -30
  37. package/dist/data/strategies.js +4 -8
  38. package/dist/data/types.d.ts +1 -73
  39. package/dist/data/types.js +0 -3
  40. package/dist/db/database.d.ts +1 -119
  41. package/dist/db/database.js +20 -14
  42. package/dist/db/export.d.ts +1 -12
  43. package/dist/db/export.js +54 -6
  44. package/dist/db/filters.d.ts +1 -22
  45. package/dist/db/filters.js +0 -16
  46. package/dist/db/index.d.ts +1 -8
  47. package/dist/db/index.js +0 -1
  48. package/dist/db/introspection.d.ts +1 -6
  49. package/dist/db/introspection.js +0 -5
  50. package/dist/db/manager.d.ts +1 -81
  51. package/dist/db/manager.js +13 -59
  52. package/dist/db/migrations.d.ts +1 -5
  53. package/dist/db/migrations.js +2 -5
  54. package/dist/db/postgres.d.ts +1 -85
  55. package/dist/db/postgres.js +21 -31
  56. package/dist/db/types.d.ts +1 -206
  57. package/dist/db/types.js +0 -1
  58. package/dist/index.d.ts +1 -23
  59. package/dist/index.js +0 -14
  60. package/dist/public/css/app.css +1 -1
  61. package/dist/public/js/api.js +1 -47
  62. package/dist/public/js/app.js +1 -262
  63. package/dist/public/js/browse.js +1 -1371
  64. package/dist/public/js/common.js +1 -0
  65. package/dist/public/js/databases.js +1 -235
  66. package/dist/public/js/designer.js +1 -258
  67. package/dist/public/js/forms.js +1 -826
  68. package/dist/public/js/home.js +1 -0
  69. package/dist/public/js/inspector.js +1 -638
  70. package/dist/public/js/query.js +1 -322
  71. package/dist/public/js/schema.js +1 -447
  72. package/dist/public/js/seed.js +1 -1090
  73. package/dist/routes/api/erd.d.ts +1 -0
  74. package/dist/routes/api/erd.js +50 -0
  75. package/dist/routes/api/helpers.d.ts +1 -42
  76. package/dist/routes/api/helpers.js +33 -3
  77. package/dist/routes/api/import-export.d.ts +1 -3
  78. package/dist/routes/api/import-export.js +0 -2
  79. package/dist/routes/api/index.d.ts +1 -4
  80. package/dist/routes/api/index.js +15 -4
  81. package/dist/routes/api/query.d.ts +1 -3
  82. package/dist/routes/api/query.js +13 -11
  83. package/dist/routes/api/rows.d.ts +1 -3
  84. package/dist/routes/api/rows.js +0 -11
  85. package/dist/routes/api/seed.d.ts +1 -3
  86. package/dist/routes/api/seed.js +79 -8
  87. package/dist/routes/api/tables.d.ts +1 -3
  88. package/dist/routes/api/tables.js +77 -6
  89. package/dist/routes/databases.d.ts +1 -26
  90. package/dist/routes/databases.js +1 -3
  91. package/dist/routes/index.d.ts +1 -3
  92. package/dist/routes/index.js +0 -1
  93. package/dist/routes/pages.d.ts +1 -9
  94. package/dist/routes/pages.js +18 -11
  95. package/dist/serverless.d.ts +1 -40
  96. package/dist/serverless.js +3 -39
  97. package/dist/sql/classifier.d.ts +1 -19
  98. package/dist/sql/classifier.js +0 -13
  99. package/dist/sql/error-analyzer.d.ts +1 -0
  100. package/dist/sql/error-analyzer.js +550 -0
  101. package/dist/sql/generator.d.ts +1 -81
  102. package/dist/sql/generator.js +0 -29
  103. package/dist/sql/index.d.ts +1 -2
  104. package/dist/sql/index.js +0 -1
  105. package/dist/types/api.d.ts +1 -412
  106. package/dist/types/api.js +0 -7
  107. package/dist/utils/colors.d.ts +1 -40
  108. package/dist/utils/colors.js +0 -10
  109. package/dist/utils/common.d.ts +1 -50
  110. package/dist/utils/common.js +19 -23
  111. package/dist/utils/csv.d.ts +1 -15
  112. package/dist/utils/csv.js +0 -13
  113. package/dist/utils/datatype.d.ts +1 -49
  114. package/dist/utils/datatype.js +0 -41
  115. package/dist/utils/icons.d.ts +1 -11
  116. package/dist/utils/icons.js +25 -16
  117. package/dist/utils/index.d.ts +1 -4
  118. package/dist/utils/index.js +0 -1
  119. package/dist/utils/logger.d.ts +1 -10
  120. package/dist/utils/logger.js +0 -4
  121. package/dist/views/layouts/main.hbs +14 -5
  122. package/dist/views/pages/databases.hbs +0 -18
  123. package/dist/views/pages/designer.hbs +0 -10
  124. package/dist/views/pages/erd.hbs +1241 -0
  125. package/dist/views/pages/error.hbs +0 -2
  126. package/dist/views/pages/form.hbs +0 -5
  127. package/dist/views/pages/home.hbs +98 -10
  128. package/dist/views/pages/info.hbs +107 -0
  129. package/dist/views/pages/login.hbs +0 -13
  130. package/dist/views/pages/query.hbs +54 -21
  131. package/dist/views/pages/schema.hbs +0 -20
  132. package/dist/views/pages/seed.hbs +435 -202
  133. package/dist/views/pages/table.hbs +0 -14
  134. package/dist/views/partials/icon.hbs +0 -1
  135. package/dist/views/partials/navbar.hbs +0 -6
  136. package/dist/views/partials/sidebar.hbs +12 -8
  137. package/docs/API.md +6 -1
  138. package/docs/EXAMPLES.md +3 -0
  139. package/package.json +7 -5
  140. package/dist/app.js.map +0 -1
  141. package/dist/auth/config.js.map +0 -1
  142. package/dist/auth/crypto.js.map +0 -1
  143. package/dist/auth/index.js.map +0 -1
  144. package/dist/auth/middleware.js.map +0 -1
  145. package/dist/auth/routes.js.map +0 -1
  146. package/dist/auth/types.js.map +0 -1
  147. package/dist/cli/args.js.map +0 -1
  148. package/dist/cli/config.js.map +0 -1
  149. package/dist/cli/index.js.map +0 -1
  150. package/dist/cli/runner.js.map +0 -1
  151. package/dist/cli.js.map +0 -1
  152. package/dist/data/datasets.js.map +0 -1
  153. package/dist/data/detector.js.map +0 -1
  154. package/dist/data/engine.js.map +0 -1
  155. package/dist/data/index.js.map +0 -1
  156. package/dist/data/strategies.js.map +0 -1
  157. package/dist/data/types.js.map +0 -1
  158. package/dist/db/database.js.map +0 -1
  159. package/dist/db/export.js.map +0 -1
  160. package/dist/db/filters.js.map +0 -1
  161. package/dist/db/index.js.map +0 -1
  162. package/dist/db/introspection.js.map +0 -1
  163. package/dist/db/manager.js.map +0 -1
  164. package/dist/db/migrations.js.map +0 -1
  165. package/dist/db/postgres.js.map +0 -1
  166. package/dist/db/types.js.map +0 -1
  167. package/dist/index.js.map +0 -1
  168. package/dist/public/css/input.css +0 -302
  169. package/dist/public/js/icons.js +0 -51
  170. package/dist/routes/api/helpers.js.map +0 -1
  171. package/dist/routes/api/import-export.js.map +0 -1
  172. package/dist/routes/api/index.js.map +0 -1
  173. package/dist/routes/api/query.js.map +0 -1
  174. package/dist/routes/api/rows.js.map +0 -1
  175. package/dist/routes/api/seed.js.map +0 -1
  176. package/dist/routes/api/tables.js.map +0 -1
  177. package/dist/routes/databases.js.map +0 -1
  178. package/dist/routes/index.js.map +0 -1
  179. package/dist/routes/pages.js.map +0 -1
  180. package/dist/serverless.js.map +0 -1
  181. package/dist/sql/classifier.js.map +0 -1
  182. package/dist/sql/generator.js.map +0 -1
  183. package/dist/sql/index.js.map +0 -1
  184. package/dist/types/api.js.map +0 -1
  185. package/dist/utils/colors.js.map +0 -1
  186. package/dist/utils/common.js.map +0 -1
  187. package/dist/utils/csv.js.map +0 -1
  188. package/dist/utils/datatype.js.map +0 -1
  189. package/dist/utils/icons.js.map +0 -1
  190. package/dist/utils/index.js.map +0 -1
  191. 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 + Tailwind/DaisyUI; a single lightweight Express process serves pages, static assets, and the REST API.
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.0
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 be mounted directly into any existing Express application under any subpath on the same port:
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
- // Or mount AdminDB for PostgreSQL
170
- app.use('/admin-pg', createRouter({
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-pg',
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
- (typeof rawTarget === 'string' && (rawTarget.startsWith('postgres://') || rawTarget.startsWith('postgresql://'))) ||
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
@@ -1,19 +1 @@
1
- import { type AuthConfig, type ResolvedAuthConfig } from './types';
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;
@@ -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
@@ -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;
@@ -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; // Expired
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
@@ -1,5 +1 @@
1
- export * from './types';
2
- export * from './crypto';
3
- export * from './config';
4
- export * from './middleware';
5
- export * from './routes';
1
+ export * from './types';export * from './crypto';export * from './config';export * from './middleware';export * from './routes';
@@ -19,4 +19,3 @@ __exportStar(require("./crypto"), exports);
19
19
  __exportStar(require("./config"), exports);
20
20
  __exportStar(require("./middleware"), exports);
21
21
  __exportStar(require("./routes"), exports);
22
- //# sourceMappingURL=index.js.map
@@ -1,14 +1 @@
1
- import type { Request, Response, NextFunction } from 'express';
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;
@@ -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
@@ -1,7 +1 @@
1
- import type { Router } from 'express';
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;