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
@@ -1 +1,85 @@
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
+ }
11
+ const authHeader = req.headers.authorization;
12
+ if (authHeader) {
13
+ const [scheme, credentials] = authHeader.split(/\s+/, 2);
14
+ if (scheme && credentials) {
15
+ if (scheme.toLowerCase() === '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 (scheme.toLowerCase() === 'bearer') {
31
+ const user = (0, crypto_1.verifySessionToken)(credentials, config.secret, config.username);
32
+ if (user) {
33
+ return { authenticated: true, user };
34
+ }
35
+ if ((0, crypto_1.verifyPassword)(credentials, config.password)) {
36
+ return { authenticated: true, user: config.username };
37
+ }
38
+ }
39
+ }
40
+ }
41
+ const cookies = (0, crypto_1.parseCookies)(req.headers.cookie);
42
+ const sessionCookie = cookies[types_1.SESSION_COOKIE_NAME];
43
+ if (sessionCookie) {
44
+ const user = (0, crypto_1.verifySessionToken)(sessionCookie, config.secret, config.username);
45
+ if (user) {
46
+ return { authenticated: true, user };
47
+ }
48
+ }
49
+ return { authenticated: false };
50
+ }
51
+ function createAuthMiddleware(config, basePath, _logger) {
52
+ return (req, res, next) => {
53
+ res.locals.authEnabled = config.enabled;
54
+ res.locals.isDefaultPassword = config.isDefaultPassword;
55
+ if (!config.enabled) {
56
+ res.locals.authUser = null;
57
+ return next();
58
+ }
59
+ const p = req.path;
60
+ if (p.startsWith('/css/') ||
61
+ p.startsWith('/js/') ||
62
+ p === '/favicon.ico' ||
63
+ p === '/login' ||
64
+ p === '/logout') {
65
+ return next();
66
+ }
67
+ const auth = authenticateRequest(req, config);
68
+ if (auth.authenticated && auth.user) {
69
+ res.locals.authUser = auth.user;
70
+ req.user = auth.user;
71
+ return next();
72
+ }
73
+ if (p.startsWith('/api/') || req.xhr || (req.headers.accept && req.headers.accept.includes('application/json'))) {
74
+ res.setHeader('WWW-Authenticate', 'Basic realm="AdminDB", Bearer');
75
+ res.status(401).json({
76
+ success: false,
77
+ error: 'Authentication required. Please log in or provide valid credentials.',
78
+ });
79
+ return;
80
+ }
81
+ const returnUrl = req.originalUrl || `${basePath}${req.url}`;
82
+ const loginUrl = `${basePath}/login${returnUrl && returnUrl !== `${basePath}/` ? `?next=${encodeURIComponent(returnUrl)}` : ''}`;
83
+ res.redirect(loginUrl);
84
+ };
85
+ }
@@ -1 +1,70 @@
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
+ }
12
+ const auth = (0, middleware_1.authenticateRequest)(req, config);
13
+ if (auth.authenticated) {
14
+ const nextUrl = String(req.query.next ?? '').trim();
15
+ return res.redirect(nextUrl.startsWith('/') ? nextUrl : `${basePath}/`);
16
+ }
17
+ const nextUrl = String(req.query.next ?? '').trim();
18
+ res.render('pages/login', {
19
+ title: 'Sign in',
20
+ noSidebar: true,
21
+ next: nextUrl.startsWith('/') ? nextUrl : '',
22
+ isDefaultPassword: config.isDefaultPassword,
23
+ error: req.query.error ? String(req.query.error) : null,
24
+ });
25
+ });
26
+ router.post('/login', (req, res) => {
27
+ if (!config.enabled) {
28
+ return res.redirect(`${basePath}/`);
29
+ }
30
+ const username = String(req.body?.username ?? '').trim();
31
+ const password = String(req.body?.password ?? '').trim();
32
+ const nextUrl = String(req.body?.next ?? req.query.next ?? '').trim();
33
+ const isValid = (0, crypto_1.constantTimeCompare)(username, config.username) &&
34
+ (0, crypto_1.verifyPassword)(password, config.password);
35
+ if (!isValid) {
36
+ logger.warn(`Failed login attempt for user "${username || '<empty>'}"`);
37
+ if (req.xhr || (req.headers.accept && req.headers.accept.includes('application/json'))) {
38
+ return res.status(401).json({ success: false, error: 'Invalid username or password.' });
39
+ }
40
+ return res.status(401).render('pages/login', {
41
+ title: 'Sign in',
42
+ noSidebar: true,
43
+ username,
44
+ next: nextUrl.startsWith('/') ? nextUrl : '',
45
+ isDefaultPassword: config.isDefaultPassword,
46
+ error: 'Invalid username or password. Please check your credentials.',
47
+ });
48
+ }
49
+ const token = (0, crypto_1.createSessionToken)(config.username, config.secret, config.sessionDurationMs);
50
+ const maxAgeSec = Math.floor(config.sessionDurationMs / 1000);
51
+ const expiresUtc = new Date(Date.now() + config.sessionDurationMs).toUTCString();
52
+ const cookiePath = basePath || '/';
53
+ res.setHeader('Set-Cookie', `${types_1.SESSION_COOKIE_NAME}=${encodeURIComponent(token)}; Path=${cookiePath}; Max-Age=${maxAgeSec}; Expires=${expiresUtc}; HttpOnly; SameSite=Lax`);
54
+ if (req.xhr || (req.headers.accept && req.headers.accept.includes('application/json'))) {
55
+ return res.json({ success: true, message: 'Logged in successfully.', user: config.username, token });
56
+ }
57
+ const target = nextUrl.startsWith('/') ? nextUrl : `${basePath}/`;
58
+ res.redirect(target);
59
+ });
60
+ const handleLogout = (req, res) => {
61
+ const cookiePath = basePath || '/';
62
+ 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`);
63
+ if (req.xhr || (req.headers.accept && req.headers.accept.includes('application/json'))) {
64
+ return res.json({ success: true, message: 'Logged out successfully.' });
65
+ }
66
+ res.redirect(`${basePath}/login`);
67
+ };
68
+ router.post('/logout', handleLogout);
69
+ router.get('/logout', handleLogout);
70
+ }
@@ -1 +1,7 @@
1
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.SESSION_COOKIE_NAME=exports.DEFAULT_SESSION_DURATION_MS=exports.DEFAULT_PASSWORD_HASH=exports.DEFAULT_USERNAME=void 0,exports.DEFAULT_USERNAME="admin",exports.DEFAULT_PASSWORD_HASH="scrypt:d9f2e64b8a1c3d5e7f0a2b4c6e801357:cb3032b16f29c8d44f75c779f070c61c846b17eb333d07596ceb8470fc1b4c06cc720c08796ebc86937dab28a3ef580ced38a71c940fd4a169fd2e9cb9fa40e2",exports.DEFAULT_SESSION_DURATION_MS=6048e5,exports.SESSION_COOKIE_NAME="admindb_session";
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SESSION_COOKIE_NAME = exports.DEFAULT_SESSION_DURATION_MS = exports.DEFAULT_PASSWORD_HASH = exports.DEFAULT_USERNAME = void 0;
4
+ exports.DEFAULT_USERNAME = 'admin';
5
+ exports.DEFAULT_PASSWORD_HASH = 'scrypt:d9f2e64b8a1c3d5e7f0a2b4c6e801357:cb3032b16f29c8d44f75c779f070c61c846b17eb333d07596ceb8470fc1b4c06cc720c08796ebc86937dab28a3ef580ced38a71c940fd4a169fd2e9cb9fa40e2';
6
+ exports.DEFAULT_SESSION_DURATION_MS = 7 * 24 * 60 * 60 * 1000;
7
+ exports.SESSION_COOKIE_NAME = 'admindb_session';
package/dist/cli/args.js CHANGED
@@ -1 +1,320 @@
1
- var __importDefault=this&&this.__importDefault||function(mod){return mod&&mod.__esModule?mod:{default:mod}};Object.defineProperty(exports,"__esModule",{value:!0}),exports.helpText=function(){return["AdminDB — browser-based SQLite & PostgreSQL administration tool.","","Usage:"," admindb [options] [path|connection_url|config.json]","","Starts a local server and opens a web UI where you can browse, select and","manage SQLite databases (.db / .sqlite / .sqlite3) and PostgreSQL databases.","",'With no path or options it runs in "manager" mode: the landing page lets',"you browse the filesystem and open any database file, or create new ones.","","Arguments:"," path Path to a database file, PostgreSQL connection URL, or"," JSON configuration file containing database credentials.","","Options & Environment Variables:"," -C, --config <file.json> Load database credentials & settings from a JSON file"," -p, --port <port> Port to listen on (default: 45531) [PORT / ADMINDB_PORT]"," -H, --host <host> Host/interface to bind (default: 0.0.0.0) [HOST / ADMINDB_HOST]"," -c, --connection, --pg <uri> Open a PostgreSQL database directly [DATABASE_URL / ADMINDB_CONNECTION]"," -o, --open, --db-path <file> Open a single database file directly [DB_PATH / ADMINDB_DB_PATH]"," -d, --dir, --db-dir <dir> Folder of database files to manage [DB_DIR / ADMINDB_DB_DIR]"," --files, --db-files <list> Comma-separated database file paths [DB_FILES / ADMINDB_DB_FILES]"," -b, --base-path, --base <p> URL prefix to serve under (default: /) [BASE_PATH / ADMINDB_BASE_PATH]"," -r, --readonly, --read-only Open databases read-only (writes disabled) [READONLY / ADMINDB_READONLY]"," --serverless Enable serverless mode (enforces read-only, disables writes) [SERVERLESS / ADMINDB_SERVERLESS]"," --auth Enable authentication (default: on) [ADMINDB_AUTH=true]"," --no-auth, --disable-auth Disable authentication completely [ADMINDB_NO_AUTH=1 / ADMINDB_AUTH=false]"," -u, --username, --user <user> Admin username (default: admin) [ADMINDB_USERNAME / ADMINDB_USER]"," -P, --password, --pass <pass> Admin password or hash [ADMINDB_PASSWORD / ADMINDB_PASS]"," --auth-secret <sec> Secret key used to sign session cookies [ADMINDB_SECRET / SESSION_SECRET]"," -l, --log-level <level> debug | info | warn | error (default: info) [LOG_LEVEL / ADMINDB_LOG_LEVEL]"," -h, --help Show this help message"," -v, --version Show version","","Examples:"," admindb name.postgres.json # load database connections from a JSON file"," admindb postgresql://user:pass@host:5432/db # open a PostgreSQL database directly"," admindb ./data/app.db # open a SQLite database file"," admindb --no-auth # run with authentication turned off"," admindb -C ./connections.json # run with JSON connections config","","See docs/EXAMPLES.md for full configuration reference and deployment recipes."].join("\n")},exports.getPackageVersion=function(){return packageVersion()},exports.versionText=function(){const v=packageVersion();return v?`admindb v${v}`:"admindb"},exports.parseArgs=function(argv){const args={help:!1,version:!1,readonly:!1},positional=[];let i=0;const invalid=error=>({args:args,error:error}),takeValue=flag=>{const eq=argv[i].indexOf("=");return-1!==eq?argv[i].slice(eq+1):(i+=1,argv[i])};for(;i<argv.length;){const tok=argv[i];if("-h"===tok||"--help"===tok)args.help=!0;else if("-v"===tok||"--version"===tok)args.version=!0;else if(isFlag(tok,"-C","--config","--json")){const v=takeValue();if(void 0===v)return invalid("Missing value for --config.");args.configPath=v;try{const fileConf=(0,config_1.parseConfigFile)(v);fileConf&&(fileConf.connections&&(args.connections=fileConf.connections),fileConf.connection&&(args.connection=fileConf.connection),void 0!==fileConf.port&&(args.port=fileConf.port),void 0!==fileConf.host&&(args.host=fileConf.host),void 0!==fileConf.basePath&&(args.basePath=fileConf.basePath),void 0!==fileConf.readonly&&(args.readonly=fileConf.readonly),void 0!==fileConf.serverless&&(args.serverless=fileConf.serverless),void 0!==fileConf.logLevel&&(args.logLevel=fileConf.logLevel),"boolean"==typeof fileConf.auth?args.auth=fileConf.auth:fileConf.auth&&"object"==typeof fileConf.auth&&(args.auth=!0,fileConf.auth.username&&(args.authUsername=fileConf.auth.username),fileConf.auth.password&&(args.authPassword=fileConf.auth.password),fileConf.auth.secret&&(args.authSecret=fileConf.auth.secret)))}catch(err){return invalid(err.message)}}else if(isFlag(tok,"-p","--port")){const v=takeValue();if(void 0===v)return invalid("Missing value for --port.");const n=Number.parseInt(v,10);if(!Number.isFinite(n)||n<=0)return invalid(`Invalid port: "${v}".`);args.port=n}else if(isFlag(tok,"-H","--host")){const v=takeValue();if(void 0===v)return invalid("Missing value for --host.");args.host=v}else if(isFlag(tok,"-c","--connection","--conn","--pg")){const v=takeValue();if(void 0===v)return invalid("Missing value for --connection.");args.connection=v,args.dbPath=v}else if(isFlag(tok,"-o","--open","--db","-f","--file","--db-path","--path")){const v=takeValue();if(void 0===v)return invalid("Missing value for --open / --db-path.");args.dbPath=v,(v.startsWith("postgres://")||v.startsWith("postgresql://"))&&(args.connection=v)}else if(isFlag(tok,"-d","--dir","--folder","--db-dir")){const v=takeValue();if(void 0===v)return invalid("Missing value for --dir / --db-dir.");args.dbDir=v}else if(isFlag(tok,"--files","--db-files")){const v=takeValue();if(void 0===v)return invalid("Missing value for --files / --db-files.");args.dbFiles=v.split(",").map(s=>s.trim()).filter(Boolean)}else if(isFlag(tok,"-b","--base-path","--base")){const v=takeValue();if(void 0===v)return invalid("Missing value for --base-path.");args.basePath=v}else if(isFlag(tok,"-l","--log-level")){const v=takeValue();if(void 0===v)return invalid("Missing value for --log-level.");if(!LOG_LEVELS.includes(v))return invalid(`Invalid log level: "${v}". Expected one of: ${LOG_LEVELS.join(", ")}.`);args.logLevel=v}else if("-r"===tok||"--readonly"===tok||"--read-only"===tok)args.readonly=!0;else if("--serverless"===tok)args.serverless=!0,args.readonly=!0;else if("--no-auth"===tok||"--disable-auth"===tok)args.auth=!1;else if("--auth"===tok)args.auth=!0;else if(isFlag(tok,"-u","--username","--user","--auth-username")){const v=takeValue();if(void 0===v)return invalid("Missing value for --username.");args.authUsername=v}else if(isFlag(tok,"-P","--password","--pass","--auth-password")){const v=takeValue();if(void 0===v)return invalid("Missing value for --password.");args.authPassword=v}else if(isFlag(tok,"--auth-secret","--secret","--session-secret")){const v=takeValue();if(void 0===v)return invalid("Missing value for --auth-secret.");args.authSecret=v}else{if(tok.startsWith("-")&&tok.length>1)return invalid(`Unknown option: "${tok}".`);positional.push(tok)}i+=1}if(positional.length>1)return invalid(`Unexpected extra arguments: ${positional.slice(1).join(" ")}.`);const p=positional[0];if(p)if(p.startsWith("postgres://")||p.startsWith("postgresql://"))args.connection=p,args.dbPath=p;else if(p.endsWith(".json")||(0,node_fs_1.existsSync)(p))try{if(p.endsWith(".json")||(0,node_fs_1.statSync)(p).isFile()&&p.includes(".json")){args.configPath=p;const fileConf=(0,config_1.parseConfigFile)(p);fileConf&&(fileConf.connections&&(args.connections=fileConf.connections),fileConf.connection&&(args.connection=fileConf.connection),void 0!==fileConf.port&&void 0===args.port&&(args.port=fileConf.port),void 0!==fileConf.host&&void 0===args.host&&(args.host=fileConf.host),void 0!==fileConf.basePath&&void 0===args.basePath&&(args.basePath=fileConf.basePath),void 0!==fileConf.readonly&&(args.readonly=fileConf.readonly),void 0!==fileConf.serverless&&(args.serverless=fileConf.serverless),void 0!==fileConf.logLevel&&void 0===args.logLevel&&(args.logLevel=fileConf.logLevel),void 0===args.auth&&("boolean"==typeof fileConf.auth?args.auth=fileConf.auth:fileConf.auth&&"object"==typeof fileConf.auth&&(args.auth=!0,fileConf.auth.username&&!args.authUsername&&(args.authUsername=fileConf.auth.username),fileConf.auth.password&&!args.authPassword&&(args.authPassword=fileConf.auth.password),fileConf.auth.secret&&!args.authSecret&&(args.authSecret=fileConf.auth.secret))))}else(0,node_fs_1.statSync)(p).isDirectory()?args.dbDir=p:args.dbPath=p}catch(err){if(p.endsWith(".json"))return invalid(err.message);args.dbPath=p}else args.dbPath=p;return{args:args}};const node_fs_1=require("node:fs"),node_path_1=__importDefault(require("node:path")),config_1=require("./config"),LOG_LEVELS=["debug","info","warn","error"];function packageVersion(){try{const candidates=[node_path_1.default.join(__dirname,"..","..","package.json"),node_path_1.default.join(__dirname,"..","package.json"),node_path_1.default.join(__dirname,"package.json")];for(const p of candidates)if((0,node_fs_1.existsSync)(p)){const raw=(0,node_fs_1.readFileSync)(p,"utf8"),pkg=JSON.parse(raw);if(pkg.version)return pkg.version}return""}catch{return""}}function isFlag(tok,...names){return names.includes(tok)||names.some(n=>n.length>2&&tok.startsWith(n+"="))}
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.helpText = helpText;
7
+ exports.getPackageVersion = getPackageVersion;
8
+ exports.versionText = versionText;
9
+ exports.parseArgs = parseArgs;
10
+ const node_fs_1 = require("node:fs");
11
+ const node_path_1 = __importDefault(require("node:path"));
12
+ const common_1 = require("../utils/common");
13
+ const config_1 = require("./config");
14
+ const LOG_LEVELS = ['debug', 'info', 'warn', 'error'];
15
+ function helpText() {
16
+ return [
17
+ 'AdminDB — browser-based SQLite & PostgreSQL administration tool.',
18
+ '',
19
+ 'Usage:',
20
+ ' admindb [options] [path|connection_url|config.json]',
21
+ '',
22
+ 'Starts a local server and opens a web UI where you can browse, select and',
23
+ 'manage SQLite databases (.db / .sqlite / .sqlite3) and PostgreSQL databases.',
24
+ '',
25
+ 'With no path or options it runs in "manager" mode: the landing page lets',
26
+ 'you browse the filesystem and open any database file, or create new ones.',
27
+ '',
28
+ 'Arguments:',
29
+ ' path Path to a database file, PostgreSQL connection URL, or',
30
+ ' JSON configuration file containing database credentials.',
31
+ '',
32
+ 'Options & Environment Variables:',
33
+ ' -C, --config <file.json> Load database credentials & settings from a JSON file',
34
+ ' -p, --port <port> Port to listen on (default: 45531) [PORT / ADMINDB_PORT]',
35
+ ' -H, --host <host> Host/interface to bind (default: 0.0.0.0) [HOST / ADMINDB_HOST]',
36
+ ' -c, --connection, --pg <uri> Open a PostgreSQL database directly [DATABASE_URL / ADMINDB_CONNECTION]',
37
+ ' -o, --open, --db-path <file> Open a single database file directly [DB_PATH / ADMINDB_DB_PATH]',
38
+ ' -d, --dir, --db-dir <dir> Folder of database files to manage [DB_DIR / ADMINDB_DB_DIR]',
39
+ ' --files, --db-files <list> Comma-separated database file paths [DB_FILES / ADMINDB_DB_FILES]',
40
+ ' -b, --base-path, --base <p> URL prefix to serve under (default: /) [BASE_PATH / ADMINDB_BASE_PATH]',
41
+ ' -r, --readonly, --read-only Open databases read-only (writes disabled) [READONLY / ADMINDB_READONLY]',
42
+ ' --serverless Enable serverless mode (enforces read-only, disables writes) [SERVERLESS / ADMINDB_SERVERLESS]',
43
+ ' --auth Enable authentication (default: on) [ADMINDB_AUTH=true]',
44
+ ' --no-auth, --disable-auth Disable authentication completely [ADMINDB_NO_AUTH=1 / ADMINDB_AUTH=false]',
45
+ ' -u, --username, --user <user> Admin username (default: admin) [ADMINDB_USERNAME / ADMINDB_USER]',
46
+ ' -P, --password, --pass <pass> Admin password or hash [ADMINDB_PASSWORD / ADMINDB_PASS]',
47
+ ' --auth-secret <sec> Secret key used to sign session cookies [ADMINDB_SECRET / SESSION_SECRET]',
48
+ ' -l, --log-level <level> debug | info | warn | error (default: info) [LOG_LEVEL / ADMINDB_LOG_LEVEL]',
49
+ ' -h, --help Show this help message',
50
+ ' -v, --version Show version',
51
+ '',
52
+ 'Examples:',
53
+ ' admindb name.postgres.json # load database connections from a JSON file',
54
+ ' admindb postgresql://user:pass@host:5432/db # open a PostgreSQL database directly',
55
+ ' admindb ./data/app.db # open a SQLite database file',
56
+ ' admindb --no-auth # run with authentication turned off',
57
+ ' admindb -C ./connections.json # run with JSON connections config',
58
+ '',
59
+ 'See docs/EXAMPLES.md for full configuration reference and deployment recipes.',
60
+ ].join('\n');
61
+ }
62
+ function packageVersion() {
63
+ try {
64
+ const candidates = [
65
+ node_path_1.default.join(__dirname, '..', '..', 'package.json'),
66
+ node_path_1.default.join(__dirname, '..', 'package.json'),
67
+ node_path_1.default.join(__dirname, 'package.json'),
68
+ ];
69
+ for (const p of candidates) {
70
+ if ((0, node_fs_1.existsSync)(p)) {
71
+ const raw = (0, node_fs_1.readFileSync)(p, 'utf8');
72
+ const pkg = JSON.parse(raw);
73
+ if (pkg.version)
74
+ return pkg.version;
75
+ }
76
+ }
77
+ return '';
78
+ }
79
+ catch {
80
+ return '';
81
+ }
82
+ }
83
+ function getPackageVersion() {
84
+ return packageVersion();
85
+ }
86
+ function versionText() {
87
+ const v = packageVersion();
88
+ return v ? `admindb v${v}` : 'admindb';
89
+ }
90
+ function isFlag(tok, ...names) {
91
+ return names.includes(tok) || names.some((n) => n.length > 2 && tok.startsWith(n + '='));
92
+ }
93
+ function parseArgs(argv) {
94
+ const args = { help: false, version: false, readonly: false };
95
+ const positional = [];
96
+ let i = 0;
97
+ const invalid = (error) => ({ args, error });
98
+ const takeValue = (flag) => {
99
+ const eq = argv[i].indexOf('=');
100
+ if (eq !== -1)
101
+ return argv[i].slice(eq + 1);
102
+ i += 1;
103
+ return argv[i];
104
+ };
105
+ while (i < argv.length) {
106
+ const tok = argv[i];
107
+ if (tok === '-h' || tok === '--help') {
108
+ args.help = true;
109
+ }
110
+ else if (tok === '-v' || tok === '--version') {
111
+ args.version = true;
112
+ }
113
+ else if (isFlag(tok, '-C', '--config', '--json')) {
114
+ const v = takeValue('--config');
115
+ if (v === undefined)
116
+ return invalid('Missing value for --config.');
117
+ args.configPath = v;
118
+ try {
119
+ const fileConf = (0, config_1.parseConfigFile)(v);
120
+ if (fileConf) {
121
+ if (fileConf.connections)
122
+ args.connections = fileConf.connections;
123
+ if (fileConf.connection)
124
+ args.connection = fileConf.connection;
125
+ if (fileConf.port !== undefined)
126
+ args.port = fileConf.port;
127
+ if (fileConf.host !== undefined)
128
+ args.host = fileConf.host;
129
+ if (fileConf.basePath !== undefined)
130
+ args.basePath = fileConf.basePath;
131
+ if (fileConf.readonly !== undefined)
132
+ args.readonly = fileConf.readonly;
133
+ if (fileConf.serverless !== undefined)
134
+ args.serverless = fileConf.serverless;
135
+ if (fileConf.logLevel !== undefined)
136
+ args.logLevel = fileConf.logLevel;
137
+ if (typeof fileConf.auth === 'boolean')
138
+ args.auth = fileConf.auth;
139
+ else if (fileConf.auth && typeof fileConf.auth === 'object') {
140
+ args.auth = true;
141
+ if (fileConf.auth.username)
142
+ args.authUsername = fileConf.auth.username;
143
+ if (fileConf.auth.password)
144
+ args.authPassword = fileConf.auth.password;
145
+ if (fileConf.auth.secret)
146
+ args.authSecret = fileConf.auth.secret;
147
+ }
148
+ }
149
+ }
150
+ catch (err) {
151
+ return invalid(err.message);
152
+ }
153
+ }
154
+ else if (isFlag(tok, '-p', '--port')) {
155
+ const v = takeValue('--port');
156
+ if (v === undefined)
157
+ return invalid('Missing value for --port.');
158
+ const n = Number.parseInt(v, 10);
159
+ if (!Number.isFinite(n) || n <= 0)
160
+ return invalid(`Invalid port: "${v}".`);
161
+ args.port = n;
162
+ }
163
+ else if (isFlag(tok, '-H', '--host')) {
164
+ const v = takeValue('--host');
165
+ if (v === undefined)
166
+ return invalid('Missing value for --host.');
167
+ args.host = v;
168
+ }
169
+ else if (isFlag(tok, '-c', '--connection', '--conn', '--pg')) {
170
+ const v = takeValue('--connection');
171
+ if (v === undefined)
172
+ return invalid('Missing value for --connection.');
173
+ args.connection = v;
174
+ args.dbPath = v;
175
+ }
176
+ else if (isFlag(tok, '-o', '--open', '--db', '-f', '--file', '--db-path', '--path')) {
177
+ const v = takeValue('--open');
178
+ if (v === undefined)
179
+ return invalid('Missing value for --open / --db-path.');
180
+ args.dbPath = v;
181
+ if ((0, common_1.isPostgresConnectionString)(v)) {
182
+ args.connection = v;
183
+ }
184
+ }
185
+ else if (isFlag(tok, '-d', '--dir', '--folder', '--db-dir')) {
186
+ const v = takeValue('--dir');
187
+ if (v === undefined)
188
+ return invalid('Missing value for --dir / --db-dir.');
189
+ args.dbDir = v;
190
+ }
191
+ else if (isFlag(tok, '--files', '--db-files')) {
192
+ const v = takeValue('--files');
193
+ if (v === undefined)
194
+ return invalid('Missing value for --files / --db-files.');
195
+ args.dbFiles = v
196
+ .split(',')
197
+ .map((s) => s.trim())
198
+ .filter(Boolean);
199
+ }
200
+ else if (isFlag(tok, '-b', '--base-path', '--base')) {
201
+ const v = takeValue('--base-path');
202
+ if (v === undefined)
203
+ return invalid('Missing value for --base-path.');
204
+ args.basePath = v;
205
+ }
206
+ else if (isFlag(tok, '-l', '--log-level')) {
207
+ const v = takeValue('--log-level');
208
+ if (v === undefined)
209
+ return invalid('Missing value for --log-level.');
210
+ if (!LOG_LEVELS.includes(v)) {
211
+ return invalid(`Invalid log level: "${v}". Expected one of: ${LOG_LEVELS.join(', ')}.`);
212
+ }
213
+ args.logLevel = v;
214
+ }
215
+ else if (tok === '-r' || tok === '--readonly' || tok === '--read-only') {
216
+ args.readonly = true;
217
+ }
218
+ else if (tok === '--serverless') {
219
+ args.serverless = true;
220
+ args.readonly = true;
221
+ }
222
+ else if (tok === '--no-auth' || tok === '--disable-auth') {
223
+ args.auth = false;
224
+ }
225
+ else if (tok === '--auth') {
226
+ args.auth = true;
227
+ }
228
+ else if (isFlag(tok, '-u', '--username', '--user', '--auth-username')) {
229
+ const v = takeValue('--username');
230
+ if (v === undefined)
231
+ return invalid('Missing value for --username.');
232
+ args.authUsername = v;
233
+ }
234
+ else if (isFlag(tok, '-P', '--password', '--pass', '--auth-password')) {
235
+ const v = takeValue('--password');
236
+ if (v === undefined)
237
+ return invalid('Missing value for --password.');
238
+ args.authPassword = v;
239
+ }
240
+ else if (isFlag(tok, '--auth-secret', '--secret', '--session-secret')) {
241
+ const v = takeValue('--auth-secret');
242
+ if (v === undefined)
243
+ return invalid('Missing value for --auth-secret.');
244
+ args.authSecret = v;
245
+ }
246
+ else if (tok.startsWith('-') && tok.length > 1) {
247
+ return invalid(`Unknown option: "${tok}".`);
248
+ }
249
+ else {
250
+ positional.push(tok);
251
+ }
252
+ i += 1;
253
+ }
254
+ if (positional.length > 1) {
255
+ return invalid(`Unexpected extra arguments: ${positional.slice(1).join(' ')}.`);
256
+ }
257
+ const p = positional[0];
258
+ if (p) {
259
+ if ((0, common_1.isPostgresConnectionString)(p)) {
260
+ args.connection = p;
261
+ args.dbPath = p;
262
+ }
263
+ else if (p.endsWith('.json') || (0, node_fs_1.existsSync)(p)) {
264
+ try {
265
+ const isJson = p.endsWith('.json');
266
+ if (isJson || ((0, node_fs_1.statSync)(p).isFile() && p.includes('.json'))) {
267
+ args.configPath = p;
268
+ const fileConf = (0, config_1.parseConfigFile)(p);
269
+ if (fileConf) {
270
+ if (fileConf.connections)
271
+ args.connections = fileConf.connections;
272
+ if (fileConf.connection)
273
+ args.connection = fileConf.connection;
274
+ if (fileConf.port !== undefined && args.port === undefined)
275
+ args.port = fileConf.port;
276
+ if (fileConf.host !== undefined && args.host === undefined)
277
+ args.host = fileConf.host;
278
+ if (fileConf.basePath !== undefined && args.basePath === undefined)
279
+ args.basePath = fileConf.basePath;
280
+ if (fileConf.readonly !== undefined)
281
+ args.readonly = fileConf.readonly;
282
+ if (fileConf.serverless !== undefined)
283
+ args.serverless = fileConf.serverless;
284
+ if (fileConf.logLevel !== undefined && args.logLevel === undefined)
285
+ args.logLevel = fileConf.logLevel;
286
+ if (args.auth === undefined) {
287
+ if (typeof fileConf.auth === 'boolean')
288
+ args.auth = fileConf.auth;
289
+ else if (fileConf.auth && typeof fileConf.auth === 'object') {
290
+ args.auth = true;
291
+ if (fileConf.auth.username && !args.authUsername)
292
+ args.authUsername = fileConf.auth.username;
293
+ if (fileConf.auth.password && !args.authPassword)
294
+ args.authPassword = fileConf.auth.password;
295
+ if (fileConf.auth.secret && !args.authSecret)
296
+ args.authSecret = fileConf.auth.secret;
297
+ }
298
+ }
299
+ }
300
+ }
301
+ else if ((0, node_fs_1.statSync)(p).isDirectory()) {
302
+ args.dbDir = p;
303
+ }
304
+ else {
305
+ args.dbPath = p;
306
+ }
307
+ }
308
+ catch (err) {
309
+ if (p.endsWith('.json')) {
310
+ return invalid(err.message);
311
+ }
312
+ args.dbPath = p;
313
+ }
314
+ }
315
+ else {
316
+ args.dbPath = p;
317
+ }
318
+ }
319
+ return { args };
320
+ }