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
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Process the http request
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import debugModule from 'debug';
|
|
6
|
-
const debug = debugModule('webplsql:request');
|
|
7
|
-
|
|
8
|
-
import util from 'node:util';
|
|
9
|
-
import {invokeProcedure} from './procedure.js';
|
|
10
|
-
import {getCGI} from './cgi.js';
|
|
11
|
-
import {getFiles} from './upload.js';
|
|
12
|
-
import {RequestError} from './requestError.js';
|
|
13
|
-
import {isStringOrArrayOfString} from '../../util/type.js';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @typedef {import('express').Request} Request
|
|
17
|
-
* @typedef {import('express').Response} Response
|
|
18
|
-
* @typedef {import('oracledb').Pool} Pool
|
|
19
|
-
* @typedef {import('oracledb').Connection} Connection
|
|
20
|
-
* @typedef {import('../../types.js').argObjType} argObjType
|
|
21
|
-
* @typedef {import('../../types.js').configPlSqlHandlerType} configPlSqlHandlerType
|
|
22
|
-
* @typedef {import('../../util/cache.js').Cache<string>} ProcedureNameCache
|
|
23
|
-
* @typedef {import('../../util/cache.js').Cache<import('./procedureNamed.js').argsType>} ArgumentCache
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Execute the request
|
|
28
|
-
*
|
|
29
|
-
* @param {Request} req - The req object represents the HTTP request.
|
|
30
|
-
* @param {Response} res - The res object represents the HTTP response that an Express app sends when it gets an HTTP request.
|
|
31
|
-
* @param {configPlSqlHandlerType} options - the options for the middleware.
|
|
32
|
-
* @param {Pool} connectionPool - The connection pool.
|
|
33
|
-
* @param {ProcedureNameCache} procedureNameCache - The procedure name cache.
|
|
34
|
-
* @param {ArgumentCache} argumentCache - The argument cache.
|
|
35
|
-
* @returns {Promise<void>} - Promise resolving to th page
|
|
36
|
-
*/
|
|
37
|
-
export const processRequest = async (req, res, options, connectionPool, procedureNameCache, argumentCache) => {
|
|
38
|
-
debug('processRequest: ENTER');
|
|
39
|
-
|
|
40
|
-
//
|
|
41
|
-
if (Array.isArray(req.params.name)) {
|
|
42
|
-
console.warn(`processRequest: WARNING: the req.params.name is not a string but an array of string: ${req.params.name.join(',')}`);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// open database connection
|
|
46
|
-
const connection = await connectionPool.getConnection();
|
|
47
|
-
|
|
48
|
-
// Get the CGI
|
|
49
|
-
const cgiObj = getCGI(req, options.documentTable ?? '', options.cgi ?? {});
|
|
50
|
-
debug('processRequest: cgiObj=', cgiObj);
|
|
51
|
-
|
|
52
|
-
// Does the request contain any files
|
|
53
|
-
const filesToUpload = getFiles(req);
|
|
54
|
-
debug('processRequest: filesToUpload=', filesToUpload);
|
|
55
|
-
|
|
56
|
-
// Add the query properties
|
|
57
|
-
/** @type {argObjType} */
|
|
58
|
-
const argObj = {};
|
|
59
|
-
Object.assign(argObj, req.query);
|
|
60
|
-
|
|
61
|
-
// For add the files that must be uploaded, we now copy the actual filename to the appropriate parameter to the invoked procedure.
|
|
62
|
-
filesToUpload.reduce((aggregator, file) => {
|
|
63
|
-
aggregator[file.fieldname] = file.filename;
|
|
64
|
-
return aggregator;
|
|
65
|
-
}, argObj);
|
|
66
|
-
|
|
67
|
-
// Does the request contain a body
|
|
68
|
-
Object.assign(argObj, normalizeBody(req));
|
|
69
|
-
debug('processRequest: argObj=', argObj);
|
|
70
|
-
|
|
71
|
-
// invoke the Oracle procedure and get the page contenst
|
|
72
|
-
await invokeProcedure(req, res, argObj, cgiObj, filesToUpload, options, connection, procedureNameCache, argumentCache);
|
|
73
|
-
|
|
74
|
-
// transaction mode
|
|
75
|
-
if (options.transactionMode === 'rollback') {
|
|
76
|
-
debug('transactionMode: rollback');
|
|
77
|
-
await connection.rollback();
|
|
78
|
-
} else if (typeof options.transactionMode === 'function') {
|
|
79
|
-
debug('transactionMode: callback');
|
|
80
|
-
const procName = Array.isArray(req.params.name) ? req.params.name[0] : req.params.name;
|
|
81
|
-
const result = options.transactionMode(connection, procName ?? '');
|
|
82
|
-
debug('transactionMode: callback restult', result);
|
|
83
|
-
if (result && typeof result.then === 'function') {
|
|
84
|
-
await result;
|
|
85
|
-
}
|
|
86
|
-
} else {
|
|
87
|
-
debug('transactionMode: commit');
|
|
88
|
-
await connection.commit();
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// close database connection
|
|
92
|
-
await connection.release();
|
|
93
|
-
|
|
94
|
-
debug('processRequest: EXIT');
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Normalize the body by making sure that only "simple" parameters and no nested objects are submitted
|
|
99
|
-
* @param {Request} req - The req object represents the HTTP request.
|
|
100
|
-
* @returns {Record<string, string | string[]>} - The normalized body.
|
|
101
|
-
*/
|
|
102
|
-
const normalizeBody = (req) => {
|
|
103
|
-
/** @type {Record<string, string | string[]>} */
|
|
104
|
-
const args = {};
|
|
105
|
-
|
|
106
|
-
/* v8 ignore else - body validation */
|
|
107
|
-
if (typeof req.body === 'object' && req.body !== null) {
|
|
108
|
-
for (const key in req.body) {
|
|
109
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
|
|
110
|
-
const value = req.body[key];
|
|
111
|
-
/* v8 ignore else - type validation */
|
|
112
|
-
if (isStringOrArrayOfString(value)) {
|
|
113
|
-
args[key] = value;
|
|
114
|
-
} else {
|
|
115
|
-
/* v8 ignore next - invalid body type */
|
|
116
|
-
throw new RequestError(
|
|
117
|
-
`The element "${key}" in the body is not a string or an array of strings!\n${util.inspect(req.body, {showHidden: false, depth: null, colors: false})}`,
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return args;
|
|
124
|
-
};
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* RequestError
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export class RequestError extends Error {
|
|
6
|
-
/** @type {Date} */
|
|
7
|
-
timestamp;
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @param {string} message - The error message.
|
|
11
|
-
*/
|
|
12
|
-
constructor(message) {
|
|
13
|
-
super(message);
|
|
14
|
-
|
|
15
|
-
// Maintains proper stack trace for where our error was thrown (only available on V8)
|
|
16
|
-
if (Error.captureStackTrace) {
|
|
17
|
-
Error.captureStackTrace(this, RequestError);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// Custom debugging information
|
|
21
|
-
this.timestamp = new Date();
|
|
22
|
-
}
|
|
23
|
-
}
|
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Page the raw page content and return the content to the client
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import debugModule from 'debug';
|
|
6
|
-
const debug = debugModule('webplsql:sendResponse');
|
|
7
|
-
|
|
8
|
-
import stream from 'node:stream';
|
|
9
|
-
import {getBlock} from '../../util/trace.js';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* @typedef {import('express').Request} Request
|
|
13
|
-
* @typedef {import('express').Response} Response
|
|
14
|
-
* @typedef {import('express').CookieOptions} CookieOptions
|
|
15
|
-
* @typedef {import('../../types.js').pageType} pageType
|
|
16
|
-
* @typedef {import('../../types.js').cookieType} cookieType
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Send "default" response to the browser
|
|
21
|
-
* @param {Request} _req - The req object represents the HTTP request.
|
|
22
|
-
* @param {Response} res - The res object represents the HTTP response that an Express app sends when it gets an HTTP request.
|
|
23
|
-
* @param {pageType} page - The page to send.
|
|
24
|
-
* @returns {Promise<void>}
|
|
25
|
-
*/
|
|
26
|
-
export const sendResponse = async (_req, res, page) => {
|
|
27
|
-
/** @type {string[]} */
|
|
28
|
-
const debugText = [];
|
|
29
|
-
|
|
30
|
-
// Send the "cookies"
|
|
31
|
-
page.head.cookies.forEach((cookie) => {
|
|
32
|
-
/* v8 ignore start */
|
|
33
|
-
if (debug.enabled) {
|
|
34
|
-
debugText.push(`res.cookie: name="${cookie.name}" value="${cookie.value}" options=${JSON.stringify(cookie.options)}`);
|
|
35
|
-
}
|
|
36
|
-
/* v8 ignore stop */
|
|
37
|
-
|
|
38
|
-
res.cookie(cookie.name, cookie.value, cookie.options);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
// If there is a "redirectLocation" header, we immediately redirect and return
|
|
42
|
-
if (typeof page.head.redirectLocation === 'string' && page.head.redirectLocation.length > 0) {
|
|
43
|
-
/* v8 ignore start */
|
|
44
|
-
if (debug.enabled) {
|
|
45
|
-
debugText.push(`res.redirect(302, "${page.head.redirectLocation}")`);
|
|
46
|
-
debug(getBlock('RESPONSE', debugText.join('\n')));
|
|
47
|
-
}
|
|
48
|
-
/* v8 ignore stop */
|
|
49
|
-
|
|
50
|
-
res.redirect(302, page.head.redirectLocation);
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Send all the "otherHeaders"
|
|
55
|
-
for (const key in page.head.otherHeaders) {
|
|
56
|
-
/* v8 ignore start */
|
|
57
|
-
if (debug.enabled) {
|
|
58
|
-
debugText.push(`res.set("${key}", "${page.head.otherHeaders[key]}")`);
|
|
59
|
-
}
|
|
60
|
-
/* v8 ignore stop */
|
|
61
|
-
|
|
62
|
-
res.set(key, page.head.otherHeaders[key]);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// If this is a file download, we eventually set the "Content-Type" and the file content and then return.
|
|
66
|
-
if (page.file.fileType === 'B' || page.file.fileType === 'F') {
|
|
67
|
-
/** @type {Record<string, string>} */
|
|
68
|
-
const headers = {};
|
|
69
|
-
|
|
70
|
-
if (typeof page.head.contentType === 'string' && page.head.contentType.length > 0) {
|
|
71
|
-
headers['Content-Type'] = page.head.contentType;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (typeof page.file.fileSize === 'number' && page.file.fileSize > 0) {
|
|
75
|
-
headers['Content-Length'] = page.file.fileSize.toString();
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (Object.keys(headers).length > 0) {
|
|
79
|
-
/* v8 ignore start */
|
|
80
|
-
if (debug.enabled) {
|
|
81
|
-
debugText.push(`res.writeHead(200, ${JSON.stringify(headers)})`);
|
|
82
|
-
}
|
|
83
|
-
/* v8 ignore stop */
|
|
84
|
-
|
|
85
|
-
res.writeHead(200, headers);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Check if fileBlob is a stream
|
|
89
|
-
if (page.file.fileBlob instanceof stream.Readable) {
|
|
90
|
-
/* v8 ignore start */
|
|
91
|
-
if (debug.enabled) {
|
|
92
|
-
debugText.push(`res.pipe("${page.file.fileType}") - streaming`);
|
|
93
|
-
debug(getBlock('RESPONSE', debugText.join('\n')));
|
|
94
|
-
}
|
|
95
|
-
/* v8 ignore stop */
|
|
96
|
-
|
|
97
|
-
/** @type {Promise<void>} */
|
|
98
|
-
const streamComplete = new Promise((resolve, reject) => {
|
|
99
|
-
if (page.file.fileBlob instanceof stream.Readable) {
|
|
100
|
-
page.file.fileBlob.pipe(res);
|
|
101
|
-
page.file.fileBlob.on('end', () => resolve());
|
|
102
|
-
/* v8 ignore next - error handler */
|
|
103
|
-
page.file.fileBlob.on('error', (/** @type {Error} */ err) => reject(err));
|
|
104
|
-
/* v8 ignore next - error handler */
|
|
105
|
-
res.on('close', () => resolve());
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
await streamComplete;
|
|
109
|
-
} else {
|
|
110
|
-
/* v8 ignore start */
|
|
111
|
-
if (debug.enabled) {
|
|
112
|
-
debugText.push(`res.end("${page.file.fileType}") - buffer`);
|
|
113
|
-
debug(getBlock('RESPONSE', debugText.join('\n')));
|
|
114
|
-
}
|
|
115
|
-
/* v8 ignore stop */
|
|
116
|
-
|
|
117
|
-
res.end(page.file.fileBlob, 'binary');
|
|
118
|
-
}
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Is the a "contentType" header
|
|
123
|
-
if (typeof page.head.contentType === 'string' && page.head.contentType.length > 0) {
|
|
124
|
-
/* v8 ignore start */
|
|
125
|
-
if (debug.enabled) {
|
|
126
|
-
debugText.push(`res.set("Content-Type", "${page.head.contentType}")`);
|
|
127
|
-
}
|
|
128
|
-
/* v8 ignore stop */
|
|
129
|
-
|
|
130
|
-
res.set('Content-Type', page.head.contentType);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// If we have a "Status" header, we send the header and then return.
|
|
134
|
-
if (typeof page.head.statusCode === 'number') {
|
|
135
|
-
/* v8 ignore start */
|
|
136
|
-
if (debug.enabled) {
|
|
137
|
-
debugText.push(`res.status(page.head.statusCode).send("${page.head.statusDescription}")`);
|
|
138
|
-
debug(getBlock('RESPONSE', debugText.join('\n')));
|
|
139
|
-
}
|
|
140
|
-
/* v8 ignore stop */
|
|
141
|
-
|
|
142
|
-
res.status(page.head.statusCode).send(page.head.statusDescription);
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Send the body
|
|
147
|
-
/* v8 ignore start */
|
|
148
|
-
if (debug.enabled) {
|
|
149
|
-
if (page.body instanceof stream.Readable) {
|
|
150
|
-
debugText.push(`${'-'.repeat(60)}\n[Stream Body]`);
|
|
151
|
-
} else {
|
|
152
|
-
debugText.push(`${'-'.repeat(60)}\n${page.body}`);
|
|
153
|
-
}
|
|
154
|
-
debug(getBlock('RESPONSE', debugText.join('\n')));
|
|
155
|
-
}
|
|
156
|
-
/* v8 ignore stop */
|
|
157
|
-
|
|
158
|
-
if (page.body instanceof stream.Readable) {
|
|
159
|
-
/** @type {Promise<void>} */
|
|
160
|
-
const streamComplete = new Promise((resolve, reject) => {
|
|
161
|
-
if (page.body instanceof stream.Readable) {
|
|
162
|
-
page.body.pipe(res);
|
|
163
|
-
page.body.on('end', () => resolve());
|
|
164
|
-
/* v8 ignore next - error handler */
|
|
165
|
-
page.body.on('error', (/** @type {Error} */ err) => reject(err));
|
|
166
|
-
/* v8 ignore next - error handler */
|
|
167
|
-
res.on('close', () => resolve());
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
await streamComplete;
|
|
171
|
-
} else {
|
|
172
|
-
res.send(page.body);
|
|
173
|
-
}
|
|
174
|
-
};
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import stream from 'node:stream';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Convert a readable stream to a buffer.
|
|
5
|
-
*
|
|
6
|
-
* @param {stream.Readable} readable - The readable stream.
|
|
7
|
-
* @returns {Promise<Buffer>} The buffer.
|
|
8
|
-
*/
|
|
9
|
-
export const streamToBuffer = async (readable) => {
|
|
10
|
-
return new Promise((resolve, reject) => {
|
|
11
|
-
/** @type {Buffer[]} */
|
|
12
|
-
const buffers = [];
|
|
13
|
-
|
|
14
|
-
readable.on('data', (buffer) => {
|
|
15
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
16
|
-
buffers.push(buffer);
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
readable.on('end', () => {
|
|
20
|
-
resolve(Buffer.concat(buffers));
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
readable.on('error', (err) => {
|
|
24
|
-
/* v8 ignore next - error handler */
|
|
25
|
-
reject(err);
|
|
26
|
-
});
|
|
27
|
-
});
|
|
28
|
-
};
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Process file uploads
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
// eslint-disable @typescript-eslint/no-unused-vars
|
|
6
|
-
|
|
7
|
-
import debugModule from 'debug';
|
|
8
|
-
const debug = debugModule('webplsql:fileUpload');
|
|
9
|
-
|
|
10
|
-
import {readFile, removeFile} from '../../util/file.js';
|
|
11
|
-
import {errorToString} from '../../util/errorToString.js';
|
|
12
|
-
import oracledb from 'oracledb';
|
|
13
|
-
import z from 'zod';
|
|
14
|
-
|
|
15
|
-
const z$reqFiles = z.array(
|
|
16
|
-
z.looseObject({
|
|
17
|
-
fieldname: z.string(),
|
|
18
|
-
originalname: z.string(),
|
|
19
|
-
encoding: z.string(),
|
|
20
|
-
mimetype: z.string(),
|
|
21
|
-
destination: z.string(),
|
|
22
|
-
filename: z.string(),
|
|
23
|
-
path: z.string(),
|
|
24
|
-
size: z.number(),
|
|
25
|
-
}),
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* @typedef {import('express').Request} Request
|
|
30
|
-
* @typedef {import('oracledb').Connection} Connection
|
|
31
|
-
* @typedef {import('../../types.js').fileUploadType} fileUploadType
|
|
32
|
-
*/
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Get the files
|
|
36
|
-
*
|
|
37
|
-
* @param {Request} req - The req object represents the HTTP request.
|
|
38
|
-
* @returns {fileUploadType[]} - Promise that resolves with an array of files to be uploaded.
|
|
39
|
-
*/
|
|
40
|
-
export const getFiles = (req) => {
|
|
41
|
-
if (!('files' in req)) {
|
|
42
|
-
debug('getFiles: no files');
|
|
43
|
-
return [];
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (typeof req.files === 'object' && req.files !== null && Object.keys(req.files).length === 0) {
|
|
47
|
-
debug('getFiles: no files');
|
|
48
|
-
return [];
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const files = z$reqFiles.parse(req.files);
|
|
52
|
-
|
|
53
|
-
for (const file of files) {
|
|
54
|
-
file.filename += `/${file.originalname}`;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
debug('getFiles', files);
|
|
58
|
-
|
|
59
|
-
return files;
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Upload the given file and return a promise.
|
|
64
|
-
*
|
|
65
|
-
* @param {fileUploadType} file - The file to upload.
|
|
66
|
-
* @param {string} doctable - The file to upload.
|
|
67
|
-
* @param {Connection} databaseConnection - The file to upload.
|
|
68
|
-
* @returns {Promise<void>} - Promise that resolves when uploaded.
|
|
69
|
-
*/
|
|
70
|
-
export const uploadFile = async (file, doctable, databaseConnection) => {
|
|
71
|
-
debug(`uploadFile`, file, doctable);
|
|
72
|
-
|
|
73
|
-
/* v8 ignore next - defensive validation */
|
|
74
|
-
if (typeof doctable !== 'string' || doctable.length === 0) {
|
|
75
|
-
throw new Error(`Unable to upload file "${file.filename}" because the option ""doctable" has not been defined`);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// read file
|
|
79
|
-
let blobContent;
|
|
80
|
-
try {
|
|
81
|
-
blobContent = await readFile(file.path);
|
|
82
|
-
} catch (err) {
|
|
83
|
-
throw new Error(`Unable to load file "${file.path}".\n${errorToString(err)}`);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// insert file in document table
|
|
87
|
-
const sql = `INSERT INTO ${doctable} (name, mime_type, doc_size, dad_charset, last_updated, content_type, blob_content) VALUES (:name, :mime_type, :doc_size, 'ascii', SYSDATE, 'BLOB', :blob_content)`;
|
|
88
|
-
const bind = {
|
|
89
|
-
name: file.filename,
|
|
90
|
-
mime_type: file.mimetype,
|
|
91
|
-
doc_size: file.size,
|
|
92
|
-
blob_content: {
|
|
93
|
-
val: blobContent,
|
|
94
|
-
type: oracledb.BUFFER,
|
|
95
|
-
},
|
|
96
|
-
};
|
|
97
|
-
try {
|
|
98
|
-
await databaseConnection.execute(sql, bind, {autoCommit: true});
|
|
99
|
-
} catch (err) {
|
|
100
|
-
throw new Error(`Unable to insert file "${file.filename}".\n${errorToString(err)}`);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// remove the file
|
|
104
|
-
try {
|
|
105
|
-
await removeFile(file.path);
|
|
106
|
-
} catch (err) {
|
|
107
|
-
throw new Error(`Unable to remove file "${file.filename}".\n${errorToString(err)}`);
|
|
108
|
-
}
|
|
109
|
-
};
|
package/src/index.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
// libraries
|
|
2
|
-
import oracledb from 'oracledb';
|
|
3
|
-
export {oracledb};
|
|
4
|
-
|
|
5
|
-
// server
|
|
6
|
-
export {getVersion} from './version.js';
|
|
7
|
-
export {createServer, startServer, loadConfig, startServerConfig} from './server/server.js';
|
|
8
|
-
export * from './util/shutdown.js';
|
|
9
|
-
|
|
10
|
-
// handler
|
|
11
|
-
export {handlerWebPlSql} from './handler/plsql/handlerPlSql.js';
|
|
12
|
-
export {handlerLogger} from './handler/handlerLogger.js';
|
|
13
|
-
export {handlerUpload} from './handler/handlerUpload.js';
|
|
14
|
-
|
|
15
|
-
// oracle
|
|
16
|
-
export * from './util/oracle.js';
|
|
17
|
-
|
|
18
|
-
// util
|
|
19
|
-
export * from './util/file.js';
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import {StatsManager} from '../util/statsManager.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @typedef {import('oracledb').Pool} Pool
|
|
5
|
-
* @typedef {import('../types.js').configType} configType
|
|
6
|
-
* @typedef {import('../handler/plsql/procedureNamed.js').argsType} argsType
|
|
7
|
-
* @typedef {import('../util/cache.js').Cache<unknown>} Cache
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Global Admin Context
|
|
12
|
-
*/
|
|
13
|
-
export const AdminContext = {
|
|
14
|
-
startTime: new Date(),
|
|
15
|
-
/** @type {configType | null} */
|
|
16
|
-
config: null,
|
|
17
|
-
/** @type {Pool[]} */
|
|
18
|
-
pools: [],
|
|
19
|
-
/** @type {Array<{poolName: string, procedureNameCache: Cache, argumentCache: Cache}>} */
|
|
20
|
-
caches: [],
|
|
21
|
-
paused: false,
|
|
22
|
-
statsManager: new StatsManager(),
|
|
23
|
-
};
|
package/src/server/config.js
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import {getVersion} from '../version.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @typedef {import('../types.js').configType} configType
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Show configuration.
|
|
9
|
-
* @param {configType} config - The config.
|
|
10
|
-
* @returns {void}
|
|
11
|
-
*/
|
|
12
|
-
export const showConfig = (config) => {
|
|
13
|
-
const LINE = '-'.repeat(80);
|
|
14
|
-
|
|
15
|
-
console.log(LINE);
|
|
16
|
-
console.log(`NODE PL/SQL SERVER version ${getVersion()}`);
|
|
17
|
-
console.log(LINE);
|
|
18
|
-
|
|
19
|
-
console.log(`Server port: ${config.port}`);
|
|
20
|
-
console.log(`Admin route: ${config.adminRoute ?? '/admin'}${config.adminUser ? ' (authenticated)' : ''}`);
|
|
21
|
-
console.log(`Access log: ${config.loggerFilename.length > 0 ? config.loggerFilename : ''}`);
|
|
22
|
-
console.log(`Upload file size limit: ${typeof config.uploadFileSizeLimit === 'number' ? `${config.uploadFileSizeLimit} bytes` : 'any'}`);
|
|
23
|
-
|
|
24
|
-
if (config.routeStatic.length > 0) {
|
|
25
|
-
console.log(LINE);
|
|
26
|
-
config.routeStatic.forEach((e) => {
|
|
27
|
-
console.log(`Static route: ${e.route}`);
|
|
28
|
-
console.log(`Directory path: ${e.directoryPath}`);
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (config.routePlSql.length > 0) {
|
|
33
|
-
console.log(LINE);
|
|
34
|
-
config.routePlSql.forEach((e) => {
|
|
35
|
-
let transactionMode = '';
|
|
36
|
-
if (typeof e.transactionMode === 'string') {
|
|
37
|
-
transactionMode = e.transactionMode;
|
|
38
|
-
} else if (typeof e.transactionMode === 'function') {
|
|
39
|
-
transactionMode = 'custom callback';
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
console.log(`Route: http://localhost:${config.port}${e.route}`);
|
|
43
|
-
console.log(`Oracle user: ${e.user}`);
|
|
44
|
-
console.log(`Oracle server: ${e.connectString}`);
|
|
45
|
-
console.log(`Oracle document table: ${e.documentTable}`);
|
|
46
|
-
console.log(`Default page: ${e.defaultPage}`);
|
|
47
|
-
console.log(`Path alias: ${e.pathAlias}`);
|
|
48
|
-
console.log(`Path alias procedure: ${e.pathAliasProcedure}`);
|
|
49
|
-
console.log(`Exclution list: ${e.exclusionList?.join(', ')}`);
|
|
50
|
-
console.log(`Validation function: ${e.requestValidationFunction}`);
|
|
51
|
-
console.log(`After request handler: ${transactionMode}`);
|
|
52
|
-
console.log(`Error style: ${e.errorStyle}`);
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
console.log(LINE);
|
|
57
|
-
};
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @typedef {import('node:net').Socket} Socket
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* @param {string} id - id
|
|
7
|
-
* @param {string} ip - address
|
|
8
|
-
* @param {string} port - port
|
|
9
|
-
* @param {string} read - bytes read
|
|
10
|
-
* @param {string} written - bytes written
|
|
11
|
-
*/
|
|
12
|
-
const line = (id, ip, port, read, written) => {
|
|
13
|
-
console.log(`${id.padStart(3)} ${ip.padEnd(15)} ${port.padStart(5)} ${read.padStart(10)} ${written.padStart(10)}`);
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Show connections.
|
|
18
|
-
* @param {Set<Socket>} connections - The connections.
|
|
19
|
-
* @returns {void}
|
|
20
|
-
*/
|
|
21
|
-
export const showConnections = (connections) => {
|
|
22
|
-
if (connections.size === 0) {
|
|
23
|
-
console.log('No open connections.');
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Print header
|
|
28
|
-
line('ID', 'IP', 'Port', 'Read', 'Written');
|
|
29
|
-
|
|
30
|
-
let i = 0;
|
|
31
|
-
for (const socket of connections) {
|
|
32
|
-
line(
|
|
33
|
-
(i++).toString(),
|
|
34
|
-
socket.remoteAddress ?? '',
|
|
35
|
-
typeof socket.remotePort === 'number' ? socket.remotePort.toFixed() : '',
|
|
36
|
-
socket.bytesRead.toFixed(),
|
|
37
|
-
socket.bytesWritten.toFixed(),
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
};
|