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,128 @@
1
- var __importDefault=this&&this.__importDefault||function(mod){return mod&&mod.__esModule?mod:{default:mod}};Object.defineProperty(exports,"__esModule",{value:!0}),exports.parseConfigFile=parseConfigFile,exports.loadConfig=function(env=process.env){const portRaw=env.ADMINDB_PORT??env.PORT??"45531",port=Number.parseInt(portRaw,10),authDisabled=["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()),dbDirRaw=env.ADMINDB_DB_DIR??env.ADMINDB_DIR??env.DB_DIR,dbFilesRaw=env.ADMINDB_DB_FILES??env.DB_FILES,connectionRaw=env.ADMINDB_CONNECTION??env.DATABASE_URL??env.PG_CONNECTION??env.POSTGRES_URL,dbPathRaw=env.ADMINDB_DB_PATH??env.ADMINDB_PATH??env.DB_PATH??connectionRaw,basePathRaw=env.ADMINDB_BASE_PATH??env.BASE_PATH??"",logLevelRaw=env.ADMINDB_LOG_LEVEL??env.LOG_LEVEL??"info",serverlessRaw=env.ADMINDB_SERVERLESS??env.SERVERLESS??env.IS_SERVERLESS??"",serverless=["1","true","yes","on"].includes(String(serverlessRaw).trim().toLowerCase())||(0,serverless_1.isServerlessEnvironment)(env),readonlyRaw=env.ADMINDB_READONLY??env.READONLY??"",readonly=serverless||["1","true","yes","on"].includes(String(readonlyRaw).trim().toLowerCase());let connections;const configJsonRaw=env.ADMINDB_CONFIG_JSON??env.ADMINDB_CONNECTIONS;if(configJsonRaw)try{const parsed=JSON.parse(configJsonRaw);parsed&&"object"==typeof parsed&&!Array.isArray(parsed)&&(connections=parsed)}catch{}const configPath=env.ADMINDB_CONFIG;if(configPath&&(0,node_fs_1.existsSync)(configPath)){const fileConf=parseConfigFile(configPath);fileConf?.connections&&(connections={...connections??{},...fileConf.connections})}return{host:env.ADMINDB_HOST??env.HOST??"0.0.0.0",port:Number.isFinite(port)?port:45531,dbPath:dbPathRaw?String(dbPathRaw):"admindb.db",connection:connectionRaw?String(connectionRaw):void 0,connections:connections,dbDir:dbDirRaw?String(dbDirRaw):void 0,dbFiles:dbFilesRaw?String(dbFilesRaw).split(",").map(s=>s.trim()).filter(Boolean):void 0,basePath:String(basePathRaw).replace(/\/+$/,""),logLevel:logLevelRaw,readonly:readonly,serverless:serverless,auth:!authDisabled,authUsername:env.ADMINDB_USERNAME??env.ADMINDB_USER?String(env.ADMINDB_USERNAME??env.ADMINDB_USER).trim():void 0,authPassword:env.ADMINDB_PASSWORD??env.ADMINDB_PASS?String(env.ADMINDB_PASSWORD??env.ADMINDB_PASS).trim():void 0,authSecret:env.ADMINDB_SECRET??env.SESSION_SECRET?String(env.ADMINDB_SECRET??env.SESSION_SECRET).trim():void 0}};const node_fs_1=require("node:fs"),node_path_1=__importDefault(require("node:path")),serverless_1=require("../serverless"),common_1=require("../utils/common");function parseConfigFile(filePath){try{const absPath=node_path_1.default.resolve(filePath);if(!(0,node_fs_1.existsSync)(absPath))return null;const content=(0,node_fs_1.readFileSync)(absPath,"utf8"),parsed=JSON.parse(content);if(!parsed||"object"!=typeof parsed||Array.isArray(parsed))return null;const result={},conns={},topDbs=parsed.databases||parsed.connections;if(topDbs&&"object"==typeof topDbs&&!Array.isArray(topDbs))for(const[k,v]of Object.entries(topDbs))"string"==typeof v&&v.trim()?conns[k.trim()]=v.trim():v&&"object"==typeof v&&"string"==typeof v.connection&&(conns[k.trim()]=v.connection.trim());for(const[k,v]of Object.entries(parsed))["databases","connections","port","host","basePath","readonly","serverless","auth","logLevel","username","password","secret"].includes(k)||("string"==typeof v&&v.trim()?conns[k.trim()]=v.trim():v&&"object"==typeof v&&"string"==typeof v.connection&&(conns[k.trim()]=v.connection.trim()));return"string"==typeof parsed.connection&&parsed.connection.trim()&&(result.connection=parsed.connection.trim()),"number"==typeof parsed.port&&(result.port=parsed.port),"string"==typeof parsed.host&&(result.host=parsed.host),"string"==typeof parsed.basePath&&(result.basePath=parsed.basePath),"boolean"==typeof parsed.readonly&&(result.readonly=parsed.readonly),"boolean"==typeof parsed.serverless&&(result.serverless=parsed.serverless),("boolean"==typeof parsed.auth||parsed.auth&&"object"==typeof parsed.auth)&&(result.auth=parsed.auth),"string"==typeof parsed.logLevel&&(result.logLevel=parsed.logLevel),Object.keys(conns).length>0&&(result.connections=conns),result}catch(err){throw new Error(`Failed to parse configuration file "${filePath}": ${(0,common_1.errorMessage)(err)}`)}}
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.parseConfigFile = parseConfigFile;
7
+ exports.loadConfig = loadConfig;
8
+ const node_fs_1 = require("node:fs");
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ const serverless_1 = require("../serverless");
11
+ const common_1 = require("../utils/common");
12
+ function parseConfigFile(filePath) {
13
+ try {
14
+ const absPath = node_path_1.default.resolve(filePath);
15
+ if (!(0, node_fs_1.existsSync)(absPath))
16
+ return null;
17
+ const content = (0, node_fs_1.readFileSync)(absPath, 'utf8');
18
+ const parsed = JSON.parse(content);
19
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
20
+ return null;
21
+ const result = {};
22
+ const conns = {};
23
+ const topDbs = parsed.databases || parsed.connections;
24
+ if (topDbs && typeof topDbs === 'object' && !Array.isArray(topDbs)) {
25
+ for (const [k, v] of Object.entries(topDbs)) {
26
+ if (typeof v === 'string' && v.trim())
27
+ conns[k.trim()] = v.trim();
28
+ else if (v && typeof v === 'object' && typeof v.connection === 'string') {
29
+ conns[k.trim()] = v.connection.trim();
30
+ }
31
+ }
32
+ }
33
+ for (const [k, v] of Object.entries(parsed)) {
34
+ if (['databases', 'connections', 'port', 'host', 'basePath', 'readonly', 'serverless', 'auth', 'logLevel', 'username', 'password', 'secret'].includes(k)) {
35
+ continue;
36
+ }
37
+ if (typeof v === 'string' && v.trim()) {
38
+ conns[k.trim()] = v.trim();
39
+ }
40
+ else if (v && typeof v === 'object' && typeof v.connection === 'string') {
41
+ conns[k.trim()] = v.connection.trim();
42
+ }
43
+ }
44
+ if (typeof parsed.connection === 'string' && parsed.connection.trim()) {
45
+ result.connection = parsed.connection.trim();
46
+ }
47
+ if (typeof parsed.port === 'number')
48
+ result.port = parsed.port;
49
+ if (typeof parsed.host === 'string')
50
+ result.host = parsed.host;
51
+ if (typeof parsed.basePath === 'string')
52
+ result.basePath = parsed.basePath;
53
+ if (typeof parsed.readonly === 'boolean')
54
+ result.readonly = parsed.readonly;
55
+ if (typeof parsed.serverless === 'boolean')
56
+ result.serverless = parsed.serverless;
57
+ if (typeof parsed.auth === 'boolean' || (parsed.auth && typeof parsed.auth === 'object')) {
58
+ result.auth = parsed.auth;
59
+ }
60
+ if (typeof parsed.logLevel === 'string')
61
+ result.logLevel = parsed.logLevel;
62
+ if (Object.keys(conns).length > 0) {
63
+ result.connections = conns;
64
+ }
65
+ return result;
66
+ }
67
+ catch (err) {
68
+ throw new Error(`Failed to parse configuration file "${filePath}": ${(0, common_1.errorMessage)(err)}`);
69
+ }
70
+ }
71
+ function loadConfig(env = process.env) {
72
+ const portRaw = env.ADMINDB_PORT ?? env.PORT ?? '45531';
73
+ const port = Number.parseInt(portRaw, 10);
74
+ const authDisabled = ['0', 'false', 'no', 'off'].includes(String(env.ADMINDB_AUTH ?? '').trim().toLowerCase()) ||
75
+ ['1', 'true', 'yes'].includes(String(env.ADMINDB_NO_AUTH ?? env.ADMINDB_DISABLE_AUTH ?? '').trim().toLowerCase());
76
+ const dbDirRaw = env.ADMINDB_DB_DIR ?? env.ADMINDB_DIR ?? env.DB_DIR;
77
+ const dbFilesRaw = env.ADMINDB_DB_FILES ?? env.DB_FILES;
78
+ const connectionRaw = env.ADMINDB_CONNECTION ?? env.DATABASE_URL ?? env.PG_CONNECTION ?? env.POSTGRES_URL;
79
+ const dbPathRaw = env.ADMINDB_DB_PATH ?? env.ADMINDB_PATH ?? env.DB_PATH ?? connectionRaw;
80
+ const basePathRaw = env.ADMINDB_BASE_PATH ?? env.BASE_PATH ?? '';
81
+ const logLevelRaw = env.ADMINDB_LOG_LEVEL ?? env.LOG_LEVEL ?? 'info';
82
+ const serverlessRaw = env.ADMINDB_SERVERLESS ?? env.SERVERLESS ?? env.IS_SERVERLESS ?? '';
83
+ const serverless = ['1', 'true', 'yes', 'on'].includes(String(serverlessRaw).trim().toLowerCase()) ||
84
+ (0, serverless_1.isServerlessEnvironment)(env);
85
+ const readonlyRaw = env.ADMINDB_READONLY ?? env.READONLY ?? '';
86
+ const readonly = serverless || ['1', 'true', 'yes', 'on'].includes(String(readonlyRaw).trim().toLowerCase());
87
+ let connections;
88
+ const configJsonRaw = env.ADMINDB_CONFIG_JSON ?? env.ADMINDB_CONNECTIONS;
89
+ if (configJsonRaw) {
90
+ try {
91
+ const parsed = JSON.parse(configJsonRaw);
92
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
93
+ connections = parsed;
94
+ }
95
+ }
96
+ catch {
97
+ }
98
+ }
99
+ const configPath = env.ADMINDB_CONFIG;
100
+ if (configPath && (0, node_fs_1.existsSync)(configPath)) {
101
+ const fileConf = parseConfigFile(configPath);
102
+ if (fileConf?.connections) {
103
+ connections = { ...(connections ?? {}), ...fileConf.connections };
104
+ }
105
+ }
106
+ return {
107
+ host: env.ADMINDB_HOST ?? env.HOST ?? '0.0.0.0',
108
+ port: Number.isFinite(port) ? port : 45531,
109
+ dbPath: dbPathRaw ? String(dbPathRaw) : 'admindb.db',
110
+ connection: connectionRaw ? String(connectionRaw) : undefined,
111
+ connections,
112
+ dbDir: dbDirRaw ? String(dbDirRaw) : undefined,
113
+ dbFiles: dbFilesRaw
114
+ ? String(dbFilesRaw)
115
+ .split(',')
116
+ .map((s) => s.trim())
117
+ .filter(Boolean)
118
+ : undefined,
119
+ basePath: String(basePathRaw).replace(/\/+$/, ''),
120
+ logLevel: logLevelRaw,
121
+ readonly,
122
+ serverless,
123
+ auth: !authDisabled,
124
+ authUsername: (env.ADMINDB_USERNAME ?? env.ADMINDB_USER) ? String(env.ADMINDB_USERNAME ?? env.ADMINDB_USER).trim() : undefined,
125
+ authPassword: (env.ADMINDB_PASSWORD ?? env.ADMINDB_PASS) ? String(env.ADMINDB_PASSWORD ?? env.ADMINDB_PASS).trim() : undefined,
126
+ authSecret: (env.ADMINDB_SECRET ?? env.SESSION_SECRET) ? String(env.ADMINDB_SECRET ?? env.SESSION_SECRET).trim() : undefined,
127
+ };
128
+ }
package/dist/cli/index.js CHANGED
@@ -1 +1,20 @@
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("./args"),exports),__exportStar(require("../utils/colors"),exports),__exportStar(require("./config"),exports),__exportStar(require("./runner"),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("./args"), exports);
18
+ __exportStar(require("../utils/colors"), exports);
19
+ __exportStar(require("./config"), exports);
20
+ __exportStar(require("./runner"), exports);
@@ -1 +1,211 @@
1
- var __importDefault=this&&this.__importDefault||function(mod){return mod&&mod.__esModule?mod:{default:mod}};Object.defineProperty(exports,"__esModule",{value:!0}),exports.runCli=function(){const env=(0,config_1.loadConfig)(),parsed=(0,args_1.parseArgs)(process.argv.slice(2));parsed.error&&(console.error(colors_1.c.red(colors_1.c.bold(`Error: ${parsed.error}`))),console.error(),console.error((0,args_1.helpText)()),process.exit(1));const args=parsed.args;args.help&&(console.log((0,args_1.helpText)()),process.exit(0));args.version&&(console.log((0,args_1.versionText)()),process.exit(0));const authEnabled=void 0!==args.auth?args.auth:env.auth,authUsername=args.authUsername??env.authUsername??"admin",authPassword=args.authPassword??env.authPassword,authSecret=args.authSecret??env.authSecret,serverless=void 0!==args.serverless?args.serverless:env.serverless,readonly=serverless||args.readonly||env.readonly,connections=args.connections??env.connections,hasConnections=Boolean(connections&&Object.keys(connections).length>0),config={host:args.host??env.host,port:args.port??env.port,dbPath:args.dbPath??env.dbPath,connection:args.connection??env.connection,connections:connections,dbDir:args.dbDir??env.dbDir,dbFiles:args.dbFiles??env.dbFiles,basePath:(args.basePath??env.basePath).replace(/\/+$/,""),logLevel:args.logLevel??env.logLevel,readonly:readonly,serverless:serverless,auth:!!authEnabled&&{enabled:!0,username:authUsername,password:authPassword,secret:authSecret}},logger=(0,logger_1.createLogger)(config.logLevel,"admindb"),app=(0,express_1.default)();app.disable("x-powered-by");const hasDir=Boolean(config.dbDir),hasFiles=Boolean(config.dbFiles&&config.dbFiles.length>0),explicitFile=args.connection??config.connection??args.dbPath??(process.env.ADMINDB_DB_PATH||process.env.ADMINDB_PATH||process.env.DB_PATH?config.dbPath:void 0),isPg=Boolean(explicitFile&&(explicitFile.startsWith("postgres://")||explicitFile.startsWith("postgresql://"))),useManager=!(!hasConnections&&(Boolean(explicitFile)||isPg)&&!hasDir&&!hasFiles),managerFiles=[...config.dbFiles??[]];useManager&&explicitFile&&!hasConnections&&managerFiles.push(explicitFile);const managerDir=hasDir?config.dbDir:hasFiles||explicitFile||hasConnections?void 0:process.cwd(),allowBrowse=useManager&&(hasDir||!hasFiles&&!explicitFile&&!hasConnections),browseRoot=hasDir?node_path_1.default.resolve(config.dbDir):void 0,router=useManager?(0,app_1.createRouter)({manager:new manager_1.DbManager({dir:managerDir,files:managerFiles,connections:connections,readonly:config.readonly},logger),basePath:config.basePath,logger:logger,readonly:config.readonly,serverless:config.serverless,allowBrowse:allowBrowse,browseRoot:browseRoot,auth:config.auth}):(0,app_1.createRouter)({dbPath:explicitFile,connection:explicitFile,basePath:config.basePath,logger:logger,readonly:config.readonly,serverless:config.serverless,auth:config.auth});app.use(config.basePath||"/",router);const server=app.listen(config.port,config.host,()=>{const v=(0,args_1.getPackageVersion)(),versionStr=v?` v${v}`:"",isAnyHost="0.0.0.0"===config.host||"::"===config.host||""===config.host,localUrl=`http://${isAnyHost?"localhost":config.host}:${config.port}${config.basePath}/`;if(console.log(),console.log(` ${colors_1.c.cyan(colors_1.c.bold("⚡ AdminDB"))}${colors_1.c.dim(versionStr)}`),console.log(),console.log(` ${colors_1.c.green("➜")} ${colors_1.c.bold("Local:")} ${colors_1.c.cyan(localUrl)}`),isAnyHost){const netIp=function(){try{const nets=node_os_1.default.networkInterfaces();for(const name of Object.keys(nets))for(const net of nets[name]??[])if("IPv4"===net.family&&!net.internal)return net.address}catch{}}();if(netIp){const netUrl=`http://${netIp}:${config.port}${config.basePath}/`;console.log(` ${colors_1.c.green("➜")} ${colors_1.c.bold("Network:")} ${colors_1.c.cyan(netUrl)}`)}}if(useManager){const modeDesc=config.serverless?"Manager [Serverless Read-Only]":config.readonly?"Manager [Read-Only]":"Manager";if(console.log(` ${colors_1.c.green("➜")} ${colors_1.c.bold("Mode:")} ${modeDesc}`),args.configPath){const count=Object.keys(connections??{}).length;console.log(` ${colors_1.c.green("➜")} ${colors_1.c.bold("Config:")} ${colors_1.c.cyan(args.configPath)}${count?` (${count} connection(s))`:""}`)}if(hasConnections)for(const[name,target]of Object.entries(connections)){const sanitized=target.replace(/:([^@]+)@/,":****@"),engine=target.startsWith("postgres://")||target.startsWith("postgresql://")?"PostgreSQL":"SQLite";console.log(` ${colors_1.c.dim("•")} ${colors_1.c.bold(name)}: ${colors_1.c.cyan(engine)} ${colors_1.c.dim(sanitized)}`)}else managerDir&&console.log(` ${colors_1.c.green("➜")} ${colors_1.c.bold("Directory:")}${colors_1.c.dim(` ${managerDir}`)}`);allowBrowse?browseRoot&&console.log(` ${colors_1.c.green("➜")} ${colors_1.c.bold("Browse:")} ${colors_1.c.dim(`Limited to ${browseRoot}`)}`):console.log(` ${colors_1.c.green("➜")} ${colors_1.c.bold("Browse:")} ${colors_1.c.dim("Disabled (configured databases only)")}`)}else{const modeDesc=config.serverless?"Single DB [Serverless Read-Only]":config.readonly?"Single DB [Read-Only]":"Single DB";if(console.log(` ${colors_1.c.green("➜")} ${colors_1.c.bold("Mode:")} ${modeDesc}`),args.configPath&&console.log(` ${colors_1.c.green("➜")} ${colors_1.c.bold("Config:")} ${colors_1.c.cyan(args.configPath)}`),isPg){const sanitized=explicitFile?.replace(/:([^@]+)@/,":****@");console.log(` ${colors_1.c.green("➜")} ${colors_1.c.bold("Engine:")} ${colors_1.c.cyan("PostgreSQL")}`),console.log(` ${colors_1.c.green("➜")} ${colors_1.c.bold("Database:")} ${sanitized}`)}else console.log(` ${colors_1.c.green("➜")} ${colors_1.c.bold("Engine:")} ${colors_1.c.cyan("SQLite")}`),console.log(` ${colors_1.c.green("➜")} ${colors_1.c.bold("Database:")} ${explicitFile}`)}if(authEnabled){"admin"===authUsername&&(!authPassword||"admin"===authPassword||authPassword===auth_1.DEFAULT_PASSWORD_HASH)?(console.log(` ${colors_1.c.green("➜")} ${colors_1.c.bold("Auth:")} User: ${colors_1.c.bold(authUsername)} ${colors_1.c.yellow("(default password)")}`),console.log(),console.log(` ${colors_1.c.yellow("⚠")} ${colors_1.c.yellow("Default password in use (admin).")} Generate a secure hash with:`),console.log(` ${colors_1.c.dim("npm run generatehash")} and set ${colors_1.c.cyan("ADMINDB_PASSWORD")} or ${colors_1.c.cyan("-P <hash>")}`)):console.log(` ${colors_1.c.green("➜")} ${colors_1.c.bold("Auth:")} User: ${colors_1.c.bold(authUsername)}`)}else console.log(` ${colors_1.c.yellow("➜")} ${colors_1.c.bold("Auth:")} ${colors_1.c.yellow("Disabled (--no-auth)")}`),console.log(` ${colors_1.c.dim("Warning: Anyone with network access can view and modify databases.")}`);console.log()}),shutdown=signal=>{console.log(`\n ${colors_1.c.dim(`Received ${signal}, shutting down…`)}`),server.close(()=>{console.log(` ${colors_1.c.green("✓")} ${colors_1.c.dim("AdminDB stopped.")}\n`),process.exit(0)}),setTimeout(()=>process.exit(0),2e3).unref()};process.on("SIGINT",()=>shutdown("SIGINT")),process.on("SIGTERM",()=>shutdown("SIGTERM"))};const express_1=__importDefault(require("express")),node_os_1=__importDefault(require("node:os")),node_path_1=__importDefault(require("node:path")),app_1=require("../app"),config_1=require("./config"),logger_1=require("../utils/logger"),manager_1=require("../db/manager"),args_1=require("./args"),auth_1=require("../auth"),colors_1=require("../utils/colors");
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.runCli = runCli;
7
+ const express_1 = __importDefault(require("express"));
8
+ const node_os_1 = __importDefault(require("node:os"));
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ const app_1 = require("../app");
11
+ const config_1 = require("./config");
12
+ const logger_1 = require("../utils/logger");
13
+ const manager_1 = require("../db/manager");
14
+ const args_1 = require("./args");
15
+ const auth_1 = require("../auth");
16
+ const common_1 = require("../utils/common");
17
+ const colors_1 = require("../utils/colors");
18
+ function runCli() {
19
+ const env = (0, config_1.loadConfig)();
20
+ const parsed = (0, args_1.parseArgs)(process.argv.slice(2));
21
+ if (parsed.error) {
22
+ console.error(colors_1.c.red(colors_1.c.bold(`Error: ${parsed.error}`)));
23
+ console.error();
24
+ console.error((0, args_1.helpText)());
25
+ process.exit(1);
26
+ }
27
+ const args = parsed.args;
28
+ if (args.help) {
29
+ console.log((0, args_1.helpText)());
30
+ process.exit(0);
31
+ }
32
+ if (args.version) {
33
+ console.log((0, args_1.versionText)());
34
+ process.exit(0);
35
+ }
36
+ const authEnabled = args.auth !== undefined ? args.auth : env.auth;
37
+ const authUsername = args.authUsername ?? env.authUsername ?? 'admin';
38
+ const authPassword = args.authPassword ?? env.authPassword;
39
+ const authSecret = args.authSecret ?? env.authSecret;
40
+ const serverless = args.serverless !== undefined ? args.serverless : env.serverless;
41
+ const readonly = serverless || args.readonly || env.readonly;
42
+ const connections = args.connections ?? env.connections;
43
+ const hasConnections = Boolean(connections && Object.keys(connections).length > 0);
44
+ const config = {
45
+ host: args.host ?? env.host,
46
+ port: args.port ?? env.port,
47
+ dbPath: args.dbPath ?? env.dbPath,
48
+ connection: args.connection ?? env.connection,
49
+ connections,
50
+ dbDir: args.dbDir ?? env.dbDir,
51
+ dbFiles: args.dbFiles ?? env.dbFiles,
52
+ basePath: (args.basePath ?? env.basePath).replace(/\/+$/, ''),
53
+ logLevel: args.logLevel ?? env.logLevel,
54
+ readonly,
55
+ serverless,
56
+ auth: authEnabled
57
+ ? {
58
+ enabled: true,
59
+ username: authUsername,
60
+ password: authPassword,
61
+ secret: authSecret,
62
+ }
63
+ : false,
64
+ };
65
+ const logger = (0, logger_1.createLogger)(config.logLevel, 'admindb');
66
+ const app = (0, express_1.default)();
67
+ app.disable('x-powered-by');
68
+ const hasDir = Boolean(config.dbDir);
69
+ const hasFiles = Boolean(config.dbFiles && config.dbFiles.length > 0);
70
+ const explicitFile = args.connection || args.dbPath || config.connection || config.dbPath;
71
+ const isPg = Boolean(explicitFile && (0, common_1.isPostgresConnectionString)(explicitFile));
72
+ const singleFileOnly = !hasConnections && (Boolean(explicitFile) || isPg) && !hasDir && !hasFiles;
73
+ const useManager = !singleFileOnly;
74
+ const managerFiles = [...(config.dbFiles ?? [])];
75
+ if (useManager && explicitFile && !hasConnections)
76
+ managerFiles.push(explicitFile);
77
+ const managerDir = hasDir ? config.dbDir : hasFiles || explicitFile || hasConnections ? undefined : process.cwd();
78
+ const allowBrowse = useManager && (hasDir || (!hasFiles && !explicitFile && !hasConnections));
79
+ const browseRoot = hasDir ? node_path_1.default.resolve(config.dbDir) : undefined;
80
+ const router = useManager
81
+ ? (0, app_1.createRouter)({
82
+ manager: new manager_1.DbManager({ dir: managerDir, files: managerFiles, connections, readonly: config.readonly }, logger),
83
+ basePath: config.basePath,
84
+ logger,
85
+ readonly: config.readonly,
86
+ serverless: config.serverless,
87
+ allowBrowse,
88
+ browseRoot,
89
+ auth: config.auth,
90
+ })
91
+ : (0, app_1.createRouter)({
92
+ dbPath: explicitFile,
93
+ connection: explicitFile,
94
+ basePath: config.basePath,
95
+ logger,
96
+ readonly: config.readonly,
97
+ serverless: config.serverless,
98
+ auth: config.auth,
99
+ });
100
+ app.use(config.basePath || '/', router);
101
+ function getNetworkIp() {
102
+ try {
103
+ const nets = node_os_1.default.networkInterfaces();
104
+ for (const name of Object.keys(nets)) {
105
+ for (const net of nets[name] ?? []) {
106
+ if (net.family === 'IPv4' && !net.internal) {
107
+ return net.address;
108
+ }
109
+ }
110
+ }
111
+ }
112
+ catch {
113
+ }
114
+ return undefined;
115
+ }
116
+ const server = app.listen(config.port, config.host, () => {
117
+ const v = (0, args_1.getPackageVersion)();
118
+ const versionStr = v ? ` v${v}` : '';
119
+ const isAnyHost = config.host === '0.0.0.0' || config.host === '::' || config.host === '';
120
+ const localHost = isAnyHost ? 'localhost' : config.host;
121
+ const localUrl = `http://${localHost}:${config.port}${config.basePath}/`;
122
+ console.log();
123
+ console.log(` ${colors_1.c.cyan(colors_1.c.bold('⚡ AdminDB'))}${colors_1.c.dim(versionStr)}`);
124
+ console.log();
125
+ console.log(` ${colors_1.c.green('➜')} ${colors_1.c.bold('Local:')} ${colors_1.c.cyan(localUrl)}`);
126
+ if (isAnyHost) {
127
+ const netIp = getNetworkIp();
128
+ if (netIp) {
129
+ const netUrl = `http://${netIp}:${config.port}${config.basePath}/`;
130
+ console.log(` ${colors_1.c.green('➜')} ${colors_1.c.bold('Network:')} ${colors_1.c.cyan(netUrl)}`);
131
+ }
132
+ }
133
+ if (useManager) {
134
+ const modeDesc = config.serverless
135
+ ? 'Manager [Serverless Read-Only]'
136
+ : config.readonly
137
+ ? 'Manager [Read-Only]'
138
+ : 'Manager';
139
+ console.log(` ${colors_1.c.green('➜')} ${colors_1.c.bold('Mode:')} ${modeDesc}`);
140
+ if (args.configPath) {
141
+ const count = Object.keys(connections ?? {}).length;
142
+ console.log(` ${colors_1.c.green('➜')} ${colors_1.c.bold('Config:')} ${colors_1.c.cyan(args.configPath)}${count ? ` (${count} connection(s))` : ''}`);
143
+ }
144
+ if (hasConnections) {
145
+ for (const [name, target] of Object.entries(connections)) {
146
+ const sanitized = (0, common_1.sanitizeConnectionString)(target);
147
+ const isTargetPg = (0, common_1.isPostgresConnectionString)(target);
148
+ const engine = isTargetPg ? 'PostgreSQL' : 'SQLite';
149
+ console.log(` ${colors_1.c.dim('•')} ${colors_1.c.bold(name)}: ${colors_1.c.cyan(engine)} ${colors_1.c.dim(sanitized)}`);
150
+ }
151
+ }
152
+ else if (managerDir) {
153
+ console.log(` ${colors_1.c.green('➜')} ${colors_1.c.bold('Directory:')}${colors_1.c.dim(` ${managerDir}`)}`);
154
+ }
155
+ if (!allowBrowse) {
156
+ console.log(` ${colors_1.c.green('➜')} ${colors_1.c.bold('Browse:')} ${colors_1.c.dim('Disabled (configured databases only)')}`);
157
+ }
158
+ else if (browseRoot) {
159
+ console.log(` ${colors_1.c.green('➜')} ${colors_1.c.bold('Browse:')} ${colors_1.c.dim(`Limited to ${browseRoot}`)}`);
160
+ }
161
+ }
162
+ else {
163
+ const modeDesc = config.serverless
164
+ ? 'Single DB [Serverless Read-Only]'
165
+ : config.readonly
166
+ ? 'Single DB [Read-Only]'
167
+ : 'Single DB';
168
+ console.log(` ${colors_1.c.green('➜')} ${colors_1.c.bold('Mode:')} ${modeDesc}`);
169
+ if (args.configPath) {
170
+ console.log(` ${colors_1.c.green('➜')} ${colors_1.c.bold('Config:')} ${colors_1.c.cyan(args.configPath)}`);
171
+ }
172
+ if (isPg) {
173
+ const sanitized = (0, common_1.sanitizeConnectionString)(explicitFile || '');
174
+ console.log(` ${colors_1.c.green('➜')} ${colors_1.c.bold('Engine:')} ${colors_1.c.cyan('PostgreSQL')}`);
175
+ console.log(` ${colors_1.c.green('➜')} ${colors_1.c.bold('Database:')} ${sanitized}`);
176
+ }
177
+ else {
178
+ console.log(` ${colors_1.c.green('➜')} ${colors_1.c.bold('Engine:')} ${colors_1.c.cyan('SQLite')}`);
179
+ console.log(` ${colors_1.c.green('➜')} ${colors_1.c.bold('Database:')} ${explicitFile}`);
180
+ }
181
+ }
182
+ if (authEnabled) {
183
+ const isDefaultPass = authUsername === 'admin' &&
184
+ (!authPassword || authPassword === 'admin' || authPassword === auth_1.DEFAULT_PASSWORD_HASH);
185
+ if (isDefaultPass) {
186
+ console.log(` ${colors_1.c.green('➜')} ${colors_1.c.bold('Auth:')} User: ${colors_1.c.bold(authUsername)} ${colors_1.c.yellow('(default password)')}`);
187
+ console.log();
188
+ console.log(` ${colors_1.c.yellow('⚠')} ${colors_1.c.yellow('Default password in use (admin).')} Generate a secure hash with:`);
189
+ console.log(` ${colors_1.c.dim('npm run generatehash')} and set ${colors_1.c.cyan('ADMINDB_PASSWORD')} or ${colors_1.c.cyan('-P <hash>')}`);
190
+ }
191
+ else {
192
+ console.log(` ${colors_1.c.green('➜')} ${colors_1.c.bold('Auth:')} User: ${colors_1.c.bold(authUsername)}`);
193
+ }
194
+ }
195
+ else {
196
+ console.log(` ${colors_1.c.yellow('➜')} ${colors_1.c.bold('Auth:')} ${colors_1.c.yellow('Disabled (--no-auth)')}`);
197
+ console.log(` ${colors_1.c.dim('Warning: Anyone with network access can view and modify databases.')}`);
198
+ }
199
+ console.log();
200
+ });
201
+ const shutdown = (signal) => {
202
+ console.log(`\n ${colors_1.c.dim(`Received ${signal}, shutting down…`)}`);
203
+ server.close(() => {
204
+ console.log(` ${colors_1.c.green('✓')} ${colors_1.c.dim('AdminDB stopped.')}\n`);
205
+ process.exit(0);
206
+ });
207
+ setTimeout(() => process.exit(0), 2000).unref();
208
+ };
209
+ process.on('SIGINT', () => shutdown('SIGINT'));
210
+ process.on('SIGTERM', () => shutdown('SIGTERM'));
211
+ }
package/dist/cli.js CHANGED
@@ -1,2 +1,5 @@
1
1
  #!/usr/bin/env node
