admindb 1.0.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 (88) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +325 -0
  3. package/dist/app.d.ts +36 -0
  4. package/dist/app.js +186 -0
  5. package/dist/app.js.map +1 -0
  6. package/dist/cli.d.ts +2 -0
  7. package/dist/cli.js +61 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/config.d.ts +17 -0
  10. package/dist/config.js +23 -0
  11. package/dist/config.js.map +1 -0
  12. package/dist/csv.d.ts +15 -0
  13. package/dist/csv.js +80 -0
  14. package/dist/csv.js.map +1 -0
  15. package/dist/db/database.d.ts +225 -0
  16. package/dist/db/database.js +497 -0
  17. package/dist/db/database.js.map +1 -0
  18. package/dist/db/export.d.ts +12 -0
  19. package/dist/db/export.js +37 -0
  20. package/dist/db/export.js.map +1 -0
  21. package/dist/db/manager.d.ts +62 -0
  22. package/dist/db/manager.js +206 -0
  23. package/dist/db/manager.js.map +1 -0
  24. package/dist/index.d.ts +16 -0
  25. package/dist/index.js +24 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/logger.d.ts +10 -0
  28. package/dist/logger.js +25 -0
  29. package/dist/logger.js.map +1 -0
  30. package/dist/public/css/app.css +1 -0
  31. package/dist/public/css/input.css +179 -0
  32. package/dist/public/js/api.js +47 -0
  33. package/dist/public/js/app.js +120 -0
  34. package/dist/public/js/browse.js +123 -0
  35. package/dist/public/js/databases.js +45 -0
  36. package/dist/public/js/designer.js +205 -0
  37. package/dist/public/js/forms.js +244 -0
  38. package/dist/public/js/query.js +232 -0
  39. package/dist/public/js/schema.js +314 -0
  40. package/dist/public/js/triggers.js +155 -0
  41. package/dist/public/js/views.js +176 -0
  42. package/dist/routes/api.d.ts +9 -0
  43. package/dist/routes/api.js +622 -0
  44. package/dist/routes/api.js.map +1 -0
  45. package/dist/routes/databases.d.ts +19 -0
  46. package/dist/routes/databases.js +99 -0
  47. package/dist/routes/databases.js.map +1 -0
  48. package/dist/routes/pages.d.ts +9 -0
  49. package/dist/routes/pages.js +253 -0
  50. package/dist/routes/pages.js.map +1 -0
  51. package/dist/sql/classifier.d.ts +19 -0
  52. package/dist/sql/classifier.js +37 -0
  53. package/dist/sql/classifier.js.map +1 -0
  54. package/dist/sql/generator.d.ts +99 -0
  55. package/dist/sql/generator.js +259 -0
  56. package/dist/sql/generator.js.map +1 -0
  57. package/dist/test/classifier.test.d.ts +1 -0
  58. package/dist/test/classifier.test.js +44 -0
  59. package/dist/test/classifier.test.js.map +1 -0
  60. package/dist/test/csv.test.d.ts +1 -0
  61. package/dist/test/csv.test.js +64 -0
  62. package/dist/test/csv.test.js.map +1 -0
  63. package/dist/test/database.test.d.ts +1 -0
  64. package/dist/test/database.test.js +224 -0
  65. package/dist/test/database.test.js.map +1 -0
  66. package/dist/test/generator.test.d.ts +1 -0
  67. package/dist/test/generator.test.js +164 -0
  68. package/dist/test/generator.test.js.map +1 -0
  69. package/dist/test/manager.test.d.ts +1 -0
  70. package/dist/test/manager.test.js +146 -0
  71. package/dist/test/manager.test.js.map +1 -0
  72. package/dist/util.d.ts +29 -0
  73. package/dist/util.js +127 -0
  74. package/dist/util.js.map +1 -0
  75. package/dist/views/layouts/main.hbs +49 -0
  76. package/dist/views/pages/databases.hbs +102 -0
  77. package/dist/views/pages/designer.hbs +81 -0
  78. package/dist/views/pages/error.hbs +12 -0
  79. package/dist/views/pages/form.hbs +62 -0
  80. package/dist/views/pages/home.hbs +123 -0
  81. package/dist/views/pages/query.hbs +94 -0
  82. package/dist/views/pages/schema.hbs +290 -0
  83. package/dist/views/pages/table.hbs +203 -0
  84. package/dist/views/pages/triggers.hbs +162 -0
  85. package/dist/views/pages/views.hbs +134 -0
  86. package/dist/views/partials/navbar.hbs +50 -0
  87. package/dist/views/partials/sidebar.hbs +128 -0
  88. package/package.json +62 -0
