web_plsql 0.18.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +24 -4
  2. package/package.json +15 -5
  3. package/src/admin/client/charts.ts +299 -0
  4. package/src/admin/client/main.js +3 -0
  5. package/src/admin/client/tailwind.css +41 -0
  6. package/src/admin/favicon.svg +3 -0
  7. package/src/admin/index.html +315 -0
  8. package/src/admin/js/api.ts +95 -0
  9. package/src/admin/js/app.ts +306 -0
  10. package/src/admin/js/eslint.config.js +74 -0
  11. package/src/admin/js/schemas.ts +153 -0
  12. package/src/admin/js/templates/config.ts +146 -0
  13. package/src/admin/js/templates/errorRow.ts +18 -0
  14. package/src/admin/js/templates/index.ts +3 -0
  15. package/src/admin/js/templates/poolCard.ts +61 -0
  16. package/src/admin/js/tsconfig.json +24 -0
  17. package/src/admin/js/types.ts +223 -0
  18. package/src/admin/js/ui/theme.ts +93 -0
  19. package/src/admin/js/ui/views.ts +164 -0
  20. package/src/admin/js/util/format.ts +27 -0
  21. package/src/admin/lib/assets/main-zpdhQ1gD.css +1 -0
  22. package/src/admin/lib/chart.bundle.js +139 -0
  23. package/src/admin/style.css +1321 -0
  24. package/src/bin/load-test.js +202 -0
  25. package/src/handler/handlerAdmin.js +198 -0
  26. package/src/handler/plsql/cgi.js +1 -1
  27. package/src/handler/plsql/errorPage.js +35 -6
  28. package/src/handler/plsql/handlerPlSql.js +32 -6
  29. package/src/handler/plsql/parsePage.js +7 -3
  30. package/src/handler/plsql/procedure.js +57 -11
  31. package/src/handler/plsql/procedureNamed.js +18 -47
  32. package/src/handler/plsql/procedureSanitize.js +96 -45
  33. package/src/handler/plsql/procedureVariable.js +2 -2
  34. package/src/handler/plsql/request.js +8 -3
  35. package/src/handler/plsql/sendResponse.js +2 -2
  36. package/src/server/config.js +1 -0
  37. package/src/server/server.js +108 -2
  38. package/src/types.js +6 -0
  39. package/src/util/cache.js +123 -0
  40. package/src/util/jsonLogger.js +47 -0
  41. package/src/util/shutdown.js +6 -2
  42. package/src/util/trace.js +5 -5
  43. package/src/version.js +1 -1
  44. package/types/handler/handlerAdmin.d.ts +9 -0
  45. package/types/handler/plsql/errorPage.d.ts +1 -0
  46. package/types/handler/plsql/handlerPlSql.d.ts +6 -1
  47. package/types/handler/plsql/procedure.d.ts +3 -1
  48. package/types/handler/plsql/procedureNamed.d.ts +2 -5
  49. package/types/handler/plsql/procedureSanitize.d.ts +2 -5
  50. package/types/handler/plsql/procedureVariable.d.ts +1 -1
  51. package/types/handler/plsql/request.d.ts +3 -1
  52. package/types/handler/plsql/sendResponse.d.ts +1 -1
  53. package/types/server/server.d.ts +22 -0
  54. package/types/types.d.ts +18 -0
  55. package/types/util/cache.d.ts +69 -0
  56. package/types/util/jsonLogger.d.ts +45 -0
@@ -0,0 +1,315 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>web_plsql Admin Console</title>
7
+ <link rel="icon" type="image/svg+xml" href="favicon.svg" />
8
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
9
+ <link rel="stylesheet" href="style.css" />
10
+ </head>
11
+ <body class="dark">
12
+ <div class="app-container">
13
+ <aside class="sidebar">
14
+ <div class="logo">
15
+ <span class="material-symbols-rounded">database</span>
16
+ <div style="display: flex; flex-direction: column; line-height: 1">
17
+ <span>web_plsql</span>
18
+ <small id="sidebar-version" style="font-size: 0.65rem; opacity: 0.7; margin-top: 2px"></small>
19
+ </div>
20
+ </div>
21
+ <nav>
22
+ <button data-view="overview" class="active">
23
+ <span class="material-symbols-rounded">dashboard</span>
24
+ Overview
25
+ </button>
26
+ <button data-view="errors">
27
+ <span class="material-symbols-rounded">error</span>
28
+ Errors
29
+ </button>
30
+ <button data-view="access">
31
+ <span class="material-symbols-rounded">list_alt</span>
32
+ Access Log
33
+ </button>
34
+ <button data-view="pools">
35
+ <span class="material-symbols-rounded">database</span>
36
+ Pools
37
+ </button>
38
+ <button data-view="config">
39
+ <span class="material-symbols-rounded">settings</span>
40
+ Configuration
41
+ </button>
42
+ <button data-view="system">
43
+ <span class="material-symbols-rounded">terminal</span>
44
+ System
45
+ </button>
46
+ </nav>
47
+ <div class="status-indicator">
48
+ <span id="server-status-dot" class="dot"></span>
49
+ <span id="server-status-text">Checking...</span>
50
+ </div>
51
+ </aside>
52
+ <main class="content">
53
+ <header class="main-header">
54
+ <h1 id="view-title"><span class="material-symbols-rounded">dashboard</span>Overview</h1>
55
+ <div class="header-toolbar">
56
+ <div class="auto-refresh-group">
57
+ <label for="auto-refresh-toggle">Auto-refresh</label>
58
+ <input type="checkbox" id="auto-refresh-toggle" checked />
59
+ <select id="refresh-interval">
60
+ <option value="5000">5s</option>
61
+ <option value="10000">10s</option>
62
+ <option value="30000" selected>30s</option>
63
+ <option value="60000">60s</option>
64
+ </select>
65
+ <button
66
+ id="manual-refresh"
67
+ class="icon-btn icon-sm"
68
+ title="Refresh Now"
69
+ style="border: none; background: none; padding: 0; min-width: auto; min-height: auto; margin-left: 4px"
70
+ >
71
+ <span class="material-symbols-rounded" style="font-size: 20px">refresh</span>
72
+ </button>
73
+ </div>
74
+ <button id="theme-toggle-btn" class="icon-btn" title="Toggle Theme">
75
+ <span class="material-symbols-rounded">dark_mode</span>
76
+ </button>
77
+ </div>
78
+ </header>
79
+
80
+ <section id="view-overview" class="view">
81
+ <div class="metrics-grid">
82
+ <div class="card">
83
+ <div class="card-header">
84
+ <span class="material-symbols-rounded">schedule</span>
85
+ <h3>Uptime</h3>
86
+ </div>
87
+ <p id="uptime-val">0s</p>
88
+ <small id="start-time-val">-</small>
89
+ </div>
90
+ <div class="card">
91
+ <div class="card-header">
92
+ <span class="material-symbols-rounded">swap_vert</span>
93
+ <h3>Requests</h3>
94
+ </div>
95
+ <p id="req-count">0</p>
96
+ <div style="display: flex; gap: 8px">
97
+ <small id="req-per-sec">0 req/s</small>
98
+ <small id="avg-resp-time">Avg: 0ms</small>
99
+ </div>
100
+ </div>
101
+ <div class="card">
102
+ <div class="card-header">
103
+ <span class="material-symbols-rounded">report</span>
104
+ <h3>Errors</h3>
105
+ </div>
106
+ <p id="err-count">0</p>
107
+ </div>
108
+ <div class="card">
109
+ <div class="card-header">
110
+ <span class="material-symbols-rounded">cached</span>
111
+ <h3>Cache Hit Rate</h3>
112
+ </div>
113
+ <p id="cache-hit-rate-val">0%</p>
114
+ <small><span id="cache-hits-val">0</span> hits, <span id="cache-misses-val">0</span> misses</small>
115
+ </div>
116
+ </div>
117
+
118
+ <div class="card" style="grid-column: span 2">
119
+ <div class="card-header">
120
+ <span class="material-symbols-rounded">monitoring</span>
121
+ <h3>Traffic & Errors</h3>
122
+ <div class="header-toolbar ml-auto">
123
+ <select
124
+ id="chart-history-points"
125
+ class="btn-sm"
126
+ style="
127
+ background: var(--bg-hover);
128
+ border: 1px solid var(--border);
129
+ border-radius: 4px;
130
+ color: var(--text-main);
131
+ font-size: 0.75rem;
132
+ padding: 2px 4px;
133
+ "
134
+ >
135
+ <option value="30">Last 30 pts</option>
136
+ <option value="60">Last 60 pts</option>
137
+ <option value="120">Last 120 pts</option>
138
+ </select>
139
+ </div>
140
+ </div>
141
+ <div class="chart-container">
142
+ <canvas id="traffic-chart"></canvas>
143
+ </div>
144
+ </div>
145
+ </section>
146
+
147
+ <section id="view-pools" class="view" style="display: none">
148
+ <div id="pools-container" class="pools-grid"></div>
149
+ <div class="card">
150
+ <div class="card-header">
151
+ <span class="material-symbols-rounded">analytics</span>
152
+ <h3>Pool Usage History</h3>
153
+ </div>
154
+ <div class="chart-container">
155
+ <canvas id="pool-chart"></canvas>
156
+ </div>
157
+ </div>
158
+ </section>
159
+
160
+ <section id="view-errors" class="view" style="display: none">
161
+ <div class="table-container">
162
+ <table id="errors-table">
163
+ <thead>
164
+ <tr>
165
+ <th>Timestamp</th>
166
+ <th>Method</th>
167
+ <th>URL</th>
168
+ <th>Message</th>
169
+ </tr>
170
+ </thead>
171
+ <tbody></tbody>
172
+ </table>
173
+ </div>
174
+ </section>
175
+
176
+ <section id="view-access" class="view" style="display: none">
177
+ <div class="terminal-container">
178
+ <pre id="access-log-view" class="terminal"></pre>
179
+ </div>
180
+ </section>
181
+
182
+ <section id="view-config" class="view" style="display: none">
183
+ <div id="config-view"></div>
184
+ </section>
185
+
186
+ <section id="view-system" class="view" style="display: none">
187
+ <div class="metrics-grid mb-6">
188
+ <div class="card">
189
+ <div class="card-header">
190
+ <span class="material-symbols-rounded">info</span>
191
+ <h3>Server Information</h3>
192
+ </div>
193
+ <div class="text-sm text-main">
194
+ <div class="stat-row">
195
+ <span>Middleware Version</span>
196
+ <span id="middleware-version" class="font-mono text-accent font-bold">-</span>
197
+ </div>
198
+ <div class="stat-row">
199
+ <span>Node.js Version</span>
200
+ <span id="node-version" class="font-mono text-accent font-bold">-</span>
201
+ </div>
202
+ <div class="stat-row">
203
+ <span>Platform</span>
204
+ <span id="platform-info" class="text-accent font-bold">-</span>
205
+ </div>
206
+ </div>
207
+ </div>
208
+ <div class="card">
209
+ <div class="card-header">
210
+ <span class="material-symbols-rounded">schedule</span>
211
+ <h3>Runtime</h3>
212
+ </div>
213
+ <div class="text-sm text-main">
214
+ <div class="stat-row">
215
+ <span>Server Uptime</span>
216
+ <span id="system-uptime" class="text-accent font-bold">-</span>
217
+ </div>
218
+ <div class="stat-row">
219
+ <span>Start Time</span>
220
+ <span id="system-start-time" class="text-accent font-bold">-</span>
221
+ </div>
222
+ <div class="stat-row">
223
+ <span>Status</span>
224
+ <span id="system-status" class="status-dot">
225
+ <span class="dot running"></span>
226
+ <span id="system-status-text" class="text-accent font-bold">-</span>
227
+ </span>
228
+ </div>
229
+ <div class="stat-row">
230
+ <span>Auto-refresh</span>
231
+ <span id="system-refresh-status" class="text-accent font-bold">Active (30s)</span>
232
+ </div>
233
+ </div>
234
+ </div>
235
+ <div class="card">
236
+ <div class="card-header">
237
+ <span class="material-symbols-rounded">insights</span>
238
+ <h3>Activity</h3>
239
+ </div>
240
+ <div class="text-sm text-main">
241
+ <div class="stat-row">
242
+ <span>Total Requests</span>
243
+ <span id="total-requests" class="font-mono text-accent font-bold">-</span>
244
+ </div>
245
+ <div class="stat-row">
246
+ <span>Total Errors</span>
247
+ <span id="total-errors" class="font-mono text-danger font-bold">-</span>
248
+ </div>
249
+ <div class="stat-row">
250
+ <span>Active Pools</span>
251
+ <span id="active-pools" class="font-mono text-accent font-bold">-</span>
252
+ </div>
253
+ </div>
254
+ </div>
255
+ </div>
256
+
257
+ <div class="metrics-grid mb-6">
258
+ <div class="card">
259
+ <div class="card-header">
260
+ <span class="material-symbols-rounded">memory</span>
261
+ <h3>Memory Usage</h3>
262
+ </div>
263
+ <div class="text-sm text-main">
264
+ <div class="stat-row">
265
+ <span>Heap Used</span>
266
+ <span id="memory-heap-used" class="font-mono text-accent font-bold">-</span>
267
+ </div>
268
+ <div class="stat-row">
269
+ <span>Heap Total</span>
270
+ <span id="memory-heap-total" class="font-mono text-accent font-bold">-</span>
271
+ </div>
272
+ <div class="stat-row">
273
+ <span>RSS</span>
274
+ <span id="memory-rss" class="font-mono text-accent font-bold">-</span>
275
+ </div>
276
+ <div class="stat-row">
277
+ <span>External</span>
278
+ <span id="memory-external" class="font-mono text-accent font-bold">-</span>
279
+ </div>
280
+ </div>
281
+ </div>
282
+ <div class="card">
283
+ <div class="card-header">
284
+ <span class="material-symbols-rounded">speed</span>
285
+ <h3>CPU Usage</h3>
286
+ </div>
287
+ <div class="text-sm text-main">
288
+ <div class="stat-row">
289
+ <span>User</span>
290
+ <span id="cpu-user" class="font-mono text-accent font-bold">-</span>
291
+ </div>
292
+ <div class="stat-row">
293
+ <span>System</span>
294
+ <span id="cpu-system" class="font-mono text-accent font-bold">-</span>
295
+ </div>
296
+ </div>
297
+ </div>
298
+ </div>
299
+
300
+ <div class="card danger-zone">
301
+ <h3 class="danger-zone-title">Danger Zone</h3>
302
+ <p class="danger-zone-text">Careful! These actions affect the production server state.</p>
303
+ <div style="display: flex; gap: 12px; margin-top: 16px">
304
+ <button id="btn-clear-all-cache" class="btn btn-sm btn-warning">Clear All Caches</button>
305
+ <button id="btn-pause" class="btn btn-sm btn-warning">Pause Server</button>
306
+ <button id="btn-resume" class="btn btn-sm btn-success" style="display: none">Resume Server</button>
307
+ <button id="btn-stop" class="btn btn-sm btn-danger">Stop Server</button>
308
+ </div>
309
+ </div>
310
+ </section>
311
+ </main>
312
+ </div>
313
+ <script type="module" src="lib/chart.bundle.js"></script>
314
+ </body>
315
+ </html>
@@ -0,0 +1,95 @@
1
+ import {z} from 'zod';
2
+ import {
3
+ statusSchema,
4
+ cacheDataSchema,
5
+ errorLogSchema,
6
+ accessLogResponseSchema,
7
+ type StatusResponse,
8
+ type CacheDataResponse,
9
+ type ErrorLogResponse,
10
+ type AccessLogResponse,
11
+ } from './schemas.js';
12
+
13
+ /**
14
+ * Validates response data against a Zod schema.
15
+ *
16
+ * @param data - The data to validate.
17
+ * @param schema - The Zod schema to validate against.
18
+ * @param path - The API path for error messages.
19
+ * @returns The validated data.
20
+ */
21
+ function validate<T>(data: unknown, schema: z.ZodType<T>, path: string): T {
22
+ const result = schema.safeParse(data);
23
+ if (!result.success) {
24
+ throw new Error(`Validation failed for ${path}: ${result.error.message}`);
25
+ }
26
+ return result.data;
27
+ }
28
+
29
+ /**
30
+ * Typed API client with Zod validation.
31
+ */
32
+ export const typedApi = {
33
+ /**
34
+ * Get server status with validation.
35
+ *
36
+ * @returns The validated status response.
37
+ */
38
+ async getStatus(): Promise<StatusResponse> {
39
+ const res = await fetch('api/status');
40
+ if (!res.ok) throw new Error(`GET api/status failed: ${res.statusText}`);
41
+ const data: unknown = await res.json();
42
+ return validate(data, statusSchema, 'api/status');
43
+ },
44
+
45
+ /**
46
+ * Get cache data with validation.
47
+ *
48
+ * @returns The validated cache data response.
49
+ */
50
+ async getCache(): Promise<CacheDataResponse[]> {
51
+ const res = await fetch('api/cache');
52
+ if (!res.ok) throw new Error(`GET api/cache failed: ${res.statusText}`);
53
+ const data: unknown = await res.json();
54
+ return validate(data, z.array(cacheDataSchema), 'api/cache');
55
+ },
56
+
57
+ /**
58
+ * Get error logs with validation.
59
+ *
60
+ * @returns The validated error log response.
61
+ */
62
+ async getErrorLogs(): Promise<ErrorLogResponse[]> {
63
+ const res = await fetch('api/logs/error');
64
+ if (!res.ok) throw new Error(`GET api/logs/error failed: ${res.statusText}`);
65
+ const data: unknown = await res.json();
66
+ return validate(data, z.array(errorLogSchema), 'api/logs/error');
67
+ },
68
+
69
+ /**
70
+ * Get access logs with validation.
71
+ *
72
+ * @returns The validated access log response.
73
+ */
74
+ async getAccessLogs(): Promise<AccessLogResponse> {
75
+ const res = await fetch('api/logs/access');
76
+ if (!res.ok) throw new Error(`GET api/logs/access failed: ${res.statusText}`);
77
+ const data: unknown = await res.json();
78
+ return validate(data, accessLogResponseSchema, 'api/logs/access');
79
+ },
80
+
81
+ /**
82
+ * POST to an endpoint without response validation.
83
+ *
84
+ * @param path - The API path.
85
+ * @param body - The request body.
86
+ */
87
+ async post(path: string, body: object = {}): Promise<void> {
88
+ const res = await fetch(path, {
89
+ method: 'POST',
90
+ headers: {'Content-Type': 'application/json'},
91
+ body: JSON.stringify(body),
92
+ });
93
+ if (!res.ok) throw new Error(`POST ${path} failed: ${res.statusText}`);
94
+ },
95
+ };