admindb 2.1.1 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/README.md +368 -335
  2. package/dist/app.js +227 -1
  3. package/dist/auth/config.d.ts +1 -1
  4. package/dist/auth/config.js +109 -1
  5. package/dist/auth/crypto.js +98 -1
  6. package/dist/auth/index.js +21 -1
  7. package/dist/auth/middleware.js +78 -1
  8. package/dist/auth/routes.js +64 -1
  9. package/dist/auth/types.js +7 -1
  10. package/dist/cli/args.d.ts +1 -1
  11. package/dist/cli/args.js +309 -1
  12. package/dist/cli/config.js +118 -1
  13. package/dist/cli/index.js +20 -1
  14. package/dist/cli/runner.js +208 -1
  15. package/dist/cli.js +4 -1
  16. package/dist/data/chain.d.ts +1 -0
  17. package/dist/data/chain.js +130 -0
  18. package/dist/data/datasets.d.ts +1 -1
  19. package/dist/data/datasets.js +171 -1
  20. package/dist/data/detector.d.ts +1 -1
  21. package/dist/data/detector.js +535 -1
  22. package/dist/data/engine.d.ts +1 -1
  23. package/dist/data/engine.js +586 -1
  24. package/dist/data/index.d.ts +1 -1
  25. package/dist/data/index.js +30 -1
  26. package/dist/data/prng.d.ts +1 -0
  27. package/dist/data/prng.js +73 -0
  28. package/dist/data/registry.d.ts +1 -0
  29. package/dist/data/registry.js +805 -0
  30. package/dist/data/schema-graph.d.ts +1 -0
  31. package/dist/data/schema-graph.js +210 -0
  32. package/dist/data/strategies.d.ts +1 -1
  33. package/dist/data/strategies.js +100 -1
  34. package/dist/data/templates.d.ts +1 -0
  35. package/dist/data/templates.js +134 -0
  36. package/dist/data/types.d.ts +1 -1
  37. package/dist/data/types.js +5 -1
  38. package/dist/data/validator.d.ts +1 -0
  39. package/dist/data/validator.js +160 -0
  40. package/dist/db/database.d.ts +1 -1
  41. package/dist/db/database.js +472 -1
  42. package/dist/db/export.d.ts +1 -1
  43. package/dist/db/export.js +67 -1
  44. package/dist/db/filters.d.ts +1 -1
  45. package/dist/db/filters.js +119 -1
  46. package/dist/db/index.js +24 -1
  47. package/dist/db/introspection.js +164 -1
  48. package/dist/db/manager.js +327 -1
  49. package/dist/db/migrations.js +100 -1
  50. package/dist/db/postgres.d.ts +1 -1
  51. package/dist/db/postgres.js +687 -1
  52. package/dist/db/types.d.ts +1 -1
  53. package/dist/db/types.js +6 -1
  54. package/dist/index.d.ts +1 -1
  55. package/dist/index.js +52 -1
  56. package/dist/public/css/app.css +1 -1
  57. package/dist/public/js/api.js +1 -1
  58. package/dist/public/js/browse.js +1 -1
  59. package/dist/public/js/common.js +1 -0
  60. package/dist/public/js/databases.js +1 -1
  61. package/dist/public/js/designer.js +1 -1
  62. package/dist/public/js/forms.js +1 -1
  63. package/dist/public/js/home.js +1 -0
  64. package/dist/public/js/inspector.js +1 -1
  65. package/dist/public/js/query.js +1 -1
  66. package/dist/public/js/schema.js +1 -1
  67. package/dist/public/js/seed.js +1 -1
  68. package/dist/routes/api/erd.d.ts +1 -0
  69. package/dist/routes/api/erd.js +45 -0
  70. package/dist/routes/api/helpers.d.ts +1 -1
  71. package/dist/routes/api/helpers.js +213 -1
  72. package/dist/routes/api/import-export.js +82 -1
  73. package/dist/routes/api/index.js +48 -1
  74. package/dist/routes/api/query.js +74 -1
  75. package/dist/routes/api/rows.js +358 -1
  76. package/dist/routes/api/seed.js +167 -1
  77. package/dist/routes/api/tables.js +216 -1
  78. package/dist/routes/databases.js +210 -1
  79. package/dist/routes/index.js +19 -1
  80. package/dist/routes/pages.js +446 -1
  81. package/dist/serverless.js +149 -1
  82. package/dist/sql/classifier.js +24 -1
  83. package/dist/sql/error-analyzer.d.ts +1 -0
  84. package/dist/sql/error-analyzer.js +550 -0
  85. package/dist/sql/generator.d.ts +1 -1
  86. package/dist/sql/generator.js +245 -1
  87. package/dist/sql/index.js +18 -1
  88. package/dist/types/api.d.ts +1 -1
  89. package/dist/types/api.js +2 -1
  90. package/dist/utils/colors.js +54 -1
  91. package/dist/utils/common.d.ts +1 -1
  92. package/dist/utils/common.js +185 -1
  93. package/dist/utils/csv.d.ts +1 -1
  94. package/dist/utils/csv.js +61 -1
  95. package/dist/utils/datatype.js +167 -1
  96. package/dist/utils/icons.js +81 -1
  97. package/dist/utils/index.js +20 -1
  98. package/dist/utils/logger.js +36 -1
  99. package/dist/views/layouts/main.hbs +1 -74
  100. package/dist/views/pages/databases.hbs +1 -233
  101. package/dist/views/pages/designer.hbs +1 -89
  102. package/dist/views/pages/erd.hbs +1 -0
  103. package/dist/views/pages/error.hbs +1 -10
  104. package/dist/views/pages/form.hbs +1 -59
  105. package/dist/views/pages/home.hbs +1 -124
  106. package/dist/views/pages/info.hbs +1 -0
  107. package/dist/views/pages/login.hbs +1 -98
  108. package/dist/views/pages/query.hbs +1 -87
  109. package/dist/views/pages/schema.hbs +1 -298
  110. package/dist/views/pages/seed-select.hbs +251 -0
  111. package/dist/views/pages/seed.hbs +273 -167
  112. package/dist/views/pages/table.hbs +1 -327
  113. package/dist/views/partials/navbar.hbs +1 -83
  114. package/dist/views/partials/sidebar.hbs +1 -117
  115. package/docs/API.md +149 -144
  116. package/docs/EXAMPLES.md +579 -576
  117. package/docs/SECURITY.md +265 -265
  118. package/package.json +79 -78
  119. package/dist/public/js/icons.js +0 -1
