admindb 2.0.0 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/app.d.ts +1 -69
- package/dist/app.js +1 -257
- package/dist/auth/config.d.ts +1 -19
- package/dist/auth/config.js +1 -147
- package/dist/auth/crypto.d.ts +1 -27
- package/dist/auth/crypto.js +1 -133
- package/dist/auth/index.d.ts +1 -5
- package/dist/auth/index.js +1 -22
- package/dist/auth/middleware.d.ts +1 -14
- package/dist/auth/middleware.js +1 -100
- package/dist/auth/routes.d.ts +1 -7
- package/dist/auth/routes.js +1 -78
- package/dist/auth/types.d.ts +1 -35
- package/dist/auth/types.js +1 -11
- package/dist/cli/args.d.ts +1 -45
- package/dist/cli/args.js +1 -328
- package/dist/cli/config.d.ts +1 -56
- package/dist/cli/config.js +1 -133
- package/dist/cli/index.d.ts +1 -4
- package/dist/cli/index.js +1 -21
- package/dist/cli/runner.d.ts +1 -1
- package/dist/cli/runner.js +1 -222
- package/dist/cli.d.ts +1 -2
- package/dist/cli.js +1 -5
- package/dist/data/datasets.d.ts +1 -17
- package/dist/data/datasets.js +1 -133
- package/dist/data/detector.d.ts +1 -30
- package/dist/data/detector.js +1 -472
- package/dist/data/engine.d.ts +1 -10
- package/dist/data/engine.js +1 -211
- package/dist/data/index.d.ts +1 -5
- package/dist/data/index.js +1 -22
- package/dist/data/strategies.d.ts +1 -30
- package/dist/data/strategies.js +1 -343
- package/dist/data/types.d.ts +1 -73
- package/dist/data/types.js +1 -8
- package/dist/db/database.d.ts +1 -119
- package/dist/db/database.js +1 -471
- package/dist/db/export.d.ts +1 -12
- package/dist/db/export.js +1 -42
- package/dist/db/filters.d.ts +1 -22
- package/dist/db/filters.js +1 -158
- package/dist/db/index.d.ts +1 -8
- package/dist/db/index.js +1 -25
- package/dist/db/introspection.d.ts +1 -6
- package/dist/db/introspection.js +1 -172
- package/dist/db/manager.d.ts +1 -81
- package/dist/db/manager.js +1 -397
- package/dist/db/migrations.d.ts +1 -5
- package/dist/db/migrations.js +1 -110
- package/dist/db/postgres.d.ts +1 -85
- package/dist/db/postgres.js +1 -745
- package/dist/db/types.d.ts +1 -206
- package/dist/db/types.js +1 -7
- package/dist/index.d.ts +1 -23
- package/dist/index.js +1 -66
- package/dist/public/js/api.js +1 -47
- package/dist/public/js/app.js +1 -262
- package/dist/public/js/browse.js +1 -1371
- package/dist/public/js/databases.js +1 -235
- package/dist/public/js/designer.js +1 -243
- package/dist/public/js/forms.js +1 -826
- package/dist/public/js/icons.js +1 -51
- package/dist/public/js/inspector.js +1 -638
- package/dist/public/js/query.js +1 -322
- package/dist/public/js/schema.js +1 -432
- package/dist/public/js/seed.js +1 -1090
- package/dist/routes/api/helpers.d.ts +1 -42
- package/dist/routes/api/helpers.js +1 -133
- package/dist/routes/api/import-export.d.ts +1 -3
- package/dist/routes/api/import-export.js +1 -85
- package/dist/routes/api/index.d.ts +1 -4
- package/dist/routes/api/index.js +1 -37
- package/dist/routes/api/query.d.ts +1 -3
- package/dist/routes/api/query.js +1 -76
- package/dist/routes/api/rows.d.ts +1 -3
- package/dist/routes/api/rows.js +1 -373
- package/dist/routes/api/seed.d.ts +1 -3
- package/dist/routes/api/seed.js +1 -82
- package/dist/routes/api/tables.d.ts +1 -3
- package/dist/routes/api/tables.js +1 -160
- package/dist/routes/databases.d.ts +1 -26
- package/dist/routes/databases.js +1 -230
- package/dist/routes/index.d.ts +1 -3
- package/dist/routes/index.js +1 -20
- package/dist/routes/pages.d.ts +1 -9
- package/dist/routes/pages.js +1 -382
- package/dist/serverless.d.ts +1 -40
- package/dist/serverless.js +1 -210
- package/dist/sql/classifier.d.ts +1 -19
- package/dist/sql/classifier.js +1 -37
- package/dist/sql/generator.d.ts +1 -78
- package/dist/sql/generator.js +1 -308
- package/dist/sql/index.d.ts +1 -2
- package/dist/sql/index.js +1 -19
- package/dist/types/api.d.ts +1 -412
- package/dist/types/api.js +1 -9
- package/dist/utils/colors.d.ts +1 -40
- package/dist/utils/colors.js +1 -64
- package/dist/utils/common.d.ts +1 -50
- package/dist/utils/common.js +1 -229
- package/dist/utils/csv.d.ts +1 -15
- package/dist/utils/csv.js +1 -80
- package/dist/utils/datatype.d.ts +1 -49
- package/dist/utils/datatype.js +1 -222
- package/dist/utils/icons.d.ts +1 -11
- package/dist/utils/icons.js +1 -72
- package/dist/utils/index.d.ts +1 -4
- package/dist/utils/index.js +1 -21
- package/dist/utils/logger.d.ts +1 -10
- package/dist/utils/logger.js +1 -40
- package/dist/views/layouts/main.hbs +8 -5
- package/dist/views/pages/databases.hbs +0 -18
- package/dist/views/pages/designer.hbs +22 -17
- package/dist/views/pages/error.hbs +0 -2
- package/dist/views/pages/form.hbs +0 -5
- package/dist/views/pages/home.hbs +0 -8
- package/dist/views/pages/login.hbs +0 -13
- package/dist/views/pages/query.hbs +0 -9
- package/dist/views/pages/schema.hbs +0 -20
- package/dist/views/pages/seed.hbs +0 -31
- package/dist/views/pages/table.hbs +0 -14
- package/dist/views/partials/icon.hbs +0 -1
- package/dist/views/partials/navbar.hbs +0 -6
- package/dist/views/partials/sidebar.hbs +0 -8
- package/docs/API.md +144 -0
- package/docs/EXAMPLES.md +576 -0
- package/docs/SECURITY.md +265 -0
- package/package.json +7 -2
- package/dist/app.js.map +0 -1
- package/dist/auth/config.js.map +0 -1
- package/dist/auth/crypto.js.map +0 -1
- package/dist/auth/index.js.map +0 -1
- package/dist/auth/middleware.js.map +0 -1
- package/dist/auth/routes.js.map +0 -1
- package/dist/auth/types.js.map +0 -1
- package/dist/cli/args.js.map +0 -1
- package/dist/cli/config.js.map +0 -1
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/runner.js.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/data/datasets.js.map +0 -1
- package/dist/data/detector.js.map +0 -1
- package/dist/data/engine.js.map +0 -1
- package/dist/data/index.js.map +0 -1
- package/dist/data/strategies.js.map +0 -1
- package/dist/data/types.js.map +0 -1
- package/dist/db/database.js.map +0 -1
- package/dist/db/export.js.map +0 -1
- package/dist/db/filters.js.map +0 -1
- package/dist/db/index.js.map +0 -1
- package/dist/db/introspection.js.map +0 -1
- package/dist/db/manager.js.map +0 -1
- package/dist/db/migrations.js.map +0 -1
- package/dist/db/postgres.js.map +0 -1
- package/dist/db/types.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/public/css/input.css +0 -302
- package/dist/routes/api/helpers.js.map +0 -1
- package/dist/routes/api/import-export.js.map +0 -1
- package/dist/routes/api/index.js.map +0 -1
- package/dist/routes/api/query.js.map +0 -1
- package/dist/routes/api/rows.js.map +0 -1
- package/dist/routes/api/seed.js.map +0 -1
- package/dist/routes/api/tables.js.map +0 -1
- package/dist/routes/databases.js.map +0 -1
- package/dist/routes/index.js.map +0 -1
- package/dist/routes/pages.js.map +0 -1
- package/dist/serverless.js.map +0 -1
- package/dist/sql/classifier.js.map +0 -1
- package/dist/sql/generator.js.map +0 -1
- package/dist/sql/index.js.map +0 -1
- package/dist/types/api.js.map +0 -1
- package/dist/utils/colors.js.map +0 -1
- package/dist/utils/common.js.map +0 -1
- package/dist/utils/csv.js.map +0 -1
- package/dist/utils/datatype.js.map +0 -1
- package/dist/utils/icons.js.map +0 -1
- package/dist/utils/index.js.map +0 -1
- package/dist/utils/logger.js.map +0 -1
|
@@ -1,42 +1 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { IDatabase, TableInfoData, WhereClause } from '../../db/index';
|
|
3
|
-
import type { Logger } from '../../utils/logger';
|
|
4
|
-
import { type ColumnPlan } from '../../data/index';
|
|
5
|
-
export interface ApiContext {
|
|
6
|
-
db: IDatabase;
|
|
7
|
-
logger: Logger;
|
|
8
|
-
}
|
|
9
|
-
export declare function ok(res: Response, data: unknown, status?: number): Response;
|
|
10
|
-
export declare function fail(res: Response, error: string, status?: number): Response;
|
|
11
|
-
export declare function wrap(fn: (req: Request, res: Response) => unknown): (req: Request, res: Response) => Promise<void>;
|
|
12
|
-
export declare function requireTable(db: IDatabase, table: string): Promise<TableInfoData | null>;
|
|
13
|
-
export declare function buildFields(info: TableInfoData, values: Record<string, unknown>, opts?: {
|
|
14
|
-
excludePk?: boolean;
|
|
15
|
-
}): {
|
|
16
|
-
column: string;
|
|
17
|
-
value: unknown;
|
|
18
|
-
}[];
|
|
19
|
-
export declare function buildUpdateFields(info: TableInfoData, values: Record<string, unknown>, nulls: string[]): {
|
|
20
|
-
column: string;
|
|
21
|
-
value: unknown;
|
|
22
|
-
}[];
|
|
23
|
-
export declare function pkWhere(info: TableInfoData, encodedId: string): {
|
|
24
|
-
column: string;
|
|
25
|
-
value: unknown;
|
|
26
|
-
}[] | null;
|
|
27
|
-
export declare const MAX_BULK_ROWS = 1000;
|
|
28
|
-
export declare function parseSeedRequest(body: unknown): {
|
|
29
|
-
count: number;
|
|
30
|
-
plan: Record<string, ColumnPlan>;
|
|
31
|
-
truncate: boolean;
|
|
32
|
-
};
|
|
33
|
-
export declare function resolvePkRows(info: TableInfoData, ids: unknown): WhereClause[][] | null;
|
|
34
|
-
export declare function computeBulkImpact(db: IDatabase, info: TableInfoData, wheres: WhereClause[][]): Promise<{
|
|
35
|
-
references: {
|
|
36
|
-
table: string;
|
|
37
|
-
from: string;
|
|
38
|
-
to: string;
|
|
39
|
-
count: number;
|
|
40
|
-
}[];
|
|
41
|
-
total: number;
|
|
42
|
-
}>;
|
|
1
|
+
import type {Request,Response} from 'express';import type {IDatabase,TableInfoData,WhereClause} from '../../db/index';import type {Logger} from '../../utils/logger';import{type ColumnPlan} from '../../data/index';export interface ApiContext{db:IDatabase;logger:Logger;}export declare function ok(res:Response,data:unknown,status?:number):Response;export declare function fail(res:Response,error:string,status?:number):Response;export declare function 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;};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,133 +1 @@
|
|
|
1
|
-
"
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.MAX_BULK_ROWS = void 0;
|
|
4
|
-
exports.ok = ok;
|
|
5
|
-
exports.fail = fail;
|
|
6
|
-
exports.wrap = wrap;
|
|
7
|
-
exports.requireTable = requireTable;
|
|
8
|
-
exports.buildFields = buildFields;
|
|
9
|
-
exports.buildUpdateFields = buildUpdateFields;
|
|
10
|
-
exports.pkWhere = pkWhere;
|
|
11
|
-
exports.parseSeedRequest = parseSeedRequest;
|
|
12
|
-
exports.resolvePkRows = resolvePkRows;
|
|
13
|
-
exports.computeBulkImpact = computeBulkImpact;
|
|
14
|
-
const common_1 = require("../../utils/common");
|
|
15
|
-
const index_1 = require("../../data/index");
|
|
16
|
-
const generator_1 = require("../../sql/generator");
|
|
17
|
-
function ok(res, data, status = 200) {
|
|
18
|
-
return res.status(status).json({ success: true, data });
|
|
19
|
-
}
|
|
20
|
-
function fail(res, error, status = 400) {
|
|
21
|
-
return res.status(status).json({ success: false, error });
|
|
22
|
-
}
|
|
23
|
-
function wrap(fn) {
|
|
24
|
-
return async (req, res) => {
|
|
25
|
-
try {
|
|
26
|
-
await fn(req, res);
|
|
27
|
-
}
|
|
28
|
-
catch (err) {
|
|
29
|
-
fail(res, (0, common_1.errorMessage)(err), 500);
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
async function requireTable(db, table) {
|
|
34
|
-
const info = await db.getTableInfo(table);
|
|
35
|
-
if (!info.success || !info.data || info.data.columns.length === 0)
|
|
36
|
-
return null;
|
|
37
|
-
return info.data;
|
|
38
|
-
}
|
|
39
|
-
function buildFields(info, values, opts = {}) {
|
|
40
|
-
const typeMap = new Map(info.columns.map((c) => [c.name, c.type]));
|
|
41
|
-
const pkSet = new Set(info.primaryKey);
|
|
42
|
-
const fields = [];
|
|
43
|
-
for (const [col, raw] of Object.entries(values ?? {})) {
|
|
44
|
-
if (!typeMap.has(col) || (opts.excludePk && pkSet.has(col)))
|
|
45
|
-
continue;
|
|
46
|
-
const val = (0, common_1.coerceFormValue)(raw, typeMap.get(col));
|
|
47
|
-
if (val !== null)
|
|
48
|
-
fields.push({ column: col, value: val });
|
|
49
|
-
}
|
|
50
|
-
return fields;
|
|
51
|
-
}
|
|
52
|
-
function buildUpdateFields(info, values, nulls) {
|
|
53
|
-
const fields = buildFields(info, values, { excludePk: true });
|
|
54
|
-
const typeMap = new Map(info.columns.map((c) => [c.name, c.type]));
|
|
55
|
-
const pkSet = new Set(info.primaryKey);
|
|
56
|
-
for (const col of nulls) {
|
|
57
|
-
if (!pkSet.has(col) && typeMap.has(col) && !fields.some((f) => f.column === col)) {
|
|
58
|
-
fields.push({ column: col, value: null });
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return fields;
|
|
62
|
-
}
|
|
63
|
-
function pkWhere(info, encodedId) {
|
|
64
|
-
const vals = (0, common_1.decodePk)(encodedId);
|
|
65
|
-
if (!info.primaryKey.length) {
|
|
66
|
-
if (vals.length === 1 && vals[0] !== '') {
|
|
67
|
-
const num = Number(vals[0]);
|
|
68
|
-
return [{ column: '_rowid_', value: Number.isFinite(num) ? num : vals[0] }];
|
|
69
|
-
}
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
if (vals.length !== info.primaryKey.length)
|
|
73
|
-
return null;
|
|
74
|
-
const typeMap = new Map(info.columns.map((c) => [c.name, c.type]));
|
|
75
|
-
return info.primaryKey.map((col, i) => ({ column: col, value: (0, common_1.coerceFormValue)(vals[i], typeMap.get(col)) }));
|
|
76
|
-
}
|
|
77
|
-
exports.MAX_BULK_ROWS = 1000;
|
|
78
|
-
function parseSeedRequest(body) {
|
|
79
|
-
const b = (body ?? {});
|
|
80
|
-
const count = Math.max(1, Math.min(index_1.MAX_SEED_ROWS, Number.parseInt(String(b.count ?? '10'), 10) || 10));
|
|
81
|
-
const truncate = Boolean(b.truncate);
|
|
82
|
-
const plan = {};
|
|
83
|
-
if (b.plan && typeof b.plan === 'object') {
|
|
84
|
-
for (const [k, v] of Object.entries(b.plan)) {
|
|
85
|
-
const p = (0, index_1.sanitizeColumnPlan)(v);
|
|
86
|
-
if (p)
|
|
87
|
-
plan[k] = p;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
return { count, plan, truncate };
|
|
91
|
-
}
|
|
92
|
-
function resolvePkRows(info, ids) {
|
|
93
|
-
const list = Array.isArray(ids) ? ids : ids == null ? [] : [ids];
|
|
94
|
-
const out = [];
|
|
95
|
-
for (const id of list) {
|
|
96
|
-
const where = pkWhere(info, String(id));
|
|
97
|
-
if (where)
|
|
98
|
-
out.push(where);
|
|
99
|
-
}
|
|
100
|
-
return out.length ? out : null;
|
|
101
|
-
}
|
|
102
|
-
async function computeBulkImpact(db, info, wheres) {
|
|
103
|
-
const [rowsR, refInfoR] = await Promise.all([
|
|
104
|
-
db.getRowsByPks(info.table, wheres),
|
|
105
|
-
db.getReferencingTables(info.table),
|
|
106
|
-
]);
|
|
107
|
-
const rows = (rowsR.data ?? []);
|
|
108
|
-
const referencing = refInfoR.data ?? [];
|
|
109
|
-
if (!rows.length || !referencing.length)
|
|
110
|
-
return { references: [], total: 0 };
|
|
111
|
-
const queries = referencing.flatMap((item) => item.refs.map(async (ref) => {
|
|
112
|
-
const targetCol = ref.to || info.primaryKey[0];
|
|
113
|
-
if (!targetCol)
|
|
114
|
-
return null;
|
|
115
|
-
const values = Array.from(new Set(rows.map((r) => r[targetCol]).filter((v) => v != null)));
|
|
116
|
-
if (!values.length)
|
|
117
|
-
return null;
|
|
118
|
-
try {
|
|
119
|
-
const placeholders = values.map(() => '?').join(', ');
|
|
120
|
-
const sql = `SELECT COUNT(*) AS c FROM ${(0, generator_1.quoteIdentifier)(item.table)} WHERE ${(0, generator_1.quoteIdentifier)(ref.from)} IN (${placeholders})`;
|
|
121
|
-
const countR = await db.all(sql, values);
|
|
122
|
-
const count = countR.success && countR.data?.[0] ? Number(countR.data[0].c ?? 0) : 0;
|
|
123
|
-
return count > 0 ? { table: item.table, from: ref.from, to: targetCol, count } : null;
|
|
124
|
-
}
|
|
125
|
-
catch {
|
|
126
|
-
return null;
|
|
127
|
-
}
|
|
128
|
-
}));
|
|
129
|
-
const results = (await Promise.all(queries)).filter((r) => r !== null);
|
|
130
|
-
const total = results.reduce((acc, r) => acc + r.count, 0);
|
|
131
|
-
return { references: results, total };
|
|
132
|
-
}
|
|
133
|
-
//# sourceMappingURL=helpers.js.map
|
|
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,3 +1 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import { type ApiContext } from './helpers';
|
|
3
|
-
export declare function registerImportExportRoutes(router: Router, ctx: ApiContext): void;
|
|
1
|
+
import type {Router} from 'express';import{type ApiContext} from './helpers';export declare function registerImportExportRoutes(router:Router,ctx:ApiContext):void;
|
|
@@ -1,85 +1 @@
|
|
|
1
|
-
"
|
|
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
|
-
// ---- Export / Import ---------------------------------------------------
|
|
10
|
-
router.get('/api/tables/:table/export', (0, helpers_1.wrap)(async (req, res) => {
|
|
11
|
-
const info = await (0, helpers_1.requireTable)(db, req.params.table);
|
|
12
|
-
if (!info)
|
|
13
|
-
return (0, helpers_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
|
|
14
|
-
const format = String(req.query.format ?? 'csv').toLowerCase();
|
|
15
|
-
const filters = (0, common_1.parseFilters)(req.query.f);
|
|
16
|
-
const r = await db.getRows(req.params.table, { limit: 1_000_000, filters });
|
|
17
|
-
if (!r.success)
|
|
18
|
-
return (0, helpers_1.fail)(res, r.error ?? 'Failed to export table.');
|
|
19
|
-
const rows = (r.data ?? []).map(common_1.normalizeRow);
|
|
20
|
-
const columns = info.columns.map((c) => c.name);
|
|
21
|
-
if (format === 'json') {
|
|
22
|
-
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
23
|
-
res.setHeader('Content-Disposition', `attachment; filename="${req.params.table}.json"`);
|
|
24
|
-
return res.send((0, csv_1.toJson)(rows));
|
|
25
|
-
}
|
|
26
|
-
res.setHeader('Content-Type', 'text/csv; charset=utf-8');
|
|
27
|
-
res.setHeader('Content-Disposition', `attachment; filename="${req.params.table}.csv"`);
|
|
28
|
-
res.send((0, csv_1.toCsv)(rows, columns));
|
|
29
|
-
}));
|
|
30
|
-
const importCsvHandler = (0, helpers_1.wrap)(async (req, res) => {
|
|
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
|
-
const raw = line[i] ?? '';
|
|
53
|
-
fields.push({ column: col, value: (0, common_1.coerceFormValue)(raw, typeMap.get(col)) });
|
|
54
|
-
}
|
|
55
|
-
return fields;
|
|
56
|
-
});
|
|
57
|
-
const r = await db.insertRows(req.params.table, rows);
|
|
58
|
-
if (!r.success)
|
|
59
|
-
return (0, helpers_1.fail)(res, r.error ?? 'Failed to import CSV.');
|
|
60
|
-
(0, helpers_1.ok)(res, { message: `${r.data?.inserted ?? 0} row(s) imported.`, inserted: r.data?.inserted });
|
|
61
|
-
});
|
|
62
|
-
router.post('/api/tables/:table/import', importCsvHandler);
|
|
63
|
-
router.post('/api/tables/:table/rows/import', importCsvHandler);
|
|
64
|
-
router.post('/api/tables/:table/import/csv', importCsvHandler);
|
|
65
|
-
router.post('/api/query/export', (0, helpers_1.wrap)(async (req, res) => {
|
|
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
|
-
}
|
|
85
|
-
//# sourceMappingURL=import-export.js.map
|
|
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,4 +1 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import { type ApiContext } from './helpers';
|
|
3
|
-
export { type ApiContext } from './helpers';
|
|
4
|
-
export declare function registerApi(router: Router, ctx: ApiContext): void;
|
|
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;
|
package/dist/routes/api/index.js
CHANGED
|
@@ -1,37 +1 @@
|
|
|
1
|
-
"use
|
|
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
|
-
function registerApi(router, ctx) {
|
|
10
|
-
const { db } = ctx;
|
|
11
|
-
// In read-only mode every mutating request is rejected (GET, the generate-only
|
|
12
|
-
// preview endpoints and the query runner stay available; the query runner
|
|
13
|
-
// rejects write statements itself).
|
|
14
|
-
router.use((req, res, next) => {
|
|
15
|
-
if (!db.isReadOnly || req.method === 'GET')
|
|
16
|
-
return next();
|
|
17
|
-
const p = req.path;
|
|
18
|
-
if (p.endsWith('/generate') ||
|
|
19
|
-
p.endsWith('/preview') ||
|
|
20
|
-
p.endsWith('/api/query/export') ||
|
|
21
|
-
p.endsWith('/api/query') ||
|
|
22
|
-
p.endsWith('/rows/bulk-impact') ||
|
|
23
|
-
p.endsWith('/rows/bulk-export')) {
|
|
24
|
-
return next();
|
|
25
|
-
}
|
|
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
|
-
(0, tables_1.registerTableRoutes)(router, ctx);
|
|
32
|
-
(0, rows_1.registerRowRoutes)(router, ctx);
|
|
33
|
-
(0, import_export_1.registerImportExportRoutes)(router, ctx);
|
|
34
|
-
(0, seed_1.registerSeedRoutes)(router, ctx);
|
|
35
|
-
(0, query_1.registerQueryRoutes)(router, ctx);
|
|
36
|
-
}
|
|
37
|
-
//# sourceMappingURL=index.js.map
|
|
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,3 +1 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import { type ApiContext } from './helpers';
|
|
3
|
-
export declare function registerQueryRoutes(router: Router, ctx: ApiContext): void;
|
|
1
|
+
import type {Router} from 'express';import{type ApiContext} from './helpers';export declare function registerQueryRoutes(router:Router,ctx:ApiContext):void;
|
package/dist/routes/api/query.js
CHANGED
|
@@ -1,76 +1 @@
|
|
|
1
|
-
"
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.registerQueryRoutes = registerQueryRoutes;
|
|
4
|
-
const classifier_1 = require("../../sql/classifier");
|
|
5
|
-
const common_1 = require("../../utils/common");
|
|
6
|
-
const helpers_1 = require("./helpers");
|
|
7
|
-
function registerQueryRoutes(router, ctx) {
|
|
8
|
-
const { db } = ctx;
|
|
9
|
-
// ---- Query runner ------------------------------------------------------
|
|
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
|
-
// Multi-statement script detection
|
|
16
|
-
if (db.hasMultipleStatements(rawSql)) {
|
|
17
|
-
if (db.isReadOnly) {
|
|
18
|
-
return (0, helpers_1.fail)(res, 'Database is open in read-only mode — write statements are disabled.', 403);
|
|
19
|
-
}
|
|
20
|
-
const r = await db.execResult(rawSql);
|
|
21
|
-
if (!r.success)
|
|
22
|
-
return (0, helpers_1.fail)(res, r.error ?? 'Query failed.', 400);
|
|
23
|
-
return (0, helpers_1.ok)(res, {
|
|
24
|
-
kind: 'script',
|
|
25
|
-
message: 'Script executed successfully.',
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
if (kind === 'select' || kind === 'read' || kind === 'count') {
|
|
29
|
-
const r = await db.all(rawSql);
|
|
30
|
-
if (!r.success)
|
|
31
|
-
return (0, helpers_1.fail)(res, r.error ?? 'Query failed.', 400);
|
|
32
|
-
const rows = (r.data ?? []).map(common_1.normalizeRow);
|
|
33
|
-
const columns = rows.length ? Object.keys(rows[0]) : [];
|
|
34
|
-
return (0, helpers_1.ok)(res, { kind: 'select', columns, rows });
|
|
35
|
-
}
|
|
36
|
-
// write statement
|
|
37
|
-
if (db.isReadOnly) {
|
|
38
|
-
return (0, helpers_1.fail)(res, 'Database is open in read-only mode — write statements are disabled.', 403);
|
|
39
|
-
}
|
|
40
|
-
const r = await db.runWrite(rawSql);
|
|
41
|
-
if (!r.success)
|
|
42
|
-
return (0, helpers_1.fail)(res, r.error ?? 'Query failed.', 400);
|
|
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
|
-
// ---- Saved queries -----------------------------------------------------
|
|
51
|
-
router.get('/api/queries', (0, helpers_1.wrap)(async (_req, res) => {
|
|
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', (0, helpers_1.wrap)(async (req, res) => {
|
|
58
|
-
const name = String(req.body?.name ?? '').trim();
|
|
59
|
-
const sql = String(req.body?.sql ?? '').trim();
|
|
60
|
-
if (!name)
|
|
61
|
-
return (0, helpers_1.fail)(res, 'Query name is required.');
|
|
62
|
-
if (!sql)
|
|
63
|
-
return (0, helpers_1.fail)(res, 'Query SQL is required.');
|
|
64
|
-
const r = await db.saveQuery(name, sql);
|
|
65
|
-
if (!r.success)
|
|
66
|
-
return (0, helpers_1.fail)(res, r.error ?? 'Failed to save query.', 400);
|
|
67
|
-
(0, helpers_1.ok)(res, { message: 'Query saved.' }, 201);
|
|
68
|
-
}));
|
|
69
|
-
router.delete('/api/queries/:id', (0, helpers_1.wrap)(async (req, res) => {
|
|
70
|
-
const r = await db.deleteSavedQuery(req.params.id);
|
|
71
|
-
if (!r.success)
|
|
72
|
-
return (0, helpers_1.fail)(res, r.error ?? 'Failed to delete query.', 400);
|
|
73
|
-
(0, helpers_1.ok)(res, { message: 'Query deleted.' });
|
|
74
|
-
}));
|
|
75
|
-
}
|
|
76
|
-
//# sourceMappingURL=query.js.map
|
|
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,3 +1 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import { type ApiContext } from './helpers';
|
|
3
|
-
export declare function registerRowRoutes(router: Router, ctx: ApiContext): void;
|
|
1
|
+
import type {Router} from 'express';import{type ApiContext} from './helpers';export declare function registerRowRoutes(router:Router,ctx:ApiContext):void;
|