admindb 2.4.1 → 2.5.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.
Files changed (135) hide show
  1. package/LICENSE +20 -20
  2. package/dist/app.js +36 -25
  3. package/dist/core/context.d.ts +1 -0
  4. package/dist/core/context.js +2 -0
  5. package/dist/core/errors.d.ts +1 -0
  6. package/dist/core/errors.js +39 -0
  7. package/dist/core/index.d.ts +1 -0
  8. package/dist/core/index.js +20 -0
  9. package/dist/core/result.d.ts +1 -0
  10. package/dist/core/result.js +10 -0
  11. package/dist/core/router.d.ts +1 -0
  12. package/dist/core/router.js +37 -0
  13. package/dist/data/chain.js +1 -1
  14. package/dist/data/engine.js +1 -1
  15. package/dist/db/database.d.ts +1 -1
  16. package/dist/db/database.js +9 -31
  17. package/dist/db/dialects/index.d.ts +1 -0
  18. package/dist/db/dialects/index.js +58 -0
  19. package/dist/db/dialects/postgres/ddl.d.ts +1 -0
  20. package/dist/db/dialects/postgres/ddl.js +107 -0
  21. package/dist/db/dialects/postgres/dialect.d.ts +1 -0
  22. package/dist/db/dialects/postgres/dialect.js +28 -0
  23. package/dist/db/dialects/postgres/introspector.d.ts +1 -0
  24. package/dist/db/dialects/postgres/introspector.js +265 -0
  25. package/dist/db/dialects/postgres/types.d.ts +1 -0
  26. package/dist/db/dialects/postgres/types.js +83 -0
  27. package/dist/db/dialects/sqlite/ddl.d.ts +1 -0
  28. package/dist/db/dialects/sqlite/ddl.js +244 -0
  29. package/dist/db/dialects/sqlite/dialect.d.ts +1 -0
  30. package/dist/db/dialects/sqlite/dialect.js +25 -0
  31. package/dist/db/dialects/sqlite/introspector.d.ts +1 -0
  32. package/dist/db/dialects/sqlite/introspector.js +214 -0
  33. package/dist/db/dialects/sqlite/types.d.ts +1 -0
  34. package/dist/db/dialects/sqlite/types.js +69 -0
  35. package/dist/db/dialects/types.d.ts +1 -0
  36. package/dist/db/dialects/types.js +15 -0
  37. package/dist/db/export.js +1 -1
  38. package/dist/db/index.d.ts +1 -1
  39. package/dist/db/index.js +1 -1
  40. package/dist/db/manager.d.ts +1 -1
  41. package/dist/db/manager.js +101 -21
  42. package/dist/db/postgres.d.ts +1 -1
  43. package/dist/db/postgres.js +20 -284
  44. package/dist/db/types.d.ts +1 -1
  45. package/dist/db/types.js +0 -4
  46. package/dist/index.d.ts +1 -1
  47. package/dist/index.js +4 -1
  48. package/dist/modules/databases/databases.routes.d.ts +1 -0
  49. package/dist/modules/databases/databases.routes.js +150 -0
  50. package/dist/modules/databases/databases.service.d.ts +1 -0
  51. package/dist/modules/databases/databases.service.js +134 -0
  52. package/dist/modules/databases/index.d.ts +1 -0
  53. package/dist/{routes → modules/databases}/index.js +2 -3
  54. package/dist/modules/erd/erd.routes.d.ts +1 -0
  55. package/dist/modules/erd/erd.routes.js +32 -0
  56. package/dist/modules/erd/erd.service.d.ts +1 -0
  57. package/dist/modules/erd/erd.service.js +39 -0
  58. package/dist/modules/erd/index.d.ts +1 -0
  59. package/dist/modules/erd/index.js +18 -0
  60. package/dist/modules/index.d.ts +1 -0
  61. package/dist/modules/index.js +62 -0
  62. package/dist/modules/query/index.d.ts +1 -0
  63. package/dist/modules/query/index.js +18 -0
  64. package/dist/modules/query/query.routes.d.ts +1 -0
  65. package/dist/modules/query/query.routes.js +54 -0
  66. package/dist/modules/query/query.service.d.ts +1 -0
  67. package/dist/modules/query/query.service.js +95 -0
  68. package/dist/modules/rows/blob.helper.d.ts +1 -0
  69. package/dist/modules/rows/blob.helper.js +40 -0
  70. package/dist/modules/rows/display.helper.d.ts +1 -0
  71. package/dist/modules/rows/display.helper.js +73 -0
  72. package/dist/modules/rows/index.d.ts +1 -0
  73. package/dist/modules/rows/index.js +20 -0
  74. package/dist/modules/rows/rows.routes.d.ts +1 -0
  75. package/dist/modules/rows/rows.routes.js +555 -0
  76. package/dist/modules/rows/rows.service.d.ts +1 -0
  77. package/dist/modules/rows/rows.service.js +433 -0
  78. package/dist/modules/schema/index.d.ts +1 -0
  79. package/dist/modules/schema/index.js +18 -0
  80. package/dist/modules/schema/schema.routes.d.ts +1 -0
  81. package/dist/modules/schema/schema.routes.js +171 -0
  82. package/dist/modules/schema/schema.service.d.ts +1 -0
  83. package/dist/modules/schema/schema.service.js +58 -0
  84. package/dist/modules/seed/index.d.ts +1 -0
  85. package/dist/modules/seed/index.js +18 -0
  86. package/dist/modules/seed/seed.routes.d.ts +1 -0
  87. package/dist/modules/seed/seed.routes.js +212 -0
  88. package/dist/modules/seed/seed.service.d.ts +1 -0
  89. package/dist/{routes/api/helpers.js → modules/seed/seed.service.js} +86 -109
  90. package/dist/modules/tables/index.d.ts +1 -0
  91. package/dist/modules/tables/index.js +18 -0
  92. package/dist/modules/tables/tables.routes.d.ts +1 -0
  93. package/dist/modules/tables/tables.routes.js +194 -0
  94. package/dist/modules/tables/tables.service.d.ts +1 -0
  95. package/dist/modules/tables/tables.service.js +218 -0
  96. package/dist/public/js/browse.js +1 -1
  97. package/dist/public/js/query.js +1 -1
  98. package/dist/types/api.d.ts +1 -1
  99. package/dist/utils/common.d.ts +1 -1
  100. package/dist/utils/common.js +1 -3
  101. package/dist/utils/stream.d.ts +1 -0
  102. package/dist/utils/stream.js +131 -0
  103. package/dist/views/pages/error.hbs +10 -1
  104. package/dist/views/pages/home.hbs +1 -1
  105. package/dist/views/pages/query.hbs +1 -1
  106. package/dist/views/pages/seed-select.hbs +1 -252
  107. package/dist/views/pages/seed.hbs +1 -307
  108. package/dist/views/pages/table.hbs +1 -1
  109. package/dist/views/partials/sidebar.hbs +1 -1
  110. package/docs/API.md +4 -17
  111. package/docs/EXAMPLES.md +579 -579
  112. package/docs/SECURITY.md +265 -265
  113. package/package.json +4 -2
  114. package/dist/db/introspection.d.ts +0 -1
  115. package/dist/db/introspection.js +0 -164
  116. package/dist/routes/api/erd.d.ts +0 -1
  117. package/dist/routes/api/erd.js +0 -45
  118. package/dist/routes/api/helpers.d.ts +0 -1
  119. package/dist/routes/api/import-export.d.ts +0 -1
  120. package/dist/routes/api/import-export.js +0 -84
  121. package/dist/routes/api/index.d.ts +0 -1
  122. package/dist/routes/api/index.js +0 -55
  123. package/dist/routes/api/query.d.ts +0 -1
  124. package/dist/routes/api/query.js +0 -77
  125. package/dist/routes/api/rows.d.ts +0 -1
  126. package/dist/routes/api/rows.js +0 -373
  127. package/dist/routes/api/seed.d.ts +0 -1
  128. package/dist/routes/api/seed.js +0 -180
  129. package/dist/routes/api/tables.d.ts +0 -1
  130. package/dist/routes/api/tables.js +0 -230
  131. package/dist/routes/databases.d.ts +0 -1
  132. package/dist/routes/databases.js +0 -212
  133. package/dist/routes/index.d.ts +0 -1
  134. package/dist/routes/pages.d.ts +0 -1
  135. package/dist/routes/pages.js +0 -540
