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/db/manager.js
CHANGED
|
@@ -27,12 +27,11 @@ class DbManager {
|
|
|
27
27
|
this.logger = logger ?? (0, logger_1.createLogger)('info', 'admindb');
|
|
28
28
|
this.dir = opts.dir ? node_path_1.default.resolve(opts.dir) : undefined;
|
|
29
29
|
this.createDir = this.dir ?? process.cwd();
|
|
30
|
-
this.readonlyMode =
|
|
30
|
+
this.readonlyMode = Boolean(opts.readonly);
|
|
31
31
|
if (this.dir)
|
|
32
32
|
(0, node_fs_1.mkdirSync)(this.dir, { recursive: true });
|
|
33
|
-
if (opts.connections)
|
|
33
|
+
if (opts.connections)
|
|
34
34
|
this.registerConnections(opts.connections);
|
|
35
|
-
}
|
|
36
35
|
this.registerDirFiles();
|
|
37
36
|
this.registerExplicitFiles(opts.files ?? []);
|
|
38
37
|
}
|
|
@@ -48,28 +47,24 @@ class DbManager {
|
|
|
48
47
|
}
|
|
49
48
|
registerConnections(conns) {
|
|
50
49
|
for (const [name, target] of Object.entries(conns ?? {})) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (!cleanTarget)
|
|
55
|
-
continue;
|
|
56
|
-
this.addNamedTarget(name.trim(), cleanTarget);
|
|
50
|
+
const cleanTarget = String(target ?? '').trim();
|
|
51
|
+
if (name && cleanTarget)
|
|
52
|
+
this.addNamedTarget(name.trim(), cleanTarget);
|
|
57
53
|
}
|
|
58
54
|
}
|
|
59
55
|
registerDirFiles() {
|
|
60
56
|
if (!this.dir)
|
|
61
57
|
return;
|
|
62
|
-
let names = [];
|
|
63
58
|
try {
|
|
64
|
-
names = (0, node_fs_1.readdirSync)(this.dir)
|
|
59
|
+
const names = (0, node_fs_1.readdirSync)(this.dir)
|
|
65
60
|
.filter((n) => this.isDbFile(n))
|
|
66
61
|
.sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' }));
|
|
62
|
+
for (const name of names)
|
|
63
|
+
this.addFile(node_path_1.default.join(this.dir, name));
|
|
67
64
|
}
|
|
68
65
|
catch (err) {
|
|
69
66
|
this.logger.warn(`Failed to list database directory ${this.dir}: ${(0, common_1.errorMessage)(err)}`);
|
|
70
67
|
}
|
|
71
|
-
for (const name of names)
|
|
72
|
-
this.addFile(node_path_1.default.join(this.dir, name));
|
|
73
68
|
}
|
|
74
69
|
registerExplicitFiles(files) {
|
|
75
70
|
for (const f of files ?? []) {
|
|
@@ -79,12 +74,9 @@ class DbManager {
|
|
|
79
74
|
if ((0, common_1.isPostgresConnectionString)(raw)) {
|
|
80
75
|
let name = 'postgres';
|
|
81
76
|
try {
|
|
82
|
-
|
|
83
|
-
name = u.pathname.replace(/^\//, '') || 'postgres';
|
|
84
|
-
}
|
|
85
|
-
catch {
|
|
86
|
-
name = 'postgres';
|
|
77
|
+
name = new URL(raw).pathname.replace(/^\//, '') || 'postgres';
|
|
87
78
|
}
|
|
79
|
+
catch { }
|
|
88
80
|
this.addNamedTarget(name, raw);
|
|
89
81
|
continue;
|
|
90
82
|
}
|
|
@@ -99,10 +91,8 @@ class DbManager {
|
|
|
99
91
|
addNamedTarget(name, target) {
|
|
100
92
|
let id = name;
|
|
101
93
|
let n = 2;
|
|
102
|
-
while (this.pathById.has(id))
|
|
103
|
-
id = `${name}__${n}`;
|
|
104
|
-
n += 1;
|
|
105
|
-
}
|
|
94
|
+
while (this.pathById.has(id))
|
|
95
|
+
id = `${name}__${n++}`;
|
|
106
96
|
this.files.push(target);
|
|
107
97
|
this.idByPath.set(target, id);
|
|
108
98
|
this.pathById.set(id, target);
|
|
@@ -116,9 +106,7 @@ class DbManager {
|
|
|
116
106
|
let n = 2;
|
|
117
107
|
while (this.pathById.has(id)) {
|
|
118
108
|
const ext = node_path_1.default.extname(base);
|
|
119
|
-
|
|
120
|
-
id = `${stem}__${n}${ext}`;
|
|
121
|
-
n += 1;
|
|
109
|
+
id = `${node_path_1.default.basename(base, ext)}__${n++}${ext}`;
|
|
122
110
|
}
|
|
123
111
|
this.files.push(abs);
|
|
124
112
|
this.idByPath.set(abs, id);
|
|
@@ -151,8 +139,7 @@ class DbManager {
|
|
|
151
139
|
size = st.size;
|
|
152
140
|
modified = st.mtime.toISOString();
|
|
153
141
|
}
|
|
154
|
-
catch {
|
|
155
|
-
}
|
|
142
|
+
catch { }
|
|
156
143
|
entries.push({
|
|
157
144
|
id,
|
|
158
145
|
name: this.nameById.get(id) ?? node_path_1.default.basename(target),
|
|
@@ -176,8 +163,7 @@ class DbManager {
|
|
|
176
163
|
if (this.readonlyMode && !readonly) {
|
|
177
164
|
throw new Error('Manager is in global read-only mode — databases cannot be made writable.');
|
|
178
165
|
}
|
|
179
|
-
|
|
180
|
-
if (current === readonly)
|
|
166
|
+
if (this.readonlyById.get(id) === readonly)
|
|
181
167
|
return;
|
|
182
168
|
this.readonlyById.set(id, readonly);
|
|
183
169
|
const db = this.openDbs.get(id);
|
|
@@ -185,8 +171,7 @@ class DbManager {
|
|
|
185
171
|
try {
|
|
186
172
|
db.close();
|
|
187
173
|
}
|
|
188
|
-
catch {
|
|
189
|
-
}
|
|
174
|
+
catch { }
|
|
190
175
|
this.openDbs.delete(id);
|
|
191
176
|
}
|
|
192
177
|
}
|
|
@@ -200,18 +185,15 @@ class DbManager {
|
|
|
200
185
|
try {
|
|
201
186
|
db.close();
|
|
202
187
|
}
|
|
203
|
-
catch {
|
|
204
|
-
}
|
|
188
|
+
catch { }
|
|
205
189
|
this.openDbs.delete(id);
|
|
206
190
|
db = undefined;
|
|
207
191
|
}
|
|
208
192
|
if (!db) {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
db = new database_1.SqliteDatabase(target, this.logger.child(`db:${id}`), { readonly: isRo });
|
|
214
|
-
}
|
|
193
|
+
const childLogger = this.logger.child(`db:${id}`);
|
|
194
|
+
db = (0, common_1.isPostgresConnectionString)(target)
|
|
195
|
+
? new postgres_1.PostgresDatabase(target, childLogger, { readonly: isRo })
|
|
196
|
+
: new database_1.SqliteDatabase(target, childLogger, { readonly: isRo });
|
|
215
197
|
this.openDbs.set(id, db);
|
|
216
198
|
}
|
|
217
199
|
return db;
|
|
@@ -220,9 +202,8 @@ class DbManager {
|
|
|
220
202
|
const abs = node_path_1.default.resolve(String(absPath ?? '').trim());
|
|
221
203
|
if (!abs)
|
|
222
204
|
throw new Error('Database path is required.');
|
|
223
|
-
if (!(0, node_fs_1.existsSync)(abs) || !(0, node_fs_1.statSync)(abs).isFile())
|
|
205
|
+
if (!(0, node_fs_1.existsSync)(abs) || !(0, node_fs_1.statSync)(abs).isFile())
|
|
224
206
|
throw new Error(`Not a file: ${abs}`);
|
|
225
|
-
}
|
|
226
207
|
const existing = this.idByPath.get(abs);
|
|
227
208
|
if (existing) {
|
|
228
209
|
if (readonly !== undefined)
|
|
@@ -253,8 +234,7 @@ class DbManager {
|
|
|
253
234
|
}
|
|
254
235
|
async countTables(id) {
|
|
255
236
|
try {
|
|
256
|
-
const
|
|
257
|
-
const r = await db.listTables();
|
|
237
|
+
const r = await this.open(id).listTables();
|
|
258
238
|
return r.success ? (r.data?.length ?? 0) : 0;
|
|
259
239
|
}
|
|
260
240
|
catch {
|
|
@@ -284,8 +264,7 @@ class DbManager {
|
|
|
284
264
|
let cleanName = String(name ?? '').trim();
|
|
285
265
|
if (!cleanName) {
|
|
286
266
|
try {
|
|
287
|
-
|
|
288
|
-
cleanName = u.pathname.replace(/^\//, '') || 'postgres';
|
|
267
|
+
cleanName = new URL(raw).pathname.replace(/^\//, '') || 'postgres';
|
|
289
268
|
}
|
|
290
269
|
catch {
|
|
291
270
|
cleanName = 'postgres';
|
|
@@ -310,8 +289,7 @@ class DbManager {
|
|
|
310
289
|
try {
|
|
311
290
|
db.close();
|
|
312
291
|
}
|
|
313
|
-
catch {
|
|
314
|
-
}
|
|
292
|
+
catch { }
|
|
315
293
|
this.openDbs.delete(id);
|
|
316
294
|
}
|
|
317
295
|
this.pathById.delete(id);
|
|
@@ -324,8 +302,7 @@ class DbManager {
|
|
|
324
302
|
try {
|
|
325
303
|
(0, node_fs_1.unlinkSync)(target + suffix);
|
|
326
304
|
}
|
|
327
|
-
catch {
|
|
328
|
-
}
|
|
305
|
+
catch { }
|
|
329
306
|
}
|
|
330
307
|
}
|
|
331
308
|
}
|
|
@@ -334,8 +311,7 @@ class DbManager {
|
|
|
334
311
|
try {
|
|
335
312
|
db.close();
|
|
336
313
|
}
|
|
337
|
-
catch {
|
|
338
|
-
}
|
|
314
|
+
catch { }
|
|
339
315
|
}
|
|
340
316
|
this.openDbs.clear();
|
|
341
317
|
}
|
package/dist/db/migrations.js
CHANGED
|
@@ -12,9 +12,8 @@ function modifyTableStructureSync(db, table, transformColumns, renamedOldCol, re
|
|
|
12
12
|
for (const ix of indexRows) {
|
|
13
13
|
if (ix.unique) {
|
|
14
14
|
const ixCols = db.prepare(`PRAGMA index_info(${(0, generator_1.quoteIdentifier)(ix.name)})`).all().map((c) => c.name);
|
|
15
|
-
if (ixCols.length === 1)
|
|
15
|
+
if (ixCols.length === 1)
|
|
16
16
|
uniqueCols.add(ixCols[0]);
|
|
17
|
-
}
|
|
18
17
|
}
|
|
19
18
|
}
|
|
20
19
|
const userIndexes = [];
|
|
@@ -32,7 +31,7 @@ function modifyTableStructureSync(db, table, transformColumns, renamedOldCol, re
|
|
|
32
31
|
}
|
|
33
32
|
userIndexes.push({
|
|
34
33
|
name: ixName,
|
|
35
|
-
unique:
|
|
34
|
+
unique: Boolean(ix.unique),
|
|
36
35
|
columns: ixCols.map((c) => (renamedOldCol && renamedNewCol && c === renamedOldCol ? renamedNewCol : c)),
|
|
37
36
|
});
|
|
38
37
|
}
|
|
@@ -43,7 +42,7 @@ function modifyTableStructureSync(db, table, transformColumns, renamedOldCol, re
|
|
|
43
42
|
name: c.name,
|
|
44
43
|
type: c.type,
|
|
45
44
|
primaryKey: c.pk > 0,
|
|
46
|
-
notNull:
|
|
45
|
+
notNull: Boolean(c.notnull),
|
|
47
46
|
unique: uniqueCols.has(c.name),
|
|
48
47
|
defaultValue: c.dflt_value,
|
|
49
48
|
foreignKey: existingFk ? { table: existingFk.table, column: existingFk.to ?? '' } : null,
|
|
@@ -56,17 +55,13 @@ function modifyTableStructureSync(db, table, transformColumns, renamedOldCol, re
|
|
|
56
55
|
const matchingOldCols = [];
|
|
57
56
|
const matchingNewCols = [];
|
|
58
57
|
for (const nc of newCols) {
|
|
59
|
-
|
|
60
|
-
if (renamedOldCol && renamedNewCol && nc.name === renamedNewCol) {
|
|
61
|
-
sourceColName = renamedOldCol;
|
|
62
|
-
}
|
|
58
|
+
const sourceColName = renamedOldCol && renamedNewCol && nc.name === renamedNewCol ? renamedOldCol : nc.name;
|
|
63
59
|
if (oldColMap.has(sourceColName)) {
|
|
64
60
|
matchingOldCols.push((0, generator_1.quoteIdentifier)(sourceColName));
|
|
65
61
|
matchingNewCols.push((0, generator_1.quoteIdentifier)(nc.name));
|
|
66
62
|
}
|
|
67
63
|
}
|
|
68
|
-
db.exec('PRAGMA foreign_keys = OFF;');
|
|
69
|
-
db.exec('BEGIN TRANSACTION;');
|
|
64
|
+
db.exec('PRAGMA foreign_keys = OFF;\nBEGIN TRANSACTION;');
|
|
70
65
|
try {
|
|
71
66
|
db.exec(createSql);
|
|
72
67
|
if (matchingOldCols.length > 0) {
|
|
@@ -80,11 +75,9 @@ function modifyTableStructureSync(db, table, transformColumns, renamedOldCol, re
|
|
|
80
75
|
try {
|
|
81
76
|
db.exec(`CREATE ${uq}INDEX IF NOT EXISTS ${(0, generator_1.quoteIdentifier)(ix.name)} ON ${(0, generator_1.quoteIdentifier)(table)} (${ixColsStr});`);
|
|
82
77
|
}
|
|
83
|
-
catch {
|
|
84
|
-
}
|
|
78
|
+
catch { }
|
|
85
79
|
}
|
|
86
|
-
|
|
87
|
-
if (fkCheck.length > 0) {
|
|
80
|
+
if (db.prepare('PRAGMA foreign_key_check;').all().length > 0) {
|
|
88
81
|
throw new Error('Foreign key constraint check failed after schema modification.');
|
|
89
82
|
}
|
|
90
83
|
db.exec('COMMIT;');
|
package/dist/db/postgres.js
CHANGED
|
@@ -30,21 +30,17 @@ class PostgresDatabase {
|
|
|
30
30
|
if (sslMode === 'disable') {
|
|
31
31
|
sslConfig = false;
|
|
32
32
|
}
|
|
33
|
-
else if (sslConfig === undefined) {
|
|
34
|
-
|
|
35
|
-
sslConfig = { rejectUnauthorized: false };
|
|
36
|
-
}
|
|
33
|
+
else if (sslConfig === undefined && (sslMode === 'require' || sslMode === 'prefer' || sslMode === 'no-verify' || !isLocal)) {
|
|
34
|
+
sslConfig = { rejectUnauthorized: false };
|
|
37
35
|
}
|
|
38
36
|
}
|
|
39
|
-
catch {
|
|
40
|
-
}
|
|
37
|
+
catch { }
|
|
41
38
|
}
|
|
42
39
|
else if (opts.host && sslConfig === undefined) {
|
|
43
40
|
const host = opts.host.toLowerCase();
|
|
44
41
|
const isLocal = host === 'localhost' || host === '127.0.0.1' || host === '::1' || host === '0.0.0.0';
|
|
45
|
-
if (!isLocal)
|
|
42
|
+
if (!isLocal)
|
|
46
43
|
sslConfig = { rejectUnauthorized: false };
|
|
47
|
-
}
|
|
48
44
|
}
|
|
49
45
|
const poolConfig = {
|
|
50
46
|
connectionString: opts.connectionString,
|
|
@@ -86,8 +82,7 @@ class PostgresDatabase {
|
|
|
86
82
|
}
|
|
87
83
|
async tryRun(fn) {
|
|
88
84
|
try {
|
|
89
|
-
|
|
90
|
-
return { success: true, data };
|
|
85
|
+
return { success: true, data: await fn() };
|
|
91
86
|
}
|
|
92
87
|
catch (err) {
|
|
93
88
|
const msg = (0, common_1.errorMessage)(err);
|
|
@@ -109,8 +104,7 @@ class PostgresDatabase {
|
|
|
109
104
|
try {
|
|
110
105
|
await client.query('ROLLBACK');
|
|
111
106
|
}
|
|
112
|
-
catch {
|
|
113
|
-
}
|
|
107
|
+
catch { }
|
|
114
108
|
const msg = (0, common_1.errorMessage)(err);
|
|
115
109
|
this.logger.error(msg);
|
|
116
110
|
return { success: false, error: msg };
|
|
@@ -120,15 +114,14 @@ class PostgresDatabase {
|
|
|
120
114
|
}
|
|
121
115
|
}
|
|
122
116
|
async initSchema() {
|
|
123
|
-
|
|
117
|
+
await this.pool.query(`
|
|
124
118
|
CREATE TABLE IF NOT EXISTS ${(0, generator_1.quoteIdentifier)(types_1.INTERNAL_TABLES.savedQueries)} (
|
|
125
119
|
id SERIAL PRIMARY KEY,
|
|
126
120
|
name VARCHAR(255) NOT NULL UNIQUE,
|
|
127
121
|
sql TEXT NOT NULL,
|
|
128
122
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
129
123
|
);
|
|
130
|
-
|
|
131
|
-
await this.pool.query(sql);
|
|
124
|
+
`);
|
|
132
125
|
}
|
|
133
126
|
hasMultipleStatements(sql) {
|
|
134
127
|
const s = String(sql ?? '').trim();
|
|
@@ -145,18 +138,15 @@ class PostgresDatabase {
|
|
|
145
138
|
}
|
|
146
139
|
inString = !inString;
|
|
147
140
|
}
|
|
148
|
-
else if (ch === ';' && !inString) {
|
|
149
|
-
|
|
150
|
-
if (rest.length > 0)
|
|
151
|
-
count++;
|
|
141
|
+
else if (ch === ';' && !inString && s.slice(i + 1).trim().length > 0) {
|
|
142
|
+
count++;
|
|
152
143
|
}
|
|
153
144
|
}
|
|
154
145
|
return count > 0;
|
|
155
146
|
}
|
|
156
147
|
async all(sql, params = []) {
|
|
157
148
|
return this.tryRun(async () => {
|
|
158
|
-
const
|
|
159
|
-
const res = await this.pool.query(pgSql, params);
|
|
149
|
+
const res = await this.pool.query((0, filters_1.convertPlaceholdersToPostgres)(sql), params);
|
|
160
150
|
return res.rows;
|
|
161
151
|
});
|
|
162
152
|
}
|
|
@@ -164,12 +154,8 @@ class PostgresDatabase {
|
|
|
164
154
|
if (this.isReadOnly)
|
|
165
155
|
return this.readonlyBlocked();
|
|
166
156
|
return this.tryRun(async () => {
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
return {
|
|
170
|
-
changes: res.rowCount ?? 0,
|
|
171
|
-
lastInsertRowid: null,
|
|
172
|
-
};
|
|
157
|
+
const res = await this.pool.query((0, filters_1.convertPlaceholdersToPostgres)(sql), params);
|
|
158
|
+
return { changes: res.rowCount ?? 0, lastInsertRowid: null };
|
|
173
159
|
});
|
|
174
160
|
}
|
|
175
161
|
async execResult(sql) {
|
|
@@ -184,15 +170,7 @@ class PostgresDatabase {
|
|
|
184
170
|
});
|
|
185
171
|
}
|
|
186
172
|
async runWrite(sql) {
|
|
187
|
-
|
|
188
|
-
return this.readonlyBlocked();
|
|
189
|
-
return this.tryRun(async () => {
|
|
190
|
-
const res = await this.pool.query(sql);
|
|
191
|
-
const changes = Array.isArray(res)
|
|
192
|
-
? res.reduce((acc, r) => acc + (r.rowCount ?? 0), 0)
|
|
193
|
-
: res.rowCount ?? undefined;
|
|
194
|
-
return { changes };
|
|
195
|
-
});
|
|
173
|
+
return this.execResult(sql);
|
|
196
174
|
}
|
|
197
175
|
async listTables() {
|
|
198
176
|
return this.tryRun(async () => {
|
|
@@ -293,7 +271,6 @@ class PostgresDatabase {
|
|
|
293
271
|
if (primaryKey.length) {
|
|
294
272
|
colDefs.push(` PRIMARY KEY (${primaryKey.map(generator_1.quoteIdentifier).join(', ')})`);
|
|
295
273
|
}
|
|
296
|
-
const synthesizedSql = `CREATE TABLE ${(0, generator_1.quoteIdentifier)(table)} (\n${colDefs.join(',\n')}\n);`;
|
|
297
274
|
return {
|
|
298
275
|
table,
|
|
299
276
|
columns,
|
|
@@ -308,7 +285,7 @@ class PostgresDatabase {
|
|
|
308
285
|
})),
|
|
309
286
|
primaryKey,
|
|
310
287
|
indexes,
|
|
311
|
-
sql:
|
|
288
|
+
sql: `CREATE TABLE ${(0, generator_1.quoteIdentifier)(table)} (\n${colDefs.join(',\n')}\n);`,
|
|
312
289
|
};
|
|
313
290
|
});
|
|
314
291
|
}
|
|
@@ -321,8 +298,7 @@ class PostgresDatabase {
|
|
|
321
298
|
pgWhere = (0, filters_1.convertPlaceholdersToPostgres)(where);
|
|
322
299
|
params = p;
|
|
323
300
|
}
|
|
324
|
-
const
|
|
325
|
-
const res = await this.pool.query(sql, params);
|
|
301
|
+
const res = await this.pool.query(`SELECT COUNT(*) AS c FROM ${(0, generator_1.quoteIdentifier)(table)}${pgWhere}`, params);
|
|
326
302
|
return Number(res.rows[0]?.c ?? 0);
|
|
327
303
|
});
|
|
328
304
|
}
|
|
@@ -342,12 +318,10 @@ class PostgresDatabase {
|
|
|
342
318
|
}
|
|
343
319
|
const nextParamIdx = filterParams.length + 1;
|
|
344
320
|
let sql = `SELECT * FROM ${(0, generator_1.quoteIdentifier)(table)}${pgWhere}`;
|
|
345
|
-
if (orderCol)
|
|
321
|
+
if (orderCol)
|
|
346
322
|
sql += ` ORDER BY ${(0, generator_1.quoteIdentifier)(orderCol)} ${orderDir}`;
|
|
347
|
-
}
|
|
348
323
|
sql += ` LIMIT $${nextParamIdx} OFFSET $${nextParamIdx + 1}`;
|
|
349
|
-
const
|
|
350
|
-
const res = await this.pool.query(sql, allParams);
|
|
324
|
+
const res = await this.pool.query(sql, [...filterParams, limit, offset]);
|
|
351
325
|
return res.rows;
|
|
352
326
|
});
|
|
353
327
|
}
|
|
@@ -373,10 +347,7 @@ class PostgresDatabase {
|
|
|
373
347
|
const placeholders = fields.map((_, idx) => `$${idx + 1}`);
|
|
374
348
|
const sql = `INSERT INTO ${(0, generator_1.quoteIdentifier)(table)} (${cols.join(', ')}) VALUES (${placeholders.join(', ')}) RETURNING *`;
|
|
375
349
|
const res = await this.pool.query(sql, fields.map((f) => f.value));
|
|
376
|
-
return {
|
|
377
|
-
changes: res.rowCount ?? 1,
|
|
378
|
-
lastInsertRowid: null,
|
|
379
|
-
};
|
|
350
|
+
return { changes: res.rowCount ?? 1, lastInsertRowid: null };
|
|
380
351
|
});
|
|
381
352
|
}
|
|
382
353
|
async insertRows(table, rows) {
|
|
@@ -402,10 +373,7 @@ class PostgresDatabase {
|
|
|
402
373
|
const conds = where.map((w, idx) => `${(0, generator_1.quoteIdentifier)(w.column)} = $${fields.length + idx + 1}`);
|
|
403
374
|
const sql = `UPDATE ${(0, generator_1.quoteIdentifier)(table)} SET ${sets.join(', ')} WHERE ${conds.join(' AND ')}`;
|
|
404
375
|
const res = await this.pool.query(sql, [...fields.map((f) => f.value), ...where.map((w) => w.value)]);
|
|
405
|
-
return {
|
|
406
|
-
changes: res.rowCount ?? 0,
|
|
407
|
-
lastInsertRowid: null,
|
|
408
|
-
};
|
|
376
|
+
return { changes: res.rowCount ?? 0, lastInsertRowid: null };
|
|
409
377
|
});
|
|
410
378
|
}
|
|
411
379
|
async updateRows(table, rows) {
|
|
@@ -430,10 +398,7 @@ class PostgresDatabase {
|
|
|
430
398
|
const conds = where.map((w, idx) => `${(0, generator_1.quoteIdentifier)(w.column)} = $${idx + 1}`);
|
|
431
399
|
const sql = `DELETE FROM ${(0, generator_1.quoteIdentifier)(table)} WHERE ${conds.join(' AND ')}`;
|
|
432
400
|
const res = await this.pool.query(sql, where.map((w) => w.value));
|
|
433
|
-
return {
|
|
434
|
-
changes: res.rowCount ?? 0,
|
|
435
|
-
lastInsertRowid: null,
|
|
436
|
-
};
|
|
401
|
+
return { changes: res.rowCount ?? 0, lastInsertRowid: null };
|
|
437
402
|
});
|
|
438
403
|
}
|
|
439
404
|
async deleteRows(table, rows) {
|
|
@@ -494,19 +459,13 @@ class PostgresDatabase {
|
|
|
494
459
|
async saveQuery(name, sql) {
|
|
495
460
|
return this.tryRun(async () => {
|
|
496
461
|
const res = await this.pool.query(`INSERT INTO ${(0, generator_1.quoteIdentifier)(types_1.INTERNAL_TABLES.savedQueries)} (name, sql) VALUES ($1, $2) RETURNING id`, [name, sql]);
|
|
497
|
-
return {
|
|
498
|
-
changes: res.rowCount ?? 1,
|
|
499
|
-
lastInsertRowid: Number(res.rows[0]?.id ?? null),
|
|
500
|
-
};
|
|
462
|
+
return { changes: res.rowCount ?? 1, lastInsertRowid: Number(res.rows[0]?.id ?? null) };
|
|
501
463
|
});
|
|
502
464
|
}
|
|
503
465
|
async deleteSavedQuery(id) {
|
|
504
466
|
return this.tryRun(async () => {
|
|
505
467
|
const res = await this.pool.query(`DELETE FROM ${(0, generator_1.quoteIdentifier)(types_1.INTERNAL_TABLES.savedQueries)} WHERE id = $1`, [Number(id)]);
|
|
506
|
-
return {
|
|
507
|
-
changes: res.rowCount ?? 0,
|
|
508
|
-
lastInsertRowid: null,
|
|
509
|
-
};
|
|
468
|
+
return { changes: res.rowCount ?? 0, lastInsertRowid: null };
|
|
510
469
|
});
|
|
511
470
|
}
|
|
512
471
|
async getSettings() {
|
|
@@ -515,11 +474,10 @@ class PostgresDatabase {
|
|
|
515
474
|
const settings = {};
|
|
516
475
|
try {
|
|
517
476
|
const res = await this.pool.query(`SELECT name, setting FROM pg_settings WHERE name = ANY($1::text[])`, [keys]);
|
|
518
|
-
for (const row of res.rows)
|
|
477
|
+
for (const row of res.rows)
|
|
519
478
|
settings[row.name] = String(row.setting);
|
|
520
|
-
}
|
|
521
479
|
}
|
|
522
|
-
catch
|
|
480
|
+
catch {
|
|
523
481
|
try {
|
|
524
482
|
const ver = await this.pool.query('SHOW server_version');
|
|
525
483
|
settings['server_version'] = String(ver.rows[0]?.server_version);
|
|
@@ -610,11 +568,7 @@ class PostgresDatabase {
|
|
|
610
568
|
for (const [tbl, refs] of byTable.entries()) {
|
|
611
569
|
try {
|
|
612
570
|
const countRes = await this.pool.query(`SELECT COUNT(*) AS c FROM ${(0, generator_1.quoteIdentifier)(tbl)} WHERE ${refs.map((r) => `${(0, generator_1.quoteIdentifier)(r.from)} IS NOT NULL`).join(' OR ')}`);
|
|
613
|
-
results.push({
|
|
614
|
-
table: tbl,
|
|
615
|
-
refs,
|
|
616
|
-
refCount: Number(countRes.rows[0]?.c ?? 0),
|
|
617
|
-
});
|
|
571
|
+
results.push({ table: tbl, refs, refCount: Number(countRes.rows[0]?.c ?? 0) });
|
|
618
572
|
}
|
|
619
573
|
catch {
|
|
620
574
|
results.push({ table: tbl, refs, refCount: 0 });
|
|
@@ -669,12 +623,10 @@ class PostgresDatabase {
|
|
|
669
623
|
await this.pool.query(`ALTER TABLE ${(0, generator_1.quoteIdentifier)(table)} ALTER COLUMN ${(0, generator_1.quoteIdentifier)(colToModify)} ${action};`);
|
|
670
624
|
}
|
|
671
625
|
if (newColDef.defaultValue !== undefined) {
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
await this.pool.query(`ALTER TABLE ${(0, generator_1.quoteIdentifier)(table)} ALTER COLUMN ${(0, generator_1.quoteIdentifier)(colToModify)} SET DEFAULT ${newColDef.defaultValue};`);
|
|
677
|
-
}
|
|
626
|
+
const action = newColDef.defaultValue === null || newColDef.defaultValue === ''
|
|
627
|
+
? 'DROP DEFAULT'
|
|
628
|
+
: `SET DEFAULT ${newColDef.defaultValue}`;
|
|
629
|
+
await this.pool.query(`ALTER TABLE ${(0, generator_1.quoteIdentifier)(table)} ALTER COLUMN ${(0, generator_1.quoteIdentifier)(colToModify)} ${action};`);
|
|
678
630
|
}
|
|
679
631
|
if (newColDef.unique !== undefined) {
|
|
680
632
|
const idxName = `idx_${table}_${colToModify}_unique`;
|
package/dist/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{createRouter,type AppOptions} from './app';export{createLogger,type Logger,type LogLevel} from './utils/logger';export{c,supportsColor} from './utils/colors';export{SqliteDatabase,PostgresDatabase,type IDatabase,type DatabaseDialect,type PostgresOptions,type MutationResult,type QueryOptions,type Result,type TableInfoData,type ColumnInfo,type ForeignKeyInfo,type WhereClause,type IndexInfo,type SchemaInfo,type ColumnDetail,type SavedQuery,type RowFilters,type FilterCondition,type FilterValue,type SQLInputValue
|
|
1
|
+
export{createRouter,type AppOptions} from './app';export{createLogger,type Logger,type LogLevel} from './utils/logger';export{c,supportsColor} from './utils/colors';export{SqliteDatabase,PostgresDatabase,type IDatabase,type DatabaseDialect,type PostgresOptions,type MutationResult,type QueryOptions,type Result,type TableInfoData,type ColumnInfo,type ForeignKeyInfo,type WhereClause,type IndexInfo,type SchemaInfo,type ColumnDetail,type SavedQuery,type RowFilters,type FilterCondition,type FilterValue,type SQLInputValue} from './db/index';export{DbManager,type DbManagerOptions,type DatabaseEntry} from './db/manager';export{type ColumnDef,type IndexDef} from './sql/generator';export{type ColumnPlan,type ColumnGeneratorConfig,type GeneratorStrategyId,type StrategyDescriptor,type GenerateResult} from './data/index';export{type AuthConfig,type ResolvedAuthConfig,resolveAuthConfig,getOrCreatePersistentSecret,createSessionToken,verifySessionToken,parseCookies,constantTimeCompare,hashPassword,verifyPassword,DEFAULT_USERNAME,DEFAULT_PASSWORD_HASH,SESSION_COOKIE_NAME} from './auth/index';export{isServerlessEnvironment,createServerlessHandler,createLambdaHandler,type LambdaProxyResult} from './serverless';export{sniffMimeType,generateHexDump,analyzeBlob,isJsonString,formatJsonSafely,type MimeAnalysis,type HexDumpLine,type HexDumpResult,type BlobMetadata} from './utils/datatype';export * from './types/api';
|