admindb 2.2.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.
- package/README.md +368 -364
- package/dist/app.js +6 -9
- package/dist/auth/config.d.ts +1 -1
- package/dist/auth/config.js +18 -30
- package/dist/auth/crypto.js +11 -21
- package/dist/auth/middleware.js +9 -16
- package/dist/auth/routes.js +9 -15
- package/dist/cli/args.d.ts +1 -1
- package/dist/cli/args.js +15 -26
- package/dist/cli/config.js +6 -16
- package/dist/cli/runner.js +4 -7
- package/dist/data/chain.d.ts +1 -1
- package/dist/data/chain.js +40 -617
- package/dist/data/datasets.d.ts +1 -1
- package/dist/data/datasets.js +43 -1
- package/dist/data/detector.js +45 -21
- package/dist/data/engine.d.ts +1 -1
- package/dist/data/engine.js +483 -142
- package/dist/data/index.d.ts +1 -1
- package/dist/data/index.js +8 -0
- package/dist/data/prng.d.ts +1 -0
- package/dist/data/prng.js +73 -0
- package/dist/data/registry.d.ts +1 -0
- package/dist/data/registry.js +805 -0
- package/dist/data/schema-graph.d.ts +1 -0
- package/dist/data/schema-graph.js +210 -0
- package/dist/data/strategies.d.ts +1 -1
- package/dist/data/strategies.js +44 -283
- package/dist/data/templates.d.ts +1 -0
- package/dist/data/templates.js +134 -0
- package/dist/data/types.d.ts +1 -1
- package/dist/data/validator.d.ts +1 -0
- package/dist/data/validator.js +160 -0
- package/dist/db/database.js +9 -14
- package/dist/db/export.js +32 -55
- package/dist/db/filters.d.ts +1 -1
- package/dist/db/filters.js +15 -38
- package/dist/db/introspection.js +4 -7
- package/dist/db/manager.js +27 -51
- package/dist/db/migrations.js +7 -14
- package/dist/db/postgres.js +30 -78
- package/dist/index.d.ts +1 -1
- package/dist/public/css/app.css +1 -1
- package/dist/public/js/browse.js +1 -1
- package/dist/public/js/inspector.js +1 -1
- package/dist/public/js/query.js +1 -1
- package/dist/public/js/seed.js +1 -1
- package/dist/routes/api/erd.js +3 -8
- package/dist/routes/api/helpers.d.ts +1 -1
- package/dist/routes/api/helpers.js +75 -25
- package/dist/routes/api/import-export.js +1 -2
- package/dist/routes/api/query.js +2 -6
- package/dist/routes/api/rows.js +4 -8
- package/dist/routes/api/seed.js +35 -21
- package/dist/routes/api/tables.js +15 -30
- package/dist/routes/databases.js +8 -26
- package/dist/routes/pages.js +67 -18
- package/dist/serverless.js +15 -40
- package/dist/sql/generator.d.ts +1 -1
- package/dist/sql/generator.js +35 -109
- package/dist/types/api.d.ts +1 -1
- package/dist/utils/common.d.ts +1 -1
- package/dist/utils/common.js +45 -85
- package/dist/utils/csv.d.ts +1 -1
- package/dist/utils/csv.js +11 -17
- package/dist/utils/datatype.js +46 -60
- package/dist/utils/icons.js +8 -8
- package/dist/views/layouts/main.hbs +1 -87
- package/dist/views/pages/databases.hbs +1 -233
- package/dist/views/pages/designer.hbs +1 -89
- package/dist/views/pages/erd.hbs +1 -1241
- package/dist/views/pages/error.hbs +1 -10
- package/dist/views/pages/form.hbs +1 -59
- package/dist/views/pages/home.hbs +1 -220
- package/dist/views/pages/info.hbs +1 -107
- package/dist/views/pages/login.hbs +1 -98
- package/dist/views/pages/query.hbs +1 -129
- package/dist/views/pages/schema.hbs +1 -298
- package/dist/views/pages/seed-select.hbs +251 -0
- package/dist/views/pages/seed.hbs +256 -414
- package/dist/views/pages/table.hbs +1 -327
- package/dist/views/partials/navbar.hbs +1 -83
- package/dist/views/partials/sidebar.hbs +1 -129
- package/docs/API.md +149 -149
- package/docs/EXAMPLES.md +579 -579
- package/docs/SECURITY.md +265 -265
- package/package.json +79 -79
package/dist/app.js
CHANGED
|
@@ -81,14 +81,11 @@ function createSingleDbApp(options) {
|
|
|
81
81
|
if (options.db) {
|
|
82
82
|
db = options.db;
|
|
83
83
|
}
|
|
84
|
+
else if (isPg) {
|
|
85
|
+
db = new postgres_1.PostgresDatabase(options.pgOptions || rawTarget, logger, { readonly });
|
|
86
|
+
}
|
|
84
87
|
else {
|
|
85
|
-
|
|
86
|
-
const conn = options.pgOptions || rawTarget;
|
|
87
|
-
db = new postgres_1.PostgresDatabase(conn, logger, { readonly });
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
db = new database_1.SqliteDatabase(options.dbPath ?? 'admindb.db', logger, { readonly });
|
|
91
|
-
}
|
|
88
|
+
db = new database_1.SqliteDatabase(options.dbPath ?? 'admindb.db', logger, { readonly });
|
|
92
89
|
}
|
|
93
90
|
const databasesUrl = options.databasesUrl;
|
|
94
91
|
const authConfig = (0, auth_1.resolveAuthConfig)(options.auth);
|
|
@@ -208,7 +205,7 @@ function createManagerApp(options) {
|
|
|
208
205
|
}
|
|
209
206
|
let sub = subApps.get(dbId);
|
|
210
207
|
if (!sub) {
|
|
211
|
-
const subBase = `${basePath
|
|
208
|
+
const subBase = `${basePath || ''}/${encodeURIComponent(dbId)}`;
|
|
212
209
|
const effectiveRo = readonly || manager.isDbReadOnly(dbId);
|
|
213
210
|
sub = createSingleDbApp({
|
|
214
211
|
db: manager.open(dbId, effectiveRo),
|
|
@@ -216,7 +213,7 @@ function createManagerApp(options) {
|
|
|
216
213
|
logger,
|
|
217
214
|
logLevel: options.logLevel,
|
|
218
215
|
dbId,
|
|
219
|
-
databasesUrl: `${basePath
|
|
216
|
+
databasesUrl: `${basePath || ''}/`,
|
|
220
217
|
readonly: effectiveRo,
|
|
221
218
|
serverless: isServerless,
|
|
222
219
|
auth: authConfig,
|
package/dist/auth/config.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{type AuthConfig,type ResolvedAuthConfig} from './types';export declare
|
|
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;
|
package/dist/auth/config.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.resetPersistentSecretCache =
|
|
6
|
+
exports.resetPersistentSecretCache = void 0;
|
|
7
7
|
exports.getOrCreatePersistentSecret = getOrCreatePersistentSecret;
|
|
8
8
|
exports.resolveAuthConfig = resolveAuthConfig;
|
|
9
9
|
const node_crypto_1 = require("node:crypto");
|
|
@@ -12,18 +12,18 @@ const node_os_1 = __importDefault(require("node:os"));
|
|
|
12
12
|
const node_path_1 = __importDefault(require("node:path"));
|
|
13
13
|
const types_1 = require("./types");
|
|
14
14
|
let cachedFallbackSecret = null;
|
|
15
|
-
|
|
15
|
+
const resetPersistentSecretCache = () => {
|
|
16
16
|
cachedFallbackSecret = null;
|
|
17
|
-
}
|
|
17
|
+
};
|
|
18
|
+
exports.resetPersistentSecretCache = resetPersistentSecretCache;
|
|
18
19
|
function getOrCreatePersistentSecret(env = process.env) {
|
|
19
20
|
const envSecret = env.ADMINDB_SECRET ?? env.SESSION_SECRET;
|
|
20
21
|
if (envSecret && String(envSecret).trim()) {
|
|
21
22
|
return String(envSecret).trim();
|
|
22
23
|
}
|
|
23
24
|
const customSecretFile = env.ADMINDB_SECRET_FILE ? String(env.ADMINDB_SECRET_FILE).trim() : undefined;
|
|
24
|
-
if (!customSecretFile && cachedFallbackSecret)
|
|
25
|
+
if (!customSecretFile && cachedFallbackSecret)
|
|
25
26
|
return cachedFallbackSecret;
|
|
26
|
-
}
|
|
27
27
|
const candidatePaths = [];
|
|
28
28
|
if (customSecretFile) {
|
|
29
29
|
candidatePaths.push(node_path_1.default.resolve(customSecretFile));
|
|
@@ -31,66 +31,54 @@ function getOrCreatePersistentSecret(env = process.env) {
|
|
|
31
31
|
else {
|
|
32
32
|
try {
|
|
33
33
|
const home = node_os_1.default.homedir();
|
|
34
|
-
if (home)
|
|
34
|
+
if (home)
|
|
35
35
|
candidatePaths.push(node_path_1.default.join(home, '.admindb-secret'));
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
catch {
|
|
39
36
|
}
|
|
37
|
+
catch { }
|
|
40
38
|
try {
|
|
41
39
|
const tmp = node_os_1.default.tmpdir();
|
|
42
|
-
if (tmp)
|
|
40
|
+
if (tmp)
|
|
43
41
|
candidatePaths.push(node_path_1.default.join(tmp, '.admindb-secret'));
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
catch {
|
|
47
42
|
}
|
|
43
|
+
catch { }
|
|
48
44
|
}
|
|
49
45
|
for (const filePath of candidatePaths) {
|
|
50
46
|
try {
|
|
51
47
|
if ((0, node_fs_1.existsSync)(filePath)) {
|
|
52
48
|
const content = (0, node_fs_1.readFileSync)(filePath, 'utf8').trim();
|
|
53
49
|
if (content.length >= 16) {
|
|
54
|
-
if (!customSecretFile)
|
|
50
|
+
if (!customSecretFile)
|
|
55
51
|
cachedFallbackSecret = content;
|
|
56
|
-
}
|
|
57
52
|
return content;
|
|
58
53
|
}
|
|
59
54
|
}
|
|
60
55
|
}
|
|
61
|
-
catch {
|
|
62
|
-
}
|
|
56
|
+
catch { }
|
|
63
57
|
}
|
|
64
58
|
const newSecret = (0, node_crypto_1.randomBytes)(32).toString('hex');
|
|
65
59
|
for (const filePath of candidatePaths) {
|
|
66
60
|
try {
|
|
67
61
|
const dir = node_path_1.default.dirname(filePath);
|
|
68
|
-
if (!(0, node_fs_1.existsSync)(dir))
|
|
62
|
+
if (!(0, node_fs_1.existsSync)(dir))
|
|
69
63
|
(0, node_fs_1.mkdirSync)(dir, { recursive: true });
|
|
70
|
-
}
|
|
71
64
|
(0, node_fs_1.writeFileSync)(filePath, newSecret, { encoding: 'utf8', mode: 0o600 });
|
|
72
|
-
if (!customSecretFile)
|
|
65
|
+
if (!customSecretFile)
|
|
73
66
|
cachedFallbackSecret = newSecret;
|
|
74
|
-
}
|
|
75
67
|
return newSecret;
|
|
76
68
|
}
|
|
77
|
-
catch {
|
|
78
|
-
}
|
|
69
|
+
catch { }
|
|
79
70
|
}
|
|
80
|
-
if (!customSecretFile)
|
|
71
|
+
if (!customSecretFile)
|
|
81
72
|
cachedFallbackSecret = newSecret;
|
|
82
|
-
}
|
|
83
73
|
return newSecret;
|
|
84
74
|
}
|
|
85
75
|
function resolveAuthConfig(optionsAuth, env = process.env) {
|
|
86
76
|
const envDisabled = ['0', 'false', 'no', 'off'].includes(String(env.ADMINDB_AUTH ?? '').trim().toLowerCase()) ||
|
|
87
77
|
['1', 'true', 'yes'].includes(String(env.ADMINDB_NO_AUTH ?? env.ADMINDB_DISABLE_AUTH ?? '').trim().toLowerCase());
|
|
88
78
|
let enabled = true;
|
|
89
|
-
let username = (env.ADMINDB_USERNAME ?? env.ADMINDB_USER
|
|
90
|
-
let password = (env.ADMINDB_PASSWORD ?? env.ADMINDB_PASS
|
|
91
|
-
let secret = (env.ADMINDB_SECRET ?? env.SESSION_SECRET)
|
|
92
|
-
? String(env.ADMINDB_SECRET ?? env.SESSION_SECRET).trim()
|
|
93
|
-
: getOrCreatePersistentSecret(env);
|
|
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);
|
|
94
82
|
let sessionDurationMs = types_1.DEFAULT_SESSION_DURATION_MS;
|
|
95
83
|
if (optionsAuth === false || envDisabled) {
|
|
96
84
|
enabled = false;
|
package/dist/auth/crypto.js
CHANGED
|
@@ -28,17 +28,14 @@ function constantTimeCompare(a, b) {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
function verifyPassword(candidatePassword, storedPasswordOrHash) {
|
|
31
|
-
if (typeof candidatePassword !== 'string' || typeof storedPasswordOrHash !== 'string')
|
|
31
|
+
if (typeof candidatePassword !== 'string' || typeof storedPasswordOrHash !== 'string')
|
|
32
32
|
return false;
|
|
33
|
-
}
|
|
34
33
|
if (storedPasswordOrHash.startsWith('scrypt:')) {
|
|
35
34
|
const parts = storedPasswordOrHash.split(':');
|
|
36
35
|
if (parts.length === 3 && parts[1] && parts[2]) {
|
|
37
|
-
const salt = parts[1];
|
|
38
|
-
const expectedKeyHex = parts[2];
|
|
39
36
|
try {
|
|
40
|
-
const derivedKey = (0, node_crypto_1.scryptSync)(candidatePassword,
|
|
41
|
-
return constantTimeCompare(derivedKey,
|
|
37
|
+
const derivedKey = (0, node_crypto_1.scryptSync)(candidatePassword, parts[1], 64).toString('hex');
|
|
38
|
+
return constantTimeCompare(derivedKey, parts[2]);
|
|
42
39
|
}
|
|
43
40
|
catch {
|
|
44
41
|
return false;
|
|
@@ -57,9 +54,8 @@ function parseCookies(cookieHeader) {
|
|
|
57
54
|
continue;
|
|
58
55
|
const key = pair.slice(0, idx).trim();
|
|
59
56
|
let val = pair.slice(idx + 1).trim();
|
|
60
|
-
if (val.startsWith('"') && val.endsWith('"') && val.length >= 2)
|
|
57
|
+
if (val.startsWith('"') && val.endsWith('"') && val.length >= 2)
|
|
61
58
|
val = val.slice(1, -1);
|
|
62
|
-
}
|
|
63
59
|
if (key) {
|
|
64
60
|
try {
|
|
65
61
|
cookies[key] = decodeURIComponent(val);
|
|
@@ -72,9 +68,7 @@ function parseCookies(cookieHeader) {
|
|
|
72
68
|
return cookies;
|
|
73
69
|
}
|
|
74
70
|
function createSessionToken(username, secret, durationMs = types_1.DEFAULT_SESSION_DURATION_MS) {
|
|
75
|
-
const
|
|
76
|
-
const userPayload = Buffer.from(username, 'utf8').toString('base64url');
|
|
77
|
-
const data = `${userPayload}.${expiresAt}`;
|
|
71
|
+
const data = `${Buffer.from(username, 'utf8').toString('base64url')}.${Date.now() + durationMs}`;
|
|
78
72
|
const signature = (0, node_crypto_1.createHmac)('sha256', secret).update(data).digest('base64url');
|
|
79
73
|
return `${data}.${signature}`;
|
|
80
74
|
}
|
|
@@ -87,22 +81,18 @@ function verifySessionToken(token, secret, expectedUser) {
|
|
|
87
81
|
const [userPayload, expiresStr, providedSignature] = parts;
|
|
88
82
|
const data = `${userPayload}.${expiresStr}`;
|
|
89
83
|
const expectedSignature = (0, node_crypto_1.createHmac)('sha256', secret).update(data).digest('base64url');
|
|
90
|
-
if (!constantTimeCompare(providedSignature, expectedSignature))
|
|
84
|
+
if (!constantTimeCompare(providedSignature, expectedSignature))
|
|
91
85
|
return null;
|
|
92
|
-
}
|
|
93
86
|
const expiresAt = Number.parseInt(expiresStr, 10);
|
|
94
|
-
if (!Number.isFinite(expiresAt) || Date.now() > expiresAt)
|
|
87
|
+
if (!Number.isFinite(expiresAt) || Date.now() > expiresAt)
|
|
95
88
|
return null;
|
|
96
|
-
}
|
|
97
|
-
let username;
|
|
98
89
|
try {
|
|
99
|
-
username = Buffer.from(userPayload, 'base64url').toString('utf8');
|
|
90
|
+
const username = Buffer.from(userPayload, 'base64url').toString('utf8');
|
|
91
|
+
if (expectedUser && !constantTimeCompare(username, expectedUser))
|
|
92
|
+
return null;
|
|
93
|
+
return username;
|
|
100
94
|
}
|
|
101
95
|
catch {
|
|
102
96
|
return null;
|
|
103
97
|
}
|
|
104
|
-
if (expectedUser && !constantTimeCompare(username, expectedUser)) {
|
|
105
|
-
return null;
|
|
106
|
-
}
|
|
107
|
-
return username;
|
|
108
98
|
}
|
package/dist/auth/middleware.js
CHANGED
|
@@ -5,14 +5,14 @@ exports.createAuthMiddleware = createAuthMiddleware;
|
|
|
5
5
|
const types_1 = require("./types");
|
|
6
6
|
const crypto_1 = require("./crypto");
|
|
7
7
|
function authenticateRequest(req, config) {
|
|
8
|
-
if (!config.enabled)
|
|
8
|
+
if (!config.enabled)
|
|
9
9
|
return { authenticated: true, user: 'anonymous' };
|
|
10
|
-
}
|
|
11
10
|
const authHeader = req.headers.authorization;
|
|
12
11
|
if (authHeader) {
|
|
13
12
|
const [scheme, credentials] = authHeader.split(/\s+/, 2);
|
|
14
13
|
if (scheme && credentials) {
|
|
15
|
-
|
|
14
|
+
const lowerScheme = scheme.toLowerCase();
|
|
15
|
+
if (lowerScheme === 'basic') {
|
|
16
16
|
try {
|
|
17
17
|
const decoded = Buffer.from(credentials, 'base64').toString('utf8');
|
|
18
18
|
const colonIdx = decoded.indexOf(':');
|
|
@@ -27,24 +27,21 @@ function authenticateRequest(req, config) {
|
|
|
27
27
|
catch {
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
-
else if (
|
|
30
|
+
else if (lowerScheme === 'bearer') {
|
|
31
31
|
const user = (0, crypto_1.verifySessionToken)(credentials, config.secret, config.username);
|
|
32
|
-
if (user)
|
|
32
|
+
if (user)
|
|
33
33
|
return { authenticated: true, user };
|
|
34
|
-
}
|
|
35
34
|
if ((0, crypto_1.verifyPassword)(credentials, config.password)) {
|
|
36
35
|
return { authenticated: true, user: config.username };
|
|
37
36
|
}
|
|
38
37
|
}
|
|
39
38
|
}
|
|
40
39
|
}
|
|
41
|
-
const
|
|
42
|
-
const sessionCookie = cookies[types_1.SESSION_COOKIE_NAME];
|
|
40
|
+
const sessionCookie = (0, crypto_1.parseCookies)(req.headers.cookie)[types_1.SESSION_COOKIE_NAME];
|
|
43
41
|
if (sessionCookie) {
|
|
44
42
|
const user = (0, crypto_1.verifySessionToken)(sessionCookie, config.secret, config.username);
|
|
45
|
-
if (user)
|
|
43
|
+
if (user)
|
|
46
44
|
return { authenticated: true, user };
|
|
47
|
-
}
|
|
48
45
|
}
|
|
49
46
|
return { authenticated: false };
|
|
50
47
|
}
|
|
@@ -57,11 +54,7 @@ function createAuthMiddleware(config, basePath, _logger) {
|
|
|
57
54
|
return next();
|
|
58
55
|
}
|
|
59
56
|
const p = req.path;
|
|
60
|
-
if (p.startsWith('/css/') ||
|
|
61
|
-
p.startsWith('/js/') ||
|
|
62
|
-
p === '/favicon.ico' ||
|
|
63
|
-
p === '/login' ||
|
|
64
|
-
p === '/logout') {
|
|
57
|
+
if (p.startsWith('/css/') || p.startsWith('/js/') || p === '/favicon.ico' || p === '/login' || p === '/logout') {
|
|
65
58
|
return next();
|
|
66
59
|
}
|
|
67
60
|
const auth = authenticateRequest(req, config);
|
|
@@ -70,7 +63,7 @@ function createAuthMiddleware(config, basePath, _logger) {
|
|
|
70
63
|
req.user = auth.user;
|
|
71
64
|
return next();
|
|
72
65
|
}
|
|
73
|
-
if (p.startsWith('/api/') || req.xhr ||
|
|
66
|
+
if (p.startsWith('/api/') || req.xhr || req.headers.accept?.includes('application/json')) {
|
|
74
67
|
res.setHeader('WWW-Authenticate', 'Basic realm="AdminDB", Bearer');
|
|
75
68
|
res.status(401).json({
|
|
76
69
|
success: false,
|
package/dist/auth/routes.js
CHANGED
|
@@ -6,15 +6,13 @@ const middleware_1 = require("./middleware");
|
|
|
6
6
|
const crypto_1 = require("./crypto");
|
|
7
7
|
function registerAuthRoutes(router, config, basePath, logger) {
|
|
8
8
|
router.get('/login', (req, res) => {
|
|
9
|
-
if (!config.enabled)
|
|
9
|
+
if (!config.enabled)
|
|
10
10
|
return res.redirect(`${basePath}/`);
|
|
11
|
-
}
|
|
12
11
|
const auth = (0, middleware_1.authenticateRequest)(req, config);
|
|
12
|
+
const nextUrl = String(req.query.next ?? '').trim();
|
|
13
13
|
if (auth.authenticated) {
|
|
14
|
-
const nextUrl = String(req.query.next ?? '').trim();
|
|
15
14
|
return res.redirect(nextUrl.startsWith('/') ? nextUrl : `${basePath}/`);
|
|
16
15
|
}
|
|
17
|
-
const nextUrl = String(req.query.next ?? '').trim();
|
|
18
16
|
res.render('pages/login', {
|
|
19
17
|
title: 'Sign in',
|
|
20
18
|
noSidebar: true,
|
|
@@ -24,17 +22,15 @@ function registerAuthRoutes(router, config, basePath, logger) {
|
|
|
24
22
|
});
|
|
25
23
|
});
|
|
26
24
|
router.post('/login', (req, res) => {
|
|
27
|
-
if (!config.enabled)
|
|
25
|
+
if (!config.enabled)
|
|
28
26
|
return res.redirect(`${basePath}/`);
|
|
29
|
-
}
|
|
30
27
|
const username = String(req.body?.username ?? '').trim();
|
|
31
28
|
const password = String(req.body?.password ?? '').trim();
|
|
32
29
|
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);
|
|
30
|
+
const isValid = (0, crypto_1.constantTimeCompare)(username, config.username) && (0, crypto_1.verifyPassword)(password, config.password);
|
|
35
31
|
if (!isValid) {
|
|
36
32
|
logger.warn(`Failed login attempt for user "${username || '<empty>'}"`);
|
|
37
|
-
if (req.xhr ||
|
|
33
|
+
if (req.xhr || req.headers.accept?.includes('application/json')) {
|
|
38
34
|
return res.status(401).json({ success: false, error: 'Invalid username or password.' });
|
|
39
35
|
}
|
|
40
36
|
return res.status(401).render('pages/login', {
|
|
@@ -51,16 +47,14 @@ function registerAuthRoutes(router, config, basePath, logger) {
|
|
|
51
47
|
const expiresUtc = new Date(Date.now() + config.sessionDurationMs).toUTCString();
|
|
52
48
|
const cookiePath = basePath || '/';
|
|
53
49
|
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 ||
|
|
50
|
+
if (req.xhr || req.headers.accept?.includes('application/json')) {
|
|
55
51
|
return res.json({ success: true, message: 'Logged in successfully.', user: config.username, token });
|
|
56
52
|
}
|
|
57
|
-
|
|
58
|
-
res.redirect(target);
|
|
53
|
+
res.redirect(nextUrl.startsWith('/') ? nextUrl : `${basePath}/`);
|
|
59
54
|
});
|
|
60
55
|
const handleLogout = (req, res) => {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if (req.xhr || (req.headers.accept && req.headers.accept.includes('application/json'))) {
|
|
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')) {
|
|
64
58
|
return res.json({ success: true, message: 'Logged out successfully.' });
|
|
65
59
|
}
|
|
66
60
|
res.redirect(`${basePath}/login`);
|
package/dist/cli/args.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import type {LogLevel} from '../utils/logger';export interface CliArgs{help:boolean;version:boolean;host?:string;port?:number;dbPath?:string;connection?:string;connections?:Record<string,string>;configPath?:string;dbDir?:string;dbFiles?:string[];basePath?:string;logLevel?:LogLevel;readonly :boolean;serverless?:boolean;auth?:boolean;authUsername?:string;authPassword?:string;authSecret?:string;}export declare function helpText():string;export declare
|
|
1
|
+
import type {LogLevel} from '../utils/logger';export interface CliArgs{help:boolean;version:boolean;host?:string;port?:number;dbPath?:string;connection?:string;connections?:Record<string,string>;configPath?:string;dbDir?:string;dbFiles?:string[];basePath?:string;logLevel?:LogLevel;readonly :boolean;serverless?:boolean;auth?:boolean;authUsername?:string;authPassword?:string;authSecret?:string;}export declare function helpText():string;export declare const getPackageVersion:()=>string;export declare const versionText:()=>string;export declare function parseArgs(argv:string[]):{args:CliArgs;error?:string;};
|
package/dist/cli/args.js
CHANGED
|
@@ -3,9 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.versionText = exports.getPackageVersion = void 0;
|
|
6
7
|
exports.helpText = helpText;
|
|
7
|
-
exports.getPackageVersion = getPackageVersion;
|
|
8
|
-
exports.versionText = versionText;
|
|
9
8
|
exports.parseArgs = parseArgs;
|
|
10
9
|
const node_fs_1 = require("node:fs");
|
|
11
10
|
const node_path_1 = __importDefault(require("node:path"));
|
|
@@ -68,28 +67,23 @@ function packageVersion() {
|
|
|
68
67
|
];
|
|
69
68
|
for (const p of candidates) {
|
|
70
69
|
if ((0, node_fs_1.existsSync)(p)) {
|
|
71
|
-
const
|
|
72
|
-
const pkg = JSON.parse(raw);
|
|
70
|
+
const pkg = JSON.parse((0, node_fs_1.readFileSync)(p, 'utf8'));
|
|
73
71
|
if (pkg.version)
|
|
74
72
|
return pkg.version;
|
|
75
73
|
}
|
|
76
74
|
}
|
|
77
|
-
return '';
|
|
78
|
-
}
|
|
79
|
-
catch {
|
|
80
|
-
return '';
|
|
81
75
|
}
|
|
76
|
+
catch { }
|
|
77
|
+
return '';
|
|
82
78
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
function versionText() {
|
|
79
|
+
const getPackageVersion = () => packageVersion();
|
|
80
|
+
exports.getPackageVersion = getPackageVersion;
|
|
81
|
+
const versionText = () => {
|
|
87
82
|
const v = packageVersion();
|
|
88
83
|
return v ? `admindb v${v}` : 'admindb';
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
84
|
+
};
|
|
85
|
+
exports.versionText = versionText;
|
|
86
|
+
const isFlag = (tok, ...names) => names.includes(tok) || names.some((n) => n.length > 2 && tok.startsWith(`${n}=`));
|
|
93
87
|
function parseArgs(argv) {
|
|
94
88
|
const args = { help: false, version: false, readonly: false };
|
|
95
89
|
const positional = [];
|
|
@@ -99,7 +93,7 @@ function parseArgs(argv) {
|
|
|
99
93
|
const eq = argv[i].indexOf('=');
|
|
100
94
|
if (eq !== -1)
|
|
101
95
|
return argv[i].slice(eq + 1);
|
|
102
|
-
i
|
|
96
|
+
i++;
|
|
103
97
|
return argv[i];
|
|
104
98
|
};
|
|
105
99
|
while (i < argv.length) {
|
|
@@ -178,9 +172,8 @@ function parseArgs(argv) {
|
|
|
178
172
|
if (v === undefined)
|
|
179
173
|
return invalid('Missing value for --open / --db-path.');
|
|
180
174
|
args.dbPath = v;
|
|
181
|
-
if ((0, common_1.isPostgresConnectionString)(v))
|
|
175
|
+
if ((0, common_1.isPostgresConnectionString)(v))
|
|
182
176
|
args.connection = v;
|
|
183
|
-
}
|
|
184
177
|
}
|
|
185
178
|
else if (isFlag(tok, '-d', '--dir', '--folder', '--db-dir')) {
|
|
186
179
|
const v = takeValue('--dir');
|
|
@@ -192,10 +185,7 @@ function parseArgs(argv) {
|
|
|
192
185
|
const v = takeValue('--files');
|
|
193
186
|
if (v === undefined)
|
|
194
187
|
return invalid('Missing value for --files / --db-files.');
|
|
195
|
-
args.dbFiles = v
|
|
196
|
-
.split(',')
|
|
197
|
-
.map((s) => s.trim())
|
|
198
|
-
.filter(Boolean);
|
|
188
|
+
args.dbFiles = v.split(',').map((s) => s.trim()).filter(Boolean);
|
|
199
189
|
}
|
|
200
190
|
else if (isFlag(tok, '-b', '--base-path', '--base')) {
|
|
201
191
|
const v = takeValue('--base-path');
|
|
@@ -249,7 +239,7 @@ function parseArgs(argv) {
|
|
|
249
239
|
else {
|
|
250
240
|
positional.push(tok);
|
|
251
241
|
}
|
|
252
|
-
i
|
|
242
|
+
i++;
|
|
253
243
|
}
|
|
254
244
|
if (positional.length > 1) {
|
|
255
245
|
return invalid(`Unexpected extra arguments: ${positional.slice(1).join(' ')}.`);
|
|
@@ -306,9 +296,8 @@ function parseArgs(argv) {
|
|
|
306
296
|
}
|
|
307
297
|
}
|
|
308
298
|
catch (err) {
|
|
309
|
-
if (p.endsWith('.json'))
|
|
299
|
+
if (p.endsWith('.json'))
|
|
310
300
|
return invalid(err.message);
|
|
311
|
-
}
|
|
312
301
|
args.dbPath = p;
|
|
313
302
|
}
|
|
314
303
|
}
|
package/dist/cli/config.js
CHANGED
|
@@ -41,9 +41,8 @@ function parseConfigFile(filePath) {
|
|
|
41
41
|
conns[k.trim()] = v.connection.trim();
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
|
-
if (typeof parsed.connection === 'string' && parsed.connection.trim())
|
|
44
|
+
if (typeof parsed.connection === 'string' && parsed.connection.trim())
|
|
45
45
|
result.connection = parsed.connection.trim();
|
|
46
|
-
}
|
|
47
46
|
if (typeof parsed.port === 'number')
|
|
48
47
|
result.port = parsed.port;
|
|
49
48
|
if (typeof parsed.host === 'string')
|
|
@@ -54,14 +53,12 @@ function parseConfigFile(filePath) {
|
|
|
54
53
|
result.readonly = parsed.readonly;
|
|
55
54
|
if (typeof parsed.serverless === 'boolean')
|
|
56
55
|
result.serverless = parsed.serverless;
|
|
57
|
-
if (typeof parsed.auth === 'boolean' || (parsed.auth && typeof parsed.auth === 'object'))
|
|
56
|
+
if (typeof parsed.auth === 'boolean' || (parsed.auth && typeof parsed.auth === 'object'))
|
|
58
57
|
result.auth = parsed.auth;
|
|
59
|
-
}
|
|
60
58
|
if (typeof parsed.logLevel === 'string')
|
|
61
59
|
result.logLevel = parsed.logLevel;
|
|
62
|
-
if (Object.keys(conns).length > 0)
|
|
60
|
+
if (Object.keys(conns).length > 0)
|
|
63
61
|
result.connections = conns;
|
|
64
|
-
}
|
|
65
62
|
return result;
|
|
66
63
|
}
|
|
67
64
|
catch (err) {
|
|
@@ -89,12 +86,10 @@ function loadConfig(env = process.env) {
|
|
|
89
86
|
if (configJsonRaw) {
|
|
90
87
|
try {
|
|
91
88
|
const parsed = JSON.parse(configJsonRaw);
|
|
92
|
-
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed))
|
|
89
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed))
|
|
93
90
|
connections = parsed;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
catch {
|
|
97
91
|
}
|
|
92
|
+
catch { }
|
|
98
93
|
}
|
|
99
94
|
const configPath = env.ADMINDB_CONFIG;
|
|
100
95
|
if (configPath && (0, node_fs_1.existsSync)(configPath)) {
|
|
@@ -110,12 +105,7 @@ function loadConfig(env = process.env) {
|
|
|
110
105
|
connection: connectionRaw ? String(connectionRaw) : undefined,
|
|
111
106
|
connections,
|
|
112
107
|
dbDir: dbDirRaw ? String(dbDirRaw) : undefined,
|
|
113
|
-
dbFiles: dbFilesRaw
|
|
114
|
-
? String(dbFilesRaw)
|
|
115
|
-
.split(',')
|
|
116
|
-
.map((s) => s.trim())
|
|
117
|
-
.filter(Boolean)
|
|
118
|
-
: undefined,
|
|
108
|
+
dbFiles: dbFilesRaw ? String(dbFilesRaw).split(',').map((s) => s.trim()).filter(Boolean) : undefined,
|
|
119
109
|
basePath: String(basePathRaw).replace(/\/+$/, ''),
|
|
120
110
|
logLevel: logLevelRaw,
|
|
121
111
|
readonly,
|
package/dist/cli/runner.js
CHANGED
|
@@ -66,7 +66,7 @@ function runCli() {
|
|
|
66
66
|
const app = (0, express_1.default)();
|
|
67
67
|
app.disable('x-powered-by');
|
|
68
68
|
const hasDir = Boolean(config.dbDir);
|
|
69
|
-
const hasFiles = Boolean(config.dbFiles
|
|
69
|
+
const hasFiles = Boolean(config.dbFiles?.length);
|
|
70
70
|
const explicitFile = args.connection || args.dbPath || config.connection || config.dbPath;
|
|
71
71
|
const isPg = Boolean(explicitFile && (0, common_1.isPostgresConnectionString)(explicitFile));
|
|
72
72
|
const singleFileOnly = !hasConnections && (Boolean(explicitFile) || isPg) && !hasDir && !hasFiles;
|
|
@@ -103,14 +103,12 @@ function runCli() {
|
|
|
103
103
|
const nets = node_os_1.default.networkInterfaces();
|
|
104
104
|
for (const name of Object.keys(nets)) {
|
|
105
105
|
for (const net of nets[name] ?? []) {
|
|
106
|
-
if (net.family === 'IPv4' && !net.internal)
|
|
106
|
+
if (net.family === 'IPv4' && !net.internal)
|
|
107
107
|
return net.address;
|
|
108
|
-
}
|
|
109
108
|
}
|
|
110
109
|
}
|
|
111
110
|
}
|
|
112
|
-
catch {
|
|
113
|
-
}
|
|
111
|
+
catch { }
|
|
114
112
|
return undefined;
|
|
115
113
|
}
|
|
116
114
|
const server = app.listen(config.port, config.host, () => {
|
|
@@ -126,8 +124,7 @@ function runCli() {
|
|
|
126
124
|
if (isAnyHost) {
|
|
127
125
|
const netIp = getNetworkIp();
|
|
128
126
|
if (netIp) {
|
|
129
|
-
|
|
130
|
-
console.log(` ${colors_1.c.green('➜')} ${colors_1.c.bold('Network:')} ${colors_1.c.cyan(netUrl)}`);
|
|
127
|
+
console.log(` ${colors_1.c.green('➜')} ${colors_1.c.bold('Network:')} ${colors_1.c.cyan(`http://${netIp}:${config.port}${config.basePath}/`)}`);
|
|
131
128
|
}
|
|
132
129
|
}
|
|
133
130
|
if (useManager) {
|
package/dist/data/chain.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import type {IDatabase,TableInfoData} from '../db/index';import{
|
|
1
|
+
import type {IDatabase,TableInfoData} from '../db/index';import{buildChainInsertSql} from './engine';import type {ColumnPlan,ErChainConfig,ErChainResult,ErChainScope,TableGenerationMode} from './types';export{buildChainInsertSql};export declare const buildErGraph:(tables:TableInfoData[])=>Map<string,import("./schema-graph").SchemaNode>;export declare const resolveErChain:(graph:Map<string,any>,rootTable:string,scope?:ErChainScope)=>import("./schema-graph").SchemaNode[];export declare const topologicalSort:(nodes:any[])=>{sorted:import("./schema-graph").SchemaNode[];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,modes?:Record<string,TableGenerationMode>,seed?:number|null):Promise<ErChainResult>;export declare function executeChainInsert(db:IDatabase,chainResult:ErChainResult,truncate?:boolean):Promise<{inserted:Record<string,number>;totalInserted:number;elapsedMs:number;warnings:string[];}>;
|