@@ -1,164 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.listTablesSync = listTablesSync;
4
- exports.getTableInfoSync = getTableInfoSync;
5
- exports.getSchemaSync = getSchemaSync;
6
- exports.getReferencingTablesSync = getReferencingTablesSync;
7
- const generator_1 = require("../sql/generator");
8
- function listTablesSync(db) {
9
- const rows = db
10
- .prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%' ORDER BY name")
11
- .all();
12
- return rows.map((r) => ({ name: r.name }));
13
- }
14
- function getTableNames(db) {
15
- return db
16
- .prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'")
17
- .all().map((r) => r.name);
18
- }
19
- function getIncomingFks(db, targetTable) {
20
- const refs = [];
21
- for (const t of getTableNames(db)) {
22
- if (t === targetTable)
23
- continue;
24
- try {
25
- const fks = db.prepare(`PRAGMA foreign_key_list(${(0, generator_1.quoteIdentifier)(t)})`).all();
26
- for (const fk of fks) {
27
- if (fk.table === targetTable)
28
- refs.push({ table: t, from: fk.from, to: fk.to ?? '' });
29
- }
30
- }
31
- catch { }
32
- }
33
- return refs;
34
- }
35
- function getTableInfoSync(db, table) {
36
- const cols = db.prepare(`PRAGMA table_info(${(0, generator_1.quoteIdentifier)(table)})`).all();
37
- const fks = db.prepare(`PRAGMA foreign_key_list(${(0, generator_1.quoteIdentifier)(table)})`).all();
38
- const primaryKey = cols
39
- .filter((c) => c.pk > 0)
40
- .sort((a, b) => a.pk - b.pk)
41
- .map((c) => c.name);
42
- const indexRows = db
43
- .prepare(`PRAGMA index_list(${(0, generator_1.quoteIdentifier)(table)})`)
44
- .all();
45
- const indexes = indexRows.map((ix) => {
46
- const ixCols = db
47
- .prepare(`PRAGMA index_info(${(0, generator_1.quoteIdentifier)(ix.name)})`)
48
- .all().map((c) => c.name);
49
- const sqlRow = db
50
- .prepare("SELECT sql FROM sqlite_master WHERE type = 'index' AND name = ?")
51
- .get(ix.name);
52
- return {
53
- name: ix.name,
54
- unique: Boolean(ix.unique),
55
- partial: ix.partial,
56
- origin: ix.origin,
57
- columns: ixCols,
58
- sql: sqlRow?.sql ?? null,
59
- };
60
- });
61
- const ddlRow = db
62
- .prepare("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?")
63
- .get(table);
64
- return {
65
- table,
66
- columns: cols.map((c) => ({
67
- cid: c.cid,
68
- name: c.name,
69
- type: c.type,
70
- notnull: c.notnull,
71
- dflt_value: c.dflt_value,
72
- pk: c.pk,
73
- })),
74
- foreignKeys: fks.map((f) => ({
75
- id: f.id,
76
- seq: f.seq,
77
- table: f.table,
78
- from: f.from,
79
- to: f.to,
80
- on_update: f.on_update,
81
- on_delete: f.on_delete,
82
- })),
83
- primaryKey,
84
- indexes,
85
- sql: ddlRow?.sql ?? null,
86
- };
87
- }
88
- function getSchemaSync(db, table) {
89
- const info = getTableInfoSync(db, table);
90
- if (!info || info.columns.length === 0)
91
- throw new Error(`Table "${table}" does not exist.`);
92
- const { foreignKeys, indexes } = info;
93
- const uniqueCols = new Set();
94
- const indexedCols = new Set();
95
- for (const ix of indexes) {
96
- if (ix.unique)
97
- ix.columns.forEach((c) => uniqueCols.add(c));
98
- if (ix.origin === 'c')
99
- ix.columns.forEach((c) => indexedCols.add(c));
100
- }
101
- const refs = getIncomingFks(db, table);
102
- const refsByTo = new Map();
103
- for (const r of refs) {
104
- const arr = refsByTo.get(r.to) ?? [];
105
- arr.push({ table: r.table, from: r.from });
106
- refsByTo.set(r.to, arr);
107
- }
108
- const fkByFrom = new Map(foreignKeys.map((fk) => [fk.from, fk]));
109
- const columns = info.columns.map((c) => {
110
- const unique = c.pk > 0 || uniqueCols.has(c.name);
111
- const indexed = indexedCols.has(c.name);
112
- const referencedBy = refsByTo.get(c.name) ?? [];
113
- const fk = fkByFrom.get(c.name) ?? null;
114
- const dropBlockers = [];
115
- if (c.pk > 0)
116
- dropBlockers.push('Is part of the primary key');
117
- if (unique && c.pk === 0)
118
- dropBlockers.push('Has a UNIQUE constraint');
119
- if (indexed)
120
- dropBlockers.push('Is used by an index');
121
- if (referencedBy.length)
122
- dropBlockers.push(`Referenced by a foreign key in "${referencedBy[0].table}"`);
123
- if (fk)
124
- dropBlockers.push('Is part of a foreign key');
125
- return {
126
- ...c,
127
- unique,
128
- indexed,
129
- referencedBy,
130
- fk,
131
- canDrop: dropBlockers.length === 0,
132
- dropBlockers,
133
- };
134
- });
135
- return {
136
- table,
137
- columns,
138
- foreignKeys,
139
- indexes,
140
- references: refs,
141
- };
142
- }
143
- function getReferencingTablesSync(db, table) {
144
- const refs = getIncomingFks(db, table);
145
- const byTable = new Map();
146
- for (const r of refs) {
147
- const arr = byTable.get(r.table) ?? [];
148
- arr.push({ from: r.from, to: r.to });
149
- byTable.set(r.table, arr);
150
- }
151
- const result = [];
152
- for (const [t, tRefs] of byTable) {
153
- let refCount = 0;
154
- try {
155
- const conds = tRefs.map((r) => `${(0, generator_1.quoteIdentifier)(r.from)} IS NOT NULL`);
156
- const row = db.prepare(`SELECT COUNT(*) AS c FROM ${(0, generator_1.quoteIdentifier)(t)} WHERE ${conds.join(' OR ')}`).get();
157
- refCount = Number(row.c);
158
- }
159
- catch { }
160
- result.push({ table: t, refs: tRefs, refCount });
161
- }
162
- result.sort((a, b) => a.table.localeCompare(b.table));
163
- return result;
164
- }
@@ -1 +0,0 @@
1
- import{type ApiContext} from './helpers';import type {Router} from 'express';export declare function registerErdRoutes(router:Router,ctx:ApiContext):void;
@@ -1,45 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.registerErdRoutes = registerErdRoutes;
4
- const helpers_1 = require("./helpers");
5
- function registerErdRoutes(router, ctx) {
6
- router.get('/api/erd/:db', async (req, res) => {
7
- try {
8
- const db = ctx.getDb(req);
9
- const tablesResult = await db.listTables();
10
- if (!tablesResult.success || !tablesResult.data) {
11
- return (0, helpers_1.fail)(res, tablesResult.error ?? 'Failed to list tables.', 500);
12
- }
13
- const tableNames = tablesResult.data.map((t) => t.name);
14
- const tables = await Promise.all(tableNames.map(async (name) => {
15
- const infoResult = await db.getTableInfo(name);
16
- if (!infoResult.success || !infoResult.data) {
17
- return { name, columns: [], foreignKeys: [], primaryKey: [] };
18
- }
19
- const { columns, foreignKeys, primaryKey } = infoResult.data;
20
- return {
21
- name,
22
- columns: columns.map((c) => ({
23
- name: c.name,
24
- type: c.type,
25
- pk: c.pk,
26
- notnull: c.notnull,
27
- dflt_value: c.dflt_value,
28
- })),
29
- foreignKeys: foreignKeys.map((fk) => ({
30
- from: fk.from,
31
- table: fk.table,
32
- to: fk.to ?? '',
33
- on_delete: fk.on_delete,
34
- on_update: fk.on_update,
35
- })),
36
- primaryKey,
37
- };
38
- }));
39
- (0, helpers_1.ok)(res, { tables });
40
- }
41
- catch (err) {
42
- (0, helpers_1.fail)(res, err.message ?? 'Unknown error.', 500);
43
- }
44
- });
45
- }
@@ -1 +0,0 @@
1
- import type {Request,Response} from 'express';import type {IDatabase,TableInfoData,WhereClause} from '../../db/index';import type {Logger} from '../../utils/logger';import{type ColumnPlan,type ErChainScope,type GenerationPlan,type TableGenerationMode} from '../../data/index';export interface ApiContext{getDb:(req:Request)=>IDatabase;logger:Logger;}export declare const ok:(res:Response,data:unknown,status?:number)=>Response;export declare const fail:(res:Response,error:string,status?:number,details?:Record<string,unknown>|null)=>Response;export declare const wrap:(fn:(req:Request,res:Response)=>unknown)=>(req:Request,res:Response)=>Promise<void>;export declare function requireTable(db:IDatabase,table:string):Promise<TableInfoData|null>;export declare function buildFields(info:TableInfoData,values:Record<string,unknown>,opts?:{excludePk?:boolean;}):{column:string;value:unknown;}[];export declare function buildUpdateFields(info:TableInfoData,values:Record<string,unknown>,nulls:string[]):{column:string;value:unknown;}[];export declare function pkWhere(info:TableInfoData,encodedId:string):{column:string;value:unknown;}[]|null;export declare const MAX_BULK_ROWS=1000;export declare function parseSeedRequest(body:unknown):{count:number;plan:Record<string,ColumnPlan>;truncate:boolean;seed?:number|null;};export declare function parseChainSeedRequest(body:unknown):{scope:ErChainScope;counts:Record<string,number>;plans:Record<string,Record<string,ColumnPlan>>;truncate:boolean;modes?:Record<string,TableGenerationMode>;seed?:number|null;};export declare function parseUnifiedGenerationPlan(body:unknown):GenerationPlan;export declare function resolvePkRows(info:TableInfoData,ids:unknown):WhereClause[][]|null;export declare function computeBulkImpact(db:IDatabase,info:TableInfoData,wheres:WhereClause[][]):Promise<{references:{table:string; from :string;to:string;count:number;}[];total:number;}>;
@@ -1 +0,0 @@
1
- import type {Router} from 'express';import{type ApiContext} from './helpers';export declare function registerImportExportRoutes(router:Router,ctx:ApiContext):void;
@@ -1,84 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.registerImportExportRoutes = registerImportExportRoutes;
4
- const common_1 = require("../../utils/common");
5
- const csv_1 = require("../../utils/csv");
6
- const helpers_1 = require("./helpers");
7
- function registerImportExportRoutes(router, ctx) {
8
- router.get('/api/tables/:db/:table/export', (0, helpers_1.wrap)(async (req, res) => {
9
- const db = ctx.getDb(req);
10
- const info = await (0, helpers_1.requireTable)(db, req.params.table);
11
- if (!info)
12
- return (0, helpers_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
13
- const format = String(req.query.format ?? 'csv').toLowerCase();
14
- const filters = (0, common_1.parseFilters)(req.query.f);
15
- const r = await db.getRows(req.params.table, { limit: 1_000_000, filters });
16
- if (!r.success)
17
- return (0, helpers_1.fail)(res, r.error ?? 'Failed to export table.');
18
- const rows = (r.data ?? []).map(common_1.normalizeRow);
19
- const columns = info.columns.map((c) => c.name);
20
- if (format === 'json') {
21
- res.setHeader('Content-Type', 'application/json; charset=utf-8');
22
- res.setHeader('Content-Disposition', `attachment; filename="${req.params.table}.json"`);
23
- return res.send((0, csv_1.toJson)(rows));
24
- }
25
- res.setHeader('Content-Type', 'text/csv; charset=utf-8');
26
- res.setHeader('Content-Disposition', `attachment; filename="${req.params.table}.csv"`);
27
- res.send((0, csv_1.toCsv)(rows, columns));
28
- }));
29
- const importCsvHandler = (0, helpers_1.wrap)(async (req, res) => {
30
- const db = ctx.getDb(req);
31
- const info = await (0, helpers_1.requireTable)(db, req.params.table);
32
- if (!info)
33
- return (0, helpers_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
34
- const csvText = typeof req.body === 'string' ? req.body : String(req.body?.csv ?? '');
35
- if (!csvText.trim())
36
- return (0, helpers_1.fail)(res, 'No CSV data provided.');
37
- const parsed = (0, csv_1.parseCsv)(csvText);
38
- if (!parsed.length)
39
- return (0, helpers_1.fail)(res, 'CSV file is empty.');
40
- const header = parsed[0].map((h) => h.trim());
41
- const typeMap = new Map(info.columns.map((c) => [c.name, c.type]));
42
- const validCols = header.filter((h) => typeMap.has(h));
43
- if (!validCols.length) {
44
- return (0, helpers_1.fail)(res, 'None of the CSV columns match columns in this table.');
45
- }
46
- const rows = parsed.slice(1).map((line) => {
47
- const fields = [];
48
- for (let i = 0; i < header.length; i++) {
49
- const col = header[i];
50
- if (!typeMap.has(col))
51
- continue;
52
- fields.push({ column: col, value: (0, common_1.coerceFormValue)(line[i] ?? '', typeMap.get(col)) });
53
- }
54
- return fields;
55
- });
56
- const r = await db.insertRows(req.params.table, rows);
57
- if (!r.success)
58
- return (0, helpers_1.fail)(res, r.error ?? 'Failed to import CSV.');
59
- (0, helpers_1.ok)(res, { message: `${r.data?.inserted ?? 0} row(s) imported.`, inserted: r.data?.inserted });
60
- });
61
- router.post('/api/tables/:db/:table/import', importCsvHandler);
62
- router.post('/api/tables/:db/:table/rows/import', importCsvHandler);
63
- router.post('/api/tables/:db/:table/import/csv', importCsvHandler);
64
- router.post('/api/query/:db/export', (0, helpers_1.wrap)(async (req, res) => {
65
- const db = ctx.getDb(req);
66
- const sql = String(req.body?.sql ?? '').trim();
67
- const format = String(req.body?.format ?? 'csv').toLowerCase();
68
- if (!sql)
69
- return (0, helpers_1.fail)(res, 'SQL query is required.');
70
- const r = await db.all(sql);
71
- if (!r.success)
72
- return (0, helpers_1.fail)(res, r.error ?? 'Query failed.', 400);
73
- const rows = (r.data ?? []).map(common_1.normalizeRow);
74
- const columns = rows.length ? Object.keys(rows[0]) : [];
75
- if (format === 'json') {
76
- res.setHeader('Content-Type', 'application/json; charset=utf-8');
77
- res.setHeader('Content-Disposition', 'attachment; filename="query_results.json"');
78
- return res.send((0, csv_1.toJson)(rows));
79
- }
80
- res.setHeader('Content-Type', 'text/csv; charset=utf-8');
81
- res.setHeader('Content-Disposition', 'attachment; filename="query_results.csv"');
82
- res.send((0, csv_1.toCsv)(rows, columns));
83
- }));
84
- }
@@ -1 +0,0 @@
1
- import type {Router} from 'express';import{type ApiContext} from './helpers';export{type ApiContext} from './helpers';export declare function registerApi(router:Router,ctx:ApiContext):void;
@@ -1,55 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.registerApi = registerApi;
4
- const tables_1 = require("./tables");
5
- const rows_1 = require("./rows");
6
- const import_export_1 = require("./import-export");
7
- const seed_1 = require("./seed");
8
- const query_1 = require("./query");
9
- const erd_1 = require("./erd");
10
- const export_1 = require("../../db/export");
11
- function registerApi(router, ctx) {
12
- router.use((req, res, next) => {
13
- if (req.method === 'GET')
14
- return next();
15
- const p = req.path;
16
- if (p.endsWith('/generate') ||
17
- p.endsWith('/preview') ||
18
- p.includes('/api/query/') ||
19
- p.endsWith('/rows/bulk-impact') ||
20
- p.endsWith('/rows/bulk-export')) {
21
- return next();
22
- }
23
- try {
24
- const db = ctx.getDb(req);
25
- if (db && db.isReadOnly) {
26
- return res.status(403).json({
27
- success: false,
28
- error: 'Database is open in read-only mode — write operations are disabled.',
29
- });
30
- }
31
- }
32
- catch {
33
- }
34
- return next();
35
- });
36
- (0, tables_1.registerTableRoutes)(router, ctx);
37
- (0, rows_1.registerRowRoutes)(router, ctx);
38
- (0, import_export_1.registerImportExportRoutes)(router, ctx);
39
- (0, seed_1.registerSeedRoutes)(router, ctx);
40
- (0, query_1.registerQueryRoutes)(router, ctx);
41
- (0, erd_1.registerErdRoutes)(router, ctx);
42
- router.get('/api/info/:db/ddl', async (req, res) => {
43
- try {
44
- const db = ctx.getDb(req);
45
- const dump = await (0, export_1.generateSchemaDump)(db);
46
- if (!dump.success) {
47
- return res.status(500).json({ success: false, error: dump.error });
48
- }
49
- res.json({ success: true, data: dump.data });
50
- }
51
- catch (err) {
52
- res.status(500).json({ success: false, error: err instanceof Error ? err.message : String(err) });
53
- }
54
- });
55
- }
@@ -1 +0,0 @@
1
- import type {Router} from 'express';import{type ApiContext} from './helpers';export declare function registerQueryRoutes(router:Router,ctx:ApiContext):void;
@@ -1,77 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.registerQueryRoutes = registerQueryRoutes;
4
- const classifier_1 = require("../../sql/classifier");
5
- const error_analyzer_1 = require("../../sql/error-analyzer");
6
- const common_1 = require("../../utils/common");
7
- const helpers_1 = require("./helpers");
8
- function registerQueryRoutes(router, ctx) {
9
- router.post('/api/query/:db', (0, helpers_1.wrap)(async (req, res) => {
10
- const db = ctx.getDb(req);
11
- const rawSql = String(req.body?.sql ?? '').trim();
12
- if (!rawSql)
13
- return (0, helpers_1.fail)(res, 'SQL query is required.');
14
- const { kind } = (0, classifier_1.classifySql)(rawSql);
15
- if (db.hasMultipleStatements(rawSql)) {
16
- if (db.isReadOnly) {
17
- return (0, helpers_1.fail)(res, 'Database is open in read-only mode — write statements are disabled.', 403);
18
- }
19
- const r = await db.execResult(rawSql);
20
- if (!r.success) {
21
- const details = await (0, error_analyzer_1.analyzeSqlError)(rawSql, r.error ?? 'Script execution failed.', db);
22
- return (0, helpers_1.fail)(res, r.error ?? 'Query failed.', 400, details);
23
- }
24
- return (0, helpers_1.ok)(res, { kind: 'script', message: 'Script executed successfully.' });
25
- }
26
- if (kind === 'select' || kind === 'read' || kind === 'count') {
27
- const r = await db.all(rawSql);
28
- if (!r.success) {
29
- const details = await (0, error_analyzer_1.analyzeSqlError)(rawSql, r.error ?? 'Query execution failed.', db);
30
- return (0, helpers_1.fail)(res, r.error ?? 'Query failed.', 400, details);
31
- }
32
- const rows = (r.data ?? []).map(common_1.normalizeRow);
33
- return (0, helpers_1.ok)(res, { kind: 'select', columns: rows.length ? Object.keys(rows[0]) : [], rows });
34
- }
35
- if (db.isReadOnly) {
36
- return (0, helpers_1.fail)(res, 'Database is open in read-only mode — write statements are disabled.', 403);
37
- }
38
- const r = await db.runWrite(rawSql);
39
- if (!r.success) {
40
- const details = await (0, error_analyzer_1.analyzeSqlError)(rawSql, r.error ?? 'Statement execution failed.', db);
41
- return (0, helpers_1.fail)(res, r.error ?? 'Query failed.', 400, details);
42
- }
43
- const changes = r.data?.changes;
44
- (0, helpers_1.ok)(res, {
45
- kind: 'write',
46
- changes: changes ?? null,
47
- message: changes != null ? `${changes} row(s) affected.` : 'Statement executed successfully.',
48
- });
49
- }));
50
- router.get('/api/queries/:db', (0, helpers_1.wrap)(async (req, res) => {
51
- const db = ctx.getDb(req);
52
- const r = await db.listSavedQueries();
53
- if (!r.success)
54
- return (0, helpers_1.fail)(res, r.error ?? 'Failed to load queries.', 500);
55
- (0, helpers_1.ok)(res, r.data);
56
- }));
57
- router.post('/api/queries/:db', (0, helpers_1.wrap)(async (req, res) => {
58
- const db = ctx.getDb(req);
59
- const name = String(req.body?.name ?? '').trim();
60
- const sql = String(req.body?.sql ?? '').trim();
61
- if (!name)
62
- return (0, helpers_1.fail)(res, 'Query name is required.');
63
- if (!sql)
64
- return (0, helpers_1.fail)(res, 'Query SQL is required.');
65
- const r = await db.saveQuery(name, sql);
66
- if (!r.success)
67
- return (0, helpers_1.fail)(res, r.error ?? 'Failed to save query.', 400);
68
- (0, helpers_1.ok)(res, { message: 'Query saved.' }, 201);
69
- }));
70
- router.delete('/api/queries/:db/:id', (0, helpers_1.wrap)(async (req, res) => {
71
- const db = ctx.getDb(req);
72
- const r = await db.deleteSavedQuery(req.params.id);
73
- if (!r.success)
74
- return (0, helpers_1.fail)(res, r.error ?? 'Failed to delete query.', 400);
75
- (0, helpers_1.ok)(res, { message: 'Query deleted.' });
76
- }));
77
- }
@@ -1 +0,0 @@
1
- import type {Router} from 'express';import{type ApiContext} from './helpers';export declare function registerRowRoutes(router:Router,ctx:ApiContext):void;