admindb 2.1.1 → 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 (91) hide show
  1. package/README.md +41 -12
  2. package/dist/app.js +230 -1
  3. package/dist/auth/config.js +121 -1
  4. package/dist/auth/crypto.js +108 -1
  5. package/dist/auth/index.js +21 -1
  6. package/dist/auth/middleware.js +85 -1
  7. package/dist/auth/routes.js +70 -1
  8. package/dist/auth/types.js +7 -1
  9. package/dist/cli/args.js +320 -1
  10. package/dist/cli/config.js +128 -1
  11. package/dist/cli/index.js +20 -1
  12. package/dist/cli/runner.js +211 -1
  13. package/dist/cli.js +4 -1
  14. package/dist/data/chain.d.ts +1 -0
  15. package/dist/data/chain.js +707 -0
  16. package/dist/data/datasets.js +129 -1
  17. package/dist/data/detector.d.ts +1 -1
  18. package/dist/data/detector.js +511 -1
  19. package/dist/data/engine.d.ts +1 -1
  20. package/dist/data/engine.js +245 -1
  21. package/dist/data/index.d.ts +1 -1
  22. package/dist/data/index.js +22 -1
  23. package/dist/data/strategies.d.ts +1 -1
  24. package/dist/data/strategies.js +339 -1
  25. package/dist/data/types.d.ts +1 -1
  26. package/dist/data/types.js +5 -1
  27. package/dist/db/database.d.ts +1 -1
  28. package/dist/db/database.js +477 -1
  29. package/dist/db/export.d.ts +1 -1
  30. package/dist/db/export.js +90 -1
  31. package/dist/db/filters.js +142 -1
  32. package/dist/db/index.js +24 -1
  33. package/dist/db/introspection.js +167 -1
  34. package/dist/db/manager.js +351 -1
  35. package/dist/db/migrations.js +107 -1
  36. package/dist/db/postgres.d.ts +1 -1
  37. package/dist/db/postgres.js +735 -1
  38. package/dist/db/types.d.ts +1 -1
  39. package/dist/db/types.js +6 -1
  40. package/dist/index.js +52 -1
  41. package/dist/public/css/app.css +1 -1
  42. package/dist/public/js/api.js +1 -1
  43. package/dist/public/js/browse.js +1 -1
  44. package/dist/public/js/common.js +1 -0
  45. package/dist/public/js/databases.js +1 -1
  46. package/dist/public/js/designer.js +1 -1
  47. package/dist/public/js/forms.js +1 -1
  48. package/dist/public/js/home.js +1 -0
  49. package/dist/public/js/inspector.js +1 -1
  50. package/dist/public/js/query.js +1 -1
  51. package/dist/public/js/schema.js +1 -1
  52. package/dist/public/js/seed.js +1 -1
  53. package/dist/routes/api/erd.d.ts +1 -0
  54. package/dist/routes/api/erd.js +50 -0
  55. package/dist/routes/api/helpers.d.ts +1 -1
  56. package/dist/routes/api/helpers.js +163 -1
  57. package/dist/routes/api/import-export.js +83 -1
  58. package/dist/routes/api/index.js +48 -1
  59. package/dist/routes/api/query.js +78 -1
  60. package/dist/routes/api/rows.js +362 -1
  61. package/dist/routes/api/seed.js +153 -1
  62. package/dist/routes/api/tables.js +231 -1
  63. package/dist/routes/databases.js +228 -1
  64. package/dist/routes/index.js +19 -1
  65. package/dist/routes/pages.js +397 -1
  66. package/dist/serverless.js +174 -1
  67. package/dist/sql/classifier.js +24 -1
  68. package/dist/sql/error-analyzer.d.ts +1 -0
  69. package/dist/sql/error-analyzer.js +550 -0
  70. package/dist/sql/generator.js +319 -1
  71. package/dist/sql/index.js +18 -1
  72. package/dist/types/api.js +2 -1
  73. package/dist/utils/colors.js +54 -1
  74. package/dist/utils/common.d.ts +1 -1
  75. package/dist/utils/common.js +225 -1
  76. package/dist/utils/csv.js +67 -1
  77. package/dist/utils/datatype.js +181 -1
  78. package/dist/utils/icons.js +81 -1
  79. package/dist/utils/index.js +20 -1
  80. package/dist/utils/logger.js +36 -1
  81. package/dist/views/layouts/main.hbs +14 -1
  82. package/dist/views/pages/erd.hbs +1241 -0
  83. package/dist/views/pages/home.hbs +98 -2
  84. package/dist/views/pages/info.hbs +107 -0
  85. package/dist/views/pages/query.hbs +54 -12
  86. package/dist/views/pages/seed.hbs +435 -171
  87. package/dist/views/partials/sidebar.hbs +12 -0
  88. package/docs/API.md +6 -1
  89. package/docs/EXAMPLES.md +3 -0
  90. package/package.json +5 -4
  91. package/dist/public/js/icons.js +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.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 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.js CHANGED