package/dist/app.js CHANGED
@@ -1 +1,227 @@
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 if (isPg) {
85
+ db = new postgres_1.PostgresDatabase(options.pgOptions || rawTarget, logger, { readonly });
86
+ }
87
+ else {
88
+ db = new database_1.SqliteDatabase(options.dbPath ?? 'admindb.db', logger, { readonly });
89
+ }
90
+ const databasesUrl = options.databasesUrl;
91
+ const authConfig = (0, auth_1.resolveAuthConfig)(options.auth);
92
+ const router = (0, express_1.default)();
93
+ router.disable('x-powered-by');
94
+ router.use(express_1.default.static(node_path_1.default.join(__dirname, 'public')));
95
+ setupViewEngine(router, logger);
96
+ router.use(express_1.default.json({ limit: '10mb' }));
97
+ router.use(express_1.default.urlencoded({ extended: false }));
98
+ (0, auth_1.registerAuthRoutes)(router, authConfig, basePath, logger);
99
+ router.use((0, auth_1.createAuthMiddleware)(authConfig, basePath, logger));
100
+ router.use(async (req, res, next) => {
101
+ try {
102
+ res.locals.basePath = basePath;
103
+ res.locals.currentPath = req.path;
104
+ res.locals.currentTable = null;
105
+ res.locals.dbPath = db.path;
106
+ res.locals.dbId = options.dbId ?? (db.dialect === 'postgres' ? 'PostgreSQL' : (node_path_1.default.basename(db.path) || 'SQLite'));
107
+ res.locals.dialect = db.dialect;
108
+ res.locals.isPostgres = db.dialect === 'postgres';
109
+ res.locals.isSqlite = db.dialect === 'sqlite';
110
+ res.locals.dialectName = db.dialect === 'postgres' ? 'PostgreSQL' : 'SQLite';
111
+ res.locals.databasesUrl = databasesUrl ?? null;
112
+ res.locals.databasesMode = false;
113
+ res.locals.readonly = db.isReadOnly;
114
+ res.locals.serverless = isServerless;
115
+ res.locals.version = APP_VERSION;
116
+ res.locals.authEnabled = authConfig.enabled;
117
+ res.locals.isDefaultPassword = authConfig.isDefaultPassword;
118
+ res.locals.icons = icons_1.ICONS;
119
+ if (!req.path.startsWith('/api/')) {
120
+ const all = await db.listTables();
121
+ const names = (all.data ?? []).map((t) => t.name);
122
+ res.locals.tables = names.filter((n) => !n.startsWith('_'));
123
+ res.locals.internalTables = names.filter((n) => n.startsWith('_'));
124
+ }
125
+ next();
126
+ }
127
+ catch (err) {
128
+ next(err);
129
+ }
130
+ });
131
+ (0, pages_1.registerPages)(router, { db, logger });
132
+ (0, index_1.registerApi)(router, { db, logger });
133
+ addErrorHandlers(router, logger);
134
+ return router;
135
+ }
136
+ function createManagerApp(options) {
137
+ const isServerless = options.serverless !== undefined ? options.serverless : (0, serverless_1.isServerlessEnvironment)();
138
+ const readonly = isServerless || Boolean(options.readonly);
139
+ const manager = options.manager;
140
+ const basePath = (options.basePath ?? '').replace(/\/+$/, '');
141
+ const logger = options.logger ?? (0, logger_1.createLogger)(options.logLevel ?? 'info', 'admindb');
142
+ const authConfig = (0, auth_1.resolveAuthConfig)(options.auth);
143
+ const app = (0, express_1.default)();
144
+ app.disable('x-powered-by');
145
+ app.use(express_1.default.static(node_path_1.default.join(__dirname, 'public')));
146
+ setupViewEngine(app, logger);
147
+ app.use(express_1.default.json({ limit: '10mb' }));
148
+ app.use(express_1.default.urlencoded({ extended: false }));
149
+ (0, auth_1.registerAuthRoutes)(app, authConfig, basePath, logger);
150
+ app.use((0, auth_1.createAuthMiddleware)(authConfig, basePath, logger));
151
+ app.use(async (req, res, next) => {
152
+ try {
153
+ res.locals.basePath = basePath;
154
+ res.locals.currentPath = req.path;
155
+ res.locals.currentTable = null;
156
+ res.locals.dbPath = manager.directory;
157
+ res.locals.dbId = null;
158
+ res.locals.databasesUrl = null;
159
+ res.locals.databasesMode = true;
160
+ res.locals.readonly = readonly;
161
+ res.locals.serverless = isServerless;
162
+ res.locals.version = APP_VERSION;
163
+ res.locals.authEnabled = authConfig.enabled;
164
+ res.locals.isDefaultPassword = authConfig.isDefaultPassword;
165
+ res.locals.icons = icons_1.ICONS;
166
+ if (!req.path.startsWith('/api/')) {
167
+ res.locals.tables = [];
168
+ res.locals.internalTables = [];
169
+ }
170
+ next();
171
+ }
172
+ catch (err) {
173
+ next(err);
174
+ }
175
+ });
176
+ const subApps = new Map();
177
+ (0, databases_1.registerDatabasesRoutes)(app, {
178
+ manager,
179
+ logger,
180
+ basePath,
181
+ readonly,
182
+ allowBrowse: isServerless ? false : options.allowBrowse,
183
+ browseRoot: options.browseRoot,
184
+ invalidate: (id) => subApps.delete(id),
185
+ });
186
+ app.use('/:dbId', (req, res, next) => {
187
+ const dbId = String(req.params.dbId);
188
+ if (!manager.has(dbId)) {
189
+ if (req.path.startsWith('/api/')) {
190
+ return res.status(404).json({ success: false, error: `Database "${dbId}" does not exist.` });
191
+ }
192
+ return res.status(404).render('pages/error', {
193
+ title: 'Not found',
194
+ status: 404,
195
+ error: `Database "${dbId}" does not exist.`,
196
+ });
197
+ }
198
+ const rawRo = req.query.readonly;
199
+ if (rawRo !== undefined) {
200
+ const explicitRo = rawRo === '1' || rawRo === 'true';
201
+ if (!readonly || explicitRo) {
202
+ manager.setReadonly(dbId, explicitRo);
203
+ subApps.delete(dbId);
204
+ }
205
+ }
206
+ let sub = subApps.get(dbId);
207
+ if (!sub) {
208
+ const subBase = `${basePath || ''}/${encodeURIComponent(dbId)}`;
209
+ const effectiveRo = readonly || manager.isDbReadOnly(dbId);
210
+ sub = createSingleDbApp({
211
+ db: manager.open(dbId, effectiveRo),
212
+ basePath: subBase,
213
+ logger,
214
+ logLevel: options.logLevel,
215
+ dbId,
216
+ databasesUrl: `${basePath || ''}/`,
217
+ readonly: effectiveRo,
218
+ serverless: isServerless,
219
+ auth: authConfig,
220
+ });
221
+ subApps.set(dbId, sub);
222
+ }
223
+ return sub(req, res, next);
224
+ });
225
+ addErrorHandlers(app, logger);
226
+ return app;
227
+ }
@@ -1 +1 @@
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;
1
+ import{type AuthConfig,type ResolvedAuthConfig} from './types';export declare const resetPersistentSecretCache:()=>void;export declare function getOrCreatePersistentSecret(env?:NodeJS.ProcessEnv):string;export declare function resolveAuthConfig(optionsAuth?:boolean|AuthConfig,env?:NodeJS.ProcessEnv):ResolvedAuthConfig;
@@ -1 +1,109 @@
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 = void 0;
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
+ const resetPersistentSecretCache = () => {
16
+ cachedFallbackSecret = null;
17
+ };
18
+ exports.resetPersistentSecretCache = resetPersistentSecretCache;
19
+ function getOrCreatePersistentSecret(env = process.env) {
20
+ const envSecret = env.ADMINDB_SECRET ?? env.SESSION_SECRET;
21
+ if (envSecret && String(envSecret).trim()) {
22
+ return String(envSecret).trim();
23
+ }
24
+ const customSecretFile = env.ADMINDB_SECRET_FILE ? String(env.ADMINDB_SECRET_FILE).trim() : undefined;
25
+ if (!customSecretFile && cachedFallbackSecret)
26
+ return cachedFallbackSecret;
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
+ catch { }
38
+ try {
39
+ const tmp = node_os_1.default.tmpdir();
40
+ if (tmp)
41
+ candidatePaths.push(node_path_1.default.join(tmp, '.admindb-secret'));
42
+ }
43
+ catch { }
44
+ }
45
+ for (const filePath of candidatePaths) {
46
+ try {
47
+ if ((0, node_fs_1.existsSync)(filePath)) {
48
+ const content = (0, node_fs_1.readFileSync)(filePath, 'utf8').trim();
49
+ if (content.length >= 16) {
50
+ if (!customSecretFile)
51
+ cachedFallbackSecret = content;
52
+ return content;
53
+ }
54
+ }
55
+ }
56
+ catch { }
57
+ }
58
+ const newSecret = (0, node_crypto_1.randomBytes)(32).toString('hex');
59
+ for (const filePath of candidatePaths) {
60
+ try {
61
+ const dir = node_path_1.default.dirname(filePath);
62
+ if (!(0, node_fs_1.existsSync)(dir))
63
+ (0, node_fs_1.mkdirSync)(dir, { recursive: true });
64
+ (0, node_fs_1.writeFileSync)(filePath, newSecret, { encoding: 'utf8', mode: 0o600 });
65
+ if (!customSecretFile)
66
+ cachedFallbackSecret = newSecret;
67
+ return newSecret;
68
+ }
69
+ catch { }
70
+ }
71
+ if (!customSecretFile)
72
+ cachedFallbackSecret = newSecret;
73
+ return newSecret;
74
+ }
75
+ function resolveAuthConfig(optionsAuth, env = process.env) {
76
+ const envDisabled = ['0', 'false', 'no', 'off'].includes(String(env.ADMINDB_AUTH ?? '').trim().toLowerCase()) ||
77
+ ['1', 'true', 'yes'].includes(String(env.ADMINDB_NO_AUTH ?? env.ADMINDB_DISABLE_AUTH ?? '').trim().toLowerCase());
78
+ let enabled = true;
79
+ let username = String(env.ADMINDB_USERNAME ?? env.ADMINDB_USER ?? types_1.DEFAULT_USERNAME).trim();
80
+ let password = String(env.ADMINDB_PASSWORD ?? env.ADMINDB_PASS ?? types_1.DEFAULT_PASSWORD_HASH).trim();
81
+ let secret = String(env.ADMINDB_SECRET ?? env.SESSION_SECRET ?? '').trim() || getOrCreatePersistentSecret(env);
82
+ let sessionDurationMs = types_1.DEFAULT_SESSION_DURATION_MS;
83
+ if (optionsAuth === false || envDisabled) {
84
+ enabled = false;
85
+ }
86
+ else if (typeof optionsAuth === 'object' && optionsAuth !== null) {
87
+ if (optionsAuth.enabled === false)
88
+ enabled = false;
89
+ if (optionsAuth.username !== undefined)
90
+ username = String(optionsAuth.username).trim();
91
+ if (optionsAuth.password !== undefined)
92
+ password = String(optionsAuth.password).trim();
93
+ if (optionsAuth.secret !== undefined && String(optionsAuth.secret).trim()) {
94
+ secret = String(optionsAuth.secret).trim();
95
+ }
96
+ if (optionsAuth.sessionDurationMs !== undefined)
97
+ sessionDurationMs = optionsAuth.sessionDurationMs;
98
+ }
99
+ const isDefaultPassword = username === types_1.DEFAULT_USERNAME &&
100
+ (password === types_1.DEFAULT_PASSWORD_HASH || password === 'admin');
101
+ return {
102
+ enabled,
103
+ username,
104
+ password,
105
+ secret,
106
+ sessionDurationMs,
107
+ isDefaultPassword,
108
+ };
109
+ }
@@ -1 +1,98 @@
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
+ if (storedPasswordOrHash.startsWith('scrypt:')) {
34
+ const parts = storedPasswordOrHash.split(':');
35
+ if (parts.length === 3 && parts[1] && parts[2]) {
36
+ try {
37
+ const derivedKey = (0, node_crypto_1.scryptSync)(candidatePassword, parts[1], 64).toString('hex');
38
+ return constantTimeCompare(derivedKey, parts[2]);
39
+ }
40
+ catch {
41
+ return false;
42
+ }
43
+ }
44
+ }
45
+ return constantTimeCompare(candidatePassword, storedPasswordOrHash);
46
+ }
47
+ function parseCookies(cookieHeader) {
48
+ const cookies = {};
49
+ if (!cookieHeader)
50
+ return cookies;
51
+ for (const pair of cookieHeader.split(';')) {
52
+ const idx = pair.indexOf('=');
53
+ if (idx === -1)
54
+ continue;
55
+ const key = pair.slice(0, idx).trim();
56
+ let val = pair.slice(idx + 1).trim();
57
+ if (val.startsWith('"') && val.endsWith('"') && val.length >= 2)
58
+ val = val.slice(1, -1);
59
+ if (key) {
60
+ try {
61
+ cookies[key] = decodeURIComponent(val);
62
+ }
63
+ catch {
64
+ cookies[key] = val;
65
+ }
66
+ }
67
+ }
68
+ return cookies;
69
+ }
70
+ function createSessionToken(username, secret, durationMs = types_1.DEFAULT_SESSION_DURATION_MS) {
71
+ const data = `${Buffer.from(username, 'utf8').toString('base64url')}.${Date.now() + durationMs}`;
72
+ const signature = (0, node_crypto_1.createHmac)('sha256', secret).update(data).digest('base64url');
73
+ return `${data}.${signature}`;
74
+ }
75
+ function verifySessionToken(token, secret, expectedUser) {
76
+ if (!token || typeof token !== 'string')
77
+ return null;
78
+ const parts = token.split('.');
79
+ if (parts.length !== 3)
80
+ return null;
81
+ const [userPayload, expiresStr, providedSignature] = parts;
82
+ const data = `${userPayload}.${expiresStr}`;
83
+ const expectedSignature = (0, node_crypto_1.createHmac)('sha256', secret).update(data).digest('base64url');
84
+ if (!constantTimeCompare(providedSignature, expectedSignature))
85
+ return null;
86
+ const expiresAt = Number.parseInt(expiresStr, 10);
87
+ if (!Number.isFinite(expiresAt) || Date.now() > expiresAt)
88
+ return null;
89
+ try {
90
+ const username = Buffer.from(userPayload, 'base64url').toString('utf8');
91
+ if (expectedUser && !constantTimeCompare(username, expectedUser))
92
+ return null;
93
+ return username;
94
+ }
95
+ catch {
96
+ return null;
97
+ }
98
+ }
@@ -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);
@@ -1 +1,78 @@
1
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.authenticateRequest=authenticateRequest,exports.createAuthMiddleware=function(config,basePath,_logger){return(req,res,next)=>{if(res.locals.authEnabled=config.enabled,res.locals.isDefaultPassword=config.isDefaultPassword,!config.enabled)return res.locals.authUser=null,next();const p=req.path;if(p.startsWith("/css/")||p.startsWith("/js/")||"/favicon.ico"===p||"/login"===p||"/logout"===p)return next();const auth=authenticateRequest(req,config);if(auth.authenticated&&auth.user)return res.locals.authUser=auth.user,req.user=auth.user,next();if(p.startsWith("/api/")||req.xhr||req.headers.accept&&req.headers.accept.includes("application/json"))return res.setHeader("WWW-Authenticate",'Basic realm="AdminDB", Bearer'),void res.status(401).json({success:!1,error:"Authentication required. Please log in or provide valid credentials."});const returnUrl=req.originalUrl||`${basePath}${req.url}`,loginUrl=`${basePath}/login${returnUrl&&returnUrl!==`${basePath}/`?`?next=${encodeURIComponent(returnUrl)}`:""}`;res.redirect(loginUrl)}};const types_1=require("./types"),crypto_1=require("./crypto");function authenticateRequest(req,config){if(!config.enabled)return{authenticated:!0,user:"anonymous"};const authHeader=req.headers.authorization;if(authHeader){const[scheme,credentials]=authHeader.split(/\s+/,2);if(scheme&&credentials)if("basic"===scheme.toLowerCase())try{const decoded=Buffer.from(credentials,"base64").toString("utf8"),colonIdx=decoded.indexOf(":");if(-1!==colonIdx){const user=decoded.slice(0,colonIdx),pass=decoded.slice(colonIdx+1);if((0,crypto_1.constantTimeCompare)(user,config.username)&&(0,crypto_1.verifyPassword)(pass,config.password))return{authenticated:!0,user:user}}}catch{}else if("bearer"===scheme.toLowerCase()){const user=(0,crypto_1.verifySessionToken)(credentials,config.secret,config.username);if(user)return{authenticated:!0,user:user};if((0,crypto_1.verifyPassword)(credentials,config.password))return{authenticated:!0,user:config.username}}}const sessionCookie=(0,crypto_1.parseCookies)(req.headers.cookie)[types_1.SESSION_COOKIE_NAME];if(sessionCookie){const user=(0,crypto_1.verifySessionToken)(sessionCookie,config.secret,config.username);if(user)return{authenticated:!0,user:user}}return{authenticated:!1}}
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.authenticateRequest = authenticateRequest;
4
+ exports.createAuthMiddleware = createAuthMiddleware;
5
+ const types_1 = require("./types");
6
+ const crypto_1 = require("./crypto");
7
+ function authenticateRequest(req, config) {
8
+ if (!config.enabled)
9
+ return { authenticated: true, user: 'anonymous' };
10
+ const authHeader = req.headers.authorization;
11
+ if (authHeader) {
12
+ const [scheme, credentials] = authHeader.split(/\s+/, 2);
13
+ if (scheme && credentials) {
14
+ const lowerScheme = scheme.toLowerCase();
15
+ if (lowerScheme === 'basic') {
16
+ try {
17
+ const decoded = Buffer.from(credentials, 'base64').toString('utf8');
18
+ const colonIdx = decoded.indexOf(':');
19
+ if (colonIdx !== -1) {
20
+ const user = decoded.slice(0, colonIdx);
21
+ const pass = decoded.slice(colonIdx + 1);
22
+ if ((0, crypto_1.constantTimeCompare)(user, config.username) && (0, crypto_1.verifyPassword)(pass, config.password)) {
23
+ return { authenticated: true, user };
24
+ }
25
+ }
26
+ }
27
+ catch {
28
+ }
29
+ }
30
+ else if (lowerScheme === 'bearer') {
31
+ const user = (0, crypto_1.verifySessionToken)(credentials, config.secret, config.username);
32
+ if (user)
33
+ return { authenticated: true, user };
34
+ if ((0, crypto_1.verifyPassword)(credentials, config.password)) {
35
+ return { authenticated: true, user: config.username };
36
+ }
37
+ }
38
+ }
39
+ }
40
+ const sessionCookie = (0, crypto_1.parseCookies)(req.headers.cookie)[types_1.SESSION_COOKIE_NAME];
41
+ if (sessionCookie) {
42
+ const user = (0, crypto_1.verifySessionToken)(sessionCookie, config.secret, config.username);
43
+ if (user)
44
+ return { authenticated: true, user };
45
+ }
46
+ return { authenticated: false };
47
+ }
48
+ function createAuthMiddleware(config, basePath, _logger) {
49
+ return (req, res, next) => {
50
+ res.locals.authEnabled = config.enabled;
51
+ res.locals.isDefaultPassword = config.isDefaultPassword;
52
+ if (!config.enabled) {
53
+ res.locals.authUser = null;
54
+ return next();
55
+ }
56
+ const p = req.path;
57
+ if (p.startsWith('/css/') || p.startsWith('/js/') || p === '/favicon.ico' || p === '/login' || p === '/logout') {
58
+ return next();
59
+ }
60
+ const auth = authenticateRequest(req, config);
61
+ if (auth.authenticated && auth.user) {
62
+ res.locals.authUser = auth.user;
63
+ req.user = auth.user;
64
+ return next();
65
+ }
66
+ if (p.startsWith('/api/') || req.xhr || req.headers.accept?.includes('application/json')) {
67
+ res.setHeader('WWW-Authenticate', 'Basic realm="AdminDB", Bearer');
68
+ res.status(401).json({
69
+ success: false,
70
+ error: 'Authentication required. Please log in or provide valid credentials.',
71
+ });
72
+ return;
73
+ }
74
+ const returnUrl = req.originalUrl || `${basePath}${req.url}`;
75
+ const loginUrl = `${basePath}/login${returnUrl && returnUrl !== `${basePath}/` ? `?next=${encodeURIComponent(returnUrl)}` : ''}`;
76
+ res.redirect(loginUrl);
77
+ };
78
+ }
@@ -1 +1,64 @@
1
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.registerAuthRoutes=function(router,config,basePath,logger){router.get("/login",(req,res)=>{if(!config.enabled)return res.redirect(`${basePath}/`);if((0,middleware_1.authenticateRequest)(req,config).authenticated){const nextUrl=String(req.query.next??"").trim();return res.redirect(nextUrl.startsWith("/")?nextUrl:`${basePath}/`)}const nextUrl=String(req.query.next??"").trim();res.render("pages/login",{title:"Sign in",noSidebar:!0,next:nextUrl.startsWith("/")?nextUrl:"",isDefaultPassword:config.isDefaultPassword,error:req.query.error?String(req.query.error):null})}),router.post("/login",(req,res)=>{if(!config.enabled)return res.redirect(`${basePath}/`);const username=String(req.body?.username??"").trim(),password=String(req.body?.password??"").trim(),nextUrl=String(req.body?.next??req.query.next??"").trim();if(!((0,crypto_1.constantTimeCompare)(username,config.username)&&(0,crypto_1.verifyPassword)(password,config.password)))return logger.warn(`Failed login attempt for user "${username||"<empty>"}"`),req.xhr||req.headers.accept&&req.headers.accept.includes("application/json")?res.status(401).json({success:!1,error:"Invalid username or password."}):res.status(401).render("pages/login",{title:"Sign in",noSidebar:!0,username:username,next:nextUrl.startsWith("/")?nextUrl:"",isDefaultPassword:config.isDefaultPassword,error:"Invalid username or password. Please check your credentials."});const token=(0,crypto_1.createSessionToken)(config.username,config.secret,config.sessionDurationMs),maxAgeSec=Math.floor(config.sessionDurationMs/1e3),expiresUtc=new Date(Date.now()+config.sessionDurationMs).toUTCString(),cookiePath=basePath||"/";if(res.setHeader("Set-Cookie",`${types_1.SESSION_COOKIE_NAME}=${encodeURIComponent(token)}; Path=${cookiePath}; Max-Age=${maxAgeSec}; Expires=${expiresUtc}; HttpOnly; SameSite=Lax`),req.xhr||req.headers.accept&&req.headers.accept.includes("application/json"))return res.json({success:!0,message:"Logged in successfully.",user:config.username,token:token});const target=nextUrl.startsWith("/")?nextUrl:`${basePath}/`;res.redirect(target)});const handleLogout=(req,res)=>{const cookiePath=basePath||"/";if(res.setHeader("Set-Cookie",`${types_1.SESSION_COOKIE_NAME}=; Path=${cookiePath}; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; SameSite=Lax`),req.xhr||req.headers.accept&&req.headers.accept.includes("application/json"))return res.json({success:!0,message:"Logged out successfully."});res.redirect(`${basePath}/login`)};router.post("/logout",handleLogout),router.get("/logout",handleLogout)};const types_1=require("./types"),middleware_1=require("./middleware"),crypto_1=require("./crypto");
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerAuthRoutes = registerAuthRoutes;
4
+ const types_1 = require("./types");
5
+ const middleware_1 = require("./middleware");
6
+ const crypto_1 = require("./crypto");
7
+ function registerAuthRoutes(router, config, basePath, logger) {
8
+ router.get('/login', (req, res) => {
9
+ if (!config.enabled)
10
+ return res.redirect(`${basePath}/`);
11
+ const auth = (0, middleware_1.authenticateRequest)(req, config);
12
+ const nextUrl = String(req.query.next ?? '').trim();
13
+ if (auth.authenticated) {
14
+ return res.redirect(nextUrl.startsWith('/') ? nextUrl : `${basePath}/`);
15
+ }
16
+ res.render('pages/login', {
17
+ title: 'Sign in',
18
+ noSidebar: true,
19
+ next: nextUrl.startsWith('/') ? nextUrl : '',
20
+ isDefaultPassword: config.isDefaultPassword,
21
+ error: req.query.error ? String(req.query.error) : null,
22
+ });
23
+ });
24
+ router.post('/login', (req, res) => {
25
+ if (!config.enabled)
26
+ return res.redirect(`${basePath}/`);
27
+ const username = String(req.body?.username ?? '').trim();
28
+ const password = String(req.body?.password ?? '').trim();
29
+ const nextUrl = String(req.body?.next ?? req.query.next ?? '').trim();
30
+ const isValid = (0, crypto_1.constantTimeCompare)(username, config.username) && (0, crypto_1.verifyPassword)(password, config.password);
31
+ if (!isValid) {
32
+ logger.warn(`Failed login attempt for user "${username || '<empty>'}"`);
33
+ if (req.xhr || req.headers.accept?.includes('application/json')) {
34
+ return res.status(401).json({ success: false, error: 'Invalid username or password.' });
35
+ }
36
+ return res.status(401).render('pages/login', {
37
+ title: 'Sign in',
38
+ noSidebar: true,
39
+ username,
40
+ next: nextUrl.startsWith('/') ? nextUrl : '',
41
+ isDefaultPassword: config.isDefaultPassword,
42
+ error: 'Invalid username or password. Please check your credentials.',
43
+ });
44
+ }
45
+ const token = (0, crypto_1.createSessionToken)(config.username, config.secret, config.sessionDurationMs);
46
+ const maxAgeSec = Math.floor(config.sessionDurationMs / 1000);
47
+ const expiresUtc = new Date(Date.now() + config.sessionDurationMs).toUTCString();
48
+ const cookiePath = basePath || '/';
49
+ res.setHeader('Set-Cookie', `${types_1.SESSION_COOKIE_NAME}=${encodeURIComponent(token)}; Path=${cookiePath}; Max-Age=${maxAgeSec}; Expires=${expiresUtc}; HttpOnly; SameSite=Lax`);
50
+ if (req.xhr || req.headers.accept?.includes('application/json')) {
51
+ return res.json({ success: true, message: 'Logged in successfully.', user: config.username, token });
52
+ }
53
+ res.redirect(nextUrl.startsWith('/') ? nextUrl : `${basePath}/`);
54
+ });
55
+ const handleLogout = (req, res) => {
56
+ res.setHeader('Set-Cookie', `${types_1.SESSION_COOKIE_NAME}=; Path=${basePath || '/'}; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; SameSite=Lax`);
57
+ if (req.xhr || req.headers.accept?.includes('application/json')) {
58
+ return res.json({ success: true, message: 'Logged out successfully.' });
59
+ }
60
+ res.redirect(`${basePath}/login`);
61
+ };
62
+ router.post('/logout', handleLogout);
63
+ router.get('/logout', handleLogout);
64
+ }