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.
- package/README.md +368 -335
- package/dist/app.js +227 -1
- package/dist/auth/config.d.ts +1 -1
- package/dist/auth/config.js +109 -1
- package/dist/auth/crypto.js +98 -1
- package/dist/auth/index.js +21 -1
- package/dist/auth/middleware.js +78 -1
- package/dist/auth/routes.js +64 -1
- package/dist/auth/types.js +7 -1
- package/dist/cli/args.d.ts +1 -1
- package/dist/cli/args.js +309 -1
- package/dist/cli/config.js +118 -1
- package/dist/cli/index.js +20 -1
- package/dist/cli/runner.js +208 -1
- package/dist/cli.js +4 -1
- package/dist/data/chain.d.ts +1 -0
- package/dist/data/chain.js +130 -0
- package/dist/data/datasets.d.ts +1 -1
- package/dist/data/datasets.js +171 -1
- package/dist/data/detector.d.ts +1 -1
- package/dist/data/detector.js +535 -1
- package/dist/data/engine.d.ts +1 -1
- package/dist/data/engine.js +586 -1
- package/dist/data/index.d.ts +1 -1
- package/dist/data/index.js +30 -1
- 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 +100 -1
- 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/types.js +5 -1
- package/dist/data/validator.d.ts +1 -0
- package/dist/data/validator.js +160 -0
- package/dist/db/database.d.ts +1 -1
- package/dist/db/database.js +472 -1
- package/dist/db/export.d.ts +1 -1
- package/dist/db/export.js +67 -1
- package/dist/db/filters.d.ts +1 -1
- package/dist/db/filters.js +119 -1
- package/dist/db/index.js +24 -1
- package/dist/db/introspection.js +164 -1
- package/dist/db/manager.js +327 -1
- package/dist/db/migrations.js +100 -1
- package/dist/db/postgres.d.ts +1 -1
- package/dist/db/postgres.js +687 -1
- package/dist/db/types.d.ts +1 -1
- package/dist/db/types.js +6 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +52 -1
- package/dist/public/css/app.css +1 -1
- package/dist/public/js/api.js +1 -1
- package/dist/public/js/browse.js +1 -1
- package/dist/public/js/common.js +1 -0
- package/dist/public/js/databases.js +1 -1
- package/dist/public/js/designer.js +1 -1
- package/dist/public/js/forms.js +1 -1
- package/dist/public/js/home.js +1 -0
- package/dist/public/js/inspector.js +1 -1
- package/dist/public/js/query.js +1 -1
- package/dist/public/js/schema.js +1 -1
- package/dist/public/js/seed.js +1 -1
- package/dist/routes/api/erd.d.ts +1 -0
- package/dist/routes/api/erd.js +45 -0
- package/dist/routes/api/helpers.d.ts +1 -1
- package/dist/routes/api/helpers.js +213 -1
- package/dist/routes/api/import-export.js +82 -1
- package/dist/routes/api/index.js +48 -1
- package/dist/routes/api/query.js +74 -1
- package/dist/routes/api/rows.js +358 -1
- package/dist/routes/api/seed.js +167 -1
- package/dist/routes/api/tables.js +216 -1
- package/dist/routes/databases.js +210 -1
- package/dist/routes/index.js +19 -1
- package/dist/routes/pages.js +446 -1
- package/dist/serverless.js +149 -1
- package/dist/sql/classifier.js +24 -1
- package/dist/sql/error-analyzer.d.ts +1 -0
- package/dist/sql/error-analyzer.js +550 -0
- package/dist/sql/generator.d.ts +1 -1
- package/dist/sql/generator.js +245 -1
- package/dist/sql/index.js +18 -1
- package/dist/types/api.d.ts +1 -1
- package/dist/types/api.js +2 -1
- package/dist/utils/colors.js +54 -1
- package/dist/utils/common.d.ts +1 -1
- package/dist/utils/common.js +185 -1
- package/dist/utils/csv.d.ts +1 -1
- package/dist/utils/csv.js +61 -1
- package/dist/utils/datatype.js +167 -1
- package/dist/utils/icons.js +81 -1
- package/dist/utils/index.js +20 -1
- package/dist/utils/logger.js +36 -1
- package/dist/views/layouts/main.hbs +1 -74
- package/dist/views/pages/databases.hbs +1 -233
- package/dist/views/pages/designer.hbs +1 -89
- package/dist/views/pages/erd.hbs +1 -0
- package/dist/views/pages/error.hbs +1 -10
- package/dist/views/pages/form.hbs +1 -59
- package/dist/views/pages/home.hbs +1 -124
- package/dist/views/pages/info.hbs +1 -0
- package/dist/views/pages/login.hbs +1 -98
- package/dist/views/pages/query.hbs +1 -87
- package/dist/views/pages/schema.hbs +1 -298
- package/dist/views/pages/seed-select.hbs +251 -0
- package/dist/views/pages/seed.hbs +273 -167
- package/dist/views/pages/table.hbs +1 -327
- package/dist/views/partials/navbar.hbs +1 -83
- package/dist/views/partials/sidebar.hbs +1 -117
- package/docs/API.md +149 -144
- package/docs/EXAMPLES.md +579 -576
- package/docs/SECURITY.md +265 -265
- package/package.json +79 -78
- package/dist/public/js/icons.js +0 -1
package/dist/routes/api/rows.js
CHANGED
|
@@ -1 +1,358 @@
|
|
|
1
|
-
Object.defineProperty(exports,"__esModule",{value:!0}),exports.registerRowRoutes=function(router,ctx){const{db:db}=ctx;router.get("/api/tables/:table/rows",(0,helpers_1.wrap)(async(req,res)=>{const page=Number.parseInt(String(req.query.page??"1"),10),limit=Number.parseInt(String(req.query.limit??"50"),10),orderBy=req.query.orderBy?String(req.query.orderBy):void 0,orderDir="desc"===req.query.orderDir?"desc":"asc",filters=(0,common_1.parseFilters)(req.query.f),[rowsR,totalR]=await Promise.all([db.getRows(req.params.table,{page:page,limit:limit,orderBy:orderBy,orderDir:orderDir,filters:filters}),db.getRowCount(req.params.table,filters)]);if(!rowsR.success)return(0,helpers_1.fail)(res,rowsR.error??"Failed to load rows.");const rows=(rowsR.data??[]).map(common_1.normalizeRow);(0,helpers_1.ok)(res,{rows:rows,total:totalR.success?totalR.data:rows.length,page:page,limit:limit})})),router.get("/api/tables/:table/rows/count",(0,helpers_1.wrap)(async(req,res)=>{const filters=(0,common_1.parseFilters)(req.query.f),r=await db.getRowCount(req.params.table,filters);if(!r.success)return(0,helpers_1.fail)(res,r.error??"Failed to count rows.");(0,helpers_1.ok)(res,{count:r.data})})),router.get("/api/tables/:table/row/:id",(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 where=(0,helpers_1.pkWhere)(info,req.params.id);if(!where)return(0,helpers_1.fail)(res,"Invalid primary key.",400);const r=await db.getRow(req.params.table,where);return r.success?r.data?void(0,helpers_1.ok)(res,(0,common_1.normalizeRow)(r.data)):(0,helpers_1.fail)(res,"Row not found.",404):(0,helpers_1.fail)(res,r.error??"Failed to load row.")})),router.get("/api/tables/:table/row/:id/blob/:column",(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 where=(0,helpers_1.pkWhere)(info,req.params.id);if(!where)return(0,helpers_1.fail)(res,"Invalid primary key.",400);const r=await db.getRow(req.params.table,where);if(!r.success)return(0,helpers_1.fail)(res,r.error??"Failed to load row.");if(!r.data)return(0,helpers_1.fail)(res,"Row not found.",404);const colName=req.params.column,rawVal=r.data[colName];if(null==rawVal)return(0,helpers_1.fail)(res,"Column value is NULL.",404);let buf;buf=Buffer.isBuffer(rawVal)||rawVal instanceof Uint8Array?Buffer.isBuffer(rawVal)?rawVal:Buffer.from(rawVal):"string"==typeof rawVal&&/^0x[0-9a-f]*$/i.test(rawVal)?Buffer.from(rawVal.slice(2),"hex"):Buffer.from(String(rawVal),"utf8");const mimeInfo=(0,datatype_1.sniffMimeType)(buf);if(res.setHeader("Content-Type",mimeInfo.mime),res.setHeader("Content-Length",buf.length),res.setHeader("Cache-Control","no-cache"),"1"===req.query.download||"true"===req.query.download){const filename=`${req.params.table}_${colName}_${req.params.id}.${mimeInfo.ext}`;res.setHeader("Content-Disposition",`attachment; filename="${filename}"`)}res.end(buf)})),router.get("/api/tables/:table/row/:id/blob/:column/meta",(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 where=(0,helpers_1.pkWhere)(info,req.params.id);if(!where)return(0,helpers_1.fail)(res,"Invalid primary key.",400);const r=await db.getRow(req.params.table,where);if(!r.success)return(0,helpers_1.fail)(res,r.error??"Failed to load row.");if(!r.data)return(0,helpers_1.fail)(res,"Row not found.",404);const colName=req.params.column,rawVal=r.data[colName];if(null==rawVal)return(0,helpers_1.ok)(res,{isNull:!0,size:0,sizeFormatted:"0 B"});const meta=(0,datatype_1.analyzeBlob)(rawVal);(0,helpers_1.ok)(res,{isNull:!1,...meta})})),router.put("/api/tables/:table/row/:id/blob/:column",(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 where=(0,helpers_1.pkWhere)(info,req.params.id);if(!where)return(0,helpers_1.fail)(res,"Invalid primary key.",400);const colName=req.params.column;let buf;if(void 0===req.body?.data)return(0,helpers_1.fail)(res,"Payload data is required.");{const format=req.body.format||"base64";if("base64"===format){const base64Str=String(req.body.data).replace(/^data:[^;]+;base64,/,"");buf=Buffer.from(base64Str,"base64")}else if("hex"===format){const hexStr=String(req.body.data).replace(/^0x/i,"");buf=Buffer.from(hexStr,"hex")}else buf=Buffer.from(String(req.body.data),"utf8")}const updateRes=await db.updateRow(req.params.table,[{column:colName,value:buf}],where);if(!updateRes.success)return(0,helpers_1.fail)(res,updateRes.error??"Failed to update BLOB.");(0,helpers_1.ok)(res,{message:`Updated BLOB in column "${colName}".`,size:buf.length})})),router.post("/api/tables/:table/rows/generate",(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 fields=(0,helpers_1.buildFields)(info,req.body?.values??req.body??{});if(!fields.length)return(0,helpers_1.fail)(res,"No valid column values provided.");const sql=(0,generator_1.generateInsert)(req.params.table,fields);(0,helpers_1.ok)(res,{sql:sql})}));const generateUpdateHandler=(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 id=req.params.id||(req.query.id?String(req.query.id):null);if(!id)return(0,helpers_1.fail)(res,"Primary key id is required.");const where=(0,helpers_1.pkWhere)(info,id);if(!where)return(0,helpers_1.fail)(res,"Invalid primary key.",400);const nulls=Array.isArray(req.body?.nulls)?req.body.nulls:[],fields=(0,helpers_1.buildUpdateFields)(info,req.body?.values??req.body??{},nulls);if(!fields.length)return(0,helpers_1.fail)(res,"No fields to update.");const sql=(0,generator_1.generateUpdate)(req.params.table,fields,where);(0,helpers_1.ok)(res,{sql:sql})});router.put("/api/tables/:table/row/:id/generate",generateUpdateHandler),router.post("/api/tables/:table/row/:id/generate",generateUpdateHandler),router.put("/api/tables/:table/rows/generate",generateUpdateHandler),router.post("/api/tables/:table/rows",(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 isBatch=Array.isArray(req.body?.rows),rowList=isBatch?req.body.rows:[req.body?.values??req.body??{}];if(!rowList.length)return(0,helpers_1.fail)(res,"No rows provided.");const fieldsList=rowList.map(row=>(0,helpers_1.buildFields)(info,row));if(!isBatch){const fields=fieldsList[0];if(!fields.length)return(0,helpers_1.fail)(res,"No valid column values provided.");const r=await db.insertRow(req.params.table,fields);return r.success?(0,helpers_1.ok)(res,{message:"Row inserted.",id:r.data?.lastInsertRowid},201):(0,helpers_1.fail)(res,r.error??"Failed to insert row.")}const r=await db.insertRows(req.params.table,fieldsList);if(!r.success)return(0,helpers_1.fail)(res,r.error??"Failed to insert rows.");(0,helpers_1.ok)(res,{message:`${r.data?.inserted??0} row(s) inserted.`,inserted:r.data?.inserted},201)}));const updateSingleRowHandler=(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 id=req.params.id||(req.query.id?String(req.query.id):null);if(!id)return(0,helpers_1.fail)(res,"Primary key id is required.");const where=(0,helpers_1.pkWhere)(info,id);if(!where)return(0,helpers_1.fail)(res,"Invalid primary key.");const nulls=Array.isArray(req.body?.nulls)?req.body.nulls:[],fields=(0,helpers_1.buildUpdateFields)(info,req.body?.values??req.body??{},nulls);if(!fields.length)return(0,helpers_1.fail)(res,"No fields to update.");const r=await db.updateRow(req.params.table,fields,where);if(!r.success)return(0,helpers_1.fail)(res,r.error??"Failed to update row.");(0,helpers_1.ok)(res,{message:"Row updated."})}),bulkUpdateHandler=(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);if(Array.isArray(req.body?.updates)){const updates=req.body.updates;if(!updates.length)return(0,helpers_1.fail)(res,"No updates provided.");if(updates.length>helpers_1.MAX_BULK_ROWS)return(0,helpers_1.fail)(res,`Cannot update more than ${helpers_1.MAX_BULK_ROWS} rows at once.`);const rowUpdates=[];for(const u of updates){if(!u.id)return(0,helpers_1.fail)(res,"Every update requires an id.");const where=(0,helpers_1.pkWhere)(info,String(u.id));if(!where)return(0,helpers_1.fail)(res,`Invalid primary key: "${u.id}".`);const nulls=Array.isArray(u.nulls)?u.nulls.map(String):[],fields=(0,helpers_1.buildUpdateFields)(info,u.values??{},nulls);fields.length&&rowUpdates.push({fields:fields,where:where})}if(!rowUpdates.length)return(0,helpers_1.ok)(res,{message:"No changes to apply.",updated:0});const r=await db.updateRows(req.params.table,rowUpdates);return r.success?(0,helpers_1.ok)(res,{message:`${r.data?.updated??0} row(s) updated.`,updated:r.data?.updated}):(0,helpers_1.fail)(res,r.error??"Failed to update rows.")}return updateSingleRowHandler(req,res)});router.put("/api/tables/:table/row/:id",updateSingleRowHandler),router.put("/api/tables/:table/rows",bulkUpdateHandler),router.post("/api/tables/:table/rows/bulk-update",bulkUpdateHandler);const deleteSingleRowHandler=(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 id=req.params.id||(req.query.id?String(req.query.id):null);if(!id)return(0,helpers_1.fail)(res,"Primary key id is required.");const where=(0,helpers_1.pkWhere)(info,id);if(!where)return(0,helpers_1.fail)(res,"Invalid primary key.");const r=await db.deleteRow(req.params.table,where);if(!r.success)return(0,helpers_1.fail)(res,r.error??"Failed to delete row.");(0,helpers_1.ok)(res,{message:"Row deleted."})});router.delete("/api/tables/:table/row/:id",deleteSingleRowHandler),router.delete("/api/tables/:table/rows",deleteSingleRowHandler),router.get("/api/tables/:table/rows/:id/references",(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 where=(0,helpers_1.pkWhere)(info,req.params.id);if(!where)return(0,helpers_1.fail)(res,"Invalid primary key.",400);const rowR=await db.getRow(req.params.table,where);if(!rowR.success||!rowR.data)return(0,helpers_1.fail)(res,"Row not found.",404);const referencing=(await db.getReferencingTables(req.params.table)).data??[],results=[];for(const item of referencing)for(const ref of item.refs){const targetCol=ref.to||info.primaryKey[0];if(!targetCol)continue;const val=rowR.data[targetCol];if(null==val)continue;const fkR=await db.getRowsByFk(item.table,ref.from,val);if(fkR.success&&fkR.data){const rows=fkR.data.rows.map(common_1.normalizeRow),columns=rows.length>0?Object.keys(rows[0]):[];results.push({table:item.table,from:ref.from,to:targetCol,value:val,columns:columns,rows:rows,count:fkR.data.total,total:fkR.data.total})}}(0,helpers_1.ok)(res,{references:results})})),router.post("/api/tables/:table/rows/bulk-delete",(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 ids=req.body?.ids??[];if(!Array.isArray(ids)||!ids.length)return(0,helpers_1.fail)(res,"No row IDs provided.");if(ids.length>helpers_1.MAX_BULK_ROWS)return(0,helpers_1.fail)(res,`Cannot delete more than ${helpers_1.MAX_BULK_ROWS} rows at once.`);const wheres=(0,helpers_1.resolvePkRows)(info,ids);if(!wheres)return(0,helpers_1.fail)(res,"Invalid or unresolvable row IDs.");const r=await db.deleteRows(req.params.table,wheres);if(!r.success)return(0,helpers_1.fail)(res,r.error??"Failed to delete rows.");(0,helpers_1.ok)(res,{message:`${r.data?.deleted??0} row(s) deleted.`,deleted:r.data?.deleted})})),router.post("/api/tables/:table/rows/bulk-impact",(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 ids=req.body?.ids??[];if(!Array.isArray(ids)||!ids.length)return(0,helpers_1.ok)(res,{references:[],total:0});const wheres=(0,helpers_1.resolvePkRows)(info,ids);if(!wheres)return(0,helpers_1.ok)(res,{references:[],total:0});const impact=await(0,helpers_1.computeBulkImpact)(db,info,wheres);(0,helpers_1.ok)(res,impact)})),router.post("/api/tables/:table/rows/bulk-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 ids=req.body?.ids??[];if(!Array.isArray(ids)||!ids.length)return(0,helpers_1.fail)(res,"No row IDs provided.");if(ids.length>helpers_1.MAX_BULK_ROWS)return(0,helpers_1.fail)(res,`Cannot export more than ${helpers_1.MAX_BULK_ROWS} rows at once.`);const wheres=(0,helpers_1.resolvePkRows)(info,ids);if(!wheres)return(0,helpers_1.fail)(res,"Invalid or unresolvable row IDs.");const format=String(req.body?.format??"csv").toLowerCase(),rowsR=await db.getRowsByPks(req.params.table,wheres);if(!rowsR.success)return(0,helpers_1.fail)(res,rowsR.error??"Failed to load rows.");const rows=(rowsR.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}_selected.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}_selected.csv"`),res.send((0,csv_1.toCsv)(rows,columns))}))};const common_1=require("../../utils/common"),datatype_1=require("../../utils/datatype"),csv_1=require("../../utils/csv"),generator_1=require("../../sql/generator"),helpers_1=require("./helpers");
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerRowRoutes = registerRowRoutes;
|
|
4
|
+
const common_1 = require("../../utils/common");
|
|
5
|
+
const datatype_1 = require("../../utils/datatype");
|
|
6
|
+
const csv_1 = require("../../utils/csv");
|
|
7
|
+
const generator_1 = require("../../sql/generator");
|
|
8
|
+
const helpers_1 = require("./helpers");
|
|
9
|
+
function registerRowRoutes(router, ctx) {
|
|
10
|
+
const { db } = ctx;
|
|
11
|
+
router.get('/api/tables/:table/rows', (0, helpers_1.wrap)(async (req, res) => {
|
|
12
|
+
const page = Number.parseInt(String(req.query.page ?? '1'), 10);
|
|
13
|
+
const limit = Number.parseInt(String(req.query.limit ?? '50'), 10);
|
|
14
|
+
const orderBy = req.query.orderBy ? String(req.query.orderBy) : undefined;
|
|
15
|
+
const orderDir = req.query.orderDir === 'desc' ? 'desc' : 'asc';
|
|
16
|
+
const filters = (0, common_1.parseFilters)(req.query.f);
|
|
17
|
+
const [rowsR, totalR] = await Promise.all([
|
|
18
|
+
db.getRows(req.params.table, { page, limit, orderBy, orderDir, filters }),
|
|
19
|
+
db.getRowCount(req.params.table, filters),
|
|
20
|
+
]);
|
|
21
|
+
if (!rowsR.success)
|
|
22
|
+
return (0, helpers_1.fail)(res, rowsR.error ?? 'Failed to load rows.');
|
|
23
|
+
const rows = (rowsR.data ?? []).map(common_1.normalizeRow);
|
|
24
|
+
(0, helpers_1.ok)(res, {
|
|
25
|
+
rows,
|
|
26
|
+
total: totalR.success ? totalR.data : rows.length,
|
|
27
|
+
page,
|
|
28
|
+
limit,
|
|
29
|
+
});
|
|
30
|
+
}));
|
|
31
|
+
router.get('/api/tables/:table/rows/count', (0, helpers_1.wrap)(async (req, res) => {
|
|
32
|
+
const filters = (0, common_1.parseFilters)(req.query.f);
|
|
33
|
+
const r = await db.getRowCount(req.params.table, filters);
|
|
34
|
+
if (!r.success)
|
|
35
|
+
return (0, helpers_1.fail)(res, r.error ?? 'Failed to count rows.');
|
|
36
|
+
(0, helpers_1.ok)(res, { count: r.data });
|
|
37
|
+
}));
|
|
38
|
+
router.get('/api/tables/:table/row/:id', (0, helpers_1.wrap)(async (req, res) => {
|
|
39
|
+
const info = await (0, helpers_1.requireTable)(db, req.params.table);
|
|
40
|
+
if (!info)
|
|
41
|
+
return (0, helpers_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
|
|
42
|
+
const where = (0, helpers_1.pkWhere)(info, req.params.id);
|
|
43
|
+
if (!where)
|
|
44
|
+
return (0, helpers_1.fail)(res, 'Invalid primary key.', 400);
|
|
45
|
+
const r = await db.getRow(req.params.table, where);
|
|
46
|
+
if (!r.success)
|
|
47
|
+
return (0, helpers_1.fail)(res, r.error ?? 'Failed to load row.');
|
|
48
|
+
if (!r.data)
|
|
49
|
+
return (0, helpers_1.fail)(res, 'Row not found.', 404);
|
|
50
|
+
(0, helpers_1.ok)(res, (0, common_1.normalizeRow)(r.data));
|
|
51
|
+
}));
|
|
52
|
+
router.get('/api/tables/:table/row/:id/blob/:column', (0, helpers_1.wrap)(async (req, res) => {
|
|
53
|
+
const info = await (0, helpers_1.requireTable)(db, req.params.table);
|
|
54
|
+
if (!info)
|
|
55
|
+
return (0, helpers_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
|
|
56
|
+
const where = (0, helpers_1.pkWhere)(info, req.params.id);
|
|
57
|
+
if (!where)
|
|
58
|
+
return (0, helpers_1.fail)(res, 'Invalid primary key.', 400);
|
|
59
|
+
const r = await db.getRow(req.params.table, where);
|
|
60
|
+
if (!r.success)
|
|
61
|
+
return (0, helpers_1.fail)(res, r.error ?? 'Failed to load row.');
|
|
62
|
+
if (!r.data)
|
|
63
|
+
return (0, helpers_1.fail)(res, 'Row not found.', 404);
|
|
64
|
+
const colName = req.params.column;
|
|
65
|
+
const rawVal = r.data[colName];
|
|
66
|
+
if (rawVal === null || rawVal === undefined) {
|
|
67
|
+
return (0, helpers_1.fail)(res, 'Column value is NULL.', 404);
|
|
68
|
+
}
|
|
69
|
+
let buf;
|
|
70
|
+
if (Buffer.isBuffer(rawVal) || rawVal instanceof Uint8Array) {
|
|
71
|
+
buf = Buffer.isBuffer(rawVal) ? rawVal : Buffer.from(rawVal);
|
|
72
|
+
}
|
|
73
|
+
else if (typeof rawVal === 'string' && (/^0x[0-9a-f]*$/i.test(rawVal))) {
|
|
74
|
+
buf = Buffer.from(rawVal.slice(2), 'hex');
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
buf = Buffer.from(String(rawVal), 'utf8');
|
|
78
|
+
}
|
|
79
|
+
const mimeInfo = (0, datatype_1.sniffMimeType)(buf);
|
|
80
|
+
res.setHeader('Content-Type', mimeInfo.mime);
|
|
81
|
+
res.setHeader('Content-Length', buf.length);
|
|
82
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
83
|
+
if (req.query.download === '1' || req.query.download === 'true') {
|
|
84
|
+
const filename = `${req.params.table}_${colName}_${req.params.id}.${mimeInfo.ext}`;
|
|
85
|
+
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
|
|
86
|
+
}
|
|
87
|
+
res.end(buf);
|
|
88
|
+
}));
|
|
89
|
+
router.get('/api/tables/:table/row/:id/blob/:column/meta', (0, helpers_1.wrap)(async (req, res) => {
|
|
90
|
+
const info = await (0, helpers_1.requireTable)(db, req.params.table);
|
|
91
|
+
if (!info)
|
|
92
|
+
return (0, helpers_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
|
|
93
|
+
const where = (0, helpers_1.pkWhere)(info, req.params.id);
|
|
94
|
+
if (!where)
|
|
95
|
+
return (0, helpers_1.fail)(res, 'Invalid primary key.', 400);
|
|
96
|
+
const r = await db.getRow(req.params.table, where);
|
|
97
|
+
if (!r.success)
|
|
98
|
+
return (0, helpers_1.fail)(res, r.error ?? 'Failed to load row.');
|
|
99
|
+
if (!r.data)
|
|
100
|
+
return (0, helpers_1.fail)(res, 'Row not found.', 404);
|
|
101
|
+
const colName = req.params.column;
|
|
102
|
+
const rawVal = r.data[colName];
|
|
103
|
+
if (rawVal === null || rawVal === undefined) {
|
|
104
|
+
return (0, helpers_1.ok)(res, { isNull: true, size: 0, sizeFormatted: '0 B' });
|
|
105
|
+
}
|
|
106
|
+
const meta = (0, datatype_1.analyzeBlob)(rawVal);
|
|
107
|
+
(0, helpers_1.ok)(res, { isNull: false, ...meta });
|
|
108
|
+
}));
|
|
109
|
+
router.put('/api/tables/:table/row/:id/blob/:column', (0, helpers_1.wrap)(async (req, res) => {
|
|
110
|
+
const info = await (0, helpers_1.requireTable)(db, req.params.table);
|
|
111
|
+
if (!info)
|
|
112
|
+
return (0, helpers_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
|
|
113
|
+
const where = (0, helpers_1.pkWhere)(info, req.params.id);
|
|
114
|
+
if (!where)
|
|
115
|
+
return (0, helpers_1.fail)(res, 'Invalid primary key.', 400);
|
|
116
|
+
const colName = req.params.column;
|
|
117
|
+
let buf;
|
|
118
|
+
if (req.body?.data !== undefined) {
|
|
119
|
+
const format = req.body.format || 'base64';
|
|
120
|
+
if (format === 'base64') {
|
|
121
|
+
const base64Str = String(req.body.data).replace(/^data:[^;]+;base64,/, '');
|
|
122
|
+
buf = Buffer.from(base64Str, 'base64');
|
|
123
|
+
}
|
|
124
|
+
else if (format === 'hex') {
|
|
125
|
+
const hexStr = String(req.body.data).replace(/^0x/i, '');
|
|
126
|
+
buf = Buffer.from(hexStr, 'hex');
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
buf = Buffer.from(String(req.body.data), 'utf8');
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
return (0, helpers_1.fail)(res, 'Payload data is required.');
|
|
134
|
+
}
|
|
135
|
+
const updateRes = await db.updateRow(req.params.table, [{ column: colName, value: buf }], where);
|
|
136
|
+
if (!updateRes.success)
|
|
137
|
+
return (0, helpers_1.fail)(res, updateRes.error ?? 'Failed to update BLOB.');
|
|
138
|
+
(0, helpers_1.ok)(res, { message: `Updated BLOB in column "${colName}".`, size: buf.length });
|
|
139
|
+
}));
|
|
140
|
+
router.post('/api/tables/:table/rows/generate', (0, helpers_1.wrap)(async (req, res) => {
|
|
141
|
+
const info = await (0, helpers_1.requireTable)(db, req.params.table);
|
|
142
|
+
if (!info)
|
|
143
|
+
return (0, helpers_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
|
|
144
|
+
const fields = (0, helpers_1.buildFields)(info, req.body?.values ?? req.body ?? {});
|
|
145
|
+
if (!fields.length)
|
|
146
|
+
return (0, helpers_1.fail)(res, 'No valid column values provided.');
|
|
147
|
+
(0, helpers_1.ok)(res, { sql: (0, generator_1.generateInsert)(req.params.table, fields) });
|
|
148
|
+
}));
|
|
149
|
+
const generateUpdateHandler = (0, helpers_1.wrap)(async (req, res) => {
|
|
150
|
+
const info = await (0, helpers_1.requireTable)(db, req.params.table);
|
|
151
|
+
if (!info)
|
|
152
|
+
return (0, helpers_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
|
|
153
|
+
const id = req.params.id || (req.query.id ? String(req.query.id) : null);
|
|
154
|
+
if (!id)
|
|
155
|
+
return (0, helpers_1.fail)(res, 'Primary key id is required.');
|
|
156
|
+
const where = (0, helpers_1.pkWhere)(info, id);
|
|
157
|
+
if (!where)
|
|
158
|
+
return (0, helpers_1.fail)(res, 'Invalid primary key.', 400);
|
|
159
|
+
const nulls = Array.isArray(req.body?.nulls) ? req.body.nulls : [];
|
|
160
|
+
const fields = (0, helpers_1.buildUpdateFields)(info, req.body?.values ?? req.body ?? {}, nulls);
|
|
161
|
+
if (!fields.length)
|
|
162
|
+
return (0, helpers_1.fail)(res, 'No fields to update.');
|
|
163
|
+
(0, helpers_1.ok)(res, { sql: (0, generator_1.generateUpdate)(req.params.table, fields, where) });
|
|
164
|
+
});
|
|
165
|
+
router.put('/api/tables/:table/row/:id/generate', generateUpdateHandler);
|
|
166
|
+
router.post('/api/tables/:table/row/:id/generate', generateUpdateHandler);
|
|
167
|
+
router.put('/api/tables/:table/rows/generate', generateUpdateHandler);
|
|
168
|
+
router.post('/api/tables/:table/rows', (0, helpers_1.wrap)(async (req, res) => {
|
|
169
|
+
const info = await (0, helpers_1.requireTable)(db, req.params.table);
|
|
170
|
+
if (!info)
|
|
171
|
+
return (0, helpers_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
|
|
172
|
+
const isBatch = Array.isArray(req.body?.rows);
|
|
173
|
+
const rowList = isBatch
|
|
174
|
+
? req.body.rows
|
|
175
|
+
: [req.body?.values ?? req.body ?? {}];
|
|
176
|
+
if (!rowList.length)
|
|
177
|
+
return (0, helpers_1.fail)(res, 'No rows provided.');
|
|
178
|
+
const fieldsList = rowList.map((row) => (0, helpers_1.buildFields)(info, row));
|
|
179
|
+
if (!isBatch) {
|
|
180
|
+
const fields = fieldsList[0];
|
|
181
|
+
if (!fields.length)
|
|
182
|
+
return (0, helpers_1.fail)(res, 'No valid column values provided.');
|
|
183
|
+
const r = await db.insertRow(req.params.table, fields);
|
|
184
|
+
if (!r.success)
|
|
185
|
+
return (0, helpers_1.fail)(res, r.error ?? 'Failed to insert row.');
|
|
186
|
+
return (0, helpers_1.ok)(res, { message: 'Row inserted.', id: r.data?.lastInsertRowid }, 201);
|
|
187
|
+
}
|
|
188
|
+
const r = await db.insertRows(req.params.table, fieldsList);
|
|
189
|
+
if (!r.success)
|
|
190
|
+
return (0, helpers_1.fail)(res, r.error ?? 'Failed to insert rows.');
|
|
191
|
+
(0, helpers_1.ok)(res, { message: `${r.data?.inserted ?? 0} row(s) inserted.`, inserted: r.data?.inserted }, 201);
|
|
192
|
+
}));
|
|
193
|
+
const updateSingleRowHandler = (0, helpers_1.wrap)(async (req, res) => {
|
|
194
|
+
const info = await (0, helpers_1.requireTable)(db, req.params.table);
|
|
195
|
+
if (!info)
|
|
196
|
+
return (0, helpers_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
|
|
197
|
+
const id = req.params.id || (req.query.id ? String(req.query.id) : null);
|
|
198
|
+
if (!id)
|
|
199
|
+
return (0, helpers_1.fail)(res, 'Primary key id is required.');
|
|
200
|
+
const where = (0, helpers_1.pkWhere)(info, id);
|
|
201
|
+
if (!where)
|
|
202
|
+
return (0, helpers_1.fail)(res, 'Invalid primary key.');
|
|
203
|
+
const nulls = Array.isArray(req.body?.nulls) ? req.body.nulls : [];
|
|
204
|
+
const fields = (0, helpers_1.buildUpdateFields)(info, req.body?.values ?? req.body ?? {}, nulls);
|
|
205
|
+
if (!fields.length)
|
|
206
|
+
return (0, helpers_1.fail)(res, 'No fields to update.');
|
|
207
|
+
const r = await db.updateRow(req.params.table, fields, where);
|
|
208
|
+
if (!r.success)
|
|
209
|
+
return (0, helpers_1.fail)(res, r.error ?? 'Failed to update row.');
|
|
210
|
+
(0, helpers_1.ok)(res, { message: 'Row updated.' });
|
|
211
|
+
});
|
|
212
|
+
const bulkUpdateHandler = (0, helpers_1.wrap)(async (req, res) => {
|
|
213
|
+
const info = await (0, helpers_1.requireTable)(db, req.params.table);
|
|
214
|
+
if (!info)
|
|
215
|
+
return (0, helpers_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
|
|
216
|
+
if (Array.isArray(req.body?.updates)) {
|
|
217
|
+
const updates = req.body.updates;
|
|
218
|
+
if (!updates.length)
|
|
219
|
+
return (0, helpers_1.fail)(res, 'No updates provided.');
|
|
220
|
+
if (updates.length > helpers_1.MAX_BULK_ROWS)
|
|
221
|
+
return (0, helpers_1.fail)(res, `Cannot update more than ${helpers_1.MAX_BULK_ROWS} rows at once.`);
|
|
222
|
+
const rowUpdates = [];
|
|
223
|
+
for (const u of updates) {
|
|
224
|
+
if (!u.id)
|
|
225
|
+
return (0, helpers_1.fail)(res, 'Every update requires an id.');
|
|
226
|
+
const where = (0, helpers_1.pkWhere)(info, String(u.id));
|
|
227
|
+
if (!where)
|
|
228
|
+
return (0, helpers_1.fail)(res, `Invalid primary key: "${u.id}".`);
|
|
229
|
+
const nulls = Array.isArray(u.nulls) ? u.nulls.map(String) : [];
|
|
230
|
+
const fields = (0, helpers_1.buildUpdateFields)(info, u.values ?? {}, nulls);
|
|
231
|
+
if (fields.length)
|
|
232
|
+
rowUpdates.push({ fields, where });
|
|
233
|
+
}
|
|
234
|
+
if (!rowUpdates.length)
|
|
235
|
+
return (0, helpers_1.ok)(res, { message: 'No changes to apply.', updated: 0 });
|
|
236
|
+
const r = await db.updateRows(req.params.table, rowUpdates);
|
|
237
|
+
if (!r.success)
|
|
238
|
+
return (0, helpers_1.fail)(res, r.error ?? 'Failed to update rows.');
|
|
239
|
+
return (0, helpers_1.ok)(res, { message: `${r.data?.updated ?? 0} row(s) updated.`, updated: r.data?.updated });
|
|
240
|
+
}
|
|
241
|
+
return updateSingleRowHandler(req, res);
|
|
242
|
+
});
|
|
243
|
+
router.put('/api/tables/:table/row/:id', updateSingleRowHandler);
|
|
244
|
+
router.put('/api/tables/:table/rows', bulkUpdateHandler);
|
|
245
|
+
router.post('/api/tables/:table/rows/bulk-update', bulkUpdateHandler);
|
|
246
|
+
const deleteSingleRowHandler = (0, helpers_1.wrap)(async (req, res) => {
|
|
247
|
+
const info = await (0, helpers_1.requireTable)(db, req.params.table);
|
|
248
|
+
if (!info)
|
|
249
|
+
return (0, helpers_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
|
|
250
|
+
const id = req.params.id || (req.query.id ? String(req.query.id) : null);
|
|
251
|
+
if (!id)
|
|
252
|
+
return (0, helpers_1.fail)(res, 'Primary key id is required.');
|
|
253
|
+
const where = (0, helpers_1.pkWhere)(info, id);
|
|
254
|
+
if (!where)
|
|
255
|
+
return (0, helpers_1.fail)(res, 'Invalid primary key.');
|
|
256
|
+
const r = await db.deleteRow(req.params.table, where);
|
|
257
|
+
if (!r.success)
|
|
258
|
+
return (0, helpers_1.fail)(res, r.error ?? 'Failed to delete row.');
|
|
259
|
+
(0, helpers_1.ok)(res, { message: 'Row deleted.' });
|
|
260
|
+
});
|
|
261
|
+
router.delete('/api/tables/:table/row/:id', deleteSingleRowHandler);
|
|
262
|
+
router.delete('/api/tables/:table/rows', deleteSingleRowHandler);
|
|
263
|
+
router.get('/api/tables/:table/rows/:id/references', (0, helpers_1.wrap)(async (req, res) => {
|
|
264
|
+
const info = await (0, helpers_1.requireTable)(db, req.params.table);
|
|
265
|
+
if (!info)
|
|
266
|
+
return (0, helpers_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
|
|
267
|
+
const where = (0, helpers_1.pkWhere)(info, req.params.id);
|
|
268
|
+
if (!where)
|
|
269
|
+
return (0, helpers_1.fail)(res, 'Invalid primary key.', 400);
|
|
270
|
+
const rowR = await db.getRow(req.params.table, where);
|
|
271
|
+
if (!rowR.success || !rowR.data)
|
|
272
|
+
return (0, helpers_1.fail)(res, 'Row not found.', 404);
|
|
273
|
+
const refInfoR = await db.getReferencingTables(req.params.table);
|
|
274
|
+
const referencing = refInfoR.data ?? [];
|
|
275
|
+
const results = [];
|
|
276
|
+
for (const item of referencing) {
|
|
277
|
+
for (const ref of item.refs) {
|
|
278
|
+
const targetCol = ref.to || info.primaryKey[0];
|
|
279
|
+
if (!targetCol)
|
|
280
|
+
continue;
|
|
281
|
+
const val = rowR.data[targetCol];
|
|
282
|
+
if (val == null)
|
|
283
|
+
continue;
|
|
284
|
+
const fkR = await db.getRowsByFk(item.table, ref.from, val);
|
|
285
|
+
if (fkR.success && fkR.data) {
|
|
286
|
+
const rows = fkR.data.rows.map(common_1.normalizeRow);
|
|
287
|
+
results.push({
|
|
288
|
+
table: item.table,
|
|
289
|
+
from: ref.from,
|
|
290
|
+
to: targetCol,
|
|
291
|
+
value: val,
|
|
292
|
+
columns: rows.length > 0 ? Object.keys(rows[0]) : [],
|
|
293
|
+
rows,
|
|
294
|
+
count: fkR.data.total,
|
|
295
|
+
total: fkR.data.total,
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
(0, helpers_1.ok)(res, { references: results });
|
|
301
|
+
}));
|
|
302
|
+
router.post('/api/tables/:table/rows/bulk-delete', (0, helpers_1.wrap)(async (req, res) => {
|
|
303
|
+
const info = await (0, helpers_1.requireTable)(db, req.params.table);
|
|
304
|
+
if (!info)
|
|
305
|
+
return (0, helpers_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
|
|
306
|
+
const ids = (req.body?.ids ?? []);
|
|
307
|
+
if (!Array.isArray(ids) || !ids.length)
|
|
308
|
+
return (0, helpers_1.fail)(res, 'No row IDs provided.');
|
|
309
|
+
if (ids.length > helpers_1.MAX_BULK_ROWS)
|
|
310
|
+
return (0, helpers_1.fail)(res, `Cannot delete more than ${helpers_1.MAX_BULK_ROWS} rows at once.`);
|
|
311
|
+
const wheres = (0, helpers_1.resolvePkRows)(info, ids);
|
|
312
|
+
if (!wheres)
|
|
313
|
+
return (0, helpers_1.fail)(res, 'Invalid or unresolvable row IDs.');
|
|
314
|
+
const r = await db.deleteRows(req.params.table, wheres);
|
|
315
|
+
if (!r.success)
|
|
316
|
+
return (0, helpers_1.fail)(res, r.error ?? 'Failed to delete rows.');
|
|
317
|
+
(0, helpers_1.ok)(res, { message: `${r.data?.deleted ?? 0} row(s) deleted.`, deleted: r.data?.deleted });
|
|
318
|
+
}));
|
|
319
|
+
router.post('/api/tables/:table/rows/bulk-impact', (0, helpers_1.wrap)(async (req, res) => {
|
|
320
|
+
const info = await (0, helpers_1.requireTable)(db, req.params.table);
|
|
321
|
+
if (!info)
|
|
322
|
+
return (0, helpers_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
|
|
323
|
+
const ids = (req.body?.ids ?? []);
|
|
324
|
+
if (!Array.isArray(ids) || !ids.length)
|
|
325
|
+
return (0, helpers_1.ok)(res, { references: [], total: 0 });
|
|
326
|
+
const wheres = (0, helpers_1.resolvePkRows)(info, ids);
|
|
327
|
+
if (!wheres)
|
|
328
|
+
return (0, helpers_1.ok)(res, { references: [], total: 0 });
|
|
329
|
+
(0, helpers_1.ok)(res, await (0, helpers_1.computeBulkImpact)(db, info, wheres));
|
|
330
|
+
}));
|
|
331
|
+
router.post('/api/tables/:table/rows/bulk-export', (0, helpers_1.wrap)(async (req, res) => {
|
|
332
|
+
const info = await (0, helpers_1.requireTable)(db, req.params.table);
|
|
333
|
+
if (!info)
|
|
334
|
+
return (0, helpers_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
|
|
335
|
+
const ids = (req.body?.ids ?? []);
|
|
336
|
+
if (!Array.isArray(ids) || !ids.length)
|
|
337
|
+
return (0, helpers_1.fail)(res, 'No row IDs provided.');
|
|
338
|
+
if (ids.length > helpers_1.MAX_BULK_ROWS)
|
|
339
|
+
return (0, helpers_1.fail)(res, `Cannot export more than ${helpers_1.MAX_BULK_ROWS} rows at once.`);
|
|
340
|
+
const wheres = (0, helpers_1.resolvePkRows)(info, ids);
|
|
341
|
+
if (!wheres)
|
|
342
|
+
return (0, helpers_1.fail)(res, 'Invalid or unresolvable row IDs.');
|
|
343
|
+
const format = String(req.body?.format ?? 'csv').toLowerCase();
|
|
344
|
+
const rowsR = await db.getRowsByPks(req.params.table, wheres);
|
|
345
|
+
if (!rowsR.success)
|
|
346
|
+
return (0, helpers_1.fail)(res, rowsR.error ?? 'Failed to load rows.');
|
|
347
|
+
const rows = (rowsR.data ?? []).map(common_1.normalizeRow);
|
|
348
|
+
const columns = info.columns.map((c) => c.name);
|
|
349
|
+
if (format === 'json') {
|
|
350
|
+
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
351
|
+
res.setHeader('Content-Disposition', `attachment; filename="${req.params.table}_selected.json"`);
|
|
352
|
+
return res.send((0, csv_1.toJson)(rows));
|
|
353
|
+
}
|
|
354
|
+
res.setHeader('Content-Type', 'text/csv; charset=utf-8');
|
|
355
|
+
res.setHeader('Content-Disposition', `attachment; filename="${req.params.table}_selected.csv"`);
|
|
356
|
+
res.send((0, csv_1.toCsv)(rows, columns));
|
|
357
|
+
}));
|
|
358
|
+
}
|
package/dist/routes/api/seed.js
CHANGED
|
@@ -1 +1,167 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerSeedRoutes = registerSeedRoutes;
|
|
4
|
+
const generator_1 = require("../../sql/generator");
|
|
5
|
+
const index_1 = require("../../data/index");
|
|
6
|
+
const helpers_1 = require("./helpers");
|
|
7
|
+
function registerSeedRoutes(router, ctx) {
|
|
8
|
+
const { db } = ctx;
|
|
9
|
+
router.post('/api/seed/validate', (0, helpers_1.wrap)(async (req, res) => {
|
|
10
|
+
(0, helpers_1.ok)(res, await (0, index_1.validateGenerationPlan)(db, (0, helpers_1.parseUnifiedGenerationPlan)(req.body)));
|
|
11
|
+
}));
|
|
12
|
+
router.post('/api/seed/preview', (0, helpers_1.wrap)(async (req, res) => {
|
|
13
|
+
(0, helpers_1.ok)(res, await index_1.SeedEngine.executePlan(db, (0, helpers_1.parseUnifiedGenerationPlan)(req.body), { previewLimit: 50 }));
|
|
14
|
+
}));
|
|
15
|
+
router.post('/api/seed/generate', (0, helpers_1.wrap)(async (req, res) => {
|
|
16
|
+
(0, helpers_1.ok)(res, await index_1.SeedEngine.executePlan(db, (0, helpers_1.parseUnifiedGenerationPlan)(req.body)));
|
|
17
|
+
}));
|
|
18
|
+
router.post('/api/seed/execute', (0, helpers_1.wrap)(async (req, res) => {
|
|
19
|
+
const plan = (0, helpers_1.parseUnifiedGenerationPlan)(req.body);
|
|
20
|
+
const truncate = Boolean(req.body?.truncate || plan.options?.truncateAll);
|
|
21
|
+
const gen = await index_1.SeedEngine.executePlan(db, plan, { truncateAll: truncate });
|
|
22
|
+
const exec = await (0, index_1.executeChainInsert)(db, gen, truncate);
|
|
23
|
+
(0, helpers_1.ok)(res, {
|
|
24
|
+
message: `${truncate ? 'Cleared tables and seeded' : 'Seeded'} ${exec.totalInserted} row(s) across ${Object.keys(exec.inserted).length} table(s) in ${exec.elapsedMs}ms.`,
|
|
25
|
+
inserted: exec.inserted,
|
|
26
|
+
totalInserted: exec.totalInserted,
|
|
27
|
+
elapsedMs: exec.elapsedMs,
|
|
28
|
+
warnings: exec.warnings,
|
|
29
|
+
}, 201);
|
|
30
|
+
}));
|
|
31
|
+
const getSeedConfigHandler = (0, helpers_1.wrap)(async (req, res) => {
|
|
32
|
+
const info = await (0, helpers_1.requireTable)(db, req.params.table);
|
|
33
|
+
if (!info)
|
|
34
|
+
return (0, helpers_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
|
|
35
|
+
(0, helpers_1.ok)(res, { table: req.params.table, columns: (0, index_1.buildColumnConfigs)(info), maxRows: index_1.MAX_SEED_ROWS });
|
|
36
|
+
});
|
|
37
|
+
router.get('/api/tables/:table/seed/config', getSeedConfigHandler);
|
|
38
|
+
router.get('/api/tables/:table/seed/plan', getSeedConfigHandler);
|
|
39
|
+
router.post('/api/tables/:table/seed/generate', (0, helpers_1.wrap)(async (req, res) => {
|
|
40
|
+
const info = await (0, helpers_1.requireTable)(db, req.params.table);
|
|
41
|
+
if (!info)
|
|
42
|
+
return (0, helpers_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
|
|
43
|
+
const { count, plan } = (0, helpers_1.parseSeedRequest)(req.body);
|
|
44
|
+
const configs = (0, index_1.buildColumnConfigs)(info);
|
|
45
|
+
const gen = await (0, index_1.generateRows)(db, info, configs, count, plan);
|
|
46
|
+
(0, helpers_1.ok)(res, {
|
|
47
|
+
table: req.params.table,
|
|
48
|
+
count: gen.rows.length,
|
|
49
|
+
sql: (0, index_1.buildSeedInsertSql)(req.params.table, gen.rows),
|
|
50
|
+
previewRows: gen.previewRows,
|
|
51
|
+
warnings: gen.warnings,
|
|
52
|
+
});
|
|
53
|
+
}));
|
|
54
|
+
router.post('/api/tables/:table/seed/preview', (0, helpers_1.wrap)(async (req, res) => {
|
|
55
|
+
const info = await (0, helpers_1.requireTable)(db, req.params.table);
|
|
56
|
+
if (!info)
|
|
57
|
+
return (0, helpers_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
|
|
58
|
+
const { count, plan } = (0, helpers_1.parseSeedRequest)(req.body);
|
|
59
|
+
const configs = (0, index_1.buildColumnConfigs)(info);
|
|
60
|
+
const gen = await (0, index_1.generateRows)(db, info, configs, Math.min(50, count), plan);
|
|
61
|
+
(0, helpers_1.ok)(res, {
|
|
62
|
+
table: req.params.table,
|
|
63
|
+
previewRows: gen.previewRows,
|
|
64
|
+
warnings: gen.warnings,
|
|
65
|
+
});
|
|
66
|
+
}));
|
|
67
|
+
router.post('/api/tables/:table/seed', (0, helpers_1.wrap)(async (req, res) => {
|
|
68
|
+
const info = await (0, helpers_1.requireTable)(db, req.params.table);
|
|
69
|
+
if (!info)
|
|
70
|
+
return (0, helpers_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
|
|
71
|
+
const { count, plan, truncate } = (0, helpers_1.parseSeedRequest)(req.body);
|
|
72
|
+
const configs = (0, index_1.buildColumnConfigs)(info);
|
|
73
|
+
const t0 = Date.now();
|
|
74
|
+
if (truncate) {
|
|
75
|
+
const clearRes = await db.run(`DELETE FROM ${(0, generator_1.quoteIdentifier)(req.params.table)}`);
|
|
76
|
+
if (!clearRes.success) {
|
|
77
|
+
return (0, helpers_1.fail)(res, clearRes.error ?? 'Failed to clear table before seeding.', 400);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const gen = await (0, index_1.generateRows)(db, info, configs, count, plan, truncate);
|
|
81
|
+
const result = await db.insertRows(req.params.table, gen.rows);
|
|
82
|
+
if (!result.success)
|
|
83
|
+
return (0, helpers_1.fail)(res, result.error ?? 'Seed failed.', 400);
|
|
84
|
+
const elapsedMs = Date.now() - t0;
|
|
85
|
+
(0, helpers_1.ok)(res, {
|
|
86
|
+
message: `${truncate ? 'Cleared table and inserted' : 'Inserted'} ${result.data?.inserted ?? 0} row(s) in ${elapsedMs}ms.`,
|
|
87
|
+
inserted: result.data?.inserted ?? 0,
|
|
88
|
+
skipped: result.data?.skipped ?? 0,
|
|
89
|
+
warnings: gen.warnings,
|
|
90
|
+
elapsedMs,
|
|
91
|
+
}, 201);
|
|
92
|
+
}));
|
|
93
|
+
router.get('/api/tables/:table/seed/chain', (0, helpers_1.wrap)(async (req, res) => {
|
|
94
|
+
const info = await (0, helpers_1.requireTable)(db, req.params.table);
|
|
95
|
+
if (!info)
|
|
96
|
+
return (0, helpers_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
|
|
97
|
+
const rawScope = String(req.query.scope ?? 'chain');
|
|
98
|
+
const scope = rawScope === 'ancestors' || rawScope === 'descendants' || rawScope === 'all' || rawScope === 'single'
|
|
99
|
+
? rawScope
|
|
100
|
+
: 'chain';
|
|
101
|
+
(0, helpers_1.ok)(res, await (0, index_1.getErChainConfig)(db, req.params.table, scope));
|
|
102
|
+
}));
|
|
103
|
+
router.post('/api/tables/:table/seed/chain/preview', (0, helpers_1.wrap)(async (req, res) => {
|
|
104
|
+
const info = await (0, helpers_1.requireTable)(db, req.params.table);
|
|
105
|
+
if (!info)
|
|
106
|
+
return (0, helpers_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
|
|
107
|
+
const { scope, counts, plans, modes, seed } = (0, helpers_1.parseChainSeedRequest)(req.body);
|
|
108
|
+
const config = await (0, index_1.getErChainConfig)(db, req.params.table, scope);
|
|
109
|
+
const previewCounts = {};
|
|
110
|
+
for (const t of config.tables) {
|
|
111
|
+
previewCounts[t.name] = Math.min(50, counts[t.name] ?? t.suggestedCount ?? 10);
|
|
112
|
+
}
|
|
113
|
+
(0, helpers_1.ok)(res, await (0, index_1.generateChainRows)(db, config, plans, previewCounts, false, modes, seed));
|
|
114
|
+
}));
|
|
115
|
+
router.post('/api/tables/:table/seed/chain/generate', (0, helpers_1.wrap)(async (req, res) => {
|
|
116
|
+
const info = await (0, helpers_1.requireTable)(db, req.params.table);
|
|
117
|
+
if (!info)
|
|
118
|
+
return (0, helpers_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
|
|
119
|
+
const { scope, counts, plans, modes, seed } = (0, helpers_1.parseChainSeedRequest)(req.body);
|
|
120
|
+
const config = await (0, index_1.getErChainConfig)(db, req.params.table, scope);
|
|
121
|
+
(0, helpers_1.ok)(res, await (0, index_1.generateChainRows)(db, config, plans, counts, false, modes, seed));
|
|
122
|
+
}));
|
|
123
|
+
router.post('/api/tables/:table/seed/chain', (0, helpers_1.wrap)(async (req, res) => {
|
|
124
|
+
const info = await (0, helpers_1.requireTable)(db, req.params.table);
|
|
125
|
+
if (!info)
|
|
126
|
+
return (0, helpers_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
|
|
127
|
+
const { scope, counts, plans, truncate, modes, seed } = (0, helpers_1.parseChainSeedRequest)(req.body);
|
|
128
|
+
const config = await (0, index_1.getErChainConfig)(db, req.params.table, scope);
|
|
129
|
+
const gen = await (0, index_1.generateChainRows)(db, config, plans, counts, truncate, modes, seed);
|
|
130
|
+
const exec = await (0, index_1.executeChainInsert)(db, gen, truncate);
|
|
131
|
+
(0, helpers_1.ok)(res, {
|
|
132
|
+
message: `${truncate ? 'Cleared tables in reverse order and seeded' : 'Seeded'} ${exec.totalInserted} row(s) across ${Object.keys(exec.inserted).length} table(s) in ${exec.elapsedMs}ms.`,
|
|
133
|
+
inserted: exec.inserted,
|
|
134
|
+
totalInserted: exec.totalInserted,
|
|
135
|
+
elapsedMs: exec.elapsedMs,
|
|
136
|
+
warnings: exec.warnings,
|
|
137
|
+
}, 201);
|
|
138
|
+
}));
|
|
139
|
+
router.get('/api/seed/chain', (0, helpers_1.wrap)(async (req, res) => {
|
|
140
|
+
const rawRoot = String(req.query.table ?? '');
|
|
141
|
+
const rawScope = String(req.query.scope ?? (rawRoot ? 'chain' : 'all'));
|
|
142
|
+
const scope = rawScope === 'ancestors' || rawScope === 'descendants' || rawScope === 'all' || rawScope === 'single'
|
|
143
|
+
? rawScope
|
|
144
|
+
: 'all';
|
|
145
|
+
const rootTable = rawRoot || (await db.listTables()).data?.[0]?.name || '';
|
|
146
|
+
if (!rootTable)
|
|
147
|
+
return (0, helpers_1.fail)(res, 'No tables found in database.', 404);
|
|
148
|
+
(0, helpers_1.ok)(res, await (0, index_1.getErChainConfig)(db, rootTable, scope));
|
|
149
|
+
}));
|
|
150
|
+
router.post('/api/seed/chain', (0, helpers_1.wrap)(async (req, res) => {
|
|
151
|
+
const { scope, counts, plans, truncate, modes, seed } = (0, helpers_1.parseChainSeedRequest)(req.body);
|
|
152
|
+
const rawRoot = String(req.body?.rootTable ?? '');
|
|
153
|
+
const rootTable = rawRoot || (await db.listTables()).data?.[0]?.name || '';
|
|
154
|
+
if (!rootTable)
|
|
155
|
+
return (0, helpers_1.fail)(res, 'No tables found in database.', 404);
|
|
156
|
+
const config = await (0, index_1.getErChainConfig)(db, rootTable, scope);
|
|
157
|
+
const gen = await (0, index_1.generateChainRows)(db, config, plans, counts, truncate, modes, seed);
|
|
158
|
+
const exec = await (0, index_1.executeChainInsert)(db, gen, truncate);
|
|
159
|
+
(0, helpers_1.ok)(res, {
|
|
160
|
+
message: `${truncate ? 'Cleared tables in reverse order and seeded' : 'Seeded'} ${exec.totalInserted} row(s) across ${Object.keys(exec.inserted).length} table(s) in ${exec.elapsedMs}ms.`,
|
|
161
|
+
inserted: exec.inserted,
|
|
162
|
+
totalInserted: exec.totalInserted,
|
|
163
|
+
elapsedMs: exec.elapsedMs,
|
|
164
|
+
warnings: exec.warnings,
|
|
165
|
+
}, 201);
|
|
166
|
+
}));
|
|
167
|
+
}
|