@@ -1 +1,230 @@
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}
1
+ "use strict";
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
+ const APP_VERSION = (0, args_1.getPackageVersion)();
22
+ function createRouter(options = {}) {
23
+ return options.manager ? createManagerApp(options) : createSingleDbApp(options);
24
+ }
25
+ function setupViewEngine(app, _logger) {
26
+ app.engine('hbs', (0, express_handlebars_1.engine)({
27
+ extname: '.hbs',
28
+ defaultLayout: 'main',
29
+ layoutsDir: node_path_1.default.join(__dirname, 'views', 'layouts'),
30
+ partialsDir: node_path_1.default.join(__dirname, 'views', 'partials'),
31
+ helpers: {
32
+ eq: (a, b) => a === b,
33
+ startsWith: (a, b) => String(a ?? '').startsWith(String(b ?? '')),
34
+ isNull: (v) => v === null || v === undefined,
35
+ truncate: (v, n) => {
36
+ const s = v == null ? '' : String(v);
37
+ return s.length > n ? `${s.slice(0, n)}…` : s;
38
+ },
39
+ json: (v) => (JSON.stringify(v ?? null) ?? 'null').replace(/</g, '\\u003c'),
40
+ add: (a, b) => Number(a) + Number(b),
41
+ sub: (a, b) => Number(a) - Number(b),
42
+ gt: (a, b) => Number(a) > Number(b),
43
+ lt: (a, b) => Number(a) < Number(b),
44
+ join: (arr, sep) => (Array.isArray(arr) ? arr.join(String(sep ?? ',')) : String(arr ?? '')),
45
+ icon: (name, options) => {
46
+ const customClass = (options && options.hash && options.hash.class) || undefined;
47
+ return (0, icons_1.renderIcon)(String(name ?? ''), customClass);
48
+ },
49
+ },
50
+ }));
51
+ app.set('view engine', 'hbs');
52
+ app.set('views', node_path_1.default.join(__dirname, 'views'));
53
+ }
54
+ function addErrorHandlers(app, logger) {
55
+ app.use((req, res) => {
56
+ if (req.path.startsWith('/api/')) {
57
+ return res.status(404).json({ success: false, error: 'Not found.' });
58
+ }
59
+ res.status(404).render('pages/error', { title: 'Not found', status: 404, error: 'The page you requested does not exist.' });
60
+ });
61
+ app.use((err, req, res, _next) => {
62
+ logger.error('Unhandled error', err);
63
+ if (req.path.startsWith('/api/')) {
64
+ return res.status(500).json({ success: false, error: (0, common_1.errorMessage)(err) });
65
+ }
66
+ res.status(500).render('pages/error', { title: 'Error', status: 500, error: (0, common_1.errorMessage)(err) });
67
+ });
68
+ }
69
+ function createSingleDbApp(options) {
70
+ const isServerless = options.serverless !== undefined ? options.serverless : (0, serverless_1.isServerlessEnvironment)();
71
+ const rawTarget = options.connection || options.dbPath || '';
72
+ const isPg = Boolean(options.pgOptions) ||
73
+ (0, common_1.isPostgresConnectionString)(rawTarget) ||
74
+ options.db?.dialect === 'postgres';
75
+ const readonly = options.readonly !== undefined
76
+ ? Boolean(options.readonly)
77
+ : (isServerless && !isPg);
78
+ const basePath = (options.basePath ?? '').replace(/\/+$/, '');
79
+ const logger = options.logger ?? (0, logger_1.createLogger)(options.logLevel ?? 'info', 'admindb');
80
+ let db;
81
+ if (options.db) {
82
+ db = options.db;
83
+ }
84
+ else {
85
+ if (isPg) {
86
+ const conn = options.pgOptions || rawTarget;
87
+ db = new postgres_1.PostgresDatabase(conn, logger, { readonly });
88
+ }
89
+ else {
90
+ db = new database_1.SqliteDatabase(options.dbPath ?? 'admindb.db', logger, { readonly });
91
+ }
92
+ }
93
+ const databasesUrl = options.databasesUrl;
94
+ const authConfig = (0, auth_1.resolveAuthConfig)(options.auth);
95
+ const router = (0, express_1.default)();
96
+ router.disable('x-powered-by');
97
+ router.use(express_1.default.static(node_path_1.default.join(__dirname, 'public')));
98
+ setupViewEngine(router, logger);
99
+ router.use(express_1.default.json({ limit: '10mb' }));
100
+ router.use(express_1.default.urlencoded({ extended: false }));
101
+ (0, auth_1.registerAuthRoutes)(router, authConfig, basePath, logger);
102
+ router.use((0, auth_1.createAuthMiddleware)(authConfig, basePath, logger));
103
+ router.use(async (req, res, next) => {
104
+ try {
105
+ res.locals.basePath = basePath;
106
+ res.locals.currentPath = req.path;
107
+ res.locals.currentTable = null;
108
+ res.locals.dbPath = db.path;
109
+ res.locals.dbId = options.dbId ?? (db.dialect === 'postgres' ? 'PostgreSQL' : (node_path_1.default.basename(db.path) || 'SQLite'));
110
+ res.locals.dialect = db.dialect;
111
+ res.locals.isPostgres = db.dialect === 'postgres';
112
+ res.locals.isSqlite = db.dialect === 'sqlite';
113
+ res.locals.dialectName = db.dialect === 'postgres' ? 'PostgreSQL' : 'SQLite';
114
+ res.locals.databasesUrl = databasesUrl ?? null;
115
+ res.locals.databasesMode = false;
116
+ res.locals.readonly = db.isReadOnly;
117
+ res.locals.serverless = isServerless;
118
+ res.locals.version = APP_VERSION;
119
+ res.locals.authEnabled = authConfig.enabled;
120
+ res.locals.isDefaultPassword = authConfig.isDefaultPassword;
121
+ res.locals.icons = icons_1.ICONS;
122
+ if (!req.path.startsWith('/api/')) {
123
+ const all = await db.listTables();
124
+ const names = (all.data ?? []).map((t) => t.name);
125
+ res.locals.tables = names.filter((n) => !n.startsWith('_'));
126
+ res.locals.internalTables = names.filter((n) => n.startsWith('_'));
127
+ }
128
+ next();
129
+ }
130
+ catch (err) {
131
+ next(err);
132
+ }
133
+ });
134
+ (0, pages_1.registerPages)(router, { db, logger });
135
+ (0, index_1.registerApi)(router, { db, logger });
136
+ addErrorHandlers(router, logger);
137
+ return router;
138
+ }
139
+ function createManagerApp(options) {
140
+ const isServerless = options.serverless !== undefined ? options.serverless : (0, serverless_1.isServerlessEnvironment)();
141
+ const readonly = isServerless || Boolean(options.readonly);
142
+ const manager = options.manager;
143
+ const basePath = (options.basePath ?? '').replace(/\/+$/, '');
144
+ const logger = options.logger ?? (0, logger_1.createLogger)(options.logLevel ?? 'info', 'admindb');
145
+ const authConfig = (0, auth_1.resolveAuthConfig)(options.auth);
146
+ const app = (0, express_1.default)();
147
+ app.disable('x-powered-by');
148
+ app.use(express_1.default.static(node_path_1.default.join(__dirname, 'public')));
149
+ setupViewEngine(app, logger);
150
+ app.use(express_1.default.json({ limit: '10mb' }));
151
+ app.use(express_1.default.urlencoded({ extended: false }));
152
+ (0, auth_1.registerAuthRoutes)(app, authConfig, basePath, logger);
153
+ app.use((0, auth_1.createAuthMiddleware)(authConfig, basePath, logger));
154
+ app.use(async (req, res, next) => {
155
+ try {
156
+ res.locals.basePath = basePath;
157
+ res.locals.currentPath = req.path;
158
+ res.locals.currentTable = null;
159
+ res.locals.dbPath = manager.directory;
160
+ res.locals.dbId = null;
161
+ res.locals.databasesUrl = null;
162
+ res.locals.databasesMode = true;
163
+ res.locals.readonly = readonly;
164
+ res.locals.serverless = isServerless;
165
+ res.locals.version = APP_VERSION;
166
+ res.locals.authEnabled = authConfig.enabled;
167
+ res.locals.isDefaultPassword = authConfig.isDefaultPassword;
168
+ res.locals.icons = icons_1.ICONS;
169
+ if (!req.path.startsWith('/api/')) {
170
+ res.locals.tables = [];
171
+ res.locals.internalTables = [];
172
+ }
173
+ next();
174
+ }
175
+ catch (err) {
176
+ next(err);
177
+ }
178
+ });
179
+ const subApps = new Map();
180
+ (0, databases_1.registerDatabasesRoutes)(app, {
181
+ manager,
182
+ logger,
183
+ basePath,
184
+ readonly,
185
+ allowBrowse: isServerless ? false : options.allowBrowse,
186
+ browseRoot: options.browseRoot,
187
+ invalidate: (id) => subApps.delete(id),
188
+ });
189
+ app.use('/:dbId', (req, res, next) => {
190
+ const dbId = String(req.params.dbId);
191
+ if (!manager.has(dbId)) {
192
+ if (req.path.startsWith('/api/')) {
193
+ return res.status(404).json({ success: false, error: `Database "${dbId}" does not exist.` });
194
+ }
195
+ return res.status(404).render('pages/error', {
196
+ title: 'Not found',
197
+ status: 404,
198
+ error: `Database "${dbId}" does not exist.`,
199
+ });
200
+ }
201
+ const rawRo = req.query.readonly;
202
+ if (rawRo !== undefined) {
203
+ const explicitRo = rawRo === '1' || rawRo === 'true';
204
+ if (!readonly || explicitRo) {
205
+ manager.setReadonly(dbId, explicitRo);
206
+ subApps.delete(dbId);
207
+ }
208
+ }
209
+ let sub = subApps.get(dbId);
210
+ if (!sub) {
211
+ const subBase = `${basePath ? basePath : ''}/${encodeURIComponent(dbId)}`;
212
+ const effectiveRo = readonly || manager.isDbReadOnly(dbId);
213
+ sub = createSingleDbApp({
214
+ db: manager.open(dbId, effectiveRo),
215
+ basePath: subBase,
216
+ logger,
217
+ logLevel: options.logLevel,
218
+ dbId,
219
+ databasesUrl: `${basePath ? basePath : ''}/`,
220
+ readonly: effectiveRo,
221
+ serverless: isServerless,
222
+ auth: authConfig,
223
+ });
224
+ subApps.set(dbId, sub);
225
+ }
226
+ return sub(req, res, next);
227
+ });
228
+ addErrorHandlers(app, logger);
229
+ return app;
230
+ }
@@ -1 +1,121 @@
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}
1
+ "use strict";
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
+ let cachedFallbackSecret = null;
15
+ function resetPersistentSecretCache() {
16
+ cachedFallbackSecret = null;
17
+ }
18
+ function getOrCreatePersistentSecret(env = process.env) {
19
+ const envSecret = env.ADMINDB_SECRET ?? env.SESSION_SECRET;
20
+ if (envSecret && String(envSecret).trim()) {
21
+ return String(envSecret).trim();
22
+ }
23
+ const customSecretFile = env.ADMINDB_SECRET_FILE ? String(env.ADMINDB_SECRET_FILE).trim() : undefined;
24
+ if (!customSecretFile && cachedFallbackSecret) {
25
+ return cachedFallbackSecret;
26
+ }
27
+ const candidatePaths = [];
28
+ if (customSecretFile) {
29
+ candidatePaths.push(node_path_1.default.resolve(customSecretFile));
30
+ }
31
+ else {
32
+ try {
33
+ const home = node_os_1.default.homedir();
34
+ if (home) {
35
+ candidatePaths.push(node_path_1.default.join(home, '.admindb-secret'));
36
+ }
37
+ }
38
+ catch {
39
+ }
40
+ try {
41
+ const tmp = node_os_1.default.tmpdir();
42
+ if (tmp) {
43
+ candidatePaths.push(node_path_1.default.join(tmp, '.admindb-secret'));
44
+ }
45
+ }
46
+ catch {
47
+ }
48
+ }
49
+ for (const filePath of candidatePaths) {
50
+ try {
51
+ if ((0, node_fs_1.existsSync)(filePath)) {
52
+ const content = (0, node_fs_1.readFileSync)(filePath, 'utf8').trim();
53
+ if (content.length >= 16) {
54
+ if (!customSecretFile) {
55
+ cachedFallbackSecret = content;
56
+ }
57
+ return content;
58
+ }
59
+ }
60
+ }
61
+ catch {
62
+ }
63
+ }
64
+ const newSecret = (0, node_crypto_1.randomBytes)(32).toString('hex');
65
+ for (const filePath of candidatePaths) {
66
+ try {
67
+ const dir = node_path_1.default.dirname(filePath);
68
+ if (!(0, node_fs_1.existsSync)(dir)) {
69
+ (0, node_fs_1.mkdirSync)(dir, { recursive: true });
70
+ }
71
+ (0, node_fs_1.writeFileSync)(filePath, newSecret, { encoding: 'utf8', mode: 0o600 });
72
+ if (!customSecretFile) {
73
+ cachedFallbackSecret = newSecret;
74
+ }
75
+ return newSecret;
76
+ }
77
+ catch {
78
+ }
79
+ }
80
+ if (!customSecretFile) {
81
+ cachedFallbackSecret = newSecret;
82
+ }
83
+ return newSecret;
84
+ }
85
+ function resolveAuthConfig(optionsAuth, env = process.env) {
86
+ const envDisabled = ['0', 'false', 'no', 'off'].includes(String(env.ADMINDB_AUTH ?? '').trim().toLowerCase()) ||
87
+ ['1', 'true', 'yes'].includes(String(env.ADMINDB_NO_AUTH ?? env.ADMINDB_DISABLE_AUTH ?? '').trim().toLowerCase());
88
+ let enabled = true;
89
+ let username = (env.ADMINDB_USERNAME ?? env.ADMINDB_USER) ? String(env.ADMINDB_USERNAME ?? env.ADMINDB_USER).trim() : types_1.DEFAULT_USERNAME;
90
+ let password = (env.ADMINDB_PASSWORD ?? env.ADMINDB_PASS) ? String(env.ADMINDB_PASSWORD ?? env.ADMINDB_PASS).trim() : types_1.DEFAULT_PASSWORD_HASH;
91
+ let secret = (env.ADMINDB_SECRET ?? env.SESSION_SECRET)
92
+ ? String(env.ADMINDB_SECRET ?? env.SESSION_SECRET).trim()
93
+ : getOrCreatePersistentSecret(env);
94
+ let sessionDurationMs = types_1.DEFAULT_SESSION_DURATION_MS;
95
+ if (optionsAuth === false || envDisabled) {
96
+ enabled = false;
97
+ }
98
+ else if (typeof optionsAuth === 'object' && optionsAuth !== null) {
99
+ if (optionsAuth.enabled === false)
100
+ enabled = false;
101
+ if (optionsAuth.username !== undefined)
102
+ username = String(optionsAuth.username).trim();
103
+ if (optionsAuth.password !== undefined)
104
+ password = String(optionsAuth.password).trim();
105
+ if (optionsAuth.secret !== undefined && String(optionsAuth.secret).trim()) {
106
+ secret = String(optionsAuth.secret).trim();
107
+ }
108
+ if (optionsAuth.sessionDurationMs !== undefined)
109
+ sessionDurationMs = optionsAuth.sessionDurationMs;
110
+ }
111
+ const isDefaultPassword = username === types_1.DEFAULT_USERNAME &&
112
+ (password === types_1.DEFAULT_PASSWORD_HASH || password === 'admin');
113
+ return {
114
+ enabled,
115
+ username,
116
+ password,
117
+ secret,
118
+ sessionDurationMs,
119
+ isDefaultPassword,
120
+ };
121
+ }
@@ -1 +1,108 @@
1
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.hashPassword=function(password,salt){const saltHex=salt||(0,node_crypto_1.randomBytes)(16).toString("hex"),derivedKey=(0,node_crypto_1.scryptSync)(password,saltHex,64).toString("hex");return`scrypt:${saltHex}:${derivedKey}`},exports.constantTimeCompare=constantTimeCompare,exports.verifyPassword=function(candidatePassword,storedPasswordOrHash){if("string"!=typeof candidatePassword||"string"!=typeof storedPasswordOrHash)return!1;if(storedPasswordOrHash.startsWith("scrypt:")){const parts=storedPasswordOrHash.split(":");if(3===parts.length&&parts[1]&&parts[2]){const salt=parts[1],expectedKeyHex=parts[2];try{return constantTimeCompare((0,node_crypto_1.scryptSync)(candidatePassword,salt,64).toString("hex"),expectedKeyHex)}catch{return!1}}}return constantTimeCompare(candidatePassword,storedPasswordOrHash)},exports.parseCookies=function(cookieHeader){const cookies={};if(!cookieHeader)return cookies;for(const pair of cookieHeader.split(";")){const idx=pair.indexOf("=");if(-1===idx)continue;const key=pair.slice(0,idx).trim();let val=pair.slice(idx+1).trim();if(val.startsWith('"')&&val.endsWith('"')&&val.length>=2&&(val=val.slice(1,-1)),key)try{cookies[key]=decodeURIComponent(val)}catch{cookies[key]=val}}return cookies},exports.createSessionToken=function(username,secret,durationMs=types_1.DEFAULT_SESSION_DURATION_MS){const expiresAt=Date.now()+durationMs,data=`${Buffer.from(username,"utf8").toString("base64url")}.${expiresAt}`,signature=(0,node_crypto_1.createHmac)("sha256",secret).update(data).digest("base64url");return`${data}.${signature}`},exports.verifySessionToken=function(token,secret,expectedUser){if(!token||"string"!=typeof token)return null;const parts=token.split(".");if(3!==parts.length)return null;const[userPayload,expiresStr,providedSignature]=parts,data=`${userPayload}.${expiresStr}`,expectedSignature=(0,node_crypto_1.createHmac)("sha256",secret).update(data).digest("base64url");if(!constantTimeCompare(providedSignature,expectedSignature))return null;const expiresAt=Number.parseInt(expiresStr,10);if(!Number.isFinite(expiresAt)||Date.now()>expiresAt)return null;let username;try{username=Buffer.from(userPayload,"base64url").toString("utf8")}catch{return null}if(expectedUser&&!constantTimeCompare(username,expectedUser))return null;return username};const node_crypto_1=require("node:crypto"),types_1=require("./types");function constantTimeCompare(a,b){try{const bufA=Buffer.from(String(a),"utf8"),bufB=Buffer.from(String(b),"utf8");return bufA.length!==bufB.length?((0,node_crypto_1.timingSafeEqual)(bufA,bufA),!1):(0,node_crypto_1.timingSafeEqual)(bufA,bufB)}catch{return!1}}
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hashPassword = hashPassword;
4
+ exports.constantTimeCompare = constantTimeCompare;
5
+ exports.verifyPassword = verifyPassword;
6
+ exports.parseCookies = parseCookies;
7
+ exports.createSessionToken = createSessionToken;
8
+ exports.verifySessionToken = verifySessionToken;
9
+ const node_crypto_1 = require("node:crypto");
10
+ const types_1 = require("./types");
11
+ function hashPassword(password, salt) {
12
+ const saltHex = salt || (0, node_crypto_1.randomBytes)(16).toString('hex');
13
+ const derivedKey = (0, node_crypto_1.scryptSync)(password, saltHex, 64).toString('hex');
14
+ return `scrypt:${saltHex}:${derivedKey}`;
15
+ }
16
+ function constantTimeCompare(a, b) {
17
+ try {
18
+ const bufA = Buffer.from(String(a), 'utf8');
19
+ const bufB = Buffer.from(String(b), 'utf8');
20
+ if (bufA.length !== bufB.length) {
21
+ (0, node_crypto_1.timingSafeEqual)(bufA, bufA);
22
+ return false;
23
+ }
24
+ return (0, node_crypto_1.timingSafeEqual)(bufA, bufB);
25
+ }
26
+ catch {
27
+ return false;
28
+ }
29
+ }
30
+ function verifyPassword(candidatePassword, storedPasswordOrHash) {
31
+ if (typeof candidatePassword !== 'string' || typeof storedPasswordOrHash !== 'string') {
32
+ return false;
33
+ }
34
+ if (storedPasswordOrHash.startsWith('scrypt:')) {
35
+ const parts = storedPasswordOrHash.split(':');
36
+ if (parts.length === 3 && parts[1] && parts[2]) {
37
+ const salt = parts[1];
38
+ const expectedKeyHex = parts[2];
39
+ try {
40
+ const derivedKey = (0, node_crypto_1.scryptSync)(candidatePassword, salt, 64).toString('hex');
41
+ return constantTimeCompare(derivedKey, expectedKeyHex);
42
+ }
43
+ catch {
44
+ return false;
45
+ }
46
+ }
47
+ }
48
+ return constantTimeCompare(candidatePassword, storedPasswordOrHash);
49
+ }
50
+ function parseCookies(cookieHeader) {
51
+ const cookies = {};
52
+ if (!cookieHeader)
53
+ return cookies;
54
+ for (const pair of cookieHeader.split(';')) {
55
+ const idx = pair.indexOf('=');
56
+ if (idx === -1)
57
+ continue;
58
+ const key = pair.slice(0, idx).trim();
59
+ let val = pair.slice(idx + 1).trim();
60
+ if (val.startsWith('"') && val.endsWith('"') && val.length >= 2) {
61
+ val = val.slice(1, -1);
62
+ }
63
+ if (key) {
64
+ try {
65
+ cookies[key] = decodeURIComponent(val);
66
+ }
67
+ catch {
68
+ cookies[key] = val;
69
+ }
70
+ }
71
+ }
72
+ return cookies;
73
+ }
74
+ function createSessionToken(username, secret, durationMs = types_1.DEFAULT_SESSION_DURATION_MS) {
75
+ const expiresAt = Date.now() + durationMs;
76
+ const userPayload = Buffer.from(username, 'utf8').toString('base64url');
77
+ const data = `${userPayload}.${expiresAt}`;
78
+ const signature = (0, node_crypto_1.createHmac)('sha256', secret).update(data).digest('base64url');
79
+ return `${data}.${signature}`;
80
+ }
81
+ function verifySessionToken(token, secret, expectedUser) {
82
+ if (!token || typeof token !== 'string')
83
+ return null;
84
+ const parts = token.split('.');
85
+ if (parts.length !== 3)
86
+ return null;
87
+ const [userPayload, expiresStr, providedSignature] = parts;
88
+ const data = `${userPayload}.${expiresStr}`;
89
+ const expectedSignature = (0, node_crypto_1.createHmac)('sha256', secret).update(data).digest('base64url');
90
+ if (!constantTimeCompare(providedSignature, expectedSignature)) {
91
+ return null;
92
+ }
93
+ const expiresAt = Number.parseInt(expiresStr, 10);
94
+ if (!Number.isFinite(expiresAt) || Date.now() > expiresAt) {
95
+ return null;
96
+ }
97
+ let username;
98
+ try {
99
+ username = Buffer.from(userPayload, 'base64url').toString('utf8');
100
+ }
101
+ catch {
102
+ return null;
103
+ }
104
+ if (expectedUser && !constantTimeCompare(username, expectedUser)) {
105
+ return null;
106
+ }
107
+ return username;
108
+ }
@@ -1 +1,21 @@
1
- var __createBinding=this&&this.__createBinding||(Object.create?function(o,m,k,k2){void 0===k2&&(k2=k);var desc=Object.getOwnPropertyDescriptor(m,k);desc&&!("get"in desc?!m.__esModule:desc.writable||desc.configurable)||(desc={enumerable:!0,get:function(){return m[k]}}),Object.defineProperty(o,k2,desc)}:function(o,m,k,k2){void 0===k2&&(k2=k),o[k2]=m[k]}),__exportStar=this&&this.__exportStar||function(m,exports){for(var p in m)"default"===p||Object.prototype.hasOwnProperty.call(exports,p)||__createBinding(exports,m,p)};Object.defineProperty(exports,"__esModule",{value:!0}),__exportStar(require("./types"),exports),__exportStar(require("./crypto"),exports),__exportStar(require("./config"),exports),__exportStar(require("./middleware"),exports),__exportStar(require("./routes"),exports);
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./types"), exports);
18
+ __exportStar(require("./crypto"), exports);
19
+ __exportStar(require("./config"), exports);
20
+ __exportStar(require("./middleware"), exports);
21
+ __exportStar(require("./routes"), exports);