admindb 2.2.1 → 2.3.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.
- package/README.md +368 -364
- package/dist/app.js +25 -16
- 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 +20 -29
- package/dist/cli/config.d.ts +1 -1
- package/dist/cli/config.js +15 -18
- package/dist/cli/runner.js +13 -13
- 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.d.ts +1 -1
- package/dist/db/manager.js +48 -53
- 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 +31 -44
- 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 +80 -79
package/dist/db/manager.js
CHANGED
|
@@ -10,12 +10,14 @@ const database_1 = require("./database");
|
|
|
10
10
|
const postgres_1 = require("./postgres");
|
|
11
11
|
const logger_1 = require("../utils/logger");
|
|
12
12
|
const common_1 = require("../utils/common");
|
|
13
|
+
const serverless_1 = require("../serverless");
|
|
13
14
|
const DB_EXTENSIONS = ['.db', '.sqlite', '.sqlite3'];
|
|
14
15
|
class DbManager {
|
|
15
16
|
logger;
|
|
16
17
|
dir;
|
|
17
18
|
createDir;
|
|
18
19
|
readonlyMode;
|
|
20
|
+
isServerless;
|
|
19
21
|
files = [];
|
|
20
22
|
idByPath = new Map();
|
|
21
23
|
pathById = new Map();
|
|
@@ -27,12 +29,12 @@ class DbManager {
|
|
|
27
29
|
this.logger = logger ?? (0, logger_1.createLogger)('info', 'admindb');
|
|
28
30
|
this.dir = opts.dir ? node_path_1.default.resolve(opts.dir) : undefined;
|
|
29
31
|
this.createDir = this.dir ?? process.cwd();
|
|
30
|
-
this.readonlyMode =
|
|
32
|
+
this.readonlyMode = Boolean(opts.readonly);
|
|
33
|
+
this.isServerless = opts.serverless !== undefined ? opts.serverless : (0, serverless_1.isServerlessEnvironment)();
|
|
31
34
|
if (this.dir)
|
|
32
35
|
(0, node_fs_1.mkdirSync)(this.dir, { recursive: true });
|
|
33
|
-
if (opts.connections)
|
|
36
|
+
if (opts.connections)
|
|
34
37
|
this.registerConnections(opts.connections);
|
|
35
|
-
}
|
|
36
38
|
this.registerDirFiles();
|
|
37
39
|
this.registerExplicitFiles(opts.files ?? []);
|
|
38
40
|
}
|
|
@@ -48,28 +50,24 @@ class DbManager {
|
|
|
48
50
|
}
|
|
49
51
|
registerConnections(conns) {
|
|
50
52
|
for (const [name, target] of Object.entries(conns ?? {})) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (!cleanTarget)
|
|
55
|
-
continue;
|
|
56
|
-
this.addNamedTarget(name.trim(), cleanTarget);
|
|
53
|
+
const cleanTarget = String(target ?? '').trim();
|
|
54
|
+
if (name && cleanTarget)
|
|
55
|
+
this.addNamedTarget(name.trim(), cleanTarget);
|
|
57
56
|
}
|
|
58
57
|
}
|
|
59
58
|
registerDirFiles() {
|
|
60
59
|
if (!this.dir)
|
|
61
60
|
return;
|
|
62
|
-
let names = [];
|
|
63
61
|
try {
|
|
64
|
-
names = (0, node_fs_1.readdirSync)(this.dir)
|
|
62
|
+
const names = (0, node_fs_1.readdirSync)(this.dir)
|
|
65
63
|
.filter((n) => this.isDbFile(n))
|
|
66
64
|
.sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' }));
|
|
65
|
+
for (const name of names)
|
|
66
|
+
this.addFile(node_path_1.default.join(this.dir, name));
|
|
67
67
|
}
|
|
68
68
|
catch (err) {
|
|
69
69
|
this.logger.warn(`Failed to list database directory ${this.dir}: ${(0, common_1.errorMessage)(err)}`);
|
|
70
70
|
}
|
|
71
|
-
for (const name of names)
|
|
72
|
-
this.addFile(node_path_1.default.join(this.dir, name));
|
|
73
71
|
}
|
|
74
72
|
registerExplicitFiles(files) {
|
|
75
73
|
for (const f of files ?? []) {
|
|
@@ -79,12 +77,9 @@ class DbManager {
|
|
|
79
77
|
if ((0, common_1.isPostgresConnectionString)(raw)) {
|
|
80
78
|
let name = 'postgres';
|
|
81
79
|
try {
|
|
82
|
-
|
|
83
|
-
name = u.pathname.replace(/^\//, '') || 'postgres';
|
|
84
|
-
}
|
|
85
|
-
catch {
|
|
86
|
-
name = 'postgres';
|
|
80
|
+
name = new URL(raw).pathname.replace(/^\//, '') || 'postgres';
|
|
87
81
|
}
|
|
82
|
+
catch { }
|
|
88
83
|
this.addNamedTarget(name, raw);
|
|
89
84
|
continue;
|
|
90
85
|
}
|
|
@@ -99,10 +94,8 @@ class DbManager {
|
|
|
99
94
|
addNamedTarget(name, target) {
|
|
100
95
|
let id = name;
|
|
101
96
|
let n = 2;
|
|
102
|
-
while (this.pathById.has(id))
|
|
103
|
-
id = `${name}__${n}`;
|
|
104
|
-
n += 1;
|
|
105
|
-
}
|
|
97
|
+
while (this.pathById.has(id))
|
|
98
|
+
id = `${name}__${n++}`;
|
|
106
99
|
this.files.push(target);
|
|
107
100
|
this.idByPath.set(target, id);
|
|
108
101
|
this.pathById.set(id, target);
|
|
@@ -116,9 +109,7 @@ class DbManager {
|
|
|
116
109
|
let n = 2;
|
|
117
110
|
while (this.pathById.has(id)) {
|
|
118
111
|
const ext = node_path_1.default.extname(base);
|
|
119
|
-
|
|
120
|
-
id = `${stem}__${n}${ext}`;
|
|
121
|
-
n += 1;
|
|
112
|
+
id = `${node_path_1.default.basename(base, ext)}__${n++}${ext}`;
|
|
122
113
|
}
|
|
123
114
|
this.files.push(abs);
|
|
124
115
|
this.idByPath.set(abs, id);
|
|
@@ -151,8 +142,7 @@ class DbManager {
|
|
|
151
142
|
size = st.size;
|
|
152
143
|
modified = st.mtime.toISOString();
|
|
153
144
|
}
|
|
154
|
-
catch {
|
|
155
|
-
}
|
|
145
|
+
catch { }
|
|
156
146
|
entries.push({
|
|
157
147
|
id,
|
|
158
148
|
name: this.nameById.get(id) ?? node_path_1.default.basename(target),
|
|
@@ -170,14 +160,29 @@ class DbManager {
|
|
|
170
160
|
return this.pathById.has(id);
|
|
171
161
|
}
|
|
172
162
|
isDbReadOnly(id) {
|
|
173
|
-
|
|
163
|
+
if (this.readonlyMode)
|
|
164
|
+
return true;
|
|
165
|
+
if (this.readonlyById.has(id))
|
|
166
|
+
return Boolean(this.readonlyById.get(id));
|
|
167
|
+
if (this.isServerless) {
|
|
168
|
+
const target = this.pathById.get(id);
|
|
169
|
+
const isPg = target ? (0, common_1.isPostgresConnectionString)(target) : false;
|
|
170
|
+
return !isPg;
|
|
171
|
+
}
|
|
172
|
+
return false;
|
|
174
173
|
}
|
|
175
174
|
setReadonly(id, readonly) {
|
|
176
175
|
if (this.readonlyMode && !readonly) {
|
|
177
176
|
throw new Error('Manager is in global read-only mode — databases cannot be made writable.');
|
|
178
177
|
}
|
|
179
|
-
|
|
180
|
-
|
|
178
|
+
if (this.isServerless && !readonly) {
|
|
179
|
+
const target = this.pathById.get(id);
|
|
180
|
+
const isPg = target ? (0, common_1.isPostgresConnectionString)(target) : false;
|
|
181
|
+
if (!isPg) {
|
|
182
|
+
throw new Error('SQLite database cannot be made writable in a serverless environment.');
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (this.readonlyById.get(id) === readonly)
|
|
181
186
|
return;
|
|
182
187
|
this.readonlyById.set(id, readonly);
|
|
183
188
|
const db = this.openDbs.get(id);
|
|
@@ -185,8 +190,7 @@ class DbManager {
|
|
|
185
190
|
try {
|
|
186
191
|
db.close();
|
|
187
192
|
}
|
|
188
|
-
catch {
|
|
189
|
-
}
|
|
193
|
+
catch { }
|
|
190
194
|
this.openDbs.delete(id);
|
|
191
195
|
}
|
|
192
196
|
}
|
|
@@ -194,24 +198,21 @@ class DbManager {
|
|
|
194
198
|
const target = this.pathById.get(id);
|
|
195
199
|
if (!target)
|
|
196
200
|
throw new Error(`Database "${id}" is not registered.`);
|
|
197
|
-
const isRo =
|
|
201
|
+
const isRo = readonlyOverride !== undefined ? readonlyOverride : this.isDbReadOnly(id);
|
|
198
202
|
let db = this.openDbs.get(id);
|
|
199
203
|
if (db && db.isReadOnly !== isRo) {
|
|
200
204
|
try {
|
|
201
205
|
db.close();
|
|
202
206
|
}
|
|
203
|
-
catch {
|
|
204
|
-
}
|
|
207
|
+
catch { }
|
|
205
208
|
this.openDbs.delete(id);
|
|
206
209
|
db = undefined;
|
|
207
210
|
}
|
|
208
211
|
if (!db) {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
db = new database_1.SqliteDatabase(target, this.logger.child(`db:${id}`), { readonly: isRo });
|
|
214
|
-
}
|
|
212
|
+
const childLogger = this.logger.child(`db:${id}`);
|
|
213
|
+
db = (0, common_1.isPostgresConnectionString)(target)
|
|
214
|
+
? new postgres_1.PostgresDatabase(target, childLogger, { readonly: isRo })
|
|
215
|
+
: new database_1.SqliteDatabase(target, childLogger, { readonly: isRo });
|
|
215
216
|
this.openDbs.set(id, db);
|
|
216
217
|
}
|
|
217
218
|
return db;
|
|
@@ -220,9 +221,8 @@ class DbManager {
|
|
|
220
221
|
const abs = node_path_1.default.resolve(String(absPath ?? '').trim());
|
|
221
222
|
if (!abs)
|
|
222
223
|
throw new Error('Database path is required.');
|
|
223
|
-
if (!(0, node_fs_1.existsSync)(abs) || !(0, node_fs_1.statSync)(abs).isFile())
|
|
224
|
+
if (!(0, node_fs_1.existsSync)(abs) || !(0, node_fs_1.statSync)(abs).isFile())
|
|
224
225
|
throw new Error(`Not a file: ${abs}`);
|
|
225
|
-
}
|
|
226
226
|
const existing = this.idByPath.get(abs);
|
|
227
227
|
if (existing) {
|
|
228
228
|
if (readonly !== undefined)
|
|
@@ -253,8 +253,7 @@ class DbManager {
|
|
|
253
253
|
}
|
|
254
254
|
async countTables(id) {
|
|
255
255
|
try {
|
|
256
|
-
const
|
|
257
|
-
const r = await db.listTables();
|
|
256
|
+
const r = await this.open(id).listTables();
|
|
258
257
|
return r.success ? (r.data?.length ?? 0) : 0;
|
|
259
258
|
}
|
|
260
259
|
catch {
|
|
@@ -284,8 +283,7 @@ class DbManager {
|
|
|
284
283
|
let cleanName = String(name ?? '').trim();
|
|
285
284
|
if (!cleanName) {
|
|
286
285
|
try {
|
|
287
|
-
|
|
288
|
-
cleanName = u.pathname.replace(/^\//, '') || 'postgres';
|
|
286
|
+
cleanName = new URL(raw).pathname.replace(/^\//, '') || 'postgres';
|
|
289
287
|
}
|
|
290
288
|
catch {
|
|
291
289
|
cleanName = 'postgres';
|
|
@@ -310,8 +308,7 @@ class DbManager {
|
|
|
310
308
|
try {
|
|
311
309
|
db.close();
|
|
312
310
|
}
|
|
313
|
-
catch {
|
|
314
|
-
}
|
|
311
|
+
catch { }
|
|
315
312
|
this.openDbs.delete(id);
|
|
316
313
|
}
|
|
317
314
|
this.pathById.delete(id);
|
|
@@ -324,8 +321,7 @@ class DbManager {
|
|
|
324
321
|
try {
|
|
325
322
|
(0, node_fs_1.unlinkSync)(target + suffix);
|
|
326
323
|
}
|
|
327
|
-
catch {
|
|
328
|
-
}
|
|
324
|
+
catch { }
|
|
329
325
|
}
|
|
330
326
|
}
|
|
331
327
|
}
|
|
@@ -334,8 +330,7 @@ class DbManager {
|
|
|
334
330
|
try {
|
|
335
331
|
db.close();
|
|
336
332
|
}
|
|
337
|
-
catch {
|
|
338
|
-
}
|
|
333
|
+
catch { }
|
|
339
334
|
}
|
|
340
335
|
this.openDbs.clear();
|
|
341
336
|
}
|
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';
|