web_plsql 1.2.0 → 1.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.
Files changed (119) hide show
  1. package/README.md +103 -8
  2. package/dist/chunk-DQk6qfdC.mjs +18 -0
  3. package/dist/frontend/assets/main-C8GKnEhz.js +147 -0
  4. package/dist/frontend/assets/main-C8GKnEhz.js.br +0 -0
  5. package/dist/frontend/assets/main-C8GKnEhz.js.gz +0 -0
  6. package/dist/frontend/assets/main-bU4x_yEW.css +1 -0
  7. package/dist/frontend/assets/main-bU4x_yEW.css.br +0 -0
  8. package/dist/frontend/assets/main-bU4x_yEW.css.gz +0 -0
  9. package/{src/admin → dist/frontend}/index.html +12 -25
  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 +3559 -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 +48 -32
  20. package/examples/config-native.js +0 -68
  21. package/src/admin/client/charts.ts +0 -598
  22. package/src/admin/client/main.js +0 -3
  23. package/src/admin/client/tailwind.css +0 -41
  24. package/src/admin/js/api.ts +0 -170
  25. package/src/admin/js/app.ts +0 -614
  26. package/src/admin/js/eslint.config.js +0 -74
  27. package/src/admin/js/schemas.ts +0 -215
  28. package/src/admin/js/templates/config.ts +0 -147
  29. package/src/admin/js/templates/errorRow.ts +0 -21
  30. package/src/admin/js/templates/index.ts +0 -4
  31. package/src/admin/js/templates/poolCard.ts +0 -61
  32. package/src/admin/js/templates/traceRow.ts +0 -24
  33. package/src/admin/js/tsconfig.json +0 -24
  34. package/src/admin/js/types.ts +0 -336
  35. package/src/admin/js/ui/components.ts +0 -44
  36. package/src/admin/js/ui/table.ts +0 -173
  37. package/src/admin/js/ui/theme.ts +0 -93
  38. package/src/admin/js/ui/views.ts +0 -427
  39. package/src/admin/js/util/format.ts +0 -46
  40. package/src/admin/js/util/metrics.ts +0 -24
  41. package/src/admin/lib/chart.bundle.css +0 -1
  42. package/src/admin/lib/chart.bundle.js +0 -146
  43. package/src/admin/style.css +0 -1437
  44. package/src/bin/load-test.js +0 -202
  45. package/src/handler/handlerAdmin.js +0 -293
  46. package/src/handler/handlerLogger.js +0 -21
  47. package/src/handler/handlerUpload.js +0 -26
  48. package/src/handler/plsql/cgi.js +0 -131
  49. package/src/handler/plsql/errorPage.js +0 -113
  50. package/src/handler/plsql/handlerPlSql.js +0 -90
  51. package/src/handler/plsql/owaPageStream.js +0 -93
  52. package/src/handler/plsql/parsePage.js +0 -213
  53. package/src/handler/plsql/procedure.js +0 -426
  54. package/src/handler/plsql/procedureError.js +0 -40
  55. package/src/handler/plsql/procedureNamed.js +0 -282
  56. package/src/handler/plsql/procedureSanitize.js +0 -286
  57. package/src/handler/plsql/procedureVariable.js +0 -56
  58. package/src/handler/plsql/request.js +0 -124
  59. package/src/handler/plsql/requestError.js +0 -23
  60. package/src/handler/plsql/sendResponse.js +0 -174
  61. package/src/handler/plsql/stream.js +0 -28
  62. package/src/handler/plsql/upload.js +0 -109
  63. package/src/index.js +0 -19
  64. package/src/server/adminContext.js +0 -23
  65. package/src/server/config.js +0 -57
  66. package/src/server/connections.js +0 -40
  67. package/src/server/server.js +0 -304
  68. package/src/types.js +0 -150
  69. package/src/util/cache.js +0 -123
  70. package/src/util/errorToString.js +0 -24
  71. package/src/util/file.js +0 -91
  72. package/src/util/html.js +0 -53
  73. package/src/util/jsonLogger.js +0 -47
  74. package/src/util/oracle.js +0 -80
  75. package/src/util/shutdown.js +0 -44
  76. package/src/util/statsManager.js +0 -368
  77. package/src/util/trace.js +0 -326
  78. package/src/util/traceManager.js +0 -68
  79. package/src/util/type.js +0 -6
  80. package/src/util/util.js +0 -66
  81. package/src/version.js +0 -5
  82. package/types/admin/js/schemas.d.ts +0 -384
  83. package/types/admin/js/types.d.ts +0 -312
  84. package/types/handler/handlerAdmin.d.ts +0 -79
  85. package/types/handler/handlerLogger.d.ts +0 -2
  86. package/types/handler/handlerUpload.d.ts +0 -7
  87. package/types/handler/plsql/cgi.d.ts +0 -4
  88. package/types/handler/plsql/errorPage.d.ts +0 -11
  89. package/types/handler/plsql/handlerPlSql.d.ts +0 -13
  90. package/types/handler/plsql/owaPageStream.d.ts +0 -28
  91. package/types/handler/plsql/parsePage.d.ts +0 -3
  92. package/types/handler/plsql/procedure.d.ts +0 -13
  93. package/types/handler/plsql/procedureError.d.ts +0 -23
  94. package/types/handler/plsql/procedureNamed.d.ts +0 -14
  95. package/types/handler/plsql/procedureSanitize.d.ts +0 -11
  96. package/types/handler/plsql/procedureVariable.d.ts +0 -10
  97. package/types/handler/plsql/request.d.ts +0 -9
  98. package/types/handler/plsql/requestError.d.ts +0 -8
  99. package/types/handler/plsql/sendResponse.d.ts +0 -6
  100. package/types/handler/plsql/upload.d.ts +0 -5
  101. package/types/index.d.ts +0 -10
  102. package/types/server/adminContext.d.ts +0 -17
  103. package/types/server/config.d.ts +0 -2
  104. package/types/server/server.d.ts +0 -57
  105. package/types/types.d.ts +0 -328
  106. package/types/util/cache.d.ts +0 -69
  107. package/types/util/errorToString.d.ts +0 -1
  108. package/types/util/file.d.ts +0 -6
  109. package/types/util/html.d.ts +0 -3
  110. package/types/util/jsonLogger.d.ts +0 -45
  111. package/types/util/oracle.d.ts +0 -5
  112. package/types/util/shutdown.d.ts +0 -2
  113. package/types/util/statsManager.d.ts +0 -395
  114. package/types/util/trace.d.ts +0 -22
  115. package/types/util/traceManager.d.ts +0 -35
  116. package/types/util/type.d.ts +0 -1
  117. package/types/util/util.d.ts +0 -3
  118. package/types/version.d.ts +0 -1
  119. /package/{src/admin/favicon.svg → dist/frontend/assets/favicon-mQAM4tVu.svg} +0 -0
@@ -1,202 +0,0 @@
1
- /**
2
- * @file Load testing CLI tool for web_plsql.
3
- * This tool simulates parallel traffic to the middleware and provides real-time statistics.
4
- */
5
-
6
- import {performance} from 'node:perf_hooks';
7
-
8
- /**
9
- * @typedef {object} Metrics
10
- * @property {number} total Total requests made.
11
- * @property {number} success Number of successful requests (2xx).
12
- * @property {number} error Number of failed requests.
13
- * @property {number} minLatency Minimum latency in milliseconds.
14
- * @property {number} maxLatency Maximum latency in milliseconds.
15
- * @property {number} sumLatency Sum of all latencies for average calculation.
16
- * @property {Map<string|number, number>} statusCodes Frequency of status codes.
17
- */
18
-
19
- /** @type {Metrics} */
20
- const metrics = {
21
- total: 0,
22
- success: 0,
23
- error: 0,
24
- minLatency: Number.MAX_SAFE_INTEGER,
25
- maxLatency: 0,
26
- sumLatency: 0,
27
- statusCodes: new Map(),
28
- };
29
-
30
- let stopped = false;
31
-
32
- /**
33
- * Parse CLI arguments.
34
- * @returns {{ url: string, concurrency: number, duration: number }} Options object.
35
- */
36
- function parseArgs() {
37
- const args = process.argv.slice(2);
38
- const options = {
39
- url: '',
40
- concurrency: 10,
41
- duration: 30,
42
- };
43
-
44
- for (let i = 0; i < args.length; i++) {
45
- const arg = args[i];
46
- const nextArg = args[i + 1] ?? '';
47
-
48
- if (arg === '--url' || arg === '-u') {
49
- options.url = nextArg;
50
- i++;
51
- } else if (arg === '--concurrency' || arg === '-c') {
52
- options.concurrency = parseInt(nextArg, 10);
53
- i++;
54
- } else if (arg === '--duration' || arg === '-d') {
55
- options.duration = parseInt(nextArg, 10);
56
- i++;
57
- }
58
- }
59
-
60
- if (!options.url) {
61
- console.error('Usage: node src/bin/load-test.js --url <url> [-c concurrency] [-d duration]');
62
- process.exit(1);
63
- }
64
-
65
- return options;
66
- }
67
-
68
- /**
69
- * Record a single request's result.
70
- * @param {boolean} ok Whether the request succeeded.
71
- * @param {number} latency Latency in milliseconds.
72
- * @param {number|string} status Status code or error string.
73
- */
74
- function recordMetrics(ok, latency, status) {
75
- metrics.total++;
76
- if (ok) {
77
- metrics.success++;
78
- } else {
79
- metrics.error++;
80
- }
81
-
82
- metrics.minLatency = Math.min(metrics.minLatency, latency);
83
- metrics.maxLatency = Math.max(metrics.maxLatency, latency);
84
- metrics.sumLatency += latency;
85
-
86
- const count = metrics.statusCodes.get(status) ?? 0;
87
- metrics.statusCodes.set(status, count + 1);
88
- }
89
-
90
- /**
91
- * Clear the console and move cursor to the top.
92
- */
93
- function clearConsole() {
94
- process.stdout.write('\x1b[2J\x1b[0;0H');
95
- }
96
-
97
- /**
98
- * Render the dashboard.
99
- * @param {number} elapsed Elapsed time in seconds.
100
- * @param {number} duration Total duration in seconds.
101
- * @param {string} url Target URL.
102
- * @param {number} concurrency Concurrency level.
103
- */
104
- function renderDashboard(elapsed, duration, url, concurrency) {
105
- clearConsole();
106
- const rps = elapsed > 0 ? (metrics.total / elapsed).toFixed(1) : '0.0';
107
- const avgLatency = metrics.total > 0 ? (metrics.sumLatency / metrics.total).toFixed(1) : '0.0';
108
- const progress = Math.min(100, (elapsed / duration) * 100).toFixed(1);
109
- const barWidth = 30;
110
- const filledWidth = Math.floor((barWidth * parseFloat(progress)) / 100);
111
- const bar = '='.repeat(filledWidth) + '>'.padEnd(barWidth - filledWidth, ' ');
112
-
113
- console.log('\x1b[1m\x1b[36mWeb PL/SQL Load Test\x1b[0m');
114
- console.log('='.repeat(40));
115
- console.log(`\x1b[33mURL:\x1b[0m ${url}`);
116
- console.log(`\x1b[33mConcurrency:\x1b[0m ${concurrency}`);
117
- console.log(`\x1b[33mDuration:\x1b[0m ${duration}s`);
118
- console.log('='.repeat(40));
119
- console.log(`\x1b[32mProgress:\x1b[0m [${bar}] ${progress}% (${elapsed.toFixed(1)}s)`);
120
- console.log(`\x1b[32mRPS:\x1b[0m ${rps}`);
121
- console.log(`\x1b[32mTotal:\x1b[0m ${metrics.total}`);
122
- console.log(`\x1b[32mSuccess:\x1b[0m ${metrics.success}`);
123
- console.log(`\x1b[31mErrors:\x1b[0m ${metrics.error}`);
124
- console.log('='.repeat(40));
125
- console.log('\x1b[1mLatency (ms)\x1b[0m');
126
- console.log(`\x1b[34mAvg:\x1b[0m ${avgLatency.padStart(8)}`);
127
- console.log(`\x1b[34mMin:\x1b[0m ${(metrics.minLatency === Number.MAX_SAFE_INTEGER ? 0 : metrics.minLatency).toFixed(1).padStart(8)}`);
128
- console.log(`\x1b[34mMax:\x1b[0m ${metrics.maxLatency.toFixed(1).padStart(8)}`);
129
- console.log('='.repeat(40));
130
-
131
- if (metrics.statusCodes.size > 0) {
132
- console.log('\x1b[1mStatus Codes\x1b[0m');
133
- for (const [code, count] of metrics.statusCodes.entries()) {
134
- const numericCode = typeof code === 'number' ? code : 0;
135
- const color = numericCode >= 200 && numericCode < 300 ? '\x1b[32m' : '\x1b[31m';
136
- console.log(`${color}${code}\x1b[0m: ${count}`);
137
- }
138
- }
139
- }
140
-
141
- /**
142
- * Worker function to maintain concurrency.
143
- * @param {string} url The target URL.
144
- * @param {number} endTime The end time in milliseconds.
145
- */
146
- async function runWorker(url, endTime) {
147
- while (performance.now() < endTime && !stopped) {
148
- const start = performance.now();
149
- try {
150
- const res = await fetch(url, {
151
- signal: AbortSignal.timeout(10000), // 10s timeout
152
- });
153
- const latency = performance.now() - start;
154
- recordMetrics(res.ok, latency, res.status);
155
- // Consume body to free up connection
156
- await res.arrayBuffer();
157
- } catch (err) {
158
- const latency = performance.now() - start;
159
- const errorName = err instanceof Error ? err.name : 'UnknownError';
160
- recordMetrics(false, latency, errorName === 'TimeoutError' ? 'Timeout' : 'NetworkErr');
161
- }
162
- }
163
- }
164
-
165
- /**
166
- * Main function.
167
- */
168
- async function main() {
169
- const options = parseArgs();
170
- const startTime = performance.now();
171
- const endTime = startTime + options.duration * 1000;
172
-
173
- console.log(`Starting load test on ${options.url}...`);
174
-
175
- // Start workers
176
- const workers = Array.from({length: options.concurrency}, () => runWorker(options.url, endTime));
177
-
178
- // Dashboard refresh interval
179
- const uiInterval = setInterval(() => {
180
- const elapsed = (performance.now() - startTime) / 1000;
181
- renderDashboard(elapsed, options.duration, options.url, options.concurrency);
182
- }, 200);
183
-
184
- // Handle Ctrl+C
185
- process.on('SIGINT', () => {
186
- stopped = true;
187
- console.log('\nStopping...');
188
- });
189
-
190
- await Promise.all(workers);
191
- clearInterval(uiInterval);
192
-
193
- // Final render
194
- const finalElapsed = (performance.now() - startTime) / 1000;
195
- renderDashboard(finalElapsed, options.duration, options.url, options.concurrency);
196
- console.log('\n\x1b[1m\x1b[32mTest Complete!\x1b[0m\n');
197
- }
198
-
199
- main().catch((/** @type {unknown} */ err) => {
200
- console.error('Fatal error:', err);
201
- process.exit(1);
202
- });
@@ -1,293 +0,0 @@
1
- import express from 'express';
2
- import fs from 'node:fs';
3
- import os from 'node:os';
4
- import readline from 'node:readline';
5
- import {AdminContext} from '../server/adminContext.js';
6
- import {traceManager} from '../util/traceManager.js';
7
- import {getVersion} from '../version.js';
8
-
9
- const version = getVersion();
10
- import {forceShutdown} from '../util/shutdown.js';
11
-
12
- /**
13
- * @typedef {import('express').Request} Request
14
- * @typedef {import('express').Response} Response
15
- * @typedef {import('express').NextFunction} NextFunction
16
- */
17
-
18
- /**
19
- * @typedef {import('../util/statsManager.js').Bucket} Bucket
20
- */
21
-
22
- /**
23
- * @typedef {object} StatsSummary
24
- * @property {Date} startTime - Server start time.
25
- * @property {number} totalRequests - Total requests handled.
26
- * @property {number} totalErrors - Total errors encountered.
27
- * @property {number} avgResponseTime - Lifetime average response time.
28
- * @property {number} minResponseTime - Lifetime minimum response time.
29
- * @property {number} maxResponseTime - Lifetime maximum response time.
30
- * @property {number} maxRequestsPerSecond - Lifetime maximum requests per second.
31
- * @property {object} maxMemory - Lifetime memory extremes.
32
- * @property {number} maxMemory.heapUsedMax - Maximum heap used.
33
- * @property {number} maxMemory.heapTotalMax - Maximum heap total.
34
- * @property {number} maxMemory.rssMax - Maximum RSS.
35
- * @property {number} maxMemory.externalMax - Maximum external memory.
36
- * @property {object} cpu - Lifetime CPU extremes.
37
- * @property {number} cpu.max - Max CPU.
38
- * @property {number} cpu.userMax - Max user CPU.
39
- * @property {number} cpu.systemMax - Max system CPU.
40
- */
41
-
42
- export const handlerAdmin = express.Router();
43
-
44
- /**
45
- * Helper to read last N lines of a file
46
- * @param {string} filePath - Path to file
47
- * @param {number} n - Number of lines
48
- * @param {string} [filter] - Optional filter string
49
- * @returns {Promise<string[]>} - The lines
50
- */
51
- const readLastLines = async (filePath, n = 100, filter = '') => {
52
- if (!fs.existsSync(filePath)) {
53
- return [];
54
- }
55
-
56
- const fileStream = fs.createReadStream(filePath);
57
- const rl = readline.createInterface({
58
- input: fileStream,
59
- crlfDelay: Infinity,
60
- });
61
-
62
- const filterLower = filter.toLowerCase();
63
-
64
- /** @type {string[]} */
65
- const lines = [];
66
- for await (const line of rl) {
67
- if (!filter || line.toLowerCase().includes(filterLower)) {
68
- lines.push(line);
69
- if (lines.length > n) {
70
- lines.shift();
71
- }
72
- }
73
- }
74
- return lines;
75
- };
76
-
77
- // GET /api/status
78
- handlerAdmin.get('/api/status', (_req, res) => {
79
- const uptime = (new Date().getTime() - AdminContext.startTime.getTime()) / 1000;
80
-
81
- const poolStats = AdminContext.pools.map((pool, index) => {
82
- const cache = AdminContext.caches[index];
83
- const name = cache?.poolName ?? `pool-${index}`;
84
- const p = /** @type {import('oracledb').Pool & {getStatistics?: () => Record<string, unknown>}} */ (pool);
85
- const procStats = cache?.procedureNameCache.getStats();
86
- const argStats = cache?.argumentCache.getStats();
87
-
88
- return {
89
- name,
90
- stats: typeof p.getStatistics === 'function' ? p.getStatistics() : null,
91
- connectionsOpen: pool.connectionsOpen,
92
- connectionsInUse: pool.connectionsInUse,
93
- cache: {
94
- procedureName: {
95
- size: cache?.procedureNameCache.keys().length ?? 0,
96
- hits: procStats?.hits ?? 0,
97
- misses: procStats?.misses ?? 0,
98
- },
99
- argument: {
100
- size: cache?.argumentCache.keys().length ?? 0,
101
- hits: argStats?.hits ?? 0,
102
- misses: argStats?.misses ?? 0,
103
- },
104
- },
105
- };
106
- });
107
-
108
- const memUsage = process.memoryUsage();
109
- const systemMemoryUsed = os.totalmem() - os.freemem();
110
- const cpuUsage = process.cpuUsage();
111
- const summary = /** @type {StatsSummary} */ (AdminContext.statsManager.getSummary());
112
-
113
- res.json({
114
- version,
115
- status: AdminContext.paused ? 'paused' : 'running',
116
- uptime,
117
- startTime: AdminContext.startTime,
118
- intervalMs: AdminContext.statsManager.config.intervalMs,
119
- metrics: {
120
- requestCount: summary.totalRequests,
121
- errorCount: summary.totalErrors,
122
- avgResponseTime: summary.avgResponseTime,
123
- minResponseTime: summary.minResponseTime,
124
- maxResponseTime: summary.maxResponseTime,
125
- maxRequestsPerSecond: summary.maxRequestsPerSecond,
126
- },
127
- history: AdminContext.statsManager.getHistory(),
128
- pools: poolStats,
129
- system: {
130
- nodeVersion: process.version,
131
- platform: process.platform,
132
- arch: process.arch,
133
- cpuCores: os.cpus().length,
134
- memory: {
135
- rss: systemMemoryUsed,
136
- heapTotal: memUsage.heapTotal,
137
- heapUsed: memUsage.heapUsed,
138
- external: memUsage.external,
139
- totalMemory: os.totalmem(),
140
- ...summary.maxMemory,
141
- },
142
- cpu: {
143
- user: cpuUsage.user,
144
- system: cpuUsage.system,
145
- max: summary.cpu.max,
146
- userMax: summary.cpu.userMax,
147
- systemMax: summary.cpu.systemMax,
148
- },
149
- },
150
- config: AdminContext.config
151
- ? {
152
- ...AdminContext.config,
153
- adminPassword: AdminContext.config.adminPassword ? '********' : undefined,
154
- routePlSql: AdminContext.config.routePlSql.map((p) => ({
155
- ...p,
156
- password: '********',
157
- })),
158
- }
159
- : null,
160
- });
161
- });
162
-
163
- // GET /api/logs/error
164
- handlerAdmin.get('/api/logs/error', async (req, res) => {
165
- try {
166
- const limit = Number(req.query.limit) || 100;
167
- const filter = typeof req.query.filter === 'string' ? req.query.filter : '';
168
- const logFile = 'error.json.log';
169
- const lines = await readLastLines(logFile, limit, filter);
170
-
171
- /** @type {Record<string, unknown>[]} */
172
- const logs = [];
173
- for (const line of lines) {
174
- try {
175
- const parsed = /** @type {unknown} */ (JSON.parse(line));
176
- if (parsed && typeof parsed === 'object') {
177
- logs.push(/** @type {Record<string, unknown>} */ (parsed));
178
- }
179
- } catch (e) {
180
- // ignore
181
- }
182
- }
183
-
184
- res.json(logs.reverse());
185
- } catch (err) {
186
- res.status(500).json({error: String(err)});
187
- }
188
- });
189
-
190
- // GET /api/logs/access
191
- handlerAdmin.get('/api/logs/access', async (req, res) => {
192
- try {
193
- const limit = Number(req.query.limit) || 100;
194
- const filter = typeof req.query.filter === 'string' ? req.query.filter : '';
195
- const logFile = AdminContext.config?.loggerFilename ?? 'access.log';
196
-
197
- if (!AdminContext.config?.loggerFilename) {
198
- res.json({message: 'Access logging not enabled'});
199
- return;
200
- }
201
-
202
- const lines = await readLastLines(logFile, limit, filter);
203
- res.json(lines.reverse());
204
- } catch (err) {
205
- res.status(500).json({error: String(err)});
206
- }
207
- });
208
-
209
- // GET /api/cache - REMOVED (Merged into /api/status)
210
-
211
- // POST /api/cache/clear
212
- handlerAdmin.post('/api/cache/clear', (req, res) => {
213
- const body = /** @type {unknown} */ (req.body);
214
- const poolName = body && typeof body === 'object' && 'poolName' in body && typeof body.poolName === 'string' ? body.poolName : undefined;
215
-
216
- let cleared = 0;
217
- AdminContext.caches.forEach((c) => {
218
- if (!poolName || c.poolName === poolName) {
219
- c.procedureNameCache.clear();
220
- cleared++;
221
- c.argumentCache.clear();
222
- cleared++;
223
- }
224
- });
225
-
226
- res.json({message: `Cleared ${cleared} caches`});
227
- });
228
-
229
- // POST /api/server/:action
230
- handlerAdmin.post('/api/server/:action', (req, res) => {
231
- const action = req.params.action;
232
-
233
- if (action === 'stop') {
234
- res.json({message: 'Server shutting down...'});
235
- setTimeout(() => {
236
- forceShutdown();
237
- }, 100);
238
- } else if (action === 'pause') {
239
- AdminContext.paused = true;
240
- res.json({message: 'Server paused', status: 'paused'});
241
- } else if (action === 'resume') {
242
- AdminContext.paused = false;
243
- res.json({message: 'Server resumed', status: 'running'});
244
- } else {
245
- res.status(400).json({error: 'Invalid action'});
246
- }
247
- });
248
-
249
- // GET /api/trace/status
250
- handlerAdmin.get('/api/trace/status', (_req, res) => {
251
- res.json({enabled: traceManager.isEnabled()});
252
- });
253
-
254
- // POST /api/trace/toggle
255
- handlerAdmin.post('/api/trace/toggle', (req, res) => {
256
- const body = /** @type {unknown} */ (req.body);
257
- const enabled = body && typeof body === 'object' && 'enabled' in body && typeof body.enabled === 'boolean' ? body.enabled : false;
258
- traceManager.setEnabled(enabled);
259
- res.json({enabled: traceManager.isEnabled()});
260
- });
261
-
262
- // GET /api/trace/logs
263
- handlerAdmin.get('/api/trace/logs', async (req, res) => {
264
- try {
265
- const limit = Number(req.query.limit) || 100;
266
- const filter = typeof req.query.filter === 'string' ? req.query.filter : '';
267
- const logFile = traceManager.getFilePath();
268
- const lines = await readLastLines(logFile, limit, filter);
269
-
270
- /** @type {Record<string, unknown>[]} */
271
- const logs = [];
272
- for (const line of lines) {
273
- try {
274
- const parsed = /** @type {unknown} */ (JSON.parse(line));
275
- if (parsed && typeof parsed === 'object') {
276
- logs.push(/** @type {Record<string, unknown>} */ (parsed));
277
- }
278
- } catch (e) {
279
- // ignore
280
- }
281
- }
282
-
283
- res.json(logs.reverse());
284
- } catch (err) {
285
- res.status(500).json({error: String(err)});
286
- }
287
- });
288
-
289
- // POST /api/trace/clear
290
- handlerAdmin.post('/api/trace/clear', (_req, res) => {
291
- traceManager.clear();
292
- res.json({message: 'Traces cleared'});
293
- });
@@ -1,21 +0,0 @@
1
- import debugModule from 'debug';
2
- const debug = debugModule('webplsql:handlerLogger');
3
-
4
- import fs from 'node:fs';
5
- import path from 'node:path';
6
- import morgan from 'morgan';
7
-
8
- /**
9
- * @typedef {import('express').RequestHandler} RequestHandler
10
- */
11
-
12
- /**
13
- * Create the upload middleware.
14
- * @param {string} filename - Output filename.
15
- * @returns {RequestHandler} - Request handler.
16
- */
17
- export const handlerLogger = (filename) => {
18
- debug('register');
19
-
20
- return morgan('combined', {stream: fs.createWriteStream(path.join(process.cwd(), filename), {flags: 'a'})});
21
- };
@@ -1,26 +0,0 @@
1
- import multer from 'multer';
2
-
3
- /**
4
- * @typedef {import('express').RequestHandler} RequestHandler
5
- * @typedef {import('express').Response} Response
6
- * @typedef {import('express').NextFunction} NextFunction
7
- * @typedef {import('oracledb').Pool} Pool
8
- * @typedef {import('../types.js').environmentType} environmentType
9
- * @typedef {import('../types.js').configType} configType
10
- */
11
-
12
- /**
13
- * Create the upload middleware.
14
- * @param {number} [uploadFileSizeLimit] - Maximum size of each uploaded file in bytes or no limit if omitted.
15
- * @returns {RequestHandler} - Request handler.
16
- */
17
- export const handlerUpload = (uploadFileSizeLimit = Infinity) => {
18
- const upload = multer({
19
- storage: multer.diskStorage({}),
20
- limits: {
21
- fileSize: uploadFileSizeLimit,
22
- },
23
- });
24
-
25
- return upload.any();
26
- };
@@ -1,131 +0,0 @@
1
- /*
2
- * Prepare the CGI information
3
- */
4
-
5
- import os from 'node:os';
6
- import {URL} from 'node:url';
7
- import debugModule from 'debug';
8
- const debug = debugModule('webplsql:cgi');
9
-
10
- /**
11
- * @typedef {import('express').Request} Request
12
- * @typedef {import('../../types.js').environmentType} environmentType
13
- * @typedef {import('../../types.js').configPlSqlType} configPlSqlType
14
- */
15
-
16
- /** @type {environmentType} */
17
- const DEFAULT_CGI = {
18
- PLSQL_GATEWAY: 'WebDb',
19
- GATEWAY_IVERSION: '2',
20
- SERVER_SOFTWARE: 'web_plsql',
21
- GATEWAY_INTERFACE: 'CGI/1.1',
22
- SERVER_PORT: '',
23
- SERVER_NAME: os.hostname(),
24
- REQUEST_METHOD: '',
25
- PATH_INFO: '',
26
- SCRIPT_NAME: '',
27
- REMOTE_ADDR: '',
28
- SERVER_PROTOCOL: '',
29
- REQUEST_PROTOCOL: '',
30
- REMOTE_USER: '',
31
- HTTP_COOKIE: '',
32
- HTTP_USER_AGENT: '',
33
- HTTP_HOST: '',
34
- HTTP_ACCEPT: '',
35
- HTTP_ACCEPT_ENCODING: '',
36
- HTTP_ACCEPT_LANGUAGE: '',
37
- HTTP_REFERER: '',
38
- HTTP_X_FORWARDED_FOR: '',
39
- WEB_AUTHENT_PREFIX: '',
40
- DAD_NAME: '',
41
- DOC_ACCESS_PATH: 'doc',
42
- DOCUMENT_TABLE: '',
43
- PATH_ALIAS: '',
44
- REQUEST_CHARSET: 'UTF8',
45
- REQUEST_IANA_CHARSET: 'UTF-8',
46
- SCRIPT_PREFIX: '',
47
- };
48
-
49
- /**
50
- * Create the HTTP_COOKIE string
51
- *
52
- * @param {Request} req - The req object represents the HTTP request.
53
- * @returns {string} - The string representation of the cookies.
54
- */
55
- const getCookieString = (req) => {
56
- let cookieString = '';
57
-
58
- for (const propName in req.cookies) {
59
- cookieString += `${propName}=${req.cookies[propName]};`;
60
- }
61
-
62
- debug('getCookieString', req.cookies, cookieString);
63
-
64
- return cookieString;
65
- };
66
-
67
- /**
68
- * Get the script name and DAD name from a URL
69
- *
70
- * @param {Request} req - The req object represents the HTTP request.
71
- * @returns {{script: string; prefix: string; dad: string}} - The DAD structure.
72
- */
73
- const getPath = (req) => {
74
- // create a valid url
75
- const validUrl = `${req.protocol}://${os.hostname()}${req.originalUrl}`;
76
-
77
- // get the pathname from the url (new URL('https://example.org/abc/xyz?123').pathname => /abc/xyz)
78
- const pathname = new URL(validUrl).pathname;
79
-
80
- const tmp = trimPath(pathname.substring(0, pathname.lastIndexOf('/') + 1));
81
- const script = `/${tmp}`;
82
- const prefix = `/${tmp.substring(0, tmp.lastIndexOf('/'))}`;
83
- const dad = tmp.substring(tmp.indexOf('/') + 1);
84
-
85
- return {script, prefix, dad};
86
- };
87
-
88
- /**
89
- * Get a path that is not enclodes in slashes
90
- *
91
- * @param {string} value - The value to trim.
92
- * @returns {string} - The trimmed value.
93
- */
94
- const trimPath = (value) => value.replace(/^\/+|\/+$/g, '');
95
-
96
- /**
97
- * Create a CGI object
98
- *
99
- * @param {Request} req - The req object represents the HTTP request.
100
- * @param {string} doctable - The document table.
101
- * @param {environmentType} cgi - The additional cgi.
102
- * @returns {environmentType} CGI object
103
- */
104
- export const getCGI = (req, doctable, cgi) => {
105
- const PROTOCOL = req.protocol ? req.protocol.toUpperCase() : '';
106
- const PATH = getPath(req);
107
-
108
- /** @type {environmentType} */
109
- const CGI = {
110
- SERVER_PORT: typeof req.socket.localPort === 'number' ? req.socket.localPort.toString() : '',
111
- REQUEST_METHOD: req.method,
112
- PATH_INFO: Array.isArray(req.params.name) ? (req.params.name[0] ?? '') : (req.params.name ?? ''),
113
- SCRIPT_NAME: PATH.script,
114
- REMOTE_ADDR: (req.ip ?? '').replace('::ffff:', ''),
115
- SERVER_PROTOCOL: `${PROTOCOL}/${req.httpVersion}`,
116
- REQUEST_PROTOCOL: PROTOCOL,
117
- HTTP_COOKIE: getCookieString(req),
118
- HTTP_USER_AGENT: req.get('user-agent') ?? '',
119
- HTTP_HOST: req.get('host') ?? '',
120
- HTTP_ACCEPT: req.get('accept') ?? '',
121
- HTTP_ACCEPT_ENCODING: req.get('accept-encoding') ?? '',
122
- HTTP_ACCEPT_LANGUAGE: req.get('accept-language') ?? '',
123
- HTTP_REFERER: req.get('referer') ?? '',
124
- HTTP_X_FORWARDED_FOR: req.get('x-forwarded-for') ?? '',
125
- DAD_NAME: PATH.dad,
126
- DOCUMENT_TABLE: doctable,
127
- SCRIPT_PREFIX: PATH.prefix,
128
- };
129
-
130
- return Object.assign({}, DEFAULT_CGI, CGI, cgi);
131
- };