admindb 2.1.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.
Files changed (178) hide show
  1. package/README.md +1 -1
  2. package/dist/app.d.ts +1 -69
  3. package/dist/app.js +1 -257
  4. package/dist/auth/config.d.ts +1 -19
  5. package/dist/auth/config.js +1 -147
  6. package/dist/auth/crypto.d.ts +1 -27
  7. package/dist/auth/crypto.js +1 -133
  8. package/dist/auth/index.d.ts +1 -5
  9. package/dist/auth/index.js +1 -22
  10. package/dist/auth/middleware.d.ts +1 -14
  11. package/dist/auth/middleware.js +1 -100
  12. package/dist/auth/routes.d.ts +1 -7
  13. package/dist/auth/routes.js +1 -78
  14. package/dist/auth/types.d.ts +1 -35
  15. package/dist/auth/types.js +1 -11
  16. package/dist/cli/args.d.ts +1 -45
  17. package/dist/cli/args.js +1 -328
  18. package/dist/cli/config.d.ts +1 -56
  19. package/dist/cli/config.js +1 -133
  20. package/dist/cli/index.d.ts +1 -4
  21. package/dist/cli/index.js +1 -21
  22. package/dist/cli/runner.d.ts +1 -1
  23. package/dist/cli/runner.js +1 -222
  24. package/dist/cli.d.ts +1 -2
  25. package/dist/cli.js +1 -5
  26. package/dist/data/datasets.d.ts +1 -17
  27. package/dist/data/datasets.js +1 -133
  28. package/dist/data/detector.d.ts +1 -30
  29. package/dist/data/detector.js +1 -472
  30. package/dist/data/engine.d.ts +1 -10
  31. package/dist/data/engine.js +1 -211
  32. package/dist/data/index.d.ts +1 -5
  33. package/dist/data/index.js +1 -22
  34. package/dist/data/strategies.d.ts +1 -30
  35. package/dist/data/strategies.js +1 -343
  36. package/dist/data/types.d.ts +1 -73
  37. package/dist/data/types.js +1 -8
  38. package/dist/db/database.d.ts +1 -119
  39. package/dist/db/database.js +1 -471
  40. package/dist/db/export.d.ts +1 -12
  41. package/dist/db/export.js +1 -42
  42. package/dist/db/filters.d.ts +1 -22
  43. package/dist/db/filters.js +1 -158
  44. package/dist/db/index.d.ts +1 -8
  45. package/dist/db/index.js +1 -25
  46. package/dist/db/introspection.d.ts +1 -6
  47. package/dist/db/introspection.js +1 -172
  48. package/dist/db/manager.d.ts +1 -81
  49. package/dist/db/manager.js +1 -397
  50. package/dist/db/migrations.d.ts +1 -5
  51. package/dist/db/migrations.js +1 -110
  52. package/dist/db/postgres.d.ts +1 -85
  53. package/dist/db/postgres.js +1 -745
  54. package/dist/db/types.d.ts +1 -206
  55. package/dist/db/types.js +1 -7
  56. package/dist/index.d.ts +1 -23
  57. package/dist/index.js +1 -66
  58. package/dist/public/js/api.js +1 -47
  59. package/dist/public/js/app.js +1 -262
  60. package/dist/public/js/browse.js +1 -1371
  61. package/dist/public/js/databases.js +1 -235
  62. package/dist/public/js/designer.js +1 -258
  63. package/dist/public/js/forms.js +1 -826
  64. package/dist/public/js/icons.js +1 -51
  65. package/dist/public/js/inspector.js +1 -638
  66. package/dist/public/js/query.js +1 -322
  67. package/dist/public/js/schema.js +1 -447
  68. package/dist/public/js/seed.js +1 -1090
  69. package/dist/routes/api/helpers.d.ts +1 -42
  70. package/dist/routes/api/helpers.js +1 -133
  71. package/dist/routes/api/import-export.d.ts +1 -3
  72. package/dist/routes/api/import-export.js +1 -85
  73. package/dist/routes/api/index.d.ts +1 -4
  74. package/dist/routes/api/index.js +1 -37
  75. package/dist/routes/api/query.d.ts +1 -3
  76. package/dist/routes/api/query.js +1 -76
  77. package/dist/routes/api/rows.d.ts +1 -3
  78. package/dist/routes/api/rows.js +1 -373
  79. package/dist/routes/api/seed.d.ts +1 -3
  80. package/dist/routes/api/seed.js +1 -82
  81. package/dist/routes/api/tables.d.ts +1 -3
  82. package/dist/routes/api/tables.js +1 -160
  83. package/dist/routes/databases.d.ts +1 -26
  84. package/dist/routes/databases.js +1 -230
  85. package/dist/routes/index.d.ts +1 -3
  86. package/dist/routes/index.js +1 -20
  87. package/dist/routes/pages.d.ts +1 -9
  88. package/dist/routes/pages.js +1 -390
  89. package/dist/serverless.d.ts +1 -40
  90. package/dist/serverless.js +1 -210
  91. package/dist/sql/classifier.d.ts +1 -19
  92. package/dist/sql/classifier.js +1 -37
  93. package/dist/sql/generator.d.ts +1 -81
  94. package/dist/sql/generator.js +1 -348
  95. package/dist/sql/index.d.ts +1 -2
  96. package/dist/sql/index.js +1 -19
  97. package/dist/types/api.d.ts +1 -412
  98. package/dist/types/api.js +1 -9
  99. package/dist/utils/colors.d.ts +1 -40
  100. package/dist/utils/colors.js +1 -64
  101. package/dist/utils/common.d.ts +1 -50
  102. package/dist/utils/common.js +1 -229
  103. package/dist/utils/csv.d.ts +1 -15
  104. package/dist/utils/csv.js +1 -80
  105. package/dist/utils/datatype.d.ts +1 -49
  106. package/dist/utils/datatype.js +1 -222
  107. package/dist/utils/icons.d.ts +1 -11
  108. package/dist/utils/icons.js +1 -72
  109. package/dist/utils/index.d.ts +1 -4
  110. package/dist/utils/index.js +1 -21
  111. package/dist/utils/logger.d.ts +1 -10
  112. package/dist/utils/logger.js +1 -40
  113. package/dist/views/layouts/main.hbs +0 -4
  114. package/dist/views/pages/databases.hbs +0 -18
  115. package/dist/views/pages/designer.hbs +0 -10
  116. package/dist/views/pages/error.hbs +0 -2
  117. package/dist/views/pages/form.hbs +0 -5
  118. package/dist/views/pages/home.hbs +0 -8
  119. package/dist/views/pages/login.hbs +0 -13
  120. package/dist/views/pages/query.hbs +0 -9
  121. package/dist/views/pages/schema.hbs +0 -20
  122. package/dist/views/pages/seed.hbs +0 -31
  123. package/dist/views/pages/table.hbs +0 -14
  124. package/dist/views/partials/icon.hbs +0 -1
  125. package/dist/views/partials/navbar.hbs +0 -6
  126. package/dist/views/partials/sidebar.hbs +0 -8
  127. package/package.json +3 -2
  128. package/dist/app.js.map +0 -1
  129. package/dist/auth/config.js.map +0 -1
  130. package/dist/auth/crypto.js.map +0 -1
  131. package/dist/auth/index.js.map +0 -1
  132. package/dist/auth/middleware.js.map +0 -1
  133. package/dist/auth/routes.js.map +0 -1
  134. package/dist/auth/types.js.map +0 -1
  135. package/dist/cli/args.js.map +0 -1
  136. package/dist/cli/config.js.map +0 -1
  137. package/dist/cli/index.js.map +0 -1
  138. package/dist/cli/runner.js.map +0 -1
  139. package/dist/cli.js.map +0 -1
  140. package/dist/data/datasets.js.map +0 -1
  141. package/dist/data/detector.js.map +0 -1
  142. package/dist/data/engine.js.map +0 -1
  143. package/dist/data/index.js.map +0 -1
  144. package/dist/data/strategies.js.map +0 -1
  145. package/dist/data/types.js.map +0 -1
  146. package/dist/db/database.js.map +0 -1
  147. package/dist/db/export.js.map +0 -1
  148. package/dist/db/filters.js.map +0 -1
  149. package/dist/db/index.js.map +0 -1
  150. package/dist/db/introspection.js.map +0 -1
  151. package/dist/db/manager.js.map +0 -1
  152. package/dist/db/migrations.js.map +0 -1
  153. package/dist/db/postgres.js.map +0 -1
  154. package/dist/db/types.js.map +0 -1
  155. package/dist/index.js.map +0 -1
  156. package/dist/public/css/input.css +0 -302
  157. package/dist/routes/api/helpers.js.map +0 -1
  158. package/dist/routes/api/import-export.js.map +0 -1
  159. package/dist/routes/api/index.js.map +0 -1
  160. package/dist/routes/api/query.js.map +0 -1
  161. package/dist/routes/api/rows.js.map +0 -1
  162. package/dist/routes/api/seed.js.map +0 -1
  163. package/dist/routes/api/tables.js.map +0 -1
  164. package/dist/routes/databases.js.map +0 -1
  165. package/dist/routes/index.js.map +0 -1
  166. package/dist/routes/pages.js.map +0 -1
  167. package/dist/serverless.js.map +0 -1
  168. package/dist/sql/classifier.js.map +0 -1
  169. package/dist/sql/generator.js.map +0 -1
  170. package/dist/sql/index.js.map +0 -1
  171. package/dist/types/api.js.map +0 -1
  172. package/dist/utils/colors.js.map +0 -1
  173. package/dist/utils/common.js.map +0 -1
  174. package/dist/utils/csv.js.map +0 -1
  175. package/dist/utils/datatype.js.map +0 -1
  176. package/dist/utils/icons.js.map +0 -1
  177. package/dist/utils/index.js.map +0 -1
  178. package/dist/utils/logger.js.map +0 -1
@@ -1,160 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.registerTableRoutes = registerTableRoutes;
4
- const generator_1 = require("../../sql/generator");
5
- const helpers_1 = require("./helpers");
6
- function registerTableRoutes(router, ctx) {
7
- const { db } = ctx;
8
- // ---- Tables ------------------------------------------------------------
9
- router.get('/api/tables', (0, helpers_1.wrap)(async (_req, res) => {
10
- const r = await db.listTables();
11
- if (!r.success)
12
- return (0, helpers_1.fail)(res, r.error ?? 'Failed to list tables.', 500);
13
- (0, helpers_1.ok)(res, r.data);
14
- }));
15
- router.get('/api/tables/:table/info', (0, helpers_1.wrap)(async (req, res) => {
16
- const info = await (0, helpers_1.requireTable)(db, req.params.table);
17
- if (!info)
18
- return (0, helpers_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
19
- (0, helpers_1.ok)(res, info);
20
- }));
21
- router.get('/api/tables/:table/fk-options', (0, helpers_1.wrap)(async (req, res) => {
22
- const info = await (0, helpers_1.requireTable)(db, req.params.table);
23
- if (!info)
24
- return (0, helpers_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
25
- const options = {};
26
- for (const fk of info.foreignKeys) {
27
- try {
28
- const refInfo = await (0, helpers_1.requireTable)(db, fk.table);
29
- if (!refInfo)
30
- continue;
31
- const refCol = fk.to || refInfo.primaryKey[0] || refInfo.columns[0]?.name;
32
- const pk = refInfo.primaryKey[0] ?? refInfo.columns[0]?.name;
33
- const labelCol = refInfo.columns.find((c) => /name|title|label|username|email/i.test(c.name))?.name ?? pk;
34
- const rows = await db.getRows(fk.table, { limit: 100, orderBy: labelCol });
35
- if (rows.success && rows.data) {
36
- options[fk.from] = rows.data.map((r) => ({
37
- value: r[refCol],
38
- label: r[labelCol] != null ? `${r[labelCol]} (${r[refCol]})` : String(r[refCol]),
39
- }));
40
- }
41
- }
42
- catch {
43
- // FK option loading is best-effort.
44
- }
45
- }
46
- (0, helpers_1.ok)(res, options);
47
- }));
48
- router.get('/api/tables/:table/ddl', (0, helpers_1.wrap)(async (req, res) => {
49
- const r = await db.getCreateStatement(req.params.table);
50
- if (!r.success)
51
- return (0, helpers_1.fail)(res, r.error ?? 'Failed to fetch DDL.', 500);
52
- if (!r.data)
53
- return (0, helpers_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
54
- (0, helpers_1.ok)(res, { sql: r.data });
55
- }));
56
- router.post('/api/tables/:table/rename', (0, helpers_1.wrap)(async (req, res) => {
57
- const newName = String(req.body?.name ?? '').trim();
58
- if (!newName)
59
- return (0, helpers_1.fail)(res, 'New table name is required.');
60
- const r = await db.renameTable(req.params.table, newName);
61
- if (!r.success)
62
- return (0, helpers_1.fail)(res, r.error ?? 'Failed to rename table.');
63
- (0, helpers_1.ok)(res, { message: `Table renamed to "${newName}".`, name: newName });
64
- }));
65
- router.delete('/api/tables/:table', (0, helpers_1.wrap)(async (req, res) => {
66
- const r = await db.dropTable(req.params.table);
67
- if (!r.success)
68
- return (0, helpers_1.fail)(res, r.error ?? 'Failed to drop table.');
69
- (0, helpers_1.ok)(res, { message: `Table "${req.params.table}" dropped.` });
70
- }));
71
- // Preview generated CREATE TABLE SQL without executing
72
- router.post('/api/tables/generate', (0, helpers_1.wrap)(async (req, res) => {
73
- const name = String(req.body?.name ?? '').trim();
74
- const columns = (req.body?.columns ?? []);
75
- if (!name)
76
- return (0, helpers_1.fail)(res, 'Table name is required.');
77
- if (!Array.isArray(columns) || columns.length === 0)
78
- return (0, helpers_1.fail)(res, 'At least one column is required.');
79
- try {
80
- const sql = (0, generator_1.generateCreateTable)(name, columns);
81
- (0, helpers_1.ok)(res, { sql });
82
- }
83
- catch (err) {
84
- (0, helpers_1.fail)(res, err.message);
85
- }
86
- }));
87
- const createTableHandler = (0, helpers_1.wrap)(async (req, res) => {
88
- const name = String(req.body?.name ?? '').trim();
89
- const columns = (req.body?.columns ?? []);
90
- if (!name)
91
- return (0, helpers_1.fail)(res, 'Table name is required.');
92
- if (!Array.isArray(columns) || columns.length === 0)
93
- return (0, helpers_1.fail)(res, 'At least one column is required.');
94
- let sql;
95
- try {
96
- sql = (0, generator_1.generateCreateTable)(name, columns);
97
- }
98
- catch (err) {
99
- return (0, helpers_1.fail)(res, err.message);
100
- }
101
- const r = await db.execResult(sql);
102
- if (!r.success)
103
- return (0, helpers_1.fail)(res, r.error ?? 'Failed to create table.');
104
- (0, helpers_1.ok)(res, { message: `Table "${name}" created.`, name }, 201);
105
- });
106
- router.post('/api/tables', createTableHandler);
107
- router.post('/api/tables/create', createTableHandler);
108
- // ---- Columns -----------------------------------------------------------
109
- router.post('/api/tables/:table/columns', (0, helpers_1.wrap)(async (req, res) => {
110
- const col = req.body;
111
- if (!col || !col.name)
112
- return (0, helpers_1.fail)(res, 'Column name is required.');
113
- const r = await db.addColumn(req.params.table, col);
114
- if (!r.success)
115
- return (0, helpers_1.fail)(res, r.error ?? 'Failed to add column.');
116
- (0, helpers_1.ok)(res, { message: `Column "${col.name}" added.` }, 201);
117
- }));
118
- router.put('/api/tables/:table/columns/:column', (0, helpers_1.wrap)(async (req, res) => {
119
- const col = req.body;
120
- if (!col)
121
- return (0, helpers_1.fail)(res, 'Column definition is required.');
122
- const r = await db.modifyColumn(req.params.table, req.params.column, col);
123
- if (!r.success)
124
- return (0, helpers_1.fail)(res, r.error ?? 'Failed to modify column.');
125
- (0, helpers_1.ok)(res, { message: `Column "${req.params.column}" updated.` });
126
- }));
127
- router.post('/api/tables/:table/columns/:column/rename', (0, helpers_1.wrap)(async (req, res) => {
128
- const newName = String(req.body?.name ?? '').trim();
129
- if (!newName)
130
- return (0, helpers_1.fail)(res, 'New column name is required.');
131
- const r = await db.renameColumn(req.params.table, req.params.column, newName);
132
- if (!r.success)
133
- return (0, helpers_1.fail)(res, r.error ?? 'Failed to rename column.');
134
- (0, helpers_1.ok)(res, { message: `Column renamed to "${newName}".`, name: newName });
135
- }));
136
- router.delete('/api/tables/:table/columns/:column', (0, helpers_1.wrap)(async (req, res) => {
137
- const r = await db.dropColumn(req.params.table, req.params.column);
138
- if (!r.success)
139
- return (0, helpers_1.fail)(res, r.error ?? 'Failed to drop column.');
140
- (0, helpers_1.ok)(res, { message: `Column "${req.params.column}" dropped.` });
141
- }));
142
- // ---- Indexes -----------------------------------------------------------
143
- router.post('/api/tables/:table/indexes', (0, helpers_1.wrap)(async (req, res) => {
144
- const def = req.body;
145
- if (!def || !Array.isArray(def.columns) || def.columns.length === 0) {
146
- return (0, helpers_1.fail)(res, 'At least one column is required for an index.');
147
- }
148
- const r = await db.createIndex(req.params.table, def);
149
- if (!r.success)
150
- return (0, helpers_1.fail)(res, r.error ?? 'Failed to create index.');
151
- (0, helpers_1.ok)(res, { message: 'Index created.' }, 201);
152
- }));
153
- router.delete('/api/tables/:table/indexes/:index', (0, helpers_1.wrap)(async (req, res) => {
154
- const r = await db.dropIndex(req.params.index);
155
- if (!r.success)
156
- return (0, helpers_1.fail)(res, r.error ?? 'Failed to drop index.');
157
- (0, helpers_1.ok)(res, { message: `Index "${req.params.index}" dropped.` });
158
- }));
159
- }
160
- //# sourceMappingURL=tables.js.map
1
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.registerTableRoutes=function(router,ctx){const{db:db}=ctx;router.get("/api/tables",(0,helpers_1.wrap)(async(_req,res)=>{const r=await db.listTables();if(!r.success)return(0,helpers_1.fail)(res,r.error??"Failed to list tables.",500);(0,helpers_1.ok)(res,r.data)})),router.get("/api/tables/:table/info",(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);(0,helpers_1.ok)(res,info)})),router.get("/api/tables/:table/fk-options",(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 options={};for(const fk of info.foreignKeys)try{const refInfo=await(0,helpers_1.requireTable)(db,fk.table);if(!refInfo)continue;const refCol=fk.to||refInfo.primaryKey[0]||refInfo.columns[0]?.name,pk=refInfo.primaryKey[0]??refInfo.columns[0]?.name,labelCol=refInfo.columns.find(c=>/name|title|label|username|email/i.test(c.name))?.name??pk,rows=await db.getRows(fk.table,{limit:100,orderBy:labelCol});rows.success&&rows.data&&(options[fk.from]=rows.data.map(r=>({value:r[refCol],label:null!=r[labelCol]?`${r[labelCol]} (${r[refCol]})`:String(r[refCol])})))}catch{}(0,helpers_1.ok)(res,options)})),router.get("/api/tables/:table/ddl",(0,helpers_1.wrap)(async(req,res)=>{const r=await db.getCreateStatement(req.params.table);return r.success?r.data?void(0,helpers_1.ok)(res,{sql:r.data}):(0,helpers_1.fail)(res,`Table "${req.params.table}" does not exist.`,404):(0,helpers_1.fail)(res,r.error??"Failed to fetch DDL.",500)})),router.post("/api/tables/:table/rename",(0,helpers_1.wrap)(async(req,res)=>{const newName=String(req.body?.name??"").trim();if(!newName)return(0,helpers_1.fail)(res,"New table name is required.");const r=await db.renameTable(req.params.table,newName);if(!r.success)return(0,helpers_1.fail)(res,r.error??"Failed to rename table.");(0,helpers_1.ok)(res,{message:`Table renamed to "${newName}".`,name:newName})})),router.delete("/api/tables/:table",(0,helpers_1.wrap)(async(req,res)=>{const r=await db.dropTable(req.params.table);if(!r.success)return(0,helpers_1.fail)(res,r.error??"Failed to drop table.");(0,helpers_1.ok)(res,{message:`Table "${req.params.table}" dropped.`})})),router.post("/api/tables/generate",(0,helpers_1.wrap)(async(req,res)=>{const name=String(req.body?.name??"").trim(),columns=req.body?.columns??[];if(!name)return(0,helpers_1.fail)(res,"Table name is required.");if(!Array.isArray(columns)||0===columns.length)return(0,helpers_1.fail)(res,"At least one column is required.");try{const sql=(0,generator_1.generateCreateTable)(name,columns);(0,helpers_1.ok)(res,{sql:sql})}catch(err){(0,helpers_1.fail)(res,err.message)}}));const createTableHandler=(0,helpers_1.wrap)(async(req,res)=>{const name=String(req.body?.name??"").trim(),columns=req.body?.columns??[];if(!name)return(0,helpers_1.fail)(res,"Table name is required.");if(!Array.isArray(columns)||0===columns.length)return(0,helpers_1.fail)(res,"At least one column is required.");let sql;try{sql=(0,generator_1.generateCreateTable)(name,columns)}catch(err){return(0,helpers_1.fail)(res,err.message)}const r=await db.execResult(sql);if(!r.success)return(0,helpers_1.fail)(res,r.error??"Failed to create table.");(0,helpers_1.ok)(res,{message:`Table "${name}" created.`,name:name},201)});router.post("/api/tables",createTableHandler),router.post("/api/tables/create",createTableHandler),router.post("/api/tables/:table/columns",(0,helpers_1.wrap)(async(req,res)=>{const col=req.body;if(!col||!col.name)return(0,helpers_1.fail)(res,"Column name is required.");const r=await db.addColumn(req.params.table,col);if(!r.success)return(0,helpers_1.fail)(res,r.error??"Failed to add column.");(0,helpers_1.ok)(res,{message:`Column "${col.name}" added.`},201)})),router.put("/api/tables/:table/columns/:column",(0,helpers_1.wrap)(async(req,res)=>{const col=req.body;if(!col)return(0,helpers_1.fail)(res,"Column definition is required.");const r=await db.modifyColumn(req.params.table,req.params.column,col);if(!r.success)return(0,helpers_1.fail)(res,r.error??"Failed to modify column.");(0,helpers_1.ok)(res,{message:`Column "${req.params.column}" updated.`})})),router.post("/api/tables/:table/columns/:column/rename",(0,helpers_1.wrap)(async(req,res)=>{const newName=String(req.body?.name??"").trim();if(!newName)return(0,helpers_1.fail)(res,"New column name is required.");const r=await db.renameColumn(req.params.table,req.params.column,newName);if(!r.success)return(0,helpers_1.fail)(res,r.error??"Failed to rename column.");(0,helpers_1.ok)(res,{message:`Column renamed to "${newName}".`,name:newName})})),router.delete("/api/tables/:table/columns/:column",(0,helpers_1.wrap)(async(req,res)=>{const r=await db.dropColumn(req.params.table,req.params.column);if(!r.success)return(0,helpers_1.fail)(res,r.error??"Failed to drop column.");(0,helpers_1.ok)(res,{message:`Column "${req.params.column}" dropped.`})})),router.post("/api/tables/:table/indexes",(0,helpers_1.wrap)(async(req,res)=>{const def=req.body;if(!def||!Array.isArray(def.columns)||0===def.columns.length)return(0,helpers_1.fail)(res,"At least one column is required for an index.");const r=await db.createIndex(req.params.table,def);if(!r.success)return(0,helpers_1.fail)(res,r.error??"Failed to create index.");(0,helpers_1.ok)(res,{message:"Index created."},201)})),router.delete("/api/tables/:table/indexes/:index",(0,helpers_1.wrap)(async(req,res)=>{const r=await db.dropIndex(req.params.index);if(!r.success)return(0,helpers_1.fail)(res,r.error??"Failed to drop index.");(0,helpers_1.ok)(res,{message:`Index "${req.params.index}" dropped.`})}))};const generator_1=require("../../sql/generator"),helpers_1=require("./helpers");
@@ -1,26 +1 @@
1
- import type { Router } from 'express';
2
- import type { DbManager, DatabaseEntry } from '../db/manager';
3
- import type { Logger } from '../utils/logger';
4
- interface DatabasesContext {
5
- manager: DbManager;
6
- logger: Logger;
7
- basePath: string;
8
- readonly?: boolean;
9
- allowBrowse?: boolean;
10
- browseRoot?: string;
11
- invalidate?: (id: string) => void;
12
- }
13
- export interface DatabaseRow extends DatabaseEntry {
14
- sizeLabel: string;
15
- modifiedLabel: string;
16
- tables: number;
17
- }
18
- export interface FsEntry {
19
- name: string;
20
- path: string;
21
- isDir: boolean;
22
- isDb: boolean;
23
- size: number;
24
- }
25
- export declare function registerDatabasesRoutes(router: Router, ctx: DatabasesContext): void;
26
- export {};
1
+ import type {Router} from 'express';import type {DbManager,DatabaseEntry} from '../db/manager';import type {Logger} from '../utils/logger';interface DatabasesContext{manager:DbManager;logger:Logger;basePath:string;readonly ?:boolean;allowBrowse?:boolean;browseRoot?:string;invalidate?:(id:string)=>void;}export interface DatabaseRow extends DatabaseEntry{sizeLabel:string;modifiedLabel:string;tables:number;}export interface FsEntry{name:string;path:string;isDir:boolean;isDb:boolean;size:number;}export declare function registerDatabasesRoutes(router:Router,ctx:DatabasesContext):void;export{};
@@ -1,230 +1 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.registerDatabasesRoutes = registerDatabasesRoutes;
7
- const node_fs_1 = require("node:fs");
8
- const node_path_1 = __importDefault(require("node:path"));
9
- const common_1 = require("../utils/common");
10
- function formatDate(iso) {
11
- try {
12
- return new Date(iso).toLocaleString();
13
- }
14
- catch {
15
- return iso;
16
- }
17
- }
18
- async function getDatabaseRows(manager) {
19
- const entries = manager.list();
20
- return Promise.all(entries.map(async (e) => ({
21
- ...e,
22
- isPostgres: e.dialect === 'postgres',
23
- isSqlite: e.dialect === 'sqlite',
24
- sizeLabel: e.dialect === 'postgres' ? 'Remote' : (0, common_1.formatBytes)(e.size),
25
- modifiedLabel: e.dialect === 'postgres' ? 'Connected' : (e.modified ? formatDate(e.modified) : '—'),
26
- tables: await manager.countTables(e.id),
27
- })));
28
- }
29
- function registerDatabasesRoutes(router, ctx) {
30
- const { manager } = ctx;
31
- const allowBrowse = ctx.allowBrowse ?? true;
32
- const browseRoot = ctx.browseRoot ? node_path_1.default.resolve(ctx.browseRoot) : undefined;
33
- const wrap = (fn) => async (req, res) => {
34
- try {
35
- await fn(req, res);
36
- }
37
- catch (err) {
38
- res.status(500).json({ success: false, error: (0, common_1.errorMessage)(err) });
39
- }
40
- };
41
- router.get('/', async (_req, res, next) => {
42
- try {
43
- const databases = await getDatabaseRows(manager);
44
- res.render('pages/databases', {
45
- title: 'Databases',
46
- databases,
47
- dbDir: manager.directory,
48
- readonly: !!ctx.readonly,
49
- allowBrowse,
50
- browseRoot,
51
- config: { basePath: ctx.basePath },
52
- });
53
- }
54
- catch (err) {
55
- next(err);
56
- }
57
- });
58
- router.get('/api/databases', wrap(async (_req, res) => {
59
- const data = await getDatabaseRows(manager);
60
- res.json({ success: true, data });
61
- }));
62
- const BLOCKED_NAMES = new Set([
63
- 'node_modules',
64
- '.git',
65
- '.svn',
66
- '.hg',
67
- 'System Volume Information',
68
- '$RECYCLE.BIN',
69
- '.env',
70
- '.aws',
71
- '.ssh',
72
- ]);
73
- function listDirectory(abs) {
74
- const entries = [];
75
- for (const name of (0, node_fs_1.readdirSync)(abs)) {
76
- if (name.startsWith('.'))
77
- continue;
78
- if (BLOCKED_NAMES.has(name) || name.toLowerCase().startsWith('.env'))
79
- continue;
80
- const full = node_path_1.default.join(abs, name);
81
- let isDir = false;
82
- let size = 0;
83
- try {
84
- const st = (0, node_fs_1.statSync)(full);
85
- isDir = st.isDirectory();
86
- size = isDir ? 0 : st.size;
87
- }
88
- catch {
89
- continue;
90
- }
91
- if (!isDir && !manager.isDbFile(name))
92
- continue;
93
- if (browseRoot && !(0, common_1.isPathWithinRoot)(browseRoot, full))
94
- continue;
95
- entries.push({ name, path: full, isDir, isDb: !isDir, size });
96
- }
97
- entries.sort((a, b) => a.isDir === b.isDir ? a.name.localeCompare(b.name, undefined, { sensitivity: 'base' }) : a.isDir ? -1 : 1);
98
- return entries;
99
- }
100
- router.get('/api/fs/list', wrap(async (req, res) => {
101
- if (!allowBrowse) {
102
- return res.status(403).json({ success: false, error: 'File browsing is disabled — this server was started with specific database files.' });
103
- }
104
- const raw = String(req.query.path ?? '').trim();
105
- if (raw.includes('\0')) {
106
- return res.status(400).json({ success: false, error: 'Invalid path.' });
107
- }
108
- const base = raw ? node_path_1.default.resolve(raw) : manager.directory;
109
- if (browseRoot && !(0, common_1.isPathWithinRoot)(browseRoot, base)) {
110
- return res.status(403).json({ success: false, error: `Path is outside the allowed folder: ${browseRoot}` });
111
- }
112
- if (!(0, node_fs_1.existsSync)(base))
113
- return res.status(404).json({ success: false, error: `Path does not exist: ${base}` });
114
- if (!(0, node_fs_1.statSync)(base).isDirectory())
115
- return res.status(400).json({ success: false, error: `Not a directory: ${base}` });
116
- let entries;
117
- try {
118
- entries = listDirectory(base);
119
- }
120
- catch (err) {
121
- return res.status(400).json({ success: false, error: `Could not list "${base}": ${(0, common_1.errorMessage)(err)}` });
122
- }
123
- const parent = node_path_1.default.dirname(base) === base ? null : node_path_1.default.dirname(base);
124
- const effectiveParent = parent && (!browseRoot || (0, common_1.isPathWithinRoot)(browseRoot, parent)) ? parent : null;
125
- res.json({
126
- success: true,
127
- data: {
128
- path: base,
129
- name: node_path_1.default.basename(base) || base,
130
- parent: effectiveParent,
131
- entries,
132
- },
133
- });
134
- }));
135
- router.post('/api/databases/open', wrap(async (req, res) => {
136
- if (!allowBrowse) {
137
- return res.status(403).json({ success: false, error: 'File browsing is disabled — this server was started with specific database files.' });
138
- }
139
- const p = String(req.body?.path ?? '').trim();
140
- if (!p || p.includes('\0'))
141
- return res.status(400).json({ success: false, error: 'Valid database path is required.' });
142
- const abs = node_path_1.default.resolve(p);
143
- if (browseRoot && !(0, common_1.isPathWithinRoot)(browseRoot, abs)) {
144
- return res.status(403).json({ success: false, error: `Cannot open a database outside the allowed folder: ${browseRoot}` });
145
- }
146
- const isRo = req.body?.readonly !== undefined ? Boolean(req.body.readonly) : undefined;
147
- let id;
148
- try {
149
- id = manager.openFile(p, isRo);
150
- }
151
- catch (err) {
152
- return res.status(400).json({ success: false, error: (0, common_1.errorMessage)(err) });
153
- }
154
- res.json({ success: true, data: { id, message: `Opened "${id}".` } });
155
- }));
156
- router.post('/api/databases/connect-postgres', wrap(async (req, res) => {
157
- if (ctx.readonly)
158
- return res.status(403).json({ success: false, error: 'Read-only mode — adding connections is disabled.' });
159
- const name = String(req.body?.name ?? '').trim();
160
- const connectionString = String(req.body?.connectionString ?? req.body?.connection ?? '').trim();
161
- const isRo = Boolean(req.body?.readonly);
162
- if (!connectionString) {
163
- return res.status(400).json({ success: false, error: 'PostgreSQL connection string is required (e.g. postgresql://user:password@localhost:5432/dbname).' });
164
- }
165
- if (!connectionString.startsWith('postgres://') && !connectionString.startsWith('postgresql://')) {
166
- return res.status(400).json({ success: false, error: 'Invalid connection protocol. Connection string must start with postgres:// or postgresql://' });
167
- }
168
- let id;
169
- try {
170
- id = manager.addConnection(name, connectionString, isRo);
171
- // Verify the connection works
172
- const db = manager.open(id);
173
- await db.listTables();
174
- }
175
- catch (err) {
176
- if (id) {
177
- manager.remove(id);
178
- }
179
- return res.status(400).json({ success: false, error: `Failed to connect to PostgreSQL: ${(0, common_1.errorMessage)(err)}` });
180
- }
181
- res.status(201).json({ success: true, data: { id, message: `Connected to PostgreSQL database "${id}".` } });
182
- }));
183
- router.post('/api/databases/:id/mode', wrap(async (req, res) => {
184
- if (ctx.readonly && req.body?.readonly === false) {
185
- return res.status(403).json({ success: false, error: 'Server is in global read-only mode.' });
186
- }
187
- const id = String(req.params.id);
188
- if (!manager.has(id))
189
- return res.status(404).json({ success: false, error: `Database "${id}" does not exist.` });
190
- const targetRo = Boolean(req.body?.readonly);
191
- manager.setReadonly(id, targetRo);
192
- if (ctx.invalidate)
193
- ctx.invalidate(id);
194
- res.json({
195
- success: true,
196
- data: {
197
- id,
198
- readonly: targetRo,
199
- message: `Database "${id}" mode set to ${targetRo ? 'Read-only' : 'Writable'}.`,
200
- },
201
- });
202
- }));
203
- router.post('/api/databases', wrap(async (req, res) => {
204
- if (ctx.readonly)
205
- return res.status(403).json({ success: false, error: 'Read-only mode — creating databases is disabled.' });
206
- const name = String(req.body?.name ?? '').trim();
207
- if (!name)
208
- return res.status(400).json({ success: false, error: 'Database name is required.' });
209
- let id;
210
- try {
211
- id = manager.create(name);
212
- }
213
- catch (err) {
214
- return res.status(400).json({ success: false, error: (0, common_1.errorMessage)(err) });
215
- }
216
- res.status(201).json({ success: true, data: { id, message: `Database "${id}" created.` } });
217
- }));
218
- router.delete('/api/databases/:id', wrap(async (req, res) => {
219
- if (ctx.readonly)
220
- return res.status(403).json({ success: false, error: 'Read-only mode — deleting databases is disabled.' });
221
- const id = String(req.params.id);
222
- if (!manager.has(id))
223
- return res.status(404).json({ success: false, error: `Database "${id}" does not exist.` });
224
- if (ctx.invalidate)
225
- ctx.invalidate(id);
226
- manager.remove(id);
227
- res.json({ success: true, data: { message: `Database "${id}" removed.` } });
228
- }));
229
- }
230
- //# sourceMappingURL=databases.js.map
1
+ var __importDefault=this&&this.__importDefault||function(mod){return mod&&mod.__esModule?mod:{default:mod}};Object.defineProperty(exports,"__esModule",{value:!0}),exports.registerDatabasesRoutes=function(router,ctx){const{manager:manager}=ctx,allowBrowse=ctx.allowBrowse??!0,browseRoot=ctx.browseRoot?node_path_1.default.resolve(ctx.browseRoot):void 0,wrap=fn=>async(req,res)=>{try{await fn(req,res)}catch(err){res.status(500).json({success:!1,error:(0,common_1.errorMessage)(err)})}};router.get("/",async(_req,res,next)=>{try{const databases=await getDatabaseRows(manager);res.render("pages/databases",{title:"Databases",databases:databases,dbDir:manager.directory,readonly:!!ctx.readonly,allowBrowse:allowBrowse,browseRoot:browseRoot,config:{basePath:ctx.basePath}})}catch(err){next(err)}}),router.get("/api/databases",wrap(async(_req,res)=>{const data=await getDatabaseRows(manager);res.json({success:!0,data:data})}));const BLOCKED_NAMES=new Set(["node_modules",".git",".svn",".hg","System Volume Information","$RECYCLE.BIN",".env",".aws",".ssh"]);router.get("/api/fs/list",wrap(async(req,res)=>{if(!allowBrowse)return res.status(403).json({success:!1,error:"File browsing is disabled — this server was started with specific database files."});const raw=String(req.query.path??"").trim();if(raw.includes("\0"))return res.status(400).json({success:!1,error:"Invalid path."});const base=raw?node_path_1.default.resolve(raw):manager.directory;if(browseRoot&&!(0,common_1.isPathWithinRoot)(browseRoot,base))return res.status(403).json({success:!1,error:`Path is outside the allowed folder: ${browseRoot}`});if(!(0,node_fs_1.existsSync)(base))return res.status(404).json({success:!1,error:`Path does not exist: ${base}`});if(!(0,node_fs_1.statSync)(base).isDirectory())return res.status(400).json({success:!1,error:`Not a directory: ${base}`});let entries;try{entries=function(abs){const entries=[];for(const name of(0,node_fs_1.readdirSync)(abs)){if(name.startsWith("."))continue;if(BLOCKED_NAMES.has(name)||name.toLowerCase().startsWith(".env"))continue;const full=node_path_1.default.join(abs,name);let isDir=!1,size=0;try{const st=(0,node_fs_1.statSync)(full);isDir=st.isDirectory(),size=isDir?0:st.size}catch{continue}(isDir||manager.isDbFile(name))&&(browseRoot&&!(0,common_1.isPathWithinRoot)(browseRoot,full)||entries.push({name:name,path:full,isDir:isDir,isDb:!isDir,size:size}))}return entries.sort((a,b)=>a.isDir===b.isDir?a.name.localeCompare(b.name,void 0,{sensitivity:"base"}):a.isDir?-1:1),entries}(base)}catch(err){return res.status(400).json({success:!1,error:`Could not list "${base}": ${(0,common_1.errorMessage)(err)}`})}const parent=node_path_1.default.dirname(base)===base?null:node_path_1.default.dirname(base),effectiveParent=!parent||browseRoot&&!(0,common_1.isPathWithinRoot)(browseRoot,parent)?null:parent;res.json({success:!0,data:{path:base,name:node_path_1.default.basename(base)||base,parent:effectiveParent,entries:entries}})})),router.post("/api/databases/open",wrap(async(req,res)=>{if(!allowBrowse)return res.status(403).json({success:!1,error:"File browsing is disabled — this server was started with specific database files."});const p=String(req.body?.path??"").trim();if(!p||p.includes("\0"))return res.status(400).json({success:!1,error:"Valid database path is required."});const abs=node_path_1.default.resolve(p);if(browseRoot&&!(0,common_1.isPathWithinRoot)(browseRoot,abs))return res.status(403).json({success:!1,error:`Cannot open a database outside the allowed folder: ${browseRoot}`});const isRo=void 0!==req.body?.readonly?Boolean(req.body.readonly):void 0;let id;try{id=manager.openFile(p,isRo)}catch(err){return res.status(400).json({success:!1,error:(0,common_1.errorMessage)(err)})}res.json({success:!0,data:{id:id,message:`Opened "${id}".`}})})),router.post("/api/databases/connect-postgres",wrap(async(req,res)=>{if(ctx.readonly)return res.status(403).json({success:!1,error:"Read-only mode — adding connections is disabled."});const name=String(req.body?.name??"").trim(),connectionString=String(req.body?.connectionString??req.body?.connection??"").trim(),isRo=Boolean(req.body?.readonly);if(!connectionString)return res.status(400).json({success:!1,error:"PostgreSQL connection string is required (e.g. postgresql://user:password@localhost:5432/dbname)."});if(!connectionString.startsWith("postgres://")&&!connectionString.startsWith("postgresql://"))return res.status(400).json({success:!1,error:"Invalid connection protocol. Connection string must start with postgres:// or postgresql://"});let id;try{id=manager.addConnection(name,connectionString,isRo);const db=manager.open(id);await db.listTables()}catch(err){return id&&manager.remove(id),res.status(400).json({success:!1,error:`Failed to connect to PostgreSQL: ${(0,common_1.errorMessage)(err)}`})}res.status(201).json({success:!0,data:{id:id,message:`Connected to PostgreSQL database "${id}".`}})})),router.post("/api/databases/:id/mode",wrap(async(req,res)=>{if(ctx.readonly&&!1===req.body?.readonly)return res.status(403).json({success:!1,error:"Server is in global read-only mode."});const id=String(req.params.id);if(!manager.has(id))return res.status(404).json({success:!1,error:`Database "${id}" does not exist.`});const targetRo=Boolean(req.body?.readonly);manager.setReadonly(id,targetRo),ctx.invalidate&&ctx.invalidate(id),res.json({success:!0,data:{id:id,readonly:targetRo,message:`Database "${id}" mode set to ${targetRo?"Read-only":"Writable"}.`}})})),router.post("/api/databases",wrap(async(req,res)=>{if(ctx.readonly)return res.status(403).json({success:!1,error:"Read-only mode — creating databases is disabled."});const name=String(req.body?.name??"").trim();if(!name)return res.status(400).json({success:!1,error:"Database name is required."});let id;try{id=manager.create(name)}catch(err){return res.status(400).json({success:!1,error:(0,common_1.errorMessage)(err)})}res.status(201).json({success:!0,data:{id:id,message:`Database "${id}" created.`}})})),router.delete("/api/databases/:id",wrap(async(req,res)=>{if(ctx.readonly)return res.status(403).json({success:!1,error:"Read-only mode — deleting databases is disabled."});const id=String(req.params.id);if(!manager.has(id))return res.status(404).json({success:!1,error:`Database "${id}" does not exist.`});ctx.invalidate&&ctx.invalidate(id),manager.remove(id),res.json({success:!0,data:{message:`Database "${id}" removed.`}})}))};const node_fs_1=require("node:fs"),node_path_1=__importDefault(require("node:path")),common_1=require("../utils/common");function formatDate(iso){try{return new Date(iso).toLocaleString()}catch{return iso}}async function getDatabaseRows(manager){const entries=manager.list();return Promise.all(entries.map(async e=>({...e,isPostgres:"postgres"===e.dialect,isSqlite:"sqlite"===e.dialect,sizeLabel:"postgres"===e.dialect?"Remote":(0,common_1.formatBytes)(e.size),modifiedLabel:"postgres"===e.dialect?"Connected":e.modified?formatDate(e.modified):"—",tables:await manager.countTables(e.id)})))}
@@ -1,3 +1 @@
1
- export * from './pages';
2
- export * from './databases';
3
- export * from './api/index';
1
+ export * from './pages';export * from './databases';export * from './api/index';
@@ -1,20 +1 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./pages"), exports);
18
- __exportStar(require("./databases"), exports);
19
- __exportStar(require("./api/index"), exports);
20
- //# sourceMappingURL=index.js.map
1
+ var __createBinding=this&&this.__createBinding||(Object.create?function(o,m,k,k2){void 0===k2&&(k2=k);var desc=Object.getOwnPropertyDescriptor(m,k);desc&&!("get"in desc?!m.__esModule:desc.writable||desc.configurable)||(desc={enumerable:!0,get:function(){return m[k]}}),Object.defineProperty(o,k2,desc)}:function(o,m,k,k2){void 0===k2&&(k2=k),o[k2]=m[k]}),__exportStar=this&&this.__exportStar||function(m,exports){for(var p in m)"default"===p||Object.prototype.hasOwnProperty.call(exports,p)||__createBinding(exports,m,p)};Object.defineProperty(exports,"__esModule",{value:!0}),__exportStar(require("./pages"),exports),__exportStar(require("./databases"),exports),__exportStar(require("./api/index"),exports);
@@ -1,9 +1 @@
1
- import type { Router } from 'express';
2
- import type { IDatabase } from '../db/index';
3
- import type { Logger } from '../utils/logger';
4
- interface PageContext {
5
- db: IDatabase;
6
- logger: Logger;
7
- }
8
- export declare function registerPages(router: Router, ctx: PageContext): void;
9
- export {};
1
+ import type {Router} from 'express';import type {IDatabase} from '../db/index';import type {Logger} from '../utils/logger';interface PageContext{db:IDatabase;logger:Logger;}export declare function registerPages(router:Router,ctx:PageContext):void;export{};