2
- Object.defineProperty(exports,"__esModule",{value:!0});(0,require("./cli/runner").runCli)();
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const runner_1 = require("./cli/runner");
5
+ (0, runner_1.runCli)();
@@ -0,0 +1 @@
1
+ import type {IDatabase,TableInfoData} from '../db/index';import{type ColumnGeneratorConfig,type ColumnPlan,type ErChainConfig,type ErChainResult,type ErChainScope} from './types';export interface ErGraphNode{name:string;info:TableInfoData;columns:ColumnGeneratorConfig[];parents:{table:string; from :string;to:string;notnull:boolean;}[];children:{table:string; from :string;to:string;notnull:boolean;}[];hasSelfRef:boolean;depth:number;}export declare function buildErGraph(tables:TableInfoData[]):Map<string,ErGraphNode>;export declare function resolveErChain(graph:Map<string,ErGraphNode>,rootTable:string,scope?:ErChainScope):ErGraphNode[];export declare function topologicalSort(nodes:ErGraphNode[]):{sorted:ErGraphNode[];cycleDetected:boolean;cycleTables:string[];};export declare function getErChainConfig(db:IDatabase,rootTable:string,scope?:ErChainScope):Promise<ErChainConfig>;export declare function generateChainRows(db:IDatabase,chainConfig:ErChainConfig,customPlans?:Record<string,Record<string,ColumnPlan>>,customCounts?:Record<string,number>,truncate?:boolean):Promise<ErChainResult>;export declare function buildChainInsertSql(executionOrder:string[],tableResults:ErChainResult['tableResults']):string;export declare function executeChainInsert(db:IDatabase,chainResult:ErChainResult,truncate?:boolean):Promise<{inserted:Record<string,number>;totalInserted:number;elapsedMs:number;warnings:string[];}>;