@@ -0,0 +1,162 @@
1
+ <div class="space-y-6">
2
+ <!-- Header -->
3
+ <div class="flex flex-wrap items-center justify-between gap-3">
4
+ <div>
5
+ <p class="text-sm font-semibold uppercase tracking-wider text-primary">Schema</p>
6
+ <h1 class="mt-1 text-3xl font-extrabold tracking-tight">Triggers</h1>
7
+ <p class="mt-1.5 text-sm text-base-content/55">Automated actions that run when rows in a table are inserted, updated, or deleted.</p>
8
+ </div>
9
+ <div class="flex gap-2">
10
+ <a href="{{basePath}}/views" class="btn btn-ghost btn-sm gap-1.5">
11
+ <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="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /><path stroke-linecap="round" stroke-linejoin="round" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" /></svg>
12
+ Views
13
+ </a>
14
+ {{#unless readonly}}
15
+ <button id="new-trigger" type="button" class="btn btn-primary btn-sm gap-1.5">
16
+ <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 4v16m8-8H4" /></svg>
17
+ New trigger
18
+ </button>
19
+ {{/unless}}
20
+ </div>
21
+ </div>
22
+
23
+ <!-- New trigger form -->
24
+ <div id="new-trigger-panel" class="app-card hidden p-5">
25
+ <div class="grid gap-4 lg:grid-cols-[1fr_360px]">
26
+ <div class="space-y-4">
27
+ <div class="grid gap-4 sm:grid-cols-3">
28
+ <div>
29
+ <label class="field-label" for="trigger-name">Trigger name</label>
30
+ <input id="trigger-name" class="field-input font-mono" placeholder="e.g. audit_user_updates" spellcheck="false" autocomplete="off" />
31
+ </div>
32
+ <div>
33
+ <label class="field-label" for="trigger-table">Table</label>
34
+ <select id="trigger-table" class="field-select font-mono">
35
+ <option value="">— select a table —</option>
36
+ {{#each tables}}
37
+ <option value="{{this}}">{{this}}</option>
38
+ {{/each}}
39
+ </select>
40
+ </div>
41
+ <div>
42
+ <label class="field-label" for="trigger-timing">Timing</label>
43
+ <select id="trigger-timing" class="field-select">
44
+ <option value="BEFORE">BEFORE</option>
45
+ <option value="AFTER" selected>AFTER</option>
46
+ <option value="INSTEAD OF">INSTEAD OF</option>
47
+ </select>
48
+ </div>
49
+ </div>
50
+
51
+ <div>
52
+ <label class="field-label" for="trigger-event">Event</label>
53
+ <select id="trigger-event" class="field-select">
54
+ <option value="INSERT" selected>INSERT</option>
55
+ <option value="UPDATE">UPDATE</option>
56
+ <option value="DELETE">DELETE</option>
57
+ </select>
58
+ <p class="mt-1 text-xs text-base-content/45">SQLite allows one event per trigger. To react to several, create one trigger per event.</p>
59
+ </div>
60
+
61
+ <div>
62
+ <label class="field-label" for="trigger-when">WHEN clause <span class="text-base-content/40">(optional)</span></label>
63
+ <input id="trigger-when" class="field-input font-mono" placeholder="NEW.age >= 18" spellcheck="false" autocomplete="off" />
64
+ <p class="mt-1 text-xs text-base-content/45">Use <code class="font-mono">NEW.</code> / <code class="font-mono">OLD.</code> to reference row values. Leave empty for “always”.</p>
65
+ </div>
66
+
67
+ <div>
68
+ <label class="field-label" for="trigger-body">
69
+ Body
70
+ <span class="chip chip-type">required</span>
71
+ </label>
72
+ <div class="sql-editor-wrap">
73
+ <div id="trigger-gutter" class="sql-gutter"></div>
74
+ <textarea id="trigger-body" class="sql-input font-mono" rows="6" spellcheck="false" placeholder="INSERT INTO audit_log (table_name, action, changed_at) VALUES ('users', 'UPDATE', datetime('now'));"></textarea>
75
+ </div>
76
+ <p class="mt-1.5 text-xs text-base-content/45">One or more statements, each ended with <code class="font-mono">;</code>. Use <code class="font-mono">NEW.col</code> / <code class="font-mono">OLD.col</code>.</p>
77
+ </div>
78
+
79
+ <div id="trigger-error" class="hidden"></div>
80
+ <div class="flex gap-2">
81
+ <button id="create-trigger" type="button" class="btn btn-primary btn-sm gap-1.5">
82
+ <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>
83
+ Create trigger
84
+ </button>
85
+ <button id="cancel-trigger" type="button" class="btn btn-ghost btn-sm">Cancel</button>
86
+ </div>
87
+ </div>
88
+ <aside>
89
+ <div class="app-card overflow-hidden">
90
+ <div class="flex items-center justify-between border-b border-base-200 bg-base-200/40 px-4 py-3">
91
+ <h2 class="flex items-center gap-2 text-sm font-semibold text-base-content/60">
92
+ <span class="grid h-5 w-5 place-items-center rounded-md bg-primary/10 text-primary">
93
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z" /></svg>
94
+ </span>
95
+ SQL preview
96
+ </h2>
97
+ </div>
98
+ <div class="p-4">
99
+ <pre id="trigger-preview" class="code-panel hidden"><code id="trigger-preview-code"></code></pre>
100
+ <p id="trigger-preview-hint" class="text-xs text-base-content/45">The generated <code class="font-mono">CREATE TRIGGER</code> statement appears here as you type.</p>
101
+ </div>
102
+ </div>
103
+ </aside>
104
+ </div>
105
+ </div>
106
+
107
+ <!-- List -->
108
+ <div class="app-card overflow-hidden">
109
+ <div class="flex flex-wrap items-center justify-between gap-2 border-b border-base-200 px-5 py-4">
110
+ <h2 class="text-sm font-semibold uppercase tracking-wide text-base-content/45">
111
+ Triggers
112
+ <span class="ml-1 rounded-md bg-base-200 px-1.5 py-0.5 text-[10px] text-base-content/50">{{triggers.length}}</span>
113
+ </h2>
114
+ </div>
115
+
116
+ {{#if triggers.length}}
117
+ <div class="overflow-x-auto">
118
+ <table class="table table-sm">
119
+ <thead>
120
+ <tr class="bg-base-200/70 text-xs uppercase tracking-wide text-base-content/50">
121
+ <th class="font-semibold">Name</th>
122
+ <th class="font-semibold">On</th>
123
+ <th class="font-semibold">Definition</th>
124
+ <th class="text-right font-semibold">Actions</th>
125
+ </tr>
126
+ </thead>
127
+ <tbody>
128
+ {{#each triggers}}
129
+ <tr class="hover:bg-primary/[0.04] align-top">
130
+ <td class="whitespace-nowrap align-middle font-mono text-[13px] font-semibold">{{this.name}}</td>
131
+ <td class="whitespace-nowrap align-middle font-mono text-[13px] text-primary">{{this.table}}</td>
132
+ <td class="align-middle">
133
+ {{#if this.sql}}
134
+ <details class="group">
135
+ <summary class="cursor-pointer text-xs font-medium text-primary/80 hover:text-primary">Trigger SQL</summary>
136
+ <pre class="mt-1 max-w-[42rem] overflow-x-auto rounded-lg bg-base-200/70 p-2.5 font-mono text-[11px] leading-relaxed text-base-content/70">{{this.sql}}</pre>
137
+ </details>
138
+ {{else}}
139
+ <span class="text-base-content/30">—</span>
140
+ {{/if}}
141
+ </td>
142
+ <td class="whitespace-nowrap text-right align-middle">
143
+ <button type="button" class="btn btn-ghost btn-xs gap-1 text-error trigger-drop" data-trigger="{{this.name}}" {{#if ../readonly}}disabled{{/if}}>
144
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" /></svg>
145
+ Drop
146
+ </button>
147
+ </td>
148
+ </tr>
149
+ {{/each}}
150
+ </tbody>
151
+ </table>
152
+ </div>
153
+ {{else}}
154
+ <div class="px-5 py-10 text-center text-sm text-base-content/45">
155
+ No triggers yet. {{#unless readonly}}Create one with “New trigger”.{{/unless}}
156
+ </div>
157
+ {{/if}}
158
+ </div>
159
+ </div>
160
+
161
+ <script id="triggers-config" type="application/json">{{{json config}}}</script>
162
+ <script src="{{basePath}}/js/triggers.js" defer></script>
@@ -0,0 +1,134 @@
1
+ <div class="space-y-6">
2
+ <!-- Header -->
3
+ <div class="flex flex-wrap items-center justify-between gap-3">
4
+ <div>
5
+ <p class="text-sm font-semibold uppercase tracking-wider text-primary">Schema</p>
6
+ <h1 class="mt-1 text-3xl font-extrabold tracking-tight">Views</h1>
7
+ <p class="mt-1.5 text-sm text-base-content/55">Saved SELECT statements you can query like a table. Views are read-only.</p>
8
+ </div>
9
+ <div class="flex gap-2">
10
+ <a href="{{basePath}}/triggers" class="btn btn-ghost btn-sm gap-1.5">
11
+ <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="M13 10V3L4 14h7v7l9-11h-7z" /></svg>
12
+ Triggers
13
+ </a>
14
+ {{#unless readonly}}
15
+ <button id="new-view" type="button" class="btn btn-primary btn-sm gap-1.5">
16
+ <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 4v16m8-8H4" /></svg>
17
+ New view
18
+ </button>
19
+ {{/unless}}
20
+ </div>
21
+ </div>
22
+
23
+ <!-- New view form -->
24
+ <div id="new-view-panel" class="app-card hidden p-5">
25
+ <div class="grid gap-4 lg:grid-cols-[1fr_360px]">
26
+ <div class="space-y-4">
27
+ <div>
28
+ <label class="field-label" for="view-name">View name</label>
29
+ <input id="view-name" class="field-input font-mono" placeholder="e.g. active_users" spellcheck="false" autocomplete="off" />
30
+ </div>
31
+ <div>
32
+ <label class="field-label" for="view-sql">
33
+ SELECT statement
34
+ <span class="chip chip-type">required</span>
35
+ </label>
36
+ <div class="sql-editor-wrap">
37
+ <div id="view-gutter" class="sql-gutter"></div>
38
+ <textarea id="view-sql" class="sql-input font-mono" rows="8" spellcheck="false" placeholder="SELECT id, email, name FROM users WHERE active = 1"></textarea>
39
+ </div>
40
+ <p class="mt-1.5 text-xs text-base-content/45">A single SELECT statement. Sub-queries and CTEs are allowed.</p>
41
+ </div>
42
+ <div id="view-error" class="hidden"></div>
43
+ <div class="flex gap-2">
44
+ <button id="create-view" type="button" class="btn btn-primary btn-sm gap-1.5">
45
+ <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>
46
+ Create view
47
+ </button>
48
+ <button id="cancel-view" type="button" class="btn btn-ghost btn-sm">Cancel</button>
49
+ </div>
50
+ </div>
51
+ <aside>
52
+ <div class="app-card overflow-hidden">
53
+ <div class="flex items-center justify-between border-b border-base-200 bg-base-200/40 px-4 py-3">
54
+ <h2 class="flex items-center gap-2 text-sm font-semibold text-base-content/60">
55
+ <span class="grid h-5 w-5 place-items-center rounded-md bg-primary/10 text-primary">
56
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z" /></svg>
57
+ </span>
58
+ SQL preview
59
+ </h2>
60
+ </div>
61
+ <div class="p-4">
62
+ <pre id="view-preview" class="code-panel hidden"><code id="view-preview-code"></code></pre>
63
+ <p id="view-preview-hint" class="text-xs text-base-content/45">The generated <code class="font-mono">CREATE VIEW</code> statement appears here as you type.</p>
64
+ </div>
65
+ </div>
66
+ </aside>
67
+ </div>
68
+ </div>
69
+
70
+ <!-- List -->
71
+ <div class="app-card overflow-hidden">
72
+ <div class="flex flex-wrap items-center justify-between gap-2 border-b border-base-200 px-5 py-4">
73
+ <h2 class="text-sm font-semibold uppercase tracking-wide text-base-content/45">
74
+ Views
75
+ <span class="ml-1 rounded-md bg-base-200 px-1.5 py-0.5 text-[10px] text-base-content/50">{{views.length}}</span>
76
+ </h2>
77
+ </div>
78
+
79
+ {{#if views.length}}
80
+ <div class="overflow-x-auto">
81
+ <table class="table table-sm">
82
+ <thead>
83
+ <tr class="bg-base-200/70 text-xs uppercase tracking-wide text-base-content/50">
84
+ <th class="font-semibold">Name</th>
85
+ <th class="font-semibold">Definition</th>
86
+ <th class="text-right font-semibold">Actions</th>
87
+ </tr>
88
+ </thead>
89
+ <tbody>
90
+ {{#each views}}
91
+ <tr class="hover:bg-primary/[0.04] align-top">
92
+ <td class="whitespace-nowrap align-middle font-mono text-[13px] font-semibold">{{this.name}}</td>
93
+ <td class="align-middle">
94
+ {{#if this.sql}}
95
+ <details class="group">
96
+ <summary class="cursor-pointer text-xs font-medium text-primary/80 hover:text-primary">View SQL</summary>
97
+ <pre class="mt-1 max-w-[42rem] overflow-x-auto rounded-lg bg-base-200/70 p-2.5 font-mono text-[11px] leading-relaxed text-base-content/70">{{this.sql}}</pre>
98
+ </details>
99
+ {{else}}
100
+ <span class="text-base-content/30">—</span>
101
+ {{/if}}
102
+ </td>
103
+ <td class="whitespace-nowrap text-right align-middle">
104
+ <div class="flex items-center justify-end gap-1">
105
+ <button type="button" class="btn btn-ghost btn-xs gap-1 text-base-content/70 hover:text-primary view-browse" data-view="{{this.name}}">
106
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /><path stroke-linecap="round" stroke-linejoin="round" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" /></svg>
107
+ Preview rows
108
+ </button>
109
+ <button type="button" class="btn btn-ghost btn-xs gap-1 text-error view-drop" data-view="{{this.name}}" {{#if ../readonly}}disabled{{/if}}>
110
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" /></svg>
111
+ Drop
112
+ </button>
113
+ </div>
114
+ </td>
115
+ </tr>
116
+ <tr class="view-preview-row hidden">
117
+ <td colspan="3" class="border-t-0 p-0">
118
+ <div class="px-5 py-4"></div>
119
+ </td>
120
+ </tr>
121
+ {{/each}}
122
+ </tbody>
123
+ </table>
124
+ </div>
125
+ {{else}}
126
+ <div class="px-5 py-10 text-center text-sm text-base-content/45">
127
+ No views yet. {{#unless readonly}}Create one with “New view”.{{/unless}}
128
+ </div>
129
+ {{/if}}
130
+ </div>
131
+ </div>
132
+
133
+ <script id="views-config" type="application/json">{{{json config}}}</script>
134
+ <script src="{{basePath}}/js/views.js" defer></script>
@@ -0,0 +1,50 @@
1
+ <div class="sticky top-0 z-30 border-b border-base-300/80 bg-base-100/85 backdrop-blur-md">
2
+ <div class="navbar mx-auto w-full max-w-[1500px] px-4 sm:px-6 lg:px-8">
3
+ <div class="flex-none lg:hidden">
4
+ <label for="sidebar-toggle" class="btn btn-square btn-ghost" aria-label="open sidebar">
5
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
6
+ <path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h16M4 18h16" />
7
+ </svg>
8
+ </label>
9
+ </div>
10
+ <div class="flex-1">
11
+ <a href="{{basePath}}/" class="flex items-center gap-2.5">
12
+ <span class="grid h-9 w-9 place-items-center rounded-xl bg-gradient-to-br from-primary to-secondary text-white shadow-soft">
13
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
14
+ <path stroke-linecap="round" stroke-linejoin="round" d="M4 7v10c0 1.1.9 2 2 2h12a2 2 0 002-2V7M4 7c0-1.1.9-2 2-2h12a2 2 0 012 2M4 7c0 1.1.9 2 2 2h12a2 2 0 002-2" />
15
+ </svg>
16
+ </span>
17
+ <span class="text-lg font-bold tracking-tight">DB<span class="text-primary">Admin</span></span>
18
+ </a>
19
+ </div>
20
+ <div class="flex items-center gap-1.5">
21
+ {{#unless databasesMode}}
22
+ {{#if databasesUrl}}
23
+ <a href="{{databasesUrl}}" class="btn btn-ghost btn-sm hidden gap-1.5 sm:inline-flex">
24
+ <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 7v10c0 1.1.9 2 2 2h12a2 2 0 002-2V7M4 7c0-1.1.9-2 2-2h12a2 2 0 012 2M4 7c0 1.1.9 2 2 2h12a2 2 0 002-2" /></svg>
25
+ Databases
26
+ </a>
27
+ {{/if}}
28
+ <a href="{{basePath}}/query" class="btn btn-ghost btn-sm hidden gap-1.5 sm:inline-flex">
29
+ <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="M13 10V3L4 14h7v7l9-11h-7z" /></svg>
30
+ Query
31
+ </a>
32
+ {{#unless readonly}}
33
+ <a href="{{basePath}}/designer" class="btn btn-ghost btn-sm hidden gap-1.5 sm:inline-flex">
34
+ <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 4v16m8-8H4" /></svg>
35
+ New table
36
+ </a>
37
+ {{/unless}}
38
+ <a href="{{basePath}}/export" class="btn btn-ghost btn-sm hidden gap-1.5 md:inline-flex">
39
+ <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 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" /></svg>
40
+ Export
41
+ </a>
42
+ {{/unless}}
43
+ <button id="theme-toggle" type="button" class="btn btn-square btn-ghost btn-sm" aria-label="Toggle dark mode" title="Toggle dark mode">
44
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
45
+ <path stroke-linecap="round" stroke-linejoin="round" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" />
46
+ </svg>
47
+ </button>
48
+ </div>
49
+ </div>
50
+ </div>
@@ -0,0 +1,128 @@
1
+ <aside class="flex h-full min-h-full w-72 flex-col bg-neutral text-neutral-content">
2
+ <div class="flex items-center gap-2.5 px-5 pb-4 pt-5">
3
+ <span class="grid h-9 w-9 shrink-0 place-items-center rounded-xl bg-gradient-to-br from-primary to-secondary text-white shadow-soft">
4
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
5
+ <path stroke-linecap="round" stroke-linejoin="round" d="M4 7v10c0 1.1.9 2 2 2h12a2 2 0 002-2V7M4 7c0-1.1.9-2 2-2h12a2 2 0 012 2M4 7c0 1.1.9 2 2 2h12a2 2 0 002-2" />
6
+ </svg>
7
+ </span>
8
+ <div>
9
+ <div class="text-base font-bold leading-tight">AdminDB</div>
10
+ <div class="text-[11px] text-neutral-content/50">{{#if databasesMode}}Database manager{{else}}Database: {{dbId}}{{/if}}</div>
11
+ </div>
12
+ </div>
13
+
14
+ {{#if databasesMode}}
15
+ <nav class="flex-1 overflow-y-auto px-3 py-2">
16
+ <ul class="space-y-0.5">
17
+ <li>
18
+ <a href="{{basePath}}/" class="side-link active">
19
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M4 7v10c0 1.1.9 2 2 2h12a2 2 0 002-2V7M4 7c0-1.1.9-2 2-2h12a2 2 0 012 2M4 7c0 1.1.9 2 2 2h12a2 2 0 002-2" /></svg>
20
+ Databases
21
+ </a>
22
+ </li>
23
+ </ul>
24
+ </nav>
25
+ {{else}}
26
+ <div class="px-4 pb-2">
27
+ <label class="relative block">
28
+ <svg xmlns="http://www.w3.org/2000/svg" class="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-neutral-content/40" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
29
+ <path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
30
+ </svg>
31
+ <input id="table-search" type="search" placeholder="Filter tables…" autocomplete="off"
32
+ class="w-full rounded-lg border border-white/10 bg-white/[0.06] py-2 pl-9 pr-3 text-sm text-neutral-content outline-none transition placeholder:text-neutral-content/40 focus:border-primary/70 focus:ring-4 focus:ring-primary/20" />
33
+ </label>
34
+ </div>
35
+
36
+ <nav class="flex-1 overflow-y-auto px-3 py-2">
37
+ <ul class="space-y-0.5">
38
+ {{#if databasesUrl}}
39
+ <li>
40
+ <a href="{{databasesUrl}}" class="side-link">
41
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M4 7v10c0 1.1.9 2 2 2h12a2 2 0 002-2V7M4 7c0-1.1.9-2 2-2h12a2 2 0 012 2M4 7c0 1.1.9 2 2 2h12a2 2 0 002-2" /></svg>
42
+ Databases
43
+ </a>
44
+ </li>
45
+ {{/if}}
46
+ <li>
47
+ <a href="{{basePath}}/" class="side-link {{#if (eq currentPath '/')}}active{{/if}}">
48
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" /></svg>
49
+ Home
50
+ </a>
51
+ </li>
52
+ <li>
53
+ <a href="{{basePath}}/query" class="side-link {{#if (startsWith currentPath '/query')}}active{{/if}}">
54
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z" /></svg>
55
+ Query editor
56
+ </a>
57
+ </li>
58
+ <li>
59
+ <a href="{{basePath}}/views" class="side-link {{#if (startsWith currentPath '/views')}}active{{/if}}">
60
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /><path stroke-linecap="round" stroke-linejoin="round" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" /></svg>
61
+ Views
62
+ </a>
63
+ </li>
64
+ <li>
65
+ <a href="{{basePath}}/triggers" class="side-link {{#if (startsWith currentPath '/triggers')}}active{{/if}}">
66
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z" /></svg>
67
+ Triggers
68
+ </a>
69
+ </li>
70
+ {{#unless readonly}}
71
+ <li>
72
+ <a href="{{basePath}}/designer" class="side-link {{#if (startsWith currentPath '/designer')}}active{{/if}}">
73
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M12 4v16m8-8H4" /></svg>
74
+ New table
75
+ </a>
76
+ </li>
77
+ {{/unless}}
78
+ </ul>
79
+
80
+ <div class="mb-2 mt-6 flex items-center justify-between px-3">
81
+ <span class="text-[11px] font-semibold uppercase tracking-wider text-neutral-content/40">Tables</span>
82
+ <span id="table-count" class="rounded-md bg-white/[0.06] px-1.5 py-0.5 text-[10px] font-semibold text-neutral-content/50">{{tables.length}}</span>
83
+ </div>
84
+
85
+ <ul id="table-list" class="space-y-0.5">
86
+ {{#each tables}}
87
+ <li class="table-item" data-name="{{this}}">
88
+ <a href="{{../basePath}}/tables/{{this}}" class="side-link {{#if (eq ../currentTable this)}}active{{/if}}">
89
+ <span class="table-icon">
90
+ <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="M3 10h18M3 14h18m-9-4v8m-7 0h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" /></svg>
91
+ </span>
92
+ <span class="truncate">{{this}}</span>
93
+ </a>
94
+ </li>
95
+ {{else}}
96
+ <li class="px-3 py-2 text-xs text-neutral-content/40">No tables yet</li>
97
+ {{/each}}
98
+ </ul>
99
+
100
+ {{#if internalTables.length}}
101
+ <div class="mb-2 mt-6 px-3 text-[11px] font-semibold uppercase tracking-wider text-neutral-content/40">Internal</div>
102
+ <ul class="space-y-0.5">
103
+ {{#each internalTables}}
104
+ <li>
105
+ <a href="{{../basePath}}/tables/{{this}}" class="side-link {{#if (eq ../currentTable this)}}active{{/if}}">
106
+ <span class="table-icon">
107
+ <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 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" /></svg>
108
+ </span>
109
+ <span class="truncate">{{this}}</span>
110
+ </a>
111
+ </li>
112
+ {{/each}}
113
+ </ul>
114
+ {{/if}}
115
+ </nav>
116
+ {{/if}}
117
+
118
+ <div class="border-t border-white/10 px-5 py-3">
119
+ <div class="text-[11px] font-medium uppercase tracking-wider text-neutral-content/40">{{#if databasesMode}}Directory{{else}}Database{{/if}}</div>
120
+ <div class="mt-0.5 truncate font-mono text-[11px] text-neutral-content/60" title="{{dbPath}}">{{dbPath}}</div>
121
+ {{#if readonly}}
122
+ <div class="mt-2 inline-flex items-center gap-1 rounded-md bg-warning/20 px-2 py-1 text-[10px] font-bold uppercase tracking-wider text-warning">
123
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M12 15v2m0 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>
124
+ Read-only
125
+ </div>
126
+ {{/if}}
127
+ </div>
128
+ </aside>
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "admindb",
3
+ "version": "1.0.1",
4
+ "description": "Browser-based SQLite database administration tool — embed it in your Express app or run it standalone.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "bin": {
8
+ "admindb": "dist/cli.js"
9
+ },
10
+ "homepage": "https://github.com/MIbnEKhalid/admindb#readme",
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/MIbnEKhalid/admindb.git"
14
+ },
15
+ "bugs": {
16
+ "url": "https://github.com/MIbnEKhalid/admindb/issues"
17
+ },
18
+ "author": "MIbnEKhalid",
19
+ "files": [
20
+ "dist",
21
+ "README.md",
22
+ "LICENSE"
23
+ ],
24
+ "engines": {
25
+ "node": ">=22.5.0"
26
+ },
27
+ "scripts": {
28
+ "build": "tsc -p tsconfig.json && node scripts/copy-assets.mjs && npm run build:css",
29
+ "build:css": "tailwindcss -i ./src/public/css/input.css -o ./dist/public/css/app.css --minify",
30
+ "start": "node dist/cli.js",
31
+ "dev": "concurrently -k -n server,css \"tsx watch src/cli.ts\" \"npm run dev:css\"",
32
+ "dev:css": "tailwindcss -i ./src/public/css/input.css -o ./src/public/css/app.css --watch",
33
+ "prepack": "npm run build",
34
+ "test": "npm run build && node --test \"dist/test/**/*.test.js\""
35
+ },
36
+ "keywords": [
37
+ "sqlite",
38
+ "admin",
39
+ "database",
40
+ "express",
41
+ "handlebars",
42
+ "tailwind",
43
+ "daisyui"
44
+ ],
45
+ "license": "MIT",
46
+ "publishConfig": {
47
+ "registry": "https://registry.npmjs.org/"
48
+ },
49
+ "dependencies": {
50
+ "express": "^5.1.0",
51
+ "express-handlebars": "^8.0.3"
52
+ },
53
+ "devDependencies": {
54
+ "@types/express": "^4.17.21",
55
+ "@types/node": "^22.10.0",
56
+ "concurrently": "^9.1.0",
57
+ "daisyui": "^4.12.0",
58
+ "tailwindcss": "^3.4.17",
59
+ "tsx": "^4.19.0",
60
+ "typescript": "^5.7.0"
61
+ }
62
+ }