admindb 2.1.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.
Files changed (119) hide show
  1. package/README.md +368 -335
  2. package/dist/app.js +227 -1
  3. package/dist/auth/config.d.ts +1 -1
  4. package/dist/auth/config.js +109 -1
  5. package/dist/auth/crypto.js +98 -1
  6. package/dist/auth/index.js +21 -1
  7. package/dist/auth/middleware.js +78 -1
  8. package/dist/auth/routes.js +64 -1
  9. package/dist/auth/types.js +7 -1
  10. package/dist/cli/args.d.ts +1 -1
  11. package/dist/cli/args.js +309 -1
  12. package/dist/cli/config.js +118 -1
  13. package/dist/cli/index.js +20 -1
  14. package/dist/cli/runner.js +208 -1
  15. package/dist/cli.js +4 -1
  16. package/dist/data/chain.d.ts +1 -0
  17. package/dist/data/chain.js +130 -0
  18. package/dist/data/datasets.d.ts +1 -1
  19. package/dist/data/datasets.js +171 -1
  20. package/dist/data/detector.d.ts +1 -1
  21. package/dist/data/detector.js +535 -1
  22. package/dist/data/engine.d.ts +1 -1
  23. package/dist/data/engine.js +586 -1
  24. package/dist/data/index.d.ts +1 -1
  25. package/dist/data/index.js +30 -1
  26. package/dist/data/prng.d.ts +1 -0
  27. package/dist/data/prng.js +73 -0
  28. package/dist/data/registry.d.ts +1 -0
  29. package/dist/data/registry.js +805 -0
  30. package/dist/data/schema-graph.d.ts +1 -0
  31. package/dist/data/schema-graph.js +210 -0
  32. package/dist/data/strategies.d.ts +1 -1
  33. package/dist/data/strategies.js +100 -1
  34. package/dist/data/templates.d.ts +1 -0
  35. package/dist/data/templates.js +134 -0
  36. package/dist/data/types.d.ts +1 -1
  37. package/dist/data/types.js +5 -1
  38. package/dist/data/validator.d.ts +1 -0
  39. package/dist/data/validator.js +160 -0
  40. package/dist/db/database.d.ts +1 -1
  41. package/dist/db/database.js +472 -1
  42. package/dist/db/export.d.ts +1 -1
  43. package/dist/db/export.js +67 -1
  44. package/dist/db/filters.d.ts +1 -1
  45. package/dist/db/filters.js +119 -1
  46. package/dist/db/index.js +24 -1
  47. package/dist/db/introspection.js +164 -1
  48. package/dist/db/manager.js +327 -1
  49. package/dist/db/migrations.js +100 -1
  50. package/dist/db/postgres.d.ts +1 -1
  51. package/dist/db/postgres.js +687 -1
  52. package/dist/db/types.d.ts +1 -1
  53. package/dist/db/types.js +6 -1
  54. package/dist/index.d.ts +1 -1
  55. package/dist/index.js +52 -1
  56. package/dist/public/css/app.css +1 -1
  57. package/dist/public/js/api.js +1 -1
  58. package/dist/public/js/browse.js +1 -1
  59. package/dist/public/js/common.js +1 -0
  60. package/dist/public/js/databases.js +1 -1
  61. package/dist/public/js/designer.js +1 -1
  62. package/dist/public/js/forms.js +1 -1
  63. package/dist/public/js/home.js +1 -0
  64. package/dist/public/js/inspector.js +1 -1
  65. package/dist/public/js/query.js +1 -1
  66. package/dist/public/js/schema.js +1 -1
  67. package/dist/public/js/seed.js +1 -1
  68. package/dist/routes/api/erd.d.ts +1 -0
  69. package/dist/routes/api/erd.js +45 -0
  70. package/dist/routes/api/helpers.d.ts +1 -1
  71. package/dist/routes/api/helpers.js +213 -1
  72. package/dist/routes/api/import-export.js +82 -1
  73. package/dist/routes/api/index.js +48 -1
  74. package/dist/routes/api/query.js +74 -1
  75. package/dist/routes/api/rows.js +358 -1
  76. package/dist/routes/api/seed.js +167 -1
  77. package/dist/routes/api/tables.js +216 -1
  78. package/dist/routes/databases.js +210 -1
  79. package/dist/routes/index.js +19 -1
  80. package/dist/routes/pages.js +446 -1
  81. package/dist/serverless.js +149 -1
  82. package/dist/sql/classifier.js +24 -1
  83. package/dist/sql/error-analyzer.d.ts +1 -0
  84. package/dist/sql/error-analyzer.js +550 -0
  85. package/dist/sql/generator.d.ts +1 -1
  86. package/dist/sql/generator.js +245 -1
  87. package/dist/sql/index.js +18 -1
  88. package/dist/types/api.d.ts +1 -1
  89. package/dist/types/api.js +2 -1
  90. package/dist/utils/colors.js +54 -1
  91. package/dist/utils/common.d.ts +1 -1
  92. package/dist/utils/common.js +185 -1
  93. package/dist/utils/csv.d.ts +1 -1
  94. package/dist/utils/csv.js +61 -1
  95. package/dist/utils/datatype.js +167 -1
  96. package/dist/utils/icons.js +81 -1
  97. package/dist/utils/index.js +20 -1
  98. package/dist/utils/logger.js +36 -1
  99. package/dist/views/layouts/main.hbs +1 -74
  100. package/dist/views/pages/databases.hbs +1 -233
  101. package/dist/views/pages/designer.hbs +1 -89
  102. package/dist/views/pages/erd.hbs +1 -0
  103. package/dist/views/pages/error.hbs +1 -10
  104. package/dist/views/pages/form.hbs +1 -59
  105. package/dist/views/pages/home.hbs +1 -124
  106. package/dist/views/pages/info.hbs +1 -0
  107. package/dist/views/pages/login.hbs +1 -98
  108. package/dist/views/pages/query.hbs +1 -87
  109. package/dist/views/pages/schema.hbs +1 -298
  110. package/dist/views/pages/seed-select.hbs +251 -0
  111. package/dist/views/pages/seed.hbs +273 -167
  112. package/dist/views/pages/table.hbs +1 -327
  113. package/dist/views/partials/navbar.hbs +1 -83
  114. package/dist/views/partials/sidebar.hbs +1 -117
  115. package/docs/API.md +149 -144
  116. package/docs/EXAMPLES.md +579 -576
  117. package/docs/SECURITY.md +265 -265
  118. package/package.json +79 -78
  119. package/dist/public/js/icons.js +0 -1
@@ -1 +1,213 @@
1
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.MAX_BULK_ROWS=void 0,exports.ok=function(res,data,status=200){return res.status(status).json({success:!0,data:data})},exports.fail=fail,exports.wrap=function(fn){return async(req,res)=>{try{await fn(req,res)}catch(err){fail(res,(0,common_1.errorMessage)(err),500)}}},exports.requireTable=async function(db,table){const info=await db.getTableInfo(table);return info.success&&info.data&&0!==info.data.columns.length?info.data:null},exports.buildFields=buildFields,exports.buildUpdateFields=function(info,values,nulls){const fields=buildFields(info,values,{excludePk:!0}),typeMap=new Map(info.columns.map(c=>[c.name,c.type])),pkSet=new Set(info.primaryKey);for(const col of nulls)pkSet.has(col)||!typeMap.has(col)||fields.some(f=>f.column===col)||fields.push({column:col,value:null});return fields},exports.pkWhere=pkWhere,exports.parseSeedRequest=function(body){const b=body??{},count=Math.max(1,Math.min(index_1.MAX_SEED_ROWS,Number.parseInt(String(b.count??"10"),10)||10)),truncate=Boolean(b.truncate),plan={};if(b.plan&&"object"==typeof b.plan)for(const[k,v]of Object.entries(b.plan)){const p=(0,index_1.sanitizeColumnPlan)(v);p&&(plan[k]=p)}return{count:count,plan:plan,truncate:truncate}},exports.resolvePkRows=function(info,ids){const list=Array.isArray(ids)?ids:null==ids?[]:[ids],out=[];for(const id of list){const where=pkWhere(info,String(id));where&&out.push(where)}return out.length?out:null},exports.computeBulkImpact=async function(db,info,wheres){const[rowsR,refInfoR]=await Promise.all([db.getRowsByPks(info.table,wheres),db.getReferencingTables(info.table)]),rows=rowsR.data??[],referencing=refInfoR.data??[];if(!rows.length||!referencing.length)return{references:[],total:0};const queries=referencing.flatMap(item=>item.refs.map(async ref=>{const targetCol=ref.to||info.primaryKey[0];if(!targetCol)return null;const values=Array.from(new Set(rows.map(r=>r[targetCol]).filter(v=>null!=v)));if(!values.length)return null;try{const placeholders=values.map(()=>"?").join(", "),sql=`SELECT COUNT(*) AS c FROM ${(0,generator_1.quoteIdentifier)(item.table)} WHERE ${(0,generator_1.quoteIdentifier)(ref.from)} IN (${placeholders})`,countR=await db.all(sql,values),count=countR.success&&countR.data?.[0]?Number(countR.data[0].c??0):0;return count>0?{table:item.table,from:ref.from,to:targetCol,count:count}:null}catch{return null}})),results=(await Promise.all(queries)).filter(r=>null!==r),total=results.reduce((acc,r)=>acc+r.count,0);return{references:results,total:total}};const common_1=require("../../utils/common"),index_1=require("../../data/index"),generator_1=require("../../sql/generator");function fail(res,error,status=400){return res.status(status).json({success:!1,error:error})}function buildFields(info,values,opts={}){const typeMap=new Map(info.columns.map(c=>[c.name,c.type])),pkSet=new Set(info.primaryKey),fields=[];for(const[col,raw]of Object.entries(values??{})){if(!typeMap.has(col)||opts.excludePk&&pkSet.has(col))continue;const val=(0,common_1.coerceFormValue)(raw,typeMap.get(col));null!==val&&fields.push({column:col,value:val})}return fields}function pkWhere(info,encodedId){const vals=(0,common_1.decodePk)(encodedId);if(!info.primaryKey.length){if(1===vals.length&&""!==vals[0]){const num=Number(vals[0]);return[{column:"_rowid_",value:Number.isFinite(num)?num:vals[0]}]}return null}if(vals.length!==info.primaryKey.length)return null;const typeMap=new Map(info.columns.map(c=>[c.name,c.type]));return info.primaryKey.map((col,i)=>({column:col,value:(0,common_1.coerceFormValue)(vals[i],typeMap.get(col))}))}exports.MAX_BULK_ROWS=1e3;
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MAX_BULK_ROWS = exports.wrap = exports.fail = exports.ok = void 0;
4
+ exports.requireTable = requireTable;
5
+ exports.buildFields = buildFields;
6
+ exports.buildUpdateFields = buildUpdateFields;
7
+ exports.pkWhere = pkWhere;
8
+ exports.parseSeedRequest = parseSeedRequest;
9
+ exports.parseChainSeedRequest = parseChainSeedRequest;
10
+ exports.parseUnifiedGenerationPlan = parseUnifiedGenerationPlan;
11
+ exports.resolvePkRows = resolvePkRows;
12
+ exports.computeBulkImpact = computeBulkImpact;
13
+ const common_1 = require("../../utils/common");
14
+ const index_1 = require("../../data/index");
15
+ const generator_1 = require("../../sql/generator");
16
+ const ok = (res, data, status = 200) => res.status(status).json({ success: true, data });
17
+ exports.ok = ok;
18
+ const fail = (res, error, status = 400, details) => res.status(status).json({ success: false, error, ...(details ? { details } : {}) });
19
+ exports.fail = fail;
20
+ const wrap = (fn) => async (req, res) => {
21
+ try {
22
+ await fn(req, res);
23
+ }
24
+ catch (err) {
25
+ (0, exports.fail)(res, (0, common_1.errorMessage)(err), 500);
26
+ }
27
+ };
28
+ exports.wrap = wrap;
29
+ async function requireTable(db, table) {
30
+ const info = await db.getTableInfo(table);
31
+ return info.success && info.data?.columns?.length ? info.data : null;
32
+ }
33
+ function buildFields(info, values, opts = {}) {
34
+ const typeMap = new Map(info.columns.map((c) => [c.name, c.type]));
35
+ const pkSet = new Set(info.primaryKey);
36
+ const fields = [];
37
+ for (const [col, raw] of Object.entries(values ?? {})) {
38
+ if (!typeMap.has(col) || (opts.excludePk && pkSet.has(col)))
39
+ continue;
40
+ const val = (0, common_1.coerceFormValue)(raw, typeMap.get(col));
41
+ if (val !== null)
42
+ fields.push({ column: col, value: val });
43
+ }
44
+ return fields;
45
+ }
46
+ function buildUpdateFields(info, values, nulls) {
47
+ const fields = buildFields(info, values, { excludePk: true });
48
+ const typeMap = new Map(info.columns.map((c) => [c.name, c.type]));
49
+ const pkSet = new Set(info.primaryKey);
50
+ for (const col of nulls) {
51
+ if (!pkSet.has(col) && typeMap.has(col) && !fields.some((f) => f.column === col)) {
52
+ fields.push({ column: col, value: null });
53
+ }
54
+ }
55
+ return fields;
56
+ }
57
+ function pkWhere(info, encodedId) {
58
+ const vals = (0, common_1.decodePk)(encodedId);
59
+ if (!info.primaryKey.length) {
60
+ if (vals.length === 1 && vals[0] !== '') {
61
+ const num = Number(vals[0]);
62
+ return [{ column: '_rowid_', value: Number.isFinite(num) ? num : vals[0] }];
63
+ }
64
+ return null;
65
+ }
66
+ if (vals.length !== info.primaryKey.length)
67
+ return null;
68
+ const typeMap = new Map(info.columns.map((c) => [c.name, c.type]));
69
+ return info.primaryKey.map((col, i) => ({ column: col, value: (0, common_1.coerceFormValue)(vals[i], typeMap.get(col)) }));
70
+ }
71
+ exports.MAX_BULK_ROWS = 1000;
72
+ function parseSeedRequest(body) {
73
+ const b = (body ?? {});
74
+ const count = Math.max(1, Math.min(index_1.MAX_SEED_ROWS, Number.parseInt(String(b.count ?? '10'), 10) || 10));
75
+ const truncate = Boolean(b.truncate);
76
+ const seed = b.seed != null && b.seed !== '' ? Number(b.seed) : null;
77
+ const plan = {};
78
+ if (b.plan && typeof b.plan === 'object') {
79
+ for (const [k, v] of Object.entries(b.plan)) {
80
+ const p = (0, index_1.sanitizeColumnPlan)(v);
81
+ if (p)
82
+ plan[k] = p;
83
+ }
84
+ }
85
+ return { count, plan, truncate, seed };
86
+ }
87
+ function parseChainSeedRequest(body) {
88
+ const b = (body ?? {});
89
+ const scope = b.scope === 'ancestors' || b.scope === 'descendants' || b.scope === 'all' || b.scope === 'single'
90
+ ? b.scope
91
+ : 'chain';
92
+ const truncate = Boolean(b.truncate);
93
+ const seed = b.seed != null && b.seed !== '' ? Number(b.seed) : null;
94
+ const counts = {};
95
+ if (b.counts && typeof b.counts === 'object') {
96
+ for (const [table, rawN] of Object.entries(b.counts)) {
97
+ const n = Number.parseInt(String(rawN), 10);
98
+ if (Number.isFinite(n) && n > 0)
99
+ counts[table] = Math.min(2000, n);
100
+ }
101
+ }
102
+ const modes = {};
103
+ if (b.modes && typeof b.modes === 'object') {
104
+ for (const [table, rawM] of Object.entries(b.modes)) {
105
+ const m = String(rawM);
106
+ if (m === 'generate' || m === 'use_existing' || m === 'generate_if_empty' || m === 'skip') {
107
+ modes[table] = m;
108
+ }
109
+ }
110
+ }
111
+ const plans = {};
112
+ if (b.plans && typeof b.plans === 'object') {
113
+ for (const [table, tablePlans] of Object.entries(b.plans)) {
114
+ if (tablePlans && typeof tablePlans === 'object') {
115
+ const pMap = {};
116
+ for (const [col, v] of Object.entries(tablePlans)) {
117
+ const p = (0, index_1.sanitizeColumnPlan)(v);
118
+ if (p)
119
+ pMap[col] = p;
120
+ }
121
+ plans[table] = pMap;
122
+ }
123
+ }
124
+ }
125
+ return { scope, counts, plans, truncate, modes, seed };
126
+ }
127
+ function parseUnifiedGenerationPlan(body) {
128
+ const b = (body ?? {});
129
+ const rawPlan = (b.plan && typeof b.plan === 'object' ? b.plan : b);
130
+ const tables = {};
131
+ if (rawPlan.tables && typeof rawPlan.tables === 'object') {
132
+ for (const [tableName, rawSpec] of Object.entries(rawPlan.tables)) {
133
+ if (!rawSpec || typeof rawSpec !== 'object')
134
+ continue;
135
+ const spec = rawSpec;
136
+ const rawMode = String(spec.mode || 'generate');
137
+ const mode = rawMode === 'use_existing' || rawMode === 'generate_if_empty' || rawMode === 'skip' ? rawMode : 'generate';
138
+ const rows = Math.max(1, Math.min(index_1.MAX_SEED_ROWS, Number.parseInt(String(spec.rows ?? '10'), 10) || 10));
139
+ const columns = {};
140
+ if (spec.columns && typeof spec.columns === 'object') {
141
+ for (const [colName, rawColPlan] of Object.entries(spec.columns)) {
142
+ const sanitized = (0, index_1.sanitizeColumnPlan)(rawColPlan);
143
+ if (sanitized)
144
+ columns[colName] = sanitized;
145
+ }
146
+ }
147
+ tables[tableName] = {
148
+ mode,
149
+ rows,
150
+ columns,
151
+ truncate: Boolean(spec.truncate),
152
+ };
153
+ }
154
+ }
155
+ const rawSeed = b.seed !== undefined ? b.seed : rawPlan.options?.seed;
156
+ const seed = rawSeed != null && rawSeed !== '' ? Number(rawSeed) : null;
157
+ return {
158
+ name: typeof rawPlan.name === 'string' ? rawPlan.name : undefined,
159
+ description: typeof rawPlan.description === 'string' ? rawPlan.description : undefined,
160
+ rootTable: typeof rawPlan.rootTable === 'string' ? rawPlan.rootTable : undefined,
161
+ scope: rawPlan.scope || 'chain',
162
+ tables,
163
+ relationships: Array.isArray(rawPlan.relationships) ? rawPlan.relationships : undefined,
164
+ options: {
165
+ seed: Number.isFinite(seed) ? seed : null,
166
+ transaction: rawPlan.options ? Boolean(rawPlan.options.transaction ?? true) : true,
167
+ rollbackOnError: rawPlan.options ? Boolean(rawPlan.options.rollbackOnError ?? true) : true,
168
+ truncateAll: Boolean(b.truncate || rawPlan.options?.truncateAll),
169
+ batchSize: Number(rawPlan.options?.batchSize ?? 500) || 500,
170
+ },
171
+ };
172
+ }
173
+ function resolvePkRows(info, ids) {
174
+ const list = Array.isArray(ids) ? ids : ids == null ? [] : [ids];
175
+ const out = [];
176
+ for (const id of list) {
177
+ const where = pkWhere(info, String(id));
178
+ if (where)
179
+ out.push(where);
180
+ }
181
+ return out.length ? out : null;
182
+ }
183
+ async function computeBulkImpact(db, info, wheres) {
184
+ const [rowsR, refInfoR] = await Promise.all([
185
+ db.getRowsByPks(info.table, wheres),
186
+ db.getReferencingTables(info.table),
187
+ ]);
188
+ const rows = (rowsR.data ?? []);
189
+ const referencing = refInfoR.data ?? [];
190
+ if (!rows.length || !referencing.length)
191
+ return { references: [], total: 0 };
192
+ const queries = referencing.flatMap((item) => item.refs.map(async (ref) => {
193
+ const targetCol = ref.to || info.primaryKey[0];
194
+ if (!targetCol)
195
+ return null;
196
+ const values = Array.from(new Set(rows.map((r) => r[targetCol]).filter((v) => v != null)));
197
+ if (!values.length)
198
+ return null;
199
+ try {
200
+ const placeholders = values.map(() => '?').join(', ');
201
+ const sql = `SELECT COUNT(*) AS c FROM ${(0, generator_1.quoteIdentifier)(item.table)} WHERE ${(0, generator_1.quoteIdentifier)(ref.from)} IN (${placeholders})`;
202
+ const countR = await db.all(sql, values);
203
+ const count = countR.success && countR.data?.[0] ? Number(countR.data[0].c ?? 0) : 0;
204
+ return count > 0 ? { table: item.table, from: ref.from, to: targetCol, count } : null;
205
+ }
206
+ catch {
207
+ return null;
208
+ }
209
+ }));
210
+ const results = (await Promise.all(queries)).filter((r) => r !== null);
211
+ const total = results.reduce((acc, r) => acc + r.count, 0);
212
+ return { references: results, total };
213
+ }
@@ -1 +1,82 @@
1
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.registerImportExportRoutes=function(router,ctx){const{db:db}=ctx;router.get("/api/tables/:table/export",(0,helpers_1.wrap)(async(req,res)=>{const info=await(0,helpers_1.requireTable)(db,req.params.table);if(!info)return(0,helpers_1.fail)(res,`Table "${req.params.table}" does not exist.`,404);const format=String(req.query.format??"csv").toLowerCase(),filters=(0,common_1.parseFilters)(req.query.f),r=await db.getRows(req.params.table,{limit:1e6,filters:filters});if(!r.success)return(0,helpers_1.fail)(res,r.error??"Failed to export table.");const rows=(r.data??[]).map(common_1.normalizeRow),columns=info.columns.map(c=>c.name);if("json"===format)return res.setHeader("Content-Type","application/json; charset=utf-8"),res.setHeader("Content-Disposition",`attachment; filename="${req.params.table}.json"`),res.send((0,csv_1.toJson)(rows));res.setHeader("Content-Type","text/csv; charset=utf-8"),res.setHeader("Content-Disposition",`attachment; filename="${req.params.table}.csv"`),res.send((0,csv_1.toCsv)(rows,columns))}));const importCsvHandler=(0,helpers_1.wrap)(async(req,res)=>{const info=await(0,helpers_1.requireTable)(db,req.params.table);if(!info)return(0,helpers_1.fail)(res,`Table "${req.params.table}" does not exist.`,404);const csvText="string"==typeof req.body?req.body:String(req.body?.csv??"");if(!csvText.trim())return(0,helpers_1.fail)(res,"No CSV data provided.");const parsed=(0,csv_1.parseCsv)(csvText);if(!parsed.length)return(0,helpers_1.fail)(res,"CSV file is empty.");const header=parsed[0].map(h=>h.trim()),typeMap=new Map(info.columns.map(c=>[c.name,c.type]));if(!header.filter(h=>typeMap.has(h)).length)return(0,helpers_1.fail)(res,"None of the CSV columns match columns in this table.");const rows=parsed.slice(1).map(line=>{const fields=[];for(let i=0;i<header.length;i++){const col=header[i];if(!typeMap.has(col))continue;const raw=line[i]??"";fields.push({column:col,value:(0,common_1.coerceFormValue)(raw,typeMap.get(col))})}return fields}),r=await db.insertRows(req.params.table,rows);if(!r.success)return(0,helpers_1.fail)(res,r.error??"Failed to import CSV.");(0,helpers_1.ok)(res,{message:`${r.data?.inserted??0} row(s) imported.`,inserted:r.data?.inserted})});router.post("/api/tables/:table/import",importCsvHandler),router.post("/api/tables/:table/rows/import",importCsvHandler),router.post("/api/tables/:table/import/csv",importCsvHandler),router.post("/api/query/export",(0,helpers_1.wrap)(async(req,res)=>{const sql=String(req.body?.sql??"").trim(),format=String(req.body?.format??"csv").toLowerCase();if(!sql)return(0,helpers_1.fail)(res,"SQL query is required.");const r=await db.all(sql);if(!r.success)return(0,helpers_1.fail)(res,r.error??"Query failed.",400);const rows=(r.data??[]).map(common_1.normalizeRow),columns=rows.length?Object.keys(rows[0]):[];if("json"===format)return res.setHeader("Content-Type","application/json; charset=utf-8"),res.setHeader("Content-Disposition",'attachment; filename="query_results.json"'),res.send((0,csv_1.toJson)(rows));res.setHeader("Content-Type","text/csv; charset=utf-8"),res.setHeader("Content-Disposition",'attachment; filename="query_results.csv"'),res.send((0,csv_1.toCsv)(rows,columns))}))};const common_1=require("../../utils/common"),csv_1=require("../../utils/csv"),helpers_1=require("./helpers");
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
+ const { db } = ctx;
9
+ router.get('/api/tables/:table/export', (0, helpers_1.wrap)(async (req, res) => {
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 info = await (0, helpers_1.requireTable)(db, req.params.table);
31
+ if (!info)
32
+ return (0, helpers_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
33
+ const csvText = typeof req.body === 'string' ? req.body : String(req.body?.csv ?? '');
34
+ if (!csvText.trim())
35
+ return (0, helpers_1.fail)(res, 'No CSV data provided.');
36
+ const parsed = (0, csv_1.parseCsv)(csvText);
37
+ if (!parsed.length)
38
+ return (0, helpers_1.fail)(res, 'CSV file is empty.');
39
+ const header = parsed[0].map((h) => h.trim());
40
+ const typeMap = new Map(info.columns.map((c) => [c.name, c.type]));
41
+ const validCols = header.filter((h) => typeMap.has(h));
42
+ if (!validCols.length) {
43
+ return (0, helpers_1.fail)(res, 'None of the CSV columns match columns in this table.');
44
+ }
45
+ const rows = parsed.slice(1).map((line) => {
46
+ const fields = [];
47
+ for (let i = 0; i < header.length; i++) {
48
+ const col = header[i];
49
+ if (!typeMap.has(col))
50
+ continue;
51
+ fields.push({ column: col, value: (0, common_1.coerceFormValue)(line[i] ?? '', typeMap.get(col)) });
52
+ }
53
+ return fields;
54
+ });
55
+ const r = await db.insertRows(req.params.table, rows);
56
+ if (!r.success)
57
+ return (0, helpers_1.fail)(res, r.error ?? 'Failed to import CSV.');
58
+ (0, helpers_1.ok)(res, { message: `${r.data?.inserted ?? 0} row(s) imported.`, inserted: r.data?.inserted });
59
+ });
60
+ router.post('/api/tables/:table/import', importCsvHandler);
61
+ router.post('/api/tables/:table/rows/import', importCsvHandler);
62
+ router.post('/api/tables/:table/import/csv', importCsvHandler);
63
+ router.post('/api/query/export', (0, helpers_1.wrap)(async (req, res) => {
64
+ const sql = String(req.body?.sql ?? '').trim();
65
+ const format = String(req.body?.format ?? 'csv').toLowerCase();
66
+ if (!sql)
67
+ return (0, helpers_1.fail)(res, 'SQL query is required.');
68
+ const r = await db.all(sql);
69
+ if (!r.success)
70
+ return (0, helpers_1.fail)(res, r.error ?? 'Query failed.', 400);
71
+ const rows = (r.data ?? []).map(common_1.normalizeRow);
72
+ const columns = rows.length ? Object.keys(rows[0]) : [];
73
+ if (format === 'json') {
74
+ res.setHeader('Content-Type', 'application/json; charset=utf-8');
75
+ res.setHeader('Content-Disposition', 'attachment; filename="query_results.json"');
76
+ return res.send((0, csv_1.toJson)(rows));
77
+ }
78
+ res.setHeader('Content-Type', 'text/csv; charset=utf-8');
79
+ res.setHeader('Content-Disposition', 'attachment; filename="query_results.csv"');
80
+ res.send((0, csv_1.toCsv)(rows, columns));
81
+ }));
82
+ }
@@ -1 +1,48 @@
1
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.registerApi=function(router,ctx){const{db:db}=ctx;router.use((req,res,next)=>{if(!db.isReadOnly||"GET"===req.method)return next();const p=req.path;return p.endsWith("/generate")||p.endsWith("/preview")||p.endsWith("/api/query/export")||p.endsWith("/api/query")||p.endsWith("/rows/bulk-impact")||p.endsWith("/rows/bulk-export")?next():res.status(403).json({success:!1,error:"Database is open in read-only mode — write operations are disabled."})}),(0,tables_1.registerTableRoutes)(router,ctx),(0,rows_1.registerRowRoutes)(router,ctx),(0,import_export_1.registerImportExportRoutes)(router,ctx),(0,seed_1.registerSeedRoutes)(router,ctx),(0,query_1.registerQueryRoutes)(router,ctx)};const tables_1=require("./tables"),rows_1=require("./rows"),import_export_1=require("./import-export"),seed_1=require("./seed"),query_1=require("./query");
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
+ const { db } = ctx;
13
+ router.use((req, res, next) => {
14
+ if (!db.isReadOnly || req.method === 'GET')
15
+ return next();
16
+ const p = req.path;
17
+ if (p.endsWith('/generate') ||
18
+ p.endsWith('/preview') ||
19
+ p.endsWith('/api/query/export') ||
20
+ p.endsWith('/api/query') ||
21
+ p.endsWith('/rows/bulk-impact') ||
22
+ p.endsWith('/rows/bulk-export')) {
23
+ return next();
24
+ }
25
+ return res.status(403).json({
26
+ success: false,
27
+ error: 'Database is open in read-only mode — write operations are disabled.',
28
+ });
29
+ });
30
+ (0, tables_1.registerTableRoutes)(router, ctx);
31
+ (0, rows_1.registerRowRoutes)(router, ctx);
32
+ (0, import_export_1.registerImportExportRoutes)(router, ctx);
33
+ (0, seed_1.registerSeedRoutes)(router, ctx);
34
+ (0, query_1.registerQueryRoutes)(router, ctx);
35
+ (0, erd_1.registerErdRoutes)(router, ctx);
36
+ router.get('/api/info/ddl', async (_req, res) => {
37
+ try {
38
+ const dump = await (0, export_1.generateSchemaDump)(db);
39
+ if (!dump.success) {
40
+ return res.status(500).json({ success: false, error: dump.error });
41
+ }
42
+ res.json({ success: true, data: dump.data });
43
+ }
44
+ catch (err) {
45
+ res.status(500).json({ success: false, error: err instanceof Error ? err.message : String(err) });
46
+ }
47
+ });
48
+ }
@@ -1 +1,74 @@
1
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.registerQueryRoutes=function(router,ctx){const{db:db}=ctx;router.post("/api/query",(0,helpers_1.wrap)(async(req,res)=>{const rawSql=String(req.body?.sql??"").trim();if(!rawSql)return(0,helpers_1.fail)(res,"SQL query is required.");const{kind:kind}=(0,classifier_1.classifySql)(rawSql);if(db.hasMultipleStatements(rawSql)){if(db.isReadOnly)return(0,helpers_1.fail)(res,"Database is open in read-only mode — write statements are disabled.",403);const r=await db.execResult(rawSql);return r.success?(0,helpers_1.ok)(res,{kind:"script",message:"Script executed successfully."}):(0,helpers_1.fail)(res,r.error??"Query failed.",400)}if("select"===kind||"read"===kind||"count"===kind){const r=await db.all(rawSql);if(!r.success)return(0,helpers_1.fail)(res,r.error??"Query failed.",400);const rows=(r.data??[]).map(common_1.normalizeRow),columns=rows.length?Object.keys(rows[0]):[];return(0,helpers_1.ok)(res,{kind:"select",columns:columns,rows:rows})}if(db.isReadOnly)return(0,helpers_1.fail)(res,"Database is open in read-only mode — write statements are disabled.",403);const r=await db.runWrite(rawSql);if(!r.success)return(0,helpers_1.fail)(res,r.error??"Query failed.",400);const changes=r.data?.changes;(0,helpers_1.ok)(res,{kind:"write",changes:changes??null,message:null!=changes?`${changes} row(s) affected.`:"Statement executed successfully."})})),router.get("/api/queries",(0,helpers_1.wrap)(async(_req,res)=>{const r=await db.listSavedQueries();if(!r.success)return(0,helpers_1.fail)(res,r.error??"Failed to load queries.",500);(0,helpers_1.ok)(res,r.data)})),router.post("/api/queries",(0,helpers_1.wrap)(async(req,res)=>{const name=String(req.body?.name??"").trim(),sql=String(req.body?.sql??"").trim();if(!name)return(0,helpers_1.fail)(res,"Query name is required.");if(!sql)return(0,helpers_1.fail)(res,"Query SQL is required.");const r=await db.saveQuery(name,sql);if(!r.success)return(0,helpers_1.fail)(res,r.error??"Failed to save query.",400);(0,helpers_1.ok)(res,{message:"Query saved."},201)})),router.delete("/api/queries/:id",(0,helpers_1.wrap)(async(req,res)=>{const r=await db.deleteSavedQuery(req.params.id);if(!r.success)return(0,helpers_1.fail)(res,r.error??"Failed to delete query.",400);(0,helpers_1.ok)(res,{message:"Query deleted."})}))};const classifier_1=require("../../sql/classifier"),common_1=require("../../utils/common"),helpers_1=require("./helpers");
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
+ const { db } = ctx;
10
+ router.post('/api/query', (0, helpers_1.wrap)(async (req, res) => {
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', (0, helpers_1.wrap)(async (_req, res) => {
51
+ const r = await db.listSavedQueries();
52
+ if (!r.success)
53
+ return (0, helpers_1.fail)(res, r.error ?? 'Failed to load queries.', 500);
54
+ (0, helpers_1.ok)(res, r.data);
55
+ }));
56
+ router.post('/api/queries', (0, helpers_1.wrap)(async (req, res) => {
57
+ const name = String(req.body?.name ?? '').trim();
58
+ const sql = String(req.body?.sql ?? '').trim();
59
+ if (!name)
60
+ return (0, helpers_1.fail)(res, 'Query name is required.');
61
+ if (!sql)
62
+ return (0, helpers_1.fail)(res, 'Query SQL is required.');
63
+ const r = await db.saveQuery(name, sql);
64
+ if (!r.success)
65
+ return (0, helpers_1.fail)(res, r.error ?? 'Failed to save query.', 400);
66
+ (0, helpers_1.ok)(res, { message: 'Query saved.' }, 201);
67
+ }));
68
+ router.delete('/api/queries/:id', (0, helpers_1.wrap)(async (req, res) => {
69
+ const r = await db.deleteSavedQuery(req.params.id);
70
+ if (!r.success)
71
+ return (0, helpers_1.fail)(res, r.error ?? 'Failed to delete query.', 400);
72
+ (0, helpers_1.ok)(res, { message: 'Query deleted.' });
73
+ }));
74
+ }