web_plsql 1.0.0 → 1.3.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 (108) hide show
  1. package/README.md +108 -13
  2. package/dist/chunk-DQk6qfdC.mjs +18 -0
  3. package/dist/frontend/assets/main-0oxRTIkL.js +147 -0
  4. package/dist/frontend/assets/main-0oxRTIkL.js.br +0 -0
  5. package/dist/frontend/assets/main-0oxRTIkL.js.gz +0 -0
  6. package/dist/frontend/assets/main-TYgLak6O.css +1 -0
  7. package/dist/frontend/assets/main-TYgLak6O.css.br +0 -0
  8. package/dist/frontend/assets/main-TYgLak6O.css.gz +0 -0
  9. package/dist/frontend/index.html +516 -0
  10. package/dist/frontend/index.html.br +0 -0
  11. package/dist/frontend/index.html.gz +0 -0
  12. package/dist/index.d.mts +646 -0
  13. package/dist/index.mjs +3548 -0
  14. package/dist/index.mjs.map +1 -0
  15. package/dist/oracledb-mock-Dn8aHtd7.mjs +68 -0
  16. package/dist/oracledb-mock-Dn8aHtd7.mjs.map +1 -0
  17. package/examples/config-apex.js +1 -1
  18. package/examples/config-docker.js +1 -2
  19. package/package.json +45 -32
  20. package/examples/config-native.js +0 -68
  21. package/src/admin/client/charts.ts +0 -299
  22. package/src/admin/client/main.js +0 -3
  23. package/src/admin/client/tailwind.css +0 -41
  24. package/src/admin/index.html +0 -315
  25. package/src/admin/js/api.ts +0 -95
  26. package/src/admin/js/app.ts +0 -306
  27. package/src/admin/js/eslint.config.js +0 -74
  28. package/src/admin/js/schemas.ts +0 -153
  29. package/src/admin/js/templates/config.ts +0 -146
  30. package/src/admin/js/templates/errorRow.ts +0 -18
  31. package/src/admin/js/templates/index.ts +0 -3
  32. package/src/admin/js/templates/poolCard.ts +0 -61
  33. package/src/admin/js/tsconfig.json +0 -24
  34. package/src/admin/js/types.ts +0 -223
  35. package/src/admin/js/ui/theme.ts +0 -93
  36. package/src/admin/js/ui/views.ts +0 -164
  37. package/src/admin/js/util/format.ts +0 -27
  38. package/src/admin/lib/assets/main-zpdhQ1gD.css +0 -1
  39. package/src/admin/lib/chart.bundle.js +0 -139
  40. package/src/admin/style.css +0 -1321
  41. package/src/bin/load-test.js +0 -202
  42. package/src/handler/handlerAdmin.js +0 -198
  43. package/src/handler/handlerLogger.js +0 -21
  44. package/src/handler/handlerMetrics.js +0 -68
  45. package/src/handler/handlerUpload.js +0 -26
  46. package/src/handler/plsql/cgi.js +0 -131
  47. package/src/handler/plsql/errorPage.js +0 -117
  48. package/src/handler/plsql/handlerPlSql.js +0 -90
  49. package/src/handler/plsql/parsePage.js +0 -213
  50. package/src/handler/plsql/procedure.js +0 -356
  51. package/src/handler/plsql/procedureError.js +0 -40
  52. package/src/handler/plsql/procedureNamed.js +0 -268
  53. package/src/handler/plsql/procedureSanitize.js +0 -262
  54. package/src/handler/plsql/procedureVariable.js +0 -54
  55. package/src/handler/plsql/request.js +0 -124
  56. package/src/handler/plsql/requestError.js +0 -23
  57. package/src/handler/plsql/sendResponse.js +0 -136
  58. package/src/handler/plsql/stream.js +0 -28
  59. package/src/handler/plsql/upload.js +0 -109
  60. package/src/index.js +0 -20
  61. package/src/server/config.js +0 -57
  62. package/src/server/connections.js +0 -40
  63. package/src/server/server.js +0 -289
  64. package/src/types.js +0 -150
  65. package/src/util/cache.js +0 -123
  66. package/src/util/errorToString.js +0 -24
  67. package/src/util/file.js +0 -91
  68. package/src/util/html.js +0 -53
  69. package/src/util/jsonLogger.js +0 -47
  70. package/src/util/oracle.js +0 -80
  71. package/src/util/shutdown.js +0 -44
  72. package/src/util/trace.js +0 -325
  73. package/src/util/type.js +0 -6
  74. package/src/util/util.js +0 -66
  75. package/src/version.js +0 -5
  76. package/types/handler/handlerAdmin.d.ts +0 -9
  77. package/types/handler/handlerLogger.d.ts +0 -2
  78. package/types/handler/handlerMetrics.d.ts +0 -25
  79. package/types/handler/handlerUpload.d.ts +0 -7
  80. package/types/handler/plsql/cgi.d.ts +0 -4
  81. package/types/handler/plsql/errorPage.d.ts +0 -11
  82. package/types/handler/plsql/handlerPlSql.d.ts +0 -13
  83. package/types/handler/plsql/parsePage.d.ts +0 -3
  84. package/types/handler/plsql/procedure.d.ts +0 -12
  85. package/types/handler/plsql/procedureError.d.ts +0 -23
  86. package/types/handler/plsql/procedureNamed.d.ts +0 -14
  87. package/types/handler/plsql/procedureSanitize.d.ts +0 -11
  88. package/types/handler/plsql/procedureVariable.d.ts +0 -10
  89. package/types/handler/plsql/request.d.ts +0 -9
  90. package/types/handler/plsql/requestError.d.ts +0 -8
  91. package/types/handler/plsql/sendResponse.d.ts +0 -6
  92. package/types/handler/plsql/upload.d.ts +0 -5
  93. package/types/index.d.ts +0 -11
  94. package/types/server/config.d.ts +0 -2
  95. package/types/server/server.d.ts +0 -74
  96. package/types/types.d.ts +0 -328
  97. package/types/util/cache.d.ts +0 -69
  98. package/types/util/errorToString.d.ts +0 -1
  99. package/types/util/file.d.ts +0 -6
  100. package/types/util/html.d.ts +0 -3
  101. package/types/util/jsonLogger.d.ts +0 -45
  102. package/types/util/oracle.d.ts +0 -5
  103. package/types/util/shutdown.d.ts +0 -2
  104. package/types/util/trace.d.ts +0 -22
  105. package/types/util/type.d.ts +0 -1
  106. package/types/util/util.d.ts +0 -3
  107. package/types/version.d.ts +0 -1
  108. /package/{src/admin/favicon.svg → dist/frontend/assets/favicon-mQAM4tVu.svg} +0 -0
@@ -1,306 +0,0 @@
1
- import {typedApi} from './api.js';
2
- import {initCharts, updateCharts} from '../client/charts.js';
3
- import {formatDuration, formatDateTime} from './util/format.js';
4
- import {initTheme} from './ui/theme.js';
5
- import {refreshErrors, refreshAccess, refreshConfig, refreshPools, refreshSystem} from './ui/views.js';
6
- import type {State} from './types.js';
7
-
8
- /**
9
- * View icons mapping.
10
- */
11
- const viewIcons: Record<string, string> = {
12
- overview: 'dashboard',
13
- errors: 'error',
14
- access: 'list_alt',
15
- cache: 'cached',
16
- pools: 'database',
17
- config: 'settings',
18
- system: 'terminal',
19
- };
20
-
21
- /**
22
- * Application state.
23
- */
24
- const state: State = {
25
- currentView: 'overview',
26
- status: {},
27
- maxHistoryPoints: 30,
28
- lastRequestCount: 0,
29
- lastErrorCount: 0,
30
- lastUpdateTime: Date.now(),
31
- refreshTimer: null,
32
- history: {
33
- labels: [],
34
- requests: [],
35
- avgResponseTimes: [],
36
- poolUsage: {},
37
- },
38
- charts: {},
39
- };
40
-
41
- /**
42
- * Clear cache for a pool.
43
- *
44
- * @param poolName - The pool name.
45
- */
46
- export async function clearCache(poolName: string): Promise<void> {
47
- await typedApi.post('api/cache/clear', {poolName});
48
- await updateStatus();
49
- }
50
-
51
- /**
52
- * Update server status.
53
- */
54
- async function updateStatus(): Promise<void> {
55
- const now = Date.now();
56
- const newStatus = await typedApi.getStatus();
57
- state.status = newStatus;
58
-
59
- if (!newStatus.metrics || !newStatus.pools) return;
60
-
61
- const deltaSec = (now - state.lastUpdateTime) / 1000;
62
- const reqCountDelta = newStatus.metrics.requestCount - state.lastRequestCount;
63
-
64
- const reqPerSec = deltaSec > 0 ? reqCountDelta / deltaSec : 0;
65
- const avgResponseTime = newStatus.metrics.avgResponseTime;
66
-
67
- state.lastRequestCount = newStatus.metrics.requestCount;
68
- state.lastErrorCount = newStatus.metrics.errorCount;
69
- state.lastUpdateTime = now;
70
-
71
- const timeLabel = new Date().toLocaleTimeString();
72
- updateCharts(state, timeLabel, reqPerSec, avgResponseTime, newStatus.pools);
73
-
74
- const sidebarVersion = document.getElementById('sidebar-version');
75
- if (sidebarVersion) sidebarVersion.textContent = `v${newStatus.version}`;
76
-
77
- const uptimeVal = document.getElementById('uptime-val');
78
- if (uptimeVal) uptimeVal.textContent = formatDuration(newStatus.uptime);
79
-
80
- const startTimeVal = document.getElementById('start-time-val');
81
- if (startTimeVal) startTimeVal.textContent = `Started: ${formatDateTime(newStatus.startTime)}`;
82
-
83
- const reqCount = document.getElementById('req-count');
84
- if (reqCount) reqCount.textContent = newStatus.metrics.requestCount.toLocaleString();
85
-
86
- const reqPerSecVal = document.getElementById('req-per-sec');
87
- if (reqPerSecVal) reqPerSecVal.textContent = `${reqPerSec.toFixed(2)} req/s`;
88
-
89
- const avgRespTimeVal = document.getElementById('avg-resp-time');
90
- if (avgRespTimeVal) avgRespTimeVal.textContent = `Avg: ${avgResponseTime.toFixed(1)}ms`;
91
-
92
- const errCount = document.getElementById('err-count');
93
- if (errCount) errCount.textContent = newStatus.metrics.errorCount.toLocaleString();
94
-
95
- // Update Cache Overview
96
- const caches = await typedApi.getCache();
97
- let totalHits = 0;
98
- let totalMisses = 0;
99
- caches.forEach((c) => {
100
- totalHits += c.procedureNameCache.stats.hits + c.argumentCache.stats.hits;
101
- totalMisses += c.procedureNameCache.stats.misses + c.argumentCache.stats.misses;
102
- });
103
- const totalRequests = totalHits + totalMisses;
104
- const hitRate = totalRequests > 0 ? Math.round((totalHits / totalRequests) * 100) : 0;
105
-
106
- const cacheHitRateVal = document.getElementById('cache-hit-rate-val');
107
- if (cacheHitRateVal) {
108
- cacheHitRateVal.textContent = `${hitRate}%`;
109
- cacheHitRateVal.style.color = hitRate > 80 ? 'var(--success)' : hitRate > 50 ? 'var(--warning)' : 'var(--danger)';
110
- }
111
- const cacheHitsVal = document.getElementById('cache-hits-val');
112
- if (cacheHitsVal) cacheHitsVal.textContent = totalHits.toLocaleString();
113
- const cacheMissesVal = document.getElementById('cache-misses-val');
114
- if (cacheMissesVal) cacheMissesVal.textContent = totalMisses.toLocaleString();
115
-
116
- const dot = document.getElementById('server-status-dot');
117
- const text = document.getElementById('server-status-text');
118
- if (dot) dot.className = 'dot ' + newStatus.status;
119
- if (text) text.textContent = newStatus.status.toUpperCase();
120
-
121
- const btnPause = document.getElementById('btn-pause');
122
- if (btnPause) btnPause.style.display = newStatus.status === 'running' ? 'flex' : 'none';
123
-
124
- const btnResume = document.getElementById('btn-resume');
125
- if (btnResume) btnResume.style.display = newStatus.status === 'paused' ? 'flex' : 'none';
126
-
127
- if (state.currentView === 'errors') await refreshErrors();
128
- if (state.currentView === 'access') await refreshAccess();
129
- if (state.currentView === 'pools') refreshPools(newStatus);
130
- if (state.currentView === 'config') refreshConfig(state);
131
- if (state.currentView === 'system') refreshSystem(newStatus);
132
- }
133
-
134
- /**
135
- * Start the refresh timer.
136
- */
137
- function startRefreshTimer(): void {
138
- stopRefreshTimer();
139
- const checkbox = document.getElementById('auto-refresh-toggle') as HTMLInputElement | null;
140
- const intervalSelect = document.getElementById('refresh-interval') as HTMLSelectElement | null;
141
- if (checkbox?.checked && intervalSelect) {
142
- const interval = parseInt(intervalSelect.value);
143
- state.refreshTimer = setInterval(() => {
144
- void updateStatus();
145
- }, interval);
146
- }
147
- }
148
-
149
- /**
150
- * Stop the refresh timer.
151
- */
152
- function stopRefreshTimer(): void {
153
- if (state.refreshTimer) {
154
- clearInterval(state.refreshTimer);
155
- state.refreshTimer = null;
156
- }
157
- }
158
-
159
- /**
160
- * Update history points labels to be time-based.
161
- */
162
- function updateHistoryLabels(): void {
163
- const intervalSelect = document.getElementById('refresh-interval') as HTMLSelectElement | null;
164
- const historySelect = document.getElementById('chart-history-points') as HTMLSelectElement | null;
165
- if (!intervalSelect || !historySelect) return;
166
-
167
- const intervalMs = parseInt(intervalSelect.value);
168
- const points = [30, 60, 120];
169
-
170
- points.forEach((pts, idx) => {
171
- const option = historySelect.options[idx];
172
- if (!option) return;
173
-
174
- const totalSeconds = (pts * intervalMs) / 1000;
175
- let timeStr = '';
176
- if (totalSeconds < 60) {
177
- timeStr = `${totalSeconds}s`;
178
- } else if (totalSeconds < 3600) {
179
- timeStr = `${Math.round(totalSeconds / 60)} min`;
180
- } else {
181
- const hours = totalSeconds / 3600;
182
- timeStr = hours === Math.round(hours) ? `${hours} hours` : `${hours.toFixed(1)} hours`;
183
- }
184
- option.textContent = `Last ${timeStr}`;
185
- });
186
- }
187
-
188
- // Navigation
189
- document.querySelectorAll('nav button').forEach((btnEl) => {
190
- const btn = btnEl as HTMLButtonElement;
191
- btn.onclick = async () => {
192
- const view = btn.dataset.view;
193
- if (!view) return;
194
- state.currentView = view;
195
- document.querySelectorAll('.view').forEach((vEl) => {
196
- const v = vEl as HTMLElement;
197
- v.style.display = 'none';
198
- });
199
- const viewEl = document.getElementById('view-' + view);
200
- if (viewEl) viewEl.style.display = 'block';
201
- document.querySelectorAll('nav button').forEach((b) => b.classList.remove('active'));
202
- btn.classList.add('active');
203
- const viewTitleEl = document.getElementById('view-title');
204
- if (viewTitleEl) {
205
- // Get text content excluding the icon span
206
- const textNodes = Array.from(btn.childNodes).filter((node) => node.nodeType === Node.TEXT_NODE);
207
- const btnText = textNodes.map((node) => node.textContent?.trim()).join('');
208
- const icon = viewIcons[view] || 'chevron_right';
209
- viewTitleEl.innerHTML = `<span class="material-symbols-rounded">${icon}</span>${btnText}`;
210
- }
211
-
212
- if (view === 'errors') await refreshErrors();
213
- if (view === 'access') await refreshAccess();
214
- if (view === 'pools') refreshPools(state.status);
215
- if (view === 'config') refreshConfig(state);
216
- if (view === 'system') refreshSystem(state.status);
217
- };
218
- });
219
-
220
- const autoRefreshToggle = document.getElementById('auto-refresh-toggle') as HTMLInputElement | null;
221
- if (autoRefreshToggle) {
222
- autoRefreshToggle.onchange = (e: Event) => {
223
- const target = e.target as HTMLInputElement;
224
- if (target.checked) startRefreshTimer();
225
- else stopRefreshTimer();
226
- };
227
- }
228
-
229
- const refreshIntervalSelect = document.getElementById('refresh-interval') as HTMLSelectElement | null;
230
- if (refreshIntervalSelect) {
231
- refreshIntervalSelect.onchange = () => {
232
- startRefreshTimer();
233
- updateHistoryLabels();
234
- refreshSystem(state.status);
235
- };
236
- }
237
-
238
- const chartHistorySelect = document.getElementById('chart-history-points') as HTMLSelectElement | null;
239
- if (chartHistorySelect) {
240
- chartHistorySelect.onchange = () => {
241
- state.maxHistoryPoints = parseInt(chartHistorySelect.value);
242
- // Trim history if needed
243
- while (state.history.labels.length > state.maxHistoryPoints) {
244
- state.history.labels.shift();
245
- state.history.requests.shift();
246
- state.history.avgResponseTimes.shift();
247
- Object.values(state.history.poolUsage).forEach((u) => u.shift());
248
- }
249
- void updateStatus();
250
- };
251
- }
252
-
253
- const manualRefreshBtn = document.getElementById('manual-refresh');
254
- if (manualRefreshBtn) {
255
- manualRefreshBtn.onclick = () => {
256
- void updateStatus();
257
- };
258
- }
259
-
260
- // Server controls
261
- const btnPause = document.getElementById('btn-pause');
262
- if (btnPause) {
263
- btnPause.onclick = () => {
264
- void typedApi.post('api/server/pause').then(() => {
265
- void updateStatus();
266
- });
267
- };
268
- }
269
-
270
- const btnResume = document.getElementById('btn-resume');
271
- if (btnResume) {
272
- btnResume.onclick = () => {
273
- void typedApi.post('api/server/resume').then(() => {
274
- void updateStatus();
275
- });
276
- };
277
- }
278
-
279
- const btnStop = document.getElementById('btn-stop');
280
- if (btnStop) {
281
- btnStop.onclick = () => {
282
- if (confirm('Stop the server?')) {
283
- void typedApi.post('api/server/stop');
284
- }
285
- };
286
- }
287
-
288
- const btnClearAllCache = document.getElementById('btn-clear-all-cache');
289
- if (btnClearAllCache) {
290
- btnClearAllCache.onclick = async () => {
291
- if (confirm('Are you sure you want to clear all caches in all pools? This action cannot be undone.')) {
292
- await typedApi.post('api/cache/clear', {});
293
- await updateStatus();
294
- }
295
- };
296
- }
297
-
298
- // Initialize
299
- initTheme(state);
300
- initCharts(state);
301
- updateHistoryLabels();
302
- void updateStatus()
303
- .then(() => {
304
- startRefreshTimer();
305
- })
306
- .catch(console.error);
@@ -1,74 +0,0 @@
1
- // @ts-check
2
-
3
- import eslint from '@eslint/js';
4
- import {defineConfig} from 'eslint/config';
5
- import tseslint from 'typescript-eslint';
6
- import globals from 'globals';
7
-
8
- export default defineConfig([
9
- {
10
- ignores: ['**/.*', 'lib/**', 'node_modules/**', '**/lib/**'],
11
- },
12
-
13
- {
14
- linterOptions: {
15
- reportUnusedDisableDirectives: 'error',
16
- reportUnusedInlineConfigs: 'error',
17
- },
18
- },
19
-
20
- eslint.configs.recommended,
21
- ...tseslint.configs.strictTypeChecked,
22
- ...tseslint.configs.stylisticTypeChecked,
23
-
24
- {
25
- languageOptions: {
26
- globals: {
27
- ...globals.browser,
28
- },
29
- parserOptions: {
30
- projectService: true,
31
- tsconfigRootDir: import.meta.dirname,
32
- },
33
- },
34
- rules: {
35
- // Allow empty functions for chart update stubs
36
- '@typescript-eslint/no-empty-function': 'off',
37
-
38
- // Disable JSDoc requirements in TypeScript files
39
- 'jsdoc/require-param': 'off',
40
- 'jsdoc/require-param-type': 'off',
41
- 'jsdoc/require-returns-type': 'off',
42
- 'jsdoc/require-param-description': 'off',
43
- 'jsdoc/require-property-description': 'off',
44
- 'jsdoc/require-returns-description': 'off',
45
-
46
- // Allow toString() for logging/debugging
47
- '@typescript-eslint/no-base-to-string': 'off',
48
-
49
- // Relaxed rules for template literals
50
- '@typescript-eslint/restrict-template-expressions': 'off',
51
-
52
- // Consistent type definitions
53
- '@typescript-eslint/consistent-type-definitions': 'off',
54
-
55
- // Unused vars
56
- '@typescript-eslint/no-unused-vars': [
57
- 'warn',
58
- {
59
- caughtErrors: 'none',
60
- argsIgnorePattern: '^_',
61
- },
62
- ],
63
-
64
- // Deprecation warnings
65
- '@typescript-eslint/no-deprecated': 'warn',
66
-
67
- // Void expression
68
- '@typescript-eslint/no-confusing-void-expression': 'off',
69
-
70
- // Unnecessary conditions
71
- '@typescript-eslint/no-unnecessary-condition': 'off',
72
- },
73
- },
74
- ]);
@@ -1,153 +0,0 @@
1
- import {z} from 'zod';
2
-
3
- /**
4
- * Cache statistics schema.
5
- */
6
- export const cacheStatsSchema = z.object({
7
- hits: z.number(),
8
- misses: z.number(),
9
- });
10
-
11
- /**
12
- * Cache information schema.
13
- */
14
- export const cacheInfoSchema = z.object({
15
- size: z.number(),
16
- stats: cacheStatsSchema,
17
- });
18
-
19
- /**
20
- * Cache data schema for a connection pool.
21
- */
22
- export const cacheDataSchema = z.object({
23
- poolName: z.string(),
24
- procedureNameCache: cacheInfoSchema,
25
- argumentCache: cacheInfoSchema,
26
- });
27
-
28
- /**
29
- * Pool statistics schema.
30
- */
31
- export const poolStatsSchema = z.object({
32
- totalRequests: z.number(),
33
- totalTimeouts: z.number(),
34
- totalRequestsEnqueued: z.number().optional(),
35
- totalRequestsDequeued: z.number().optional(),
36
- totalRequestsFailed: z.number().optional(),
37
- });
38
-
39
- /**
40
- * Pool information schema.
41
- */
42
- export const poolInfoSchema = z.object({
43
- name: z.string(),
44
- connectionsInUse: z.number(),
45
- connectionsOpen: z.number(),
46
- stats: poolStatsSchema.nullable(),
47
- });
48
-
49
- /**
50
- * Server metrics schema.
51
- */
52
- export const metricsSchema = z.object({
53
- requestCount: z.number(),
54
- errorCount: z.number(),
55
- avgResponseTime: z.number(),
56
- });
57
-
58
- /**
59
- * Route configuration schema.
60
- */
61
- export const routeConfigSchema = z.object({
62
- route: z.string(),
63
- // PL/SQL specific
64
- user: z.string().optional(),
65
- password: z.string().optional(),
66
- connectString: z.string().optional(),
67
- defaultPage: z.string().optional(),
68
- pathAlias: z.string().optional(),
69
- pathAliasProcedure: z.string().optional(),
70
- documentTable: z.string().optional(),
71
- exclusionList: z.array(z.string()).optional(),
72
- requestValidationFunction: z.string().optional(),
73
- transactionMode: z.unknown().optional(),
74
- errorStyle: z.string().optional(),
75
- // Static specific
76
- directoryPath: z.string().optional(),
77
- });
78
-
79
- /**
80
- * Server configuration schema.
81
- */
82
- export const serverConfigSchema = z.object({
83
- port: z.number(),
84
- adminRoute: z.string().optional(),
85
- adminUser: z.string().optional(),
86
- adminPassword: z.string().optional(),
87
- loggerFilename: z.string(),
88
- uploadFileSizeLimit: z.number().optional(),
89
- routePlSql: z.array(routeConfigSchema),
90
- routeStatic: z.array(routeConfigSchema),
91
- });
92
-
93
- /**
94
- * System information schema.
95
- */
96
- export const systemInfoSchema = z.object({
97
- nodeVersion: z.string(),
98
- platform: z.string(),
99
- arch: z.string(),
100
- memory: z.object({
101
- rss: z.number(),
102
- heapTotal: z.number(),
103
- heapUsed: z.number(),
104
- external: z.number(),
105
- }),
106
- cpu: z.object({
107
- user: z.number(),
108
- system: z.number(),
109
- }),
110
- });
111
-
112
- /**
113
- * Server status response schema.
114
- */
115
- export const statusSchema = z.object({
116
- version: z.string(),
117
- status: z.enum(['running', 'paused', 'stopped']),
118
- uptime: z.number(),
119
- startTime: z.string(),
120
- metrics: metricsSchema,
121
- pools: z.array(poolInfoSchema),
122
- system: systemInfoSchema,
123
- config: serverConfigSchema.partial(),
124
- });
125
-
126
- /**
127
- * Error log entry schema.
128
- */
129
- export const errorLogSchema = z.object({
130
- timestamp: z.string(),
131
- req: z
132
- .object({
133
- method: z.string().optional(),
134
- url: z.string().optional(),
135
- })
136
- .optional(),
137
- message: z.string(),
138
- details: z
139
- .object({
140
- fullMessage: z.string().optional(),
141
- })
142
- .optional(),
143
- });
144
-
145
- export const accessLogResponseSchema = z.union([z.array(z.string()), z.object({message: z.string()})]);
146
-
147
- /**
148
- * API response type helpers.
149
- */
150
- export type StatusResponse = z.infer<typeof statusSchema>;
151
- export type CacheDataResponse = z.infer<typeof cacheDataSchema>;
152
- export type ErrorLogResponse = z.infer<typeof errorLogSchema>;
153
- export type AccessLogResponse = z.infer<typeof accessLogResponseSchema>;
@@ -1,146 +0,0 @@
1
- import type {ServerConfig, RouteConfig} from '../types.js';
2
-
3
- /**
4
- * Render a stat row using the standardized format.
5
- * @param label - The field label.
6
- * @param value - The value to display.
7
- * @param isMono - Whether to use monospaced font.
8
- * @param colorClass - The CSS class for coloring the value.
9
- * @returns HTML string.
10
- */
11
- function renderStatRow(label: string, value: string | number, isMono = true, colorClass = 'text-accent'): string {
12
- const valClass = `${isMono ? 'font-mono' : ''} ${colorClass} font-bold break-all`.trim();
13
- return `
14
- <div class="stat-row text-sm">
15
- <span>${label}</span>
16
- <span class="${valClass}">${value}</span>
17
- </div>
18
- `;
19
- }
20
-
21
- /**
22
- * Render a configuration section header.
23
- * @param title - The section title.
24
- * @param icon - The Material icon name.
25
- * @returns HTML string.
26
- */
27
- function renderSectionTitle(title: string, icon: string): string {
28
- return `
29
- <div class="card-header mt-6 mb-2">
30
- <span class="material-symbols-rounded icon-sm">${icon}</span>
31
- <h3 class="text-xs uppercase tracking-wider font-bold text-accent">${title}</h3>
32
- </div>
33
- `;
34
- }
35
-
36
- /**
37
- * Render a PL/SQL route card.
38
- * @param r - The route configuration.
39
- * @param index - The index of the route.
40
- * @returns HTML string.
41
- */
42
- function renderPlSqlRoute(r: RouteConfig, index: number): string {
43
- const exclusionList = r.exclusionList?.length ? r.exclusionList.join(', ') : '(None)';
44
- const transactionMode = typeof r.transactionMode === 'string' ? r.transactionMode : r.transactionMode ? 'Custom' : 'commit';
45
-
46
- return `
47
- <div class="card mb-6">
48
- <div class="flex justify-between items-center mb-4">
49
- <div class="flex items-center gap-3">
50
- <span class="badge badge-info">PL/SQL Route #${index + 1}</span>
51
- <code class="text-lg font-bold text-bright">${r.route}</code>
52
- </div>
53
- </div>
54
-
55
- <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
56
- <div>
57
- <h4 class="text-xs font-bold text-main uppercase mb-3 opacity-75">Database Connection</h4>
58
- ${renderStatRow('Oracle User', r.user ?? '(Not set)')}
59
- ${renderStatRow('Connect String', r.connectString ?? '(Not set)')}
60
- ${renderStatRow('Password', r.password ?? '(None)')}
61
- </div>
62
- <div>
63
- <h4 class="text-xs font-bold text-main uppercase mb-3 opacity-75">Request Handler</h4>
64
- ${renderStatRow('Default Page', r.defaultPage ?? 'index')}
65
- ${renderStatRow('Document Table', r.documentTable ?? '(None)')}
66
- ${renderStatRow('Error Style', r.errorStyle ?? 'table')}
67
- ${renderStatRow('Transaction Mode', transactionMode)}
68
- </div>
69
- </div>
70
-
71
- <div class="mt-4 pt-4 border-t">
72
- <h4 class="text-xs font-bold text-main uppercase mb-3 opacity-75">Path & Validation</h4>
73
- ${renderStatRow('Path Alias', r.pathAlias ?? '(None)')}
74
- ${renderStatRow('Path Alias Proc', r.pathAliasProcedure ?? '(None)')}
75
- ${renderStatRow('Exclusion List', exclusionList)}
76
- ${renderStatRow('Validation Func', r.requestValidationFunction ?? '(None)')}
77
- </div>
78
- </div>
79
- `;
80
- }
81
-
82
- /**
83
- * Render a Static route card.
84
- * @param r - The route configuration.
85
- * @param index - The index of the route.
86
- * @returns HTML string.
87
- */
88
- function renderStaticRoute(r: RouteConfig, index: number): string {
89
- return `
90
- <div class="card mb-4">
91
- <div class="flex items-center gap-3 mb-4">
92
- <span class="badge badge-success">Static Route #${index + 1}</span>
93
- <code class="text-lg font-bold text-bright">${r.route}</code>
94
- </div>
95
- ${renderStatRow('Directory Path', r.directoryPath ?? '(Not set)')}
96
- </div>
97
- `;
98
- }
99
-
100
- /**
101
- * Render the full server configuration.
102
- * @param config - The server configuration.
103
- * @returns HTML string.
104
- */
105
- export function renderConfig(config: Partial<ServerConfig>): string {
106
- let html = '<div class="config-view-content">';
107
-
108
- // Global Server Section
109
- html += '<div class="card mb-8">';
110
- html += '<div class="card-header"><span class="material-symbols-rounded">dns</span><h3>Server Settings</h3></div>';
111
- if (typeof config.port === 'number') {
112
- html += renderStatRow('Port', config.port);
113
- }
114
- html += renderStatRow('Admin Route', config.adminRoute ?? '/admin');
115
- html += renderStatRow('Admin User', config.adminUser ?? '(Not authenticated)');
116
- html += renderStatRow('Admin Password', config.adminPassword ?? '(None)');
117
- html += renderStatRow('Logger Filename', config.loggerFilename ?? '(Logging disabled)');
118
-
119
- if (typeof config.uploadFileSizeLimit === 'number') {
120
- const limit = config.uploadFileSizeLimit;
121
- const mb = (limit / (1024 * 1024)).toFixed(2);
122
- html += renderStatRow('Upload Size Limit', `${mb} MB (${limit.toLocaleString()} bytes)`);
123
- } else {
124
- html += renderStatRow('Upload Size Limit', 'No limit');
125
- }
126
- html += '</div>';
127
-
128
- // PL/SQL Routes
129
- html += renderSectionTitle('PL/SQL Routes', 'database');
130
- if (config.routePlSql?.length) {
131
- html += config.routePlSql.map((r, i) => renderPlSqlRoute(r, i)).join('');
132
- } else {
133
- html += '<div class="empty-state">No PL/SQL routes configured</div>';
134
- }
135
-
136
- // Static Routes
137
- html += renderSectionTitle('Static Routes', 'folder_shared');
138
- if (config.routeStatic?.length) {
139
- html += config.routeStatic.map((r, i) => renderStaticRoute(r, i)).join('');
140
- } else {
141
- html += '<div class="empty-state">No static routes configured</div>';
142
- }
143
-
144
- html += '</div>';
145
- return html;
146
- }
@@ -1,18 +0,0 @@
1
- import type {ErrorLogResponse} from '../schemas.js';
2
-
3
- /**
4
- * Error log row template.
5
- *
6
- * @param l - The error log entry.
7
- * @returns HTML string for the error row.
8
- */
9
- export function errorRow(l: ErrorLogResponse): string {
10
- return `
11
- <tr>
12
- <td>${new Date(l.timestamp).toLocaleString()}</td>
13
- <td><code>${l.req?.method ?? '-'}</code></td>
14
- <td>${l.req?.url ?? '-'}</td>
15
- <td title="${l.details?.fullMessage ?? ''}">${l.message}</td>
16
- </tr>
17
- `;
18
- }
@@ -1,3 +0,0 @@
1
- export {errorRow} from './errorRow.js';
2
- export {poolCard} from './poolCard.js';
3
- export {renderConfig} from './config.js';