admindb 2.2.1 → 2.3.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 +368 -364
- package/dist/app.js +25 -16
- package/dist/auth/config.d.ts +1 -1
- package/dist/auth/config.js +18 -30
- package/dist/auth/crypto.js +11 -21
- package/dist/auth/middleware.js +9 -16
- package/dist/auth/routes.js +9 -15
- package/dist/cli/args.d.ts +1 -1
- package/dist/cli/args.js +20 -29
- package/dist/cli/config.d.ts +1 -1
- package/dist/cli/config.js +15 -18
- package/dist/cli/runner.js +13 -13
- package/dist/data/chain.d.ts +1 -1
- package/dist/data/chain.js +40 -617
- package/dist/data/datasets.d.ts +1 -1
- package/dist/data/datasets.js +43 -1
- package/dist/data/detector.js +45 -21
- package/dist/data/engine.d.ts +1 -1
- package/dist/data/engine.js +483 -142
- package/dist/data/index.d.ts +1 -1
- package/dist/data/index.js +8 -0
- 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 +44 -283
- 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/validator.d.ts +1 -0
- package/dist/data/validator.js +160 -0
- package/dist/db/database.js +9 -14
- package/dist/db/export.js +32 -55
- package/dist/db/filters.d.ts +1 -1
- package/dist/db/filters.js +15 -38
- package/dist/db/introspection.js +4 -7
- package/dist/db/manager.d.ts +1 -1
- package/dist/db/manager.js +48 -53
- package/dist/db/migrations.js +7 -14
- package/dist/db/postgres.js +30 -78
- package/dist/index.d.ts +1 -1
- package/dist/public/css/app.css +1 -1
- package/dist/public/js/browse.js +1 -1
- package/dist/public/js/inspector.js +1 -1
- package/dist/public/js/query.js +1 -1
- package/dist/public/js/seed.js +1 -1
- package/dist/routes/api/erd.js +3 -8
- package/dist/routes/api/helpers.d.ts +1 -1
- package/dist/routes/api/helpers.js +75 -25
- package/dist/routes/api/import-export.js +1 -2
- package/dist/routes/api/query.js +2 -6
- package/dist/routes/api/rows.js +4 -8
- package/dist/routes/api/seed.js +35 -21
- package/dist/routes/api/tables.js +15 -30
- package/dist/routes/databases.js +8 -26
- package/dist/routes/pages.js +67 -18
- package/dist/serverless.js +31 -44
- package/dist/sql/generator.d.ts +1 -1
- package/dist/sql/generator.js +35 -109
- package/dist/types/api.d.ts +1 -1
- package/dist/utils/common.d.ts +1 -1
- package/dist/utils/common.js +45 -85
- package/dist/utils/csv.d.ts +1 -1
- package/dist/utils/csv.js +11 -17
- package/dist/utils/datatype.js +46 -60
- package/dist/utils/icons.js +8 -8
- package/dist/views/layouts/main.hbs +1 -87
- package/dist/views/pages/databases.hbs +1 -233
- package/dist/views/pages/designer.hbs +1 -89
- package/dist/views/pages/erd.hbs +1 -1241
- package/dist/views/pages/error.hbs +1 -10
- package/dist/views/pages/form.hbs +1 -59
- package/dist/views/pages/home.hbs +1 -220
- package/dist/views/pages/info.hbs +1 -107
- package/dist/views/pages/login.hbs +1 -98
- package/dist/views/pages/query.hbs +1 -129
- package/dist/views/pages/schema.hbs +1 -298
- package/dist/views/pages/seed-select.hbs +251 -0
- package/dist/views/pages/seed.hbs +256 -414
- package/dist/views/pages/table.hbs +1 -327
- package/dist/views/partials/navbar.hbs +1 -83
- package/dist/views/partials/sidebar.hbs +1 -129
- package/docs/API.md +149 -149
- package/docs/EXAMPLES.md +579 -579
- package/docs/SECURITY.md +265 -265
- package/package.json +80 -79
package/dist/utils/datatype.js
CHANGED
|
@@ -6,60 +6,66 @@ exports.analyzeBlob = analyzeBlob;
|
|
|
6
6
|
exports.isJsonString = isJsonString;
|
|
7
7
|
exports.formatJsonSafely = formatJsonSafely;
|
|
8
8
|
const common_js_1 = require("./common.js");
|
|
9
|
+
const mimeResult = (mime, ext, flags = {}) => ({
|
|
10
|
+
mime,
|
|
11
|
+
ext,
|
|
12
|
+
isImage: false,
|
|
13
|
+
isText: false,
|
|
14
|
+
isPdf: false,
|
|
15
|
+
isAudio: false,
|
|
16
|
+
isVideo: false,
|
|
17
|
+
...flags,
|
|
18
|
+
});
|
|
9
19
|
function sniffMimeType(buf) {
|
|
10
20
|
const len = buf.length;
|
|
11
|
-
if (len === 0)
|
|
12
|
-
return
|
|
13
|
-
}
|
|
21
|
+
if (len === 0)
|
|
22
|
+
return mimeResult('application/octet-stream', 'bin');
|
|
14
23
|
if (len >= 8 && buf[0] === 0x89 && buf[1] === 0x50 && buf[2] === 0x4e && buf[3] === 0x47 && buf[4] === 0x0d && buf[5] === 0x0a && buf[6] === 0x1a && buf[7] === 0x0a) {
|
|
15
|
-
return
|
|
24
|
+
return mimeResult('image/png', 'png', { isImage: true });
|
|
16
25
|
}
|
|
17
26
|
if (len >= 3 && buf[0] === 0xff && buf[1] === 0xd8 && buf[2] === 0xff) {
|
|
18
|
-
return
|
|
27
|
+
return mimeResult('image/jpeg', 'jpg', { isImage: true });
|
|
19
28
|
}
|
|
20
29
|
if (len >= 6 && buf[0] === 0x47 && buf[1] === 0x49 && buf[2] === 0x46 && buf[3] === 0x38 && (buf[4] === 0x37 || buf[4] === 0x39) && buf[5] === 0x61) {
|
|
21
|
-
return
|
|
30
|
+
return mimeResult('image/gif', 'gif', { isImage: true });
|
|
22
31
|
}
|
|
23
32
|
if (len >= 12 && buf[0] === 0x52 && buf[1] === 0x49 && buf[2] === 0x46 && buf[3] === 0x46 && buf[8] === 0x57 && buf[9] === 0x45 && buf[10] === 0x42 && buf[11] === 0x50) {
|
|
24
|
-
return
|
|
33
|
+
return mimeResult('image/webp', 'webp', { isImage: true });
|
|
25
34
|
}
|
|
26
35
|
if (len >= 2 && buf[0] === 0x42 && buf[1] === 0x4d) {
|
|
27
|
-
return
|
|
36
|
+
return mimeResult('image/bmp', 'bmp', { isImage: true });
|
|
28
37
|
}
|
|
29
38
|
if (len >= 4 && buf[0] === 0x00 && buf[1] === 0x00 && buf[2] === 0x01 && buf[3] === 0x00) {
|
|
30
|
-
return
|
|
39
|
+
return mimeResult('image/x-icon', 'ico', { isImage: true });
|
|
31
40
|
}
|
|
32
41
|
if (len >= 4 && buf[0] === 0x25 && buf[1] === 0x50 && buf[2] === 0x44 && buf[3] === 0x46) {
|
|
33
|
-
return
|
|
34
|
-
}
|
|
35
|
-
if (len >= 3 && buf[0] === 0x49 && buf[1] === 0x44 && buf[2] === 0x33) {
|
|
36
|
-
return { mime: 'audio/mpeg', ext: 'mp3', isImage: false, isText: false, isPdf: false, isAudio: true, isVideo: false };
|
|
42
|
+
return mimeResult('application/pdf', 'pdf', { isPdf: true });
|
|
37
43
|
}
|
|
38
|
-
if (len >= 2 && buf[0] === 0xff && (buf[1] & 0xe0) === 0xe0) {
|
|
39
|
-
return
|
|
44
|
+
if ((len >= 3 && buf[0] === 0x49 && buf[1] === 0x44 && buf[2] === 0x33) || (len >= 2 && buf[0] === 0xff && (buf[1] & 0xe0) === 0xe0)) {
|
|
45
|
+
return mimeResult('audio/mpeg', 'mp3', { isAudio: true });
|
|
40
46
|
}
|
|
41
47
|
if (len >= 12 && buf[0] === 0x52 && buf[1] === 0x49 && buf[2] === 0x46 && buf[3] === 0x46 && buf[8] === 0x57 && buf[9] === 0x41 && buf[10] === 0x56 && buf[11] === 0x45) {
|
|
42
|
-
return
|
|
48
|
+
return mimeResult('audio/wav', 'wav', { isAudio: true });
|
|
43
49
|
}
|
|
44
50
|
if (len >= 4 && buf[0] === 0x4f && buf[1] === 0x67 && buf[2] === 0x67 && buf[3] === 0x53) {
|
|
45
|
-
return
|
|
51
|
+
return mimeResult('audio/ogg', 'ogg', { isAudio: true });
|
|
46
52
|
}
|
|
47
53
|
if (len >= 8 && buf[4] === 0x66 && buf[5] === 0x74 && buf[6] === 0x79 && buf[7] === 0x70) {
|
|
48
|
-
return
|
|
54
|
+
return mimeResult('video/mp4', 'mp4', { isVideo: true });
|
|
49
55
|
}
|
|
50
56
|
if (len >= 4 && buf[0] === 0x1a && buf[1] === 0x45 && buf[2] === 0xdf && buf[3] === 0xa3) {
|
|
51
|
-
return
|
|
57
|
+
return mimeResult('video/webm', 'webm', { isVideo: true });
|
|
52
58
|
}
|
|
53
59
|
const sampleLen = Math.min(len, 1024);
|
|
54
60
|
const sample = Buffer.from(buf.subarray(0, sampleLen)).toString('utf8');
|
|
55
61
|
const trimmed = sample.trim();
|
|
56
62
|
if (trimmed.startsWith('<svg') || (trimmed.startsWith('<?xml') && trimmed.includes('<svg'))) {
|
|
57
|
-
return
|
|
63
|
+
return mimeResult('image/svg+xml', 'svg', { isImage: true, isText: true });
|
|
58
64
|
}
|
|
59
65
|
if ((trimmed.startsWith('{') && trimmed.endsWith('}')) || (trimmed.startsWith('[') && trimmed.endsWith(']'))) {
|
|
60
66
|
try {
|
|
61
67
|
JSON.parse(sample);
|
|
62
|
-
return
|
|
68
|
+
return mimeResult('application/json', 'json', { isText: true });
|
|
63
69
|
}
|
|
64
70
|
catch {
|
|
65
71
|
}
|
|
@@ -72,15 +78,16 @@ function sniffMimeType(buf) {
|
|
|
72
78
|
}
|
|
73
79
|
}
|
|
74
80
|
if (nonPrintable === 0) {
|
|
75
|
-
return
|
|
81
|
+
return mimeResult('text/plain; charset=utf-8', 'txt', { isText: true });
|
|
76
82
|
}
|
|
77
|
-
return
|
|
83
|
+
return mimeResult('application/octet-stream', 'bin');
|
|
78
84
|
}
|
|
79
85
|
function generateHexDump(buf, maxBytes = 4096, bytesPerLine = 16) {
|
|
80
86
|
const totalBytes = buf.length;
|
|
81
87
|
const limit = Math.min(totalBytes, maxBytes);
|
|
82
88
|
const truncated = totalBytes > maxBytes;
|
|
83
89
|
const lines = [];
|
|
90
|
+
const mid = Math.floor(bytesPerLine / 2);
|
|
84
91
|
for (let i = 0; i < limit; i += bytesPerLine) {
|
|
85
92
|
const offset = i.toString(16).padStart(8, '0');
|
|
86
93
|
const chunk = buf.subarray(i, Math.min(i + bytesPerLine, limit));
|
|
@@ -90,18 +97,16 @@ function generateHexDump(buf, maxBytes = 4096, bytesPerLine = 16) {
|
|
|
90
97
|
if (j < chunk.length) {
|
|
91
98
|
const byte = chunk[j];
|
|
92
99
|
hexParts.push(byte.toString(16).padStart(2, '0'));
|
|
93
|
-
asciiPart +=
|
|
100
|
+
asciiPart += byte >= 32 && byte <= 126 ? String.fromCharCode(byte) : '.';
|
|
94
101
|
}
|
|
95
102
|
else {
|
|
96
103
|
hexParts.push(' ');
|
|
97
104
|
asciiPart += ' ';
|
|
98
105
|
}
|
|
99
106
|
}
|
|
100
|
-
const mid = Math.floor(bytesPerLine / 2);
|
|
101
|
-
const hexFormatted = hexParts.slice(0, mid).join(' ') + ' ' + hexParts.slice(mid).join(' ');
|
|
102
107
|
lines.push({
|
|
103
108
|
offset,
|
|
104
|
-
hex:
|
|
109
|
+
hex: `${hexParts.slice(0, mid).join(' ')} ${hexParts.slice(mid).join(' ')}`,
|
|
105
110
|
ascii: asciiPart,
|
|
106
111
|
});
|
|
107
112
|
}
|
|
@@ -110,12 +115,7 @@ function generateHexDump(buf, maxBytes = 4096, bytesPerLine = 16) {
|
|
|
110
115
|
function analyzeBlob(data) {
|
|
111
116
|
let buf;
|
|
112
117
|
if (typeof data === 'string') {
|
|
113
|
-
|
|
114
|
-
buf = Buffer.from(data.slice(2), 'hex');
|
|
115
|
-
}
|
|
116
|
-
else {
|
|
117
|
-
buf = Buffer.from(data, 'utf8');
|
|
118
|
-
}
|
|
118
|
+
buf = /^0x/i.test(data) ? Buffer.from(data.slice(2), 'hex') : Buffer.from(data, 'utf8');
|
|
119
119
|
}
|
|
120
120
|
else {
|
|
121
121
|
buf = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
@@ -144,38 +144,24 @@ function isJsonString(value) {
|
|
|
144
144
|
if (typeof value !== 'string')
|
|
145
145
|
return false;
|
|
146
146
|
const trimmed = value.trim();
|
|
147
|
-
if (!trimmed)
|
|
147
|
+
if (!trimmed || (!trimmed.startsWith('{') && !trimmed.startsWith('[')))
|
|
148
|
+
return false;
|
|
149
|
+
try {
|
|
150
|
+
JSON.parse(trimmed);
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
148
154
|
return false;
|
|
149
|
-
if ((trimmed.startsWith('{') && trimmed.endsWith('}')) || (trimmed.startsWith('[') && trimmed.endsWith(']'))) {
|
|
150
|
-
try {
|
|
151
|
-
JSON.parse(trimmed);
|
|
152
|
-
return true;
|
|
153
|
-
}
|
|
154
|
-
catch {
|
|
155
|
-
return false;
|
|
156
|
-
}
|
|
157
155
|
}
|
|
158
|
-
return false;
|
|
159
156
|
}
|
|
160
157
|
function formatJsonSafely(value, indent = 2) {
|
|
161
|
-
if (value
|
|
158
|
+
if (value == null)
|
|
162
159
|
return null;
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
}
|
|
167
|
-
catch {
|
|
168
|
-
return null;
|
|
169
|
-
}
|
|
160
|
+
try {
|
|
161
|
+
const obj = typeof value === 'string' ? JSON.parse(value) : value;
|
|
162
|
+
return typeof obj === 'object' && obj !== null ? JSON.stringify(obj, null, indent) : null;
|
|
170
163
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
const parsed = JSON.parse(value);
|
|
174
|
-
return JSON.stringify(parsed, null, indent);
|
|
175
|
-
}
|
|
176
|
-
catch {
|
|
177
|
-
return null;
|
|
178
|
-
}
|
|
164
|
+
catch {
|
|
165
|
+
return null;
|
|
179
166
|
}
|
|
180
|
-
return null;
|
|
181
167
|
}
|
package/dist/utils/icons.js
CHANGED
|
@@ -45,6 +45,9 @@ exports.ICONS = {
|
|
|
45
45
|
spinner: '<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>',
|
|
46
46
|
terminal: '<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/></svg>',
|
|
47
47
|
format: '<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h10M4 18h14"/></svg>',
|
|
48
|
+
columns: '<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M9 4.5v15m6-15v15m-10.875 0h15.75c.621 0 1.125-.504 1.125-1.125V5.625c0-.621-.504-1.125-1.125-1.125H4.125C3.504 4.5 3 5.004 3 5.625v12.75c0 .621.504 1.125 1.125 1.125z" /></svg>',
|
|
49
|
+
pin: '<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M19 13.5l-5-5m3-3.5a2.121 2.121 0 00-3 0L9.5 9.5 5 10l5 5 .5 4.5 4.5-4.5a2.121 2.121 0 000-3zM4 20l5-5" /></svg>',
|
|
50
|
+
grip: '<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><circle cx="9" cy="7" r="1.2" fill="currentColor"/><circle cx="15" cy="7" r="1.2" fill="currentColor"/><circle cx="9" cy="12" r="1.2" fill="currentColor"/><circle cx="15" cy="12" r="1.2" fill="currentColor"/><circle cx="9" cy="17" r="1.2" fill="currentColor"/><circle cx="15" cy="17" r="1.2" fill="currentColor"/></svg>',
|
|
48
51
|
check: '<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" /></svg>',
|
|
49
52
|
x: '<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /></svg>',
|
|
50
53
|
warning: '<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" /></svg>',
|
|
@@ -70,12 +73,9 @@ exports.ICONS = {
|
|
|
70
73
|
};
|
|
71
74
|
function renderIcon(name, customClass) {
|
|
72
75
|
const svg = exports.ICONS[name];
|
|
73
|
-
if (!svg)
|
|
74
|
-
return '';
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
return svg.replace(/class="[^"]*"/, `class="${customClass}"`);
|
|
79
|
-
}
|
|
80
|
-
return svg.replace('<svg', `<svg class="${customClass}"`);
|
|
76
|
+
if (!svg || !customClass)
|
|
77
|
+
return svg || '';
|
|
78
|
+
return svg.includes('class="')
|
|
79
|
+
? svg.replace(/class="[^"]*"/, `class="${customClass}"`)
|
|
80
|
+
: svg.replace('<svg', `<svg class="${customClass}"`);
|
|
81
81
|
}
|
|
@@ -1,87 +1 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en" data-theme="admindb">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
-
<title>{{title}} · AdminDB</title>
|
|
7
|
-
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
8
|
-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
9
|
-
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet" />
|
|
10
|
-
<link rel="stylesheet" href="{{basePath}}/css/app.css" />
|
|
11
|
-
<script>
|
|
12
|
-
window.APP = {
|
|
13
|
-
basePath: {{{json basePath}}},
|
|
14
|
-
readonly: {{{json readonly}}},
|
|
15
|
-
serverless: {{{json serverless}}},
|
|
16
|
-
dialect: {{{json dialect}}},
|
|
17
|
-
isSqlite: {{{json isSqlite}}},
|
|
18
|
-
isPostgres: {{{json isPostgres}}}
|
|
19
|
-
};
|
|
20
|
-
// Apply the saved theme before first paint to avoid a flash.
|
|
21
|
-
(function () {
|
|
22
|
-
try {
|
|
23
|
-
var t = localStorage.getItem('admindb-theme');
|
|
24
|
-
if (t === 'dark') document.documentElement.setAttribute('data-theme', 'admindbDark');
|
|
25
|
-
} catch (e) {}
|
|
26
|
-
})();
|
|
27
|
-
</script>
|
|
28
|
-
<script>
|
|
29
|
-
window.AdminIcons = {
|
|
30
|
-
map: {{{json icons}}},
|
|
31
|
-
get: function(name, customClass) {
|
|
32
|
-
var svg = this.map[name] || '';
|
|
33
|
-
if (!svg || !customClass) return svg;
|
|
34
|
-
if (svg.indexOf('class="') !== -1) {
|
|
35
|
-
return svg.replace(/class="[^"]*"/, 'class="' + customClass + '"');
|
|
36
|
-
}
|
|
37
|
-
return svg.replace('<svg', '<svg class="' + customClass + '"');
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
</script>
|
|
41
|
-
<script src="{{basePath}}/js/common.js" defer></script>
|
|
42
|
-
<script src="{{basePath}}/js/api.js" defer></script>
|
|
43
|
-
<script src="{{basePath}}/js/app.js" defer></script>
|
|
44
|
-
<script src="{{basePath}}/js/inspector.js" defer></script>
|
|
45
|
-
</head>
|
|
46
|
-
<body class="min-h-screen bg-base-200 text-base-content">
|
|
47
|
-
{{#if readonly}}
|
|
48
|
-
<div class="flex items-center justify-center gap-2 bg-warning/15 px-4 py-2 text-center text-sm font-semibold text-warning">
|
|
49
|
-
<span class="shrink-0">{{{icon "warning" class="h-4 w-4"}}}</span>
|
|
50
|
-
{{#if serverless}}
|
|
51
|
-
Serverless mode active — database is open in read-only mode. Writes and schema modifications are disabled.
|
|
52
|
-
{{else}}
|
|
53
|
-
Read-only mode — this database is open without write access. All writes are disabled.
|
|
54
|
-
{{/if}}
|
|
55
|
-
</div>
|
|
56
|
-
{{/if}}
|
|
57
|
-
{{#if noSidebar}}
|
|
58
|
-
<div class="flex min-h-screen flex-col">
|
|
59
|
-
{{> navbar}}
|
|
60
|
-
<main class="mx-auto w-full max-w-[1500px] flex-1 px-4 py-6 sm:px-6 lg:px-8 lg:py-8">
|
|
61
|
-
{{{body}}}
|
|
62
|
-
</main>
|
|
63
|
-
<footer class="border-t border-base-300 px-6 py-4 text-center text-xs text-base-content/40">
|
|
64
|
-
AdminDB{{#if version}} v{{version}}{{/if}} · SQLite & PostgreSQL database administration
|
|
65
|
-
</footer>
|
|
66
|
-
</div>
|
|
67
|
-
{{else}}
|
|
68
|
-
<div class="drawer lg:drawer-open">
|
|
69
|
-
<input id="sidebar-toggle" type="checkbox" class="drawer-toggle" />
|
|
70
|
-
<div class="drawer-content flex min-h-screen flex-col">
|
|
71
|
-
{{> navbar}}
|
|
72
|
-
<main class="mx-auto w-full max-w-[1500px] flex-1 px-4 py-6 sm:px-6 lg:px-8 lg:py-8">
|
|
73
|
-
{{{body}}}
|
|
74
|
-
</main>
|
|
75
|
-
<footer class="border-t border-base-300 px-6 py-4 text-center text-xs text-base-content/40">
|
|
76
|
-
AdminDB{{#if version}} v{{version}}{{/if}} · SQLite & PostgreSQL database administration
|
|
77
|
-
</footer>
|
|
78
|
-
</div>
|
|
79
|
-
<div class="drawer-side z-40">
|
|
80
|
-
<label for="sidebar-toggle" aria-label="close sidebar" class="drawer-overlay"></label>
|
|
81
|
-
{{> sidebar}}
|
|
82
|
-
</div>
|
|
83
|
-
</div>
|
|
84
|
-
{{/if}}
|
|
85
|
-
<div id="toast-container" class="pointer-events-none fixed inset-x-0 top-4 z-50 flex flex-col items-center gap-2 px-4"></div>
|
|
86
|
-
</body>
|
|
87
|
-
</html>
|
|
1
|
+
<!DOCTYPE html>
|
|
88
2
|
<meta charset="utf-8" />
|
|
89
3
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
90
4
|
<title>{{title}} · AdminDB</title>
|
|
91
5
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
92
6
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
93
7
|
<link href="https://fonts.googleapis.com/css2?family=Sora:wght@400;500;600;700;800&family=IBM+Plex+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet" />
|
|
94
8
|
<link rel="stylesheet" href="{{basePath}}/css/app.css" />
|
|
95
9
|
<script>
|
|
96
10
|
window.APP = {
|
|
97
11
|
basePath: {{{json basePath}}},
|
|
98
12
|
readonly: {{{json readonly}}},
|
|
99
13
|
serverless: {{{json serverless}}},
|
|
100
14
|
dialect: {{{json dialect}}},
|
|
101
15
|
isSqlite: {{{json isSqlite}}},
|
|
102
16
|
isPostgres: {{{json isPostgres}}}
|
|
103
17
|
};
|
|
104
18
|
// Apply the saved theme before first paint to avoid a flash.
|
|
105
19
|
(function () {
|
|
106
20
|
try {
|
|
107
21
|
var t = localStorage.getItem('admindb-theme');
|
|
108
22
|
if (t === 'dark') document.documentElement.setAttribute('data-theme', 'admindbDark');
|
|
109
23
|
} catch (e) {}
|
|
110
24
|
})();
|
|
111
25
|
</script>
|
|
112
26
|
<script>
|
|
113
27
|
window.AdminIcons = {
|
|
114
28
|
map: {{{json icons}}},
|
|
115
29
|
get: function(name, customClass) {
|
|
116
30
|
var svg = this.map[name] || '';
|
|
117
31
|
if (!svg || !customClass) return svg;
|
|
118
32
|
if (svg.indexOf('class="') !== -1) {
|
|
119
33
|
return svg.replace(/class="[^"]*"/, 'class="' + customClass + '"');
|
|
120
34
|
}
|
|
121
35
|
return svg.replace('<svg', '<svg class="' + customClass + '"');
|
|
122
36
|
}
|
|
123
37
|
};
|
|
124
38
|
</script>
|
|
125
39
|
<script src="{{basePath}}/js/common.js" defer></script>
|
|
126
40
|
<script src="{{basePath}}/js/api.js" defer></script>
|
|
127
41
|
<script src="{{basePath}}/js/app.js" defer></script>
|
|
128
42
|
<script src="{{basePath}}/js/inspector.js" defer></script>
|
|
129
43
|
<div class="pointer-events-none fixed -left-24 top-32 -z-10 h-72 w-72 rounded-full bg-primary/15 blur-3xl"></div>
|
|
130
44
|
<div class="pointer-events-none fixed -right-24 bottom-20 -z-10 h-80 w-80 rounded-full bg-orange-500/10 blur-3xl"></div>
|
|
131
45
|
{{#if readonly}}
|
|
132
46
|
<div class="flex items-center justify-center gap-2 bg-warning/15 px-4 py-2 text-center text-sm font-semibold text-warning">
|
|
133
47
|
<span class="shrink-0">{{{icon "warning" class="h-4 w-4"}}}</span>
|
|
134
48
|
{{#if serverless}}
|
|
135
49
|
Serverless mode active — database is open in read-only mode. Writes and schema modifications are disabled.
|
|
136
50
|
{{else}}
|
|
137
51
|
Read-only mode — this database is open without write access. All writes are disabled.
|
|
138
52
|
{{/if}}
|
|
139
53
|
</div>
|
|
140
54
|
{{/if}}
|
|
141
55
|
{{#if noSidebar}}
|
|
142
56
|
<div class="flex min-h-screen flex-col">
|
|
143
57
|
{{> navbar}}
|
|
144
58
|
<main class="mx-auto w-full max-w-[1500px] flex-1 px-4 py-6 sm:px-6 lg:px-8 lg:py-8">
|
|
145
59
|
{{{body}}}
|
|
146
60
|
</main>
|
|
147
61
|
<footer class="border-t border-base-300 px-6 py-4 text-center text-xs text-base-content/40">
|
|
148
62
|
AdminDB{{#if version}} v{{version}}{{/if}} · SQLite & PostgreSQL database administration
|
|
149
63
|
</footer>
|
|
150
64
|
</div>
|
|
151
65
|
{{else}}
|
|
152
66
|
<div class="drawer lg:drawer-open">
|
|
153
67
|
<input id="sidebar-toggle" type="checkbox" class="drawer-toggle" />
|
|
154
68
|
<div class="drawer-content flex min-h-screen flex-col">
|
|
155
69
|
{{> navbar}}
|
|
156
70
|
<main class="mx-auto w-full max-w-[1500px] flex-1 px-4 py-6 sm:px-6 lg:px-8 lg:py-8">
|
|
157
71
|
{{{body}}}
|
|
158
72
|
</main>
|
|
159
73
|
<footer class="border-t border-base-300 px-6 py-4 text-center text-xs text-base-content/40">
|
|
160
74
|
AdminDB{{#if version}} v{{version}}{{/if}} · SQLite & PostgreSQL database administration
|
|
161
75
|
</footer>
|
|
162
76
|
</div>
|
|
163
77
|
<div class="drawer-side z-40">
|
|
164
78
|
<label for="sidebar-toggle" aria-label="close sidebar" class="drawer-overlay"></label>
|
|
165
79
|
{{> sidebar}}
|
|
166
80
|
</div>
|
|
167
81
|
</div>
|
|
168
82
|
{{/if}}
|
|
169
83
|
<div id="toast-container" class="pointer-events-none fixed inset-x-0 top-4 z-50 flex flex-col items-center gap-2 px-4"></div>
|