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.
- package/README.md +103 -8
- package/dist/chunk-DQk6qfdC.mjs +18 -0
- package/dist/frontend/assets/main-C8GKnEhz.js +147 -0
- package/dist/frontend/assets/main-C8GKnEhz.js.br +0 -0
- package/dist/frontend/assets/main-C8GKnEhz.js.gz +0 -0
- package/dist/frontend/assets/main-bU4x_yEW.css +1 -0
- package/dist/frontend/assets/main-bU4x_yEW.css.br +0 -0
- package/dist/frontend/assets/main-bU4x_yEW.css.gz +0 -0
- package/{src/admin → dist/frontend}/index.html +12 -25
- package/dist/frontend/index.html.br +0 -0
- package/dist/frontend/index.html.gz +0 -0
- package/dist/index.d.mts +646 -0
- package/dist/index.mjs +3559 -0
- package/dist/index.mjs.map +1 -0
- package/dist/oracledb-mock-Dn8aHtd7.mjs +68 -0
- package/dist/oracledb-mock-Dn8aHtd7.mjs.map +1 -0
- package/examples/config-apex.js +1 -1
- package/examples/config-docker.js +1 -2
- package/package.json +48 -32
- package/examples/config-native.js +0 -68
- package/src/admin/client/charts.ts +0 -598
- package/src/admin/client/main.js +0 -3
- package/src/admin/client/tailwind.css +0 -41
- package/src/admin/js/api.ts +0 -170
- package/src/admin/js/app.ts +0 -614
- package/src/admin/js/eslint.config.js +0 -74
- package/src/admin/js/schemas.ts +0 -215
- package/src/admin/js/templates/config.ts +0 -147
- package/src/admin/js/templates/errorRow.ts +0 -21
- package/src/admin/js/templates/index.ts +0 -4
- package/src/admin/js/templates/poolCard.ts +0 -61
- package/src/admin/js/templates/traceRow.ts +0 -24
- package/src/admin/js/tsconfig.json +0 -24
- package/src/admin/js/types.ts +0 -336
- package/src/admin/js/ui/components.ts +0 -44
- package/src/admin/js/ui/table.ts +0 -173
- package/src/admin/js/ui/theme.ts +0 -93
- package/src/admin/js/ui/views.ts +0 -427
- package/src/admin/js/util/format.ts +0 -46
- package/src/admin/js/util/metrics.ts +0 -24
- package/src/admin/lib/chart.bundle.css +0 -1
- package/src/admin/lib/chart.bundle.js +0 -146
- package/src/admin/style.css +0 -1437
- package/src/bin/load-test.js +0 -202
- package/src/handler/handlerAdmin.js +0 -293
- package/src/handler/handlerLogger.js +0 -21
- package/src/handler/handlerUpload.js +0 -26
- package/src/handler/plsql/cgi.js +0 -131
- package/src/handler/plsql/errorPage.js +0 -113
- package/src/handler/plsql/handlerPlSql.js +0 -90
- package/src/handler/plsql/owaPageStream.js +0 -93
- package/src/handler/plsql/parsePage.js +0 -213
- package/src/handler/plsql/procedure.js +0 -426
- package/src/handler/plsql/procedureError.js +0 -40
- package/src/handler/plsql/procedureNamed.js +0 -282
- package/src/handler/plsql/procedureSanitize.js +0 -286
- package/src/handler/plsql/procedureVariable.js +0 -56
- package/src/handler/plsql/request.js +0 -124
- package/src/handler/plsql/requestError.js +0 -23
- package/src/handler/plsql/sendResponse.js +0 -174
- package/src/handler/plsql/stream.js +0 -28
- package/src/handler/plsql/upload.js +0 -109
- package/src/index.js +0 -19
- package/src/server/adminContext.js +0 -23
- package/src/server/config.js +0 -57
- package/src/server/connections.js +0 -40
- package/src/server/server.js +0 -304
- package/src/types.js +0 -150
- package/src/util/cache.js +0 -123
- package/src/util/errorToString.js +0 -24
- package/src/util/file.js +0 -91
- package/src/util/html.js +0 -53
- package/src/util/jsonLogger.js +0 -47
- package/src/util/oracle.js +0 -80
- package/src/util/shutdown.js +0 -44
- package/src/util/statsManager.js +0 -368
- package/src/util/trace.js +0 -326
- package/src/util/traceManager.js +0 -68
- package/src/util/type.js +0 -6
- package/src/util/util.js +0 -66
- package/src/version.js +0 -5
- package/types/admin/js/schemas.d.ts +0 -384
- package/types/admin/js/types.d.ts +0 -312
- package/types/handler/handlerAdmin.d.ts +0 -79
- package/types/handler/handlerLogger.d.ts +0 -2
- package/types/handler/handlerUpload.d.ts +0 -7
- package/types/handler/plsql/cgi.d.ts +0 -4
- package/types/handler/plsql/errorPage.d.ts +0 -11
- package/types/handler/plsql/handlerPlSql.d.ts +0 -13
- package/types/handler/plsql/owaPageStream.d.ts +0 -28
- package/types/handler/plsql/parsePage.d.ts +0 -3
- package/types/handler/plsql/procedure.d.ts +0 -13
- package/types/handler/plsql/procedureError.d.ts +0 -23
- package/types/handler/plsql/procedureNamed.d.ts +0 -14
- package/types/handler/plsql/procedureSanitize.d.ts +0 -11
- package/types/handler/plsql/procedureVariable.d.ts +0 -10
- package/types/handler/plsql/request.d.ts +0 -9
- package/types/handler/plsql/requestError.d.ts +0 -8
- package/types/handler/plsql/sendResponse.d.ts +0 -6
- package/types/handler/plsql/upload.d.ts +0 -5
- package/types/index.d.ts +0 -10
- package/types/server/adminContext.d.ts +0 -17
- package/types/server/config.d.ts +0 -2
- package/types/server/server.d.ts +0 -57
- package/types/types.d.ts +0 -328
- package/types/util/cache.d.ts +0 -69
- package/types/util/errorToString.d.ts +0 -1
- package/types/util/file.d.ts +0 -6
- package/types/util/html.d.ts +0 -3
- package/types/util/jsonLogger.d.ts +0 -45
- package/types/util/oracle.d.ts +0 -5
- package/types/util/shutdown.d.ts +0 -2
- package/types/util/statsManager.d.ts +0 -395
- package/types/util/trace.d.ts +0 -22
- package/types/util/traceManager.d.ts +0 -35
- package/types/util/type.d.ts +0 -1
- package/types/util/util.d.ts +0 -3
- package/types/version.d.ts +0 -1
- /package/{src/admin/favicon.svg → dist/frontend/assets/favicon-mQAM4tVu.svg} +0 -0
package/src/server/server.js
DELETED
|
@@ -1,304 +0,0 @@
|
|
|
1
|
-
import debugModule from 'debug';
|
|
2
|
-
const debug = debugModule('webplsql:server');
|
|
3
|
-
import http from 'node:http';
|
|
4
|
-
import https from 'node:https';
|
|
5
|
-
import path from 'node:path';
|
|
6
|
-
import {fileURLToPath} from 'node:url';
|
|
7
|
-
import express from 'express';
|
|
8
|
-
import cookieParser from 'cookie-parser';
|
|
9
|
-
import compression from 'compression';
|
|
10
|
-
import {z$configType} from '../types.js';
|
|
11
|
-
import {installShutdown} from '../util/shutdown.js';
|
|
12
|
-
import {poolCreate, poolsClose} from '../util/oracle.js';
|
|
13
|
-
import {handlerUpload} from '../handler/handlerUpload.js';
|
|
14
|
-
import {handlerLogger} from '../handler/handlerLogger.js';
|
|
15
|
-
import {handlerWebPlSql} from '../handler/plsql/handlerPlSql.js';
|
|
16
|
-
import {handlerAdmin} from '../handler/handlerAdmin.js';
|
|
17
|
-
import {readFileSyncUtf8, getJsonFile} from '../util/file.js';
|
|
18
|
-
import {showConfig} from './config.js';
|
|
19
|
-
import {AdminContext} from './adminContext.js';
|
|
20
|
-
|
|
21
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
22
|
-
const __dirname = path.dirname(__filename);
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* @typedef {import('node:net').Socket} Socket
|
|
26
|
-
* @typedef {import('express').Express} Express
|
|
27
|
-
* @typedef {import('express').Request} Request
|
|
28
|
-
* @typedef {import('express').Response} Response
|
|
29
|
-
* @typedef {import('express').NextFunction} NextFunction
|
|
30
|
-
* @typedef {import('oracledb').Pool} Pool
|
|
31
|
-
* @typedef {import('../types.js').environmentType} environmentType
|
|
32
|
-
* @typedef {import('../types.js').configType} configType
|
|
33
|
-
* @typedef {import('../handler/plsql/procedureNamed.js').argsType} argsType
|
|
34
|
-
*/
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* @typedef {import('express').RequestHandler & {
|
|
38
|
-
* procedureNameCache: import('../util/cache.js').Cache<string>;
|
|
39
|
-
* argumentCache: import('../util/cache.js').Cache<argsType>;
|
|
40
|
-
* }} ExtendedRequestHandler
|
|
41
|
-
*/
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* @typedef {object} webServer - Web server interface.
|
|
45
|
-
* @property {configType} config - Configuration object.
|
|
46
|
-
* @property {Pool[]} connectionPools - Oracle connection pools.
|
|
47
|
-
* @property {Express} app - Express app.
|
|
48
|
-
* @property {http.Server | https.Server} server - Native Node http(s) server instance.
|
|
49
|
-
* @property {() => Promise<void>} shutdown - Shutdown function.
|
|
50
|
-
*/
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* @typedef {object} sslConfig - SSL configuration.
|
|
54
|
-
* @property {string} keyFilename - key filename.
|
|
55
|
-
* @property {string} certFilename - cert filename.
|
|
56
|
-
*/
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Create HTTPS server.
|
|
60
|
-
* @param {Express} app - express application
|
|
61
|
-
* @param {sslConfig} [ssl] - ssl configuration.
|
|
62
|
-
* @returns {http.Server | https.Server} - server
|
|
63
|
-
*/
|
|
64
|
-
export const createServer = (app, ssl) => {
|
|
65
|
-
if (ssl) {
|
|
66
|
-
const key = readFileSyncUtf8(ssl.keyFilename);
|
|
67
|
-
const cert = readFileSyncUtf8(ssl.certFilename);
|
|
68
|
-
|
|
69
|
-
return https.createServer({key, cert}, app);
|
|
70
|
-
} else {
|
|
71
|
-
return http.createServer(app);
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Start server.
|
|
77
|
-
* @param {configType} config - The config.
|
|
78
|
-
* @param {sslConfig} [ssl] - ssl configuration.
|
|
79
|
-
* @returns {Promise<webServer>} - Promise resolving to the web server object.
|
|
80
|
-
*/
|
|
81
|
-
export const startServer = async (config, ssl) => {
|
|
82
|
-
debug('startServer: BEGIN', config, ssl);
|
|
83
|
-
|
|
84
|
-
const internalConfig = /** @type {configType} */ (z$configType.parse(config));
|
|
85
|
-
AdminContext.config = internalConfig;
|
|
86
|
-
|
|
87
|
-
showConfig(internalConfig);
|
|
88
|
-
|
|
89
|
-
// Create express app
|
|
90
|
-
const app = express();
|
|
91
|
-
|
|
92
|
-
// Default middleware
|
|
93
|
-
app.use(handlerUpload(internalConfig.uploadFileSizeLimit));
|
|
94
|
-
app.use(express.json({limit: '50mb'}));
|
|
95
|
-
app.use(express.urlencoded({limit: '50mb', extended: true}));
|
|
96
|
-
app.use(cookieParser());
|
|
97
|
-
app.use(compression());
|
|
98
|
-
|
|
99
|
-
// Pause & Admin Auth middleware
|
|
100
|
-
app.use((req, res, next) => {
|
|
101
|
-
const adminRoute = internalConfig.adminRoute ?? '/admin';
|
|
102
|
-
|
|
103
|
-
// Simple pause check for all PL/SQL routes (not admin)
|
|
104
|
-
if (AdminContext.paused && !req.path.startsWith(adminRoute)) {
|
|
105
|
-
res.status(503).send('Server Paused');
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Basic Auth for Admin Route
|
|
110
|
-
if (req.path.startsWith(adminRoute)) {
|
|
111
|
-
const user = internalConfig.adminUser;
|
|
112
|
-
const pass = internalConfig.adminPassword;
|
|
113
|
-
|
|
114
|
-
if (user && pass) {
|
|
115
|
-
const auth = {login: user, password: pass};
|
|
116
|
-
const b64auth = (req.headers.authorization ?? '').split(' ')[1] ?? '';
|
|
117
|
-
const [login, password] = Buffer.from(b64auth, 'base64').toString().split(':');
|
|
118
|
-
|
|
119
|
-
if (login !== auth.login || password !== auth.password) {
|
|
120
|
-
res.set('WWW-Authenticate', 'Basic realm="Admin Console"');
|
|
121
|
-
res.status(401).send('Authentication required.');
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
next();
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
// Access log
|
|
131
|
-
if (internalConfig.loggerFilename.length > 0) {
|
|
132
|
-
app.use(handlerLogger(internalConfig.loggerFilename));
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Admin console
|
|
136
|
-
const adminRoute = internalConfig.adminRoute ?? '/admin';
|
|
137
|
-
const adminDirectory = path.resolve(__dirname, '../admin');
|
|
138
|
-
|
|
139
|
-
// Ensure trailing slash for admin route to support relative paths
|
|
140
|
-
app.get(adminRoute, (req, res, next) => {
|
|
141
|
-
const [path] = req.originalUrl.split('?');
|
|
142
|
-
if (path === adminRoute) {
|
|
143
|
-
const query = req.originalUrl.split('?')[1];
|
|
144
|
-
return res.redirect(adminRoute + '/' + (query ? '?' + query : ''));
|
|
145
|
-
}
|
|
146
|
-
next();
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
app.use(adminRoute, handlerAdmin);
|
|
150
|
-
app.use(adminRoute, express.static(adminDirectory));
|
|
151
|
-
|
|
152
|
-
// Serving static files
|
|
153
|
-
for (const i of internalConfig.routeStatic) {
|
|
154
|
-
app.use(i.route, express.static(i.directoryPath));
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/** @type {Pool[]} */
|
|
158
|
-
const connectionPools = [];
|
|
159
|
-
AdminContext.pools = connectionPools;
|
|
160
|
-
AdminContext.caches = [];
|
|
161
|
-
|
|
162
|
-
// Oracle pl/sql express middleware
|
|
163
|
-
for (const i of internalConfig.routePlSql) {
|
|
164
|
-
// Allocate the Oracle database pool
|
|
165
|
-
const pool = await poolCreate(i.user, i.password, i.connectString);
|
|
166
|
-
connectionPools.push(pool);
|
|
167
|
-
|
|
168
|
-
const handler = handlerWebPlSql(pool, i);
|
|
169
|
-
|
|
170
|
-
// Capture caches for admin console
|
|
171
|
-
AdminContext.caches.push({
|
|
172
|
-
poolName: i.route,
|
|
173
|
-
procedureNameCache: handler.procedureNameCache,
|
|
174
|
-
argumentCache: handler.argumentCache,
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
app.use([`${i.route}/:name`, i.route], (req, res, next) => {
|
|
178
|
-
const start = process.hrtime();
|
|
179
|
-
res.on('finish', () => {
|
|
180
|
-
const diff = process.hrtime(start);
|
|
181
|
-
const duration = diff[0] * 1000 + diff[1] / 1_000_000;
|
|
182
|
-
AdminContext.statsManager.recordRequest(duration, res.statusCode >= 400);
|
|
183
|
-
});
|
|
184
|
-
handler(req, res, next);
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// Update pools in StatsManager on each rotation
|
|
189
|
-
const originalRotate = AdminContext.statsManager.rotateBucket.bind(AdminContext.statsManager);
|
|
190
|
-
AdminContext.statsManager.rotateBucket = () => {
|
|
191
|
-
const poolSnapshots = AdminContext.pools.map((pool, index) => {
|
|
192
|
-
const cache = AdminContext.caches[index];
|
|
193
|
-
const name = cache?.poolName ?? `pool-${index}`;
|
|
194
|
-
const procStats = cache?.procedureNameCache.getStats();
|
|
195
|
-
const argStats = cache?.argumentCache.getStats();
|
|
196
|
-
|
|
197
|
-
return {
|
|
198
|
-
name,
|
|
199
|
-
connectionsOpen: pool.connectionsOpen,
|
|
200
|
-
connectionsInUse: pool.connectionsInUse,
|
|
201
|
-
cache: {
|
|
202
|
-
procedureName: {
|
|
203
|
-
size: cache?.procedureNameCache.keys().length ?? 0,
|
|
204
|
-
hits: procStats?.hits ?? 0,
|
|
205
|
-
misses: procStats?.misses ?? 0,
|
|
206
|
-
},
|
|
207
|
-
argument: {
|
|
208
|
-
size: cache?.argumentCache.keys().length ?? 0,
|
|
209
|
-
hits: argStats?.hits ?? 0,
|
|
210
|
-
misses: argStats?.misses ?? 0,
|
|
211
|
-
},
|
|
212
|
-
},
|
|
213
|
-
};
|
|
214
|
-
});
|
|
215
|
-
originalRotate(poolSnapshots);
|
|
216
|
-
};
|
|
217
|
-
|
|
218
|
-
// create server
|
|
219
|
-
debug('startServer: createServer');
|
|
220
|
-
const server = createServer(app, ssl);
|
|
221
|
-
|
|
222
|
-
// Track open connections
|
|
223
|
-
/** @type {Set<Socket>} */
|
|
224
|
-
const connections = new Set();
|
|
225
|
-
server.on('connection', (/** @type {Socket} */ socket) => {
|
|
226
|
-
connections.add(socket);
|
|
227
|
-
socket.on('close', () => {
|
|
228
|
-
connections.delete(socket);
|
|
229
|
-
});
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
const closeAllConnections = () => {
|
|
233
|
-
for (const socket of connections) {
|
|
234
|
-
socket.destroy(); // forcibly closes the connection
|
|
235
|
-
connections.delete(socket);
|
|
236
|
-
}
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
const shutdown = async () => {
|
|
240
|
-
debug('startServer: onShutdown');
|
|
241
|
-
|
|
242
|
-
AdminContext.statsManager.stop();
|
|
243
|
-
|
|
244
|
-
await poolsClose(connectionPools);
|
|
245
|
-
|
|
246
|
-
server.close(() => {
|
|
247
|
-
console.log('Server has closed');
|
|
248
|
-
process.exit(0);
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
closeAllConnections();
|
|
252
|
-
};
|
|
253
|
-
|
|
254
|
-
// Install shutdown handler
|
|
255
|
-
installShutdown(shutdown);
|
|
256
|
-
|
|
257
|
-
// Listen
|
|
258
|
-
debug('startServer: start listener');
|
|
259
|
-
await /** @type {Promise<void>} */ (
|
|
260
|
-
new Promise((resolve, reject) => {
|
|
261
|
-
server
|
|
262
|
-
.listen(internalConfig.port)
|
|
263
|
-
.on('listening', () => {
|
|
264
|
-
debug('startServer: listener running');
|
|
265
|
-
resolve();
|
|
266
|
-
})
|
|
267
|
-
.on('error', (err) => {
|
|
268
|
-
if ('code' in err) {
|
|
269
|
-
if (err.code === 'EADDRINUSE') {
|
|
270
|
-
err.message = `Port ${internalConfig.port} is already in use`;
|
|
271
|
-
} else if (err.code === 'EACCES') {
|
|
272
|
-
err.message = `Port ${internalConfig.port} requires elevated privileges`;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
reject(err);
|
|
276
|
-
});
|
|
277
|
-
})
|
|
278
|
-
);
|
|
279
|
-
|
|
280
|
-
debug('startServer: END');
|
|
281
|
-
|
|
282
|
-
return {
|
|
283
|
-
config: internalConfig,
|
|
284
|
-
connectionPools,
|
|
285
|
-
app,
|
|
286
|
-
server,
|
|
287
|
-
shutdown,
|
|
288
|
-
};
|
|
289
|
-
};
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* Load configuration.
|
|
293
|
-
* @param {string} [filename] - The configuration filename.
|
|
294
|
-
* @returns {configType} - Promise.
|
|
295
|
-
*/
|
|
296
|
-
export const loadConfig = (filename = 'config.json') => /** @type {configType} */ (z$configType.parse(getJsonFile(filename)));
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Start server from config file.
|
|
300
|
-
* @param {string} [filename] - The configuration filename.
|
|
301
|
-
* @param {sslConfig} [ssl] - ssl configuration.
|
|
302
|
-
* @returns {Promise<webServer>} - Promise resolving to the web server object.
|
|
303
|
-
*/
|
|
304
|
-
export const startServerConfig = async (filename = 'config.json', ssl) => startServer(loadConfig(filename), ssl);
|
package/src/types.js
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import z from 'zod';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @typedef {import('oracledb').BindParameter} BindParameter
|
|
5
|
-
* @typedef {import('oracledb').Connection} Connection
|
|
6
|
-
* @typedef {import('express').CookieOptions} CookieOptions
|
|
7
|
-
* @typedef {import('express').Request} Request
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* @typedef {'basic' | 'debug'} errorStyleType
|
|
12
|
-
*/
|
|
13
|
-
export const z$errorStyleType = z.enum(['basic', 'debug']);
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @typedef {object} configStaticType
|
|
17
|
-
* @property {string} route - The Static route path.
|
|
18
|
-
* @property {string} directoryPath - The Static directory.
|
|
19
|
-
*/
|
|
20
|
-
export const z$configStaticType = z.strictObject({
|
|
21
|
-
route: z.string(),
|
|
22
|
-
directoryPath: z.string(),
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* @typedef {(connection: Connection, procedure: string) => void | Promise<void>} transactionCallbackType
|
|
27
|
-
* @typedef {'commit' | 'rollback' | transactionCallbackType | undefined | null} transactionModeType
|
|
28
|
-
*/
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* @typedef {object} configPlSqlHandlerType
|
|
32
|
-
* @property {string} defaultPage - The default page.
|
|
33
|
-
* @property {string} [pathAlias] - The path alias.
|
|
34
|
-
* @property {string} [pathAliasProcedure] - The path alias.
|
|
35
|
-
* @property {string} documentTable - The document table.
|
|
36
|
-
* @property {string[]} [exclusionList] - The exclusion list.
|
|
37
|
-
* @property {string} [requestValidationFunction] - The request validation function.
|
|
38
|
-
* @property {Record<string, string>} [cgi] - The additional CGI.
|
|
39
|
-
* @property {transactionModeType} [transactionMode] - Specifies an optional transaction mode.
|
|
40
|
-
* "commit" this automatically commits any open transaction after each request. This is the defaults because this is what mod_plsql and ohs are doing.
|
|
41
|
-
* "rollback" this automatically rolles back any open transaction after each request.
|
|
42
|
-
* "transactionCallbackType" this allows to defined a custom handler as a JavaScript function.
|
|
43
|
-
* @property {errorStyleType} errorStyle - The error style.
|
|
44
|
-
*/
|
|
45
|
-
export const z$configPlSqlHandlerType = z.strictObject({
|
|
46
|
-
defaultPage: z.string(),
|
|
47
|
-
pathAlias: z.string().optional(),
|
|
48
|
-
pathAliasProcedure: z.string().optional(),
|
|
49
|
-
documentTable: z.string(),
|
|
50
|
-
exclusionList: z.array(z.string()).optional(),
|
|
51
|
-
requestValidationFunction: z.string().optional(),
|
|
52
|
-
transactionMode: z.unknown().optional(),
|
|
53
|
-
errorStyle: z$errorStyleType,
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* @typedef {object} configPlSqlConfigType
|
|
58
|
-
* @property {string} route - The PL/SQL route path.
|
|
59
|
-
* @property {string} user - The Oracle username.
|
|
60
|
-
* @property {string} password - The Oracle password.
|
|
61
|
-
* @property {string} connectString - The Oracle connect string.
|
|
62
|
-
*/
|
|
63
|
-
export const z$configPlSqlConfigType = z.strictObject({
|
|
64
|
-
route: z.string(),
|
|
65
|
-
user: z.string(),
|
|
66
|
-
password: z.string(),
|
|
67
|
-
connectString: z.string(),
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* @typedef {configPlSqlHandlerType & configPlSqlConfigType} configPlSqlType
|
|
72
|
-
*/
|
|
73
|
-
export const z$configPlSqlType = z.strictObject({
|
|
74
|
-
...z$configPlSqlHandlerType.shape,
|
|
75
|
-
...z$configPlSqlConfigType.shape,
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* @typedef {object} configType
|
|
80
|
-
* @property {number} port - The server port number.
|
|
81
|
-
* @property {configStaticType[]} routeStatic - The static routes.
|
|
82
|
-
* @property {configPlSqlType[]} routePlSql - The PL/SQL routes.
|
|
83
|
-
* @property {number} [uploadFileSizeLimit] - Maximum size of each uploaded file in bytes or no limit if omitted.
|
|
84
|
-
* @property {string} loggerFilename - name of the request logger filename or '' if not required.
|
|
85
|
-
* @property {string} [adminRoute] - Optional route for the admin console (defaults to /admin).
|
|
86
|
-
* @property {string} [adminUser] - Optional username for admin console basic auth.
|
|
87
|
-
* @property {string} [adminPassword] - Optional password for admin console basic auth.
|
|
88
|
-
*/
|
|
89
|
-
export const z$configType = z.strictObject({
|
|
90
|
-
port: z.number(),
|
|
91
|
-
routeStatic: z.array(z$configStaticType),
|
|
92
|
-
routePlSql: z.array(z$configPlSqlType),
|
|
93
|
-
uploadFileSizeLimit: z.number().optional(),
|
|
94
|
-
loggerFilename: z.string(),
|
|
95
|
-
adminRoute: z.string().optional(),
|
|
96
|
-
adminUser: z.string().optional(),
|
|
97
|
-
adminPassword: z.string().optional(),
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Environment variables as string key-value pairs
|
|
102
|
-
* @typedef {Record<string, string>} environmentType
|
|
103
|
-
*/
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Oracle database bind parameter configuration
|
|
107
|
-
* @typedef {Record<string, BindParameter>} BindParameterConfig
|
|
108
|
-
*/
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Arguments object with string or string array values
|
|
112
|
-
* @typedef {Record<string, string | string[]>} argObjType
|
|
113
|
-
*/
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* File upload metadata
|
|
117
|
-
* @typedef {object} fileUploadType
|
|
118
|
-
* @property {string} fieldname - The field value.
|
|
119
|
-
* @property {string} originalname - The filename.
|
|
120
|
-
* @property {string} encoding - The encoding.
|
|
121
|
-
* @property {string} mimetype - The mimetype.
|
|
122
|
-
* @property {string} filename - The filename.
|
|
123
|
-
* @property {string} path - The path.
|
|
124
|
-
* @property {number} size - The size.
|
|
125
|
-
*/
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* @typedef {object} cookieType
|
|
129
|
-
* @property {string} name - The name of the cookie.
|
|
130
|
-
* @property {string} value - The value of the cookie.
|
|
131
|
-
* @property {CookieOptions} options - The cookie options.
|
|
132
|
-
*/
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* @typedef {object} pageType - The page.
|
|
136
|
-
* @property {string | import('node:stream').Readable} body - The body of the page.
|
|
137
|
-
* @property {object} head - The head of the page.
|
|
138
|
-
* @property {cookieType[]} head.cookies - The cookies.
|
|
139
|
-
* @property {string} [head.contentType] - The content type.
|
|
140
|
-
* @property {number} [head.contentLength] - The content length.
|
|
141
|
-
* @property {number} [head.statusCode] - The status code.
|
|
142
|
-
* @property {string} [head.statusDescription] - The status description.
|
|
143
|
-
* @property {string} [head.redirectLocation] - The redirect location.
|
|
144
|
-
* @property {Record<string, string>} head.otherHeaders - The other headers.
|
|
145
|
-
* @property {string} [head.server] - The server.
|
|
146
|
-
* @property {object} file - The file.
|
|
147
|
-
* @property {string | null} file.fileType - The file type.
|
|
148
|
-
* @property {number | null} file.fileSize - The file size.
|
|
149
|
-
* @property {import('node:stream').Readable | Buffer | null} file.fileBlob - The file blob.
|
|
150
|
-
*/
|
package/src/util/cache.js
DELETED
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @template T
|
|
3
|
-
* @typedef {{hitCount: number, value: T}} cacheEntryType
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Generic Cache class with LFU (Least Frequently Used) eviction policy.
|
|
8
|
-
* @template T
|
|
9
|
-
*/
|
|
10
|
-
export class Cache {
|
|
11
|
-
/**
|
|
12
|
-
* @param {number} maxSize - Maximum number of entries in the cache.
|
|
13
|
-
*/
|
|
14
|
-
constructor(maxSize = 10000) {
|
|
15
|
-
/** @type {Map<string, cacheEntryType<T>>} */
|
|
16
|
-
this.cache = new Map();
|
|
17
|
-
this.maxSize = maxSize;
|
|
18
|
-
this.hits = 0;
|
|
19
|
-
this.misses = 0;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Get an entry from the cache.
|
|
24
|
-
* @param {string} key - The key.
|
|
25
|
-
* @returns {T | undefined} - The value or undefined if not found.
|
|
26
|
-
*/
|
|
27
|
-
get(key) {
|
|
28
|
-
const entry = this.cache.get(key);
|
|
29
|
-
if (entry) {
|
|
30
|
-
entry.hitCount++;
|
|
31
|
-
this.hits++;
|
|
32
|
-
return entry.value;
|
|
33
|
-
}
|
|
34
|
-
this.misses++;
|
|
35
|
-
return undefined;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Set an entry in the cache.
|
|
40
|
-
* @param {string} key - The key.
|
|
41
|
-
* @param {T} value - The value.
|
|
42
|
-
*/
|
|
43
|
-
set(key, value) {
|
|
44
|
-
// If updating an existing key, preserve its hitCount?
|
|
45
|
-
// Typically LFU implies resetting or keeping.
|
|
46
|
-
// For simplicity and avoiding complex aging, if we set it again, we reset hitCount or keep it?
|
|
47
|
-
// The requirement is "cache invalidation" (delete) or "cache loading" (set).
|
|
48
|
-
// If we overwrite, it's usually a new value. Let's reset hitCount to 0 for a fresh start or 1.
|
|
49
|
-
|
|
50
|
-
// Ensure we have space
|
|
51
|
-
if (this.cache.size >= this.maxSize && !this.cache.has(key)) {
|
|
52
|
-
this.prune();
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
this.cache.set(key, {hitCount: 0, value});
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Delete an entry from the cache.
|
|
60
|
-
* @param {string} key - The key.
|
|
61
|
-
*/
|
|
62
|
-
delete(key) {
|
|
63
|
-
this.cache.delete(key);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Clear the cache.
|
|
68
|
-
*/
|
|
69
|
-
clear() {
|
|
70
|
-
this.cache.clear();
|
|
71
|
-
this.hits = 0;
|
|
72
|
-
this.misses = 0;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Prune the cache by removing the least frequently used entries.
|
|
77
|
-
* Removes 10% of the cache size.
|
|
78
|
-
*/
|
|
79
|
-
prune() {
|
|
80
|
-
// Convert cache entries to an array
|
|
81
|
-
const entries = Array.from(this.cache.entries());
|
|
82
|
-
|
|
83
|
-
// Sort entries by hitCount in ascending order
|
|
84
|
-
entries.sort((a, b) => a[1].hitCount - b[1].hitCount);
|
|
85
|
-
|
|
86
|
-
// Remove the bottom 10%
|
|
87
|
-
const removeCount = Math.max(1, Math.floor(this.maxSize * 0.1));
|
|
88
|
-
const keysToRemove = entries.slice(0, removeCount).map(([key]) => key);
|
|
89
|
-
|
|
90
|
-
for (const key of keysToRemove) {
|
|
91
|
-
this.cache.delete(key);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Get the size of the cache.
|
|
97
|
-
* @returns {number} - The size.
|
|
98
|
-
*/
|
|
99
|
-
get size() {
|
|
100
|
-
return this.cache.size;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Get all keys in the cache.
|
|
105
|
-
* @returns {string[]} - The keys.
|
|
106
|
-
*/
|
|
107
|
-
keys() {
|
|
108
|
-
return Array.from(this.cache.keys());
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Get cache statistics.
|
|
113
|
-
* @returns {{size: number, maxSize: number, hits: number, misses: number}} - The statistics.
|
|
114
|
-
*/
|
|
115
|
-
getStats() {
|
|
116
|
-
return {
|
|
117
|
-
size: this.cache.size,
|
|
118
|
-
maxSize: this.maxSize,
|
|
119
|
-
hits: this.hits,
|
|
120
|
-
misses: this.misses,
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import {inspect} from './trace.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Convert Error to a string.
|
|
5
|
-
*
|
|
6
|
-
* @param {unknown} error - The error.
|
|
7
|
-
* @returns {string} The string representation.
|
|
8
|
-
*/
|
|
9
|
-
export const errorToString = (error) => {
|
|
10
|
-
if (typeof error === 'string') {
|
|
11
|
-
return error;
|
|
12
|
-
} else if (error instanceof Error) {
|
|
13
|
-
const parts = [error.name];
|
|
14
|
-
if (typeof error.message === 'string' && error.message.length > 0) {
|
|
15
|
-
parts.push(error.message);
|
|
16
|
-
}
|
|
17
|
-
if (typeof error.stack === 'string' && error.stack.length > 0) {
|
|
18
|
-
parts.push(error.stack);
|
|
19
|
-
}
|
|
20
|
-
return parts.join('\n');
|
|
21
|
-
} else {
|
|
22
|
-
return inspect(error);
|
|
23
|
-
}
|
|
24
|
-
};
|
package/src/util/file.js
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import {promises as fs, readFileSync} from 'node:fs';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Read file.
|
|
5
|
-
*
|
|
6
|
-
* @param {string} filePath - File name.
|
|
7
|
-
* @returns {string} The string.
|
|
8
|
-
*/
|
|
9
|
-
export const readFileSyncUtf8 = (filePath) => {
|
|
10
|
-
try {
|
|
11
|
-
return readFileSync(filePath, 'utf8');
|
|
12
|
-
} catch (err) {
|
|
13
|
-
throw new Error(`Unable to read file "${filePath}"`);
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Read file.
|
|
19
|
-
*
|
|
20
|
-
* @param {string} filePath - File name.
|
|
21
|
-
* @returns {Promise<Buffer>} The buffer.
|
|
22
|
-
*/
|
|
23
|
-
export const readFile = async (filePath) => {
|
|
24
|
-
try {
|
|
25
|
-
return await fs.readFile(filePath);
|
|
26
|
-
} catch (err) {
|
|
27
|
-
throw new Error(`Unable to read file "${filePath}"`);
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Remove file.
|
|
33
|
-
*
|
|
34
|
-
* @param {string} filePath - File name.
|
|
35
|
-
* @returns {Promise<void>}.
|
|
36
|
-
*/
|
|
37
|
-
export const removeFile = async (filePath) => {
|
|
38
|
-
try {
|
|
39
|
-
return await fs.unlink(filePath);
|
|
40
|
-
} catch (err) {
|
|
41
|
-
throw new Error(`Unable to remove file "${filePath}"`);
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Load a json file.
|
|
47
|
-
*
|
|
48
|
-
* @param {string} filePath - File name.
|
|
49
|
-
* @returns {unknown} The json object.
|
|
50
|
-
*/
|
|
51
|
-
export const getJsonFile = (filePath) => {
|
|
52
|
-
try {
|
|
53
|
-
const fileContent = readFileSync(filePath, 'utf8');
|
|
54
|
-
return JSON.parse(fileContent);
|
|
55
|
-
} catch (err) {
|
|
56
|
-
throw new Error(`Unable to load file "${filePath}"`);
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Is this a directory.
|
|
62
|
-
* @param {unknown} directoryPath - Directory name.
|
|
63
|
-
* @returns {Promise<boolean>} - Return true if it is a directory.
|
|
64
|
-
*/
|
|
65
|
-
export const isDirectory = async (directoryPath) => {
|
|
66
|
-
if (typeof directoryPath !== 'string') {
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const stats = await fs.stat(directoryPath);
|
|
71
|
-
|
|
72
|
-
return stats.isDirectory();
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Is this a file.
|
|
77
|
-
* @param {unknown} filePath - File name.
|
|
78
|
-
* @returns {Promise<boolean>} - Return true if it is a file.
|
|
79
|
-
*/
|
|
80
|
-
export const isFile = async (filePath) => {
|
|
81
|
-
if (typeof filePath !== 'string') {
|
|
82
|
-
return false;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
try {
|
|
86
|
-
const stats = await fs.stat(filePath);
|
|
87
|
-
return stats.isFile();
|
|
88
|
-
} catch {
|
|
89
|
-
return false;
|
|
90
|
-
}
|
|
91
|
-
};
|