web_plsql 0.13.2 → 0.14.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.
- package/README.md +51 -23
- package/examples/config-native.js +41 -0
- package/examples/sql/install.sql +4 -0
- package/examples/sql/sample_package.sql +1 -1
- package/examples/sql/sample_package_body.sql +9 -3
- package/package.json +11 -11
- package/src/handler/handlerUpload.js +4 -2
- package/src/handler/plsql/handlerPlSql.js +1 -0
- package/src/handler/plsql/request.js +18 -1
- package/src/handler/plsql/upload.js +30 -11
- package/src/server/config.js +22 -14
- package/src/server/server.js +9 -9
- package/src/types.js +17 -0
- package/src/util/type.js +2 -0
- package/types/types.d.ts +27 -0
package/README.md
CHANGED
|
@@ -50,11 +50,6 @@ The `startServer` api uses the following configuration object:
|
|
|
50
50
|
/**
|
|
51
51
|
* @typedef {'basic' | 'debug'} errorStyleType
|
|
52
52
|
*/
|
|
53
|
-
export const z$errorStyleType = z.enum(['basic', 'debug']);
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* @typedef {{alias: string, procedure: string}} pathAliasType
|
|
57
|
-
*/
|
|
58
53
|
|
|
59
54
|
/**
|
|
60
55
|
* @typedef {object} configStaticType
|
|
@@ -63,14 +58,42 @@ export const z$errorStyleType = z.enum(['basic', 'debug']);
|
|
|
63
58
|
*/
|
|
64
59
|
|
|
65
60
|
/**
|
|
66
|
-
* @
|
|
61
|
+
* @callback transactionCallbackType
|
|
62
|
+
* @param {Request} req - Incoming request object.
|
|
63
|
+
* @param {import('oracledb').Connection} connection - Active database connection pool.
|
|
64
|
+
* @returns {void | Promise<void>}
|
|
65
|
+
*/
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @typedef {'commit' | 'rollback' | transactionCallbackType | undefined | null} transactionModeType
|
|
69
|
+
*/
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @typedef {object} configPlSqlHandlerType
|
|
73
|
+
* @property {string} defaultPage - The default page.
|
|
74
|
+
* @property {string} [pathAlias] - The path alias.
|
|
75
|
+
* @property {string} [pathAliasProcedure] - The path alias.
|
|
76
|
+
* @property {string} documentTable - The document table.
|
|
77
|
+
* @property {string[]} [exclusionList] - The exclusion list.
|
|
78
|
+
* @property {string} [requestValidationFunction] - The request validation function.
|
|
79
|
+
* @property {Record<string, string>} [cgi] - The additional CGI.
|
|
80
|
+
* @property {transactionModeType} [transactionMode='commit'] - Specifies an optional transaction mode.
|
|
81
|
+
* "commit" this automatically commits any open transaction after each request. This is the defaults because this is what mod_plsql and ohs are doing.
|
|
82
|
+
* "rollback" this automatically rolles back any open transaction after each request.
|
|
83
|
+
* "transactionCallbackType" this allows to defined a custom handler as a JavaScript function.
|
|
84
|
+
* @property {errorStyleType} errorStyle - The error style.
|
|
85
|
+
*/
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @typedef {object} configPlSqlConfigType
|
|
67
89
|
* @property {string} route - The PL/SQL route path.
|
|
68
90
|
* @property {string} user - The Oracle username.
|
|
69
91
|
* @property {string} password - The Oracle password.
|
|
70
92
|
* @property {string} connectString - The Oracle connect string.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
93
|
+
*/
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @typedef {configPlSqlHandlerType & configPlSqlConfigType} configPlSqlType
|
|
74
97
|
*/
|
|
75
98
|
|
|
76
99
|
/**
|
|
@@ -78,7 +101,6 @@ export const z$errorStyleType = z.enum(['basic', 'debug']);
|
|
|
78
101
|
* @property {number} port - The server port number.
|
|
79
102
|
* @property {configStaticType[]} routeStatic - The static routes.
|
|
80
103
|
* @property {configPlSqlType[]} routePlSql - The PL/SQL routes.
|
|
81
|
-
* @property {errorStyleType} errorStyle - The error style.
|
|
82
104
|
* @property {string} loggerFilename - name of the request logger filename or '' if not required.
|
|
83
105
|
*/
|
|
84
106
|
```
|
|
@@ -147,30 +169,36 @@ The following mod_plsql DAD configuration translates to the configuration option
|
|
|
147
169
|
# Configuration options
|
|
148
170
|
|
|
149
171
|
## Supported mod_plsql configuration options
|
|
150
|
-
- PlsqlDatabaseConnectString
|
|
151
|
-
- PlsqlDatabaseUserName
|
|
152
|
-
- PlsqlDatabasePassword
|
|
153
|
-
- PlsqlDefaultPage
|
|
154
|
-
- PlsqlDocumentTablename
|
|
155
|
-
- PlsqlErrorStyle
|
|
156
|
-
- PlsqlLogEnable
|
|
157
|
-
- PlsqlLogDirectory
|
|
158
|
-
- PlsqlPathAlias
|
|
159
|
-
- PlsqlPathAliasProcedure
|
|
172
|
+
- PlsqlDatabaseConnectString -> routePlSql[].connectString
|
|
173
|
+
- PlsqlDatabaseUserName -> routePlSql[].user
|
|
174
|
+
- PlsqlDatabasePassword -> routePlSql[].password
|
|
175
|
+
- PlsqlDefaultPage -> routePlSql[].defaultPage
|
|
176
|
+
- PlsqlDocumentTablename -> routePlSql[].documentTable
|
|
177
|
+
- PlsqlErrorStyle -> routePlSql[].errorStyle
|
|
178
|
+
- PlsqlLogEnable -> loggerFilename
|
|
179
|
+
- PlsqlLogDirectory -> loggerFilename
|
|
180
|
+
- PlsqlPathAlias -> routePlSql[].pathAlias
|
|
181
|
+
- PlsqlPathAliasProcedure -> routePlSql[].pathAliasProcedure
|
|
160
182
|
- Default exclusion list.
|
|
161
|
-
- PlsqlRequestValidationFunction
|
|
183
|
+
- PlsqlRequestValidationFunction -> routePlSql[].pathAliasProcedure
|
|
162
184
|
- PlsqlExclusionList
|
|
163
185
|
- Basic and custom authentication methods, based on the OWA_SEC package and custom packages.
|
|
164
186
|
|
|
187
|
+
## Features that are only available in web_plsql
|
|
188
|
+
- The option `transactionModeType` specifies an optional transaction mode.
|
|
189
|
+
"commit" this automatically commits any open transaction after each request. This is the defaults because this is what mod_plsql and ohs are doing.
|
|
190
|
+
"rollback" this automatically rolles back any open transaction after each request.
|
|
191
|
+
"transactionCallbackType" this allows to defined a custom handler as a JavaScript function.
|
|
192
|
+
|
|
165
193
|
## Features that are planned to be available in web_plsql
|
|
166
194
|
- Support for APEX 5 or greater.
|
|
167
195
|
|
|
168
196
|
## Configuration options that will not be supported:
|
|
169
|
-
- PlsqlDocumentProcedure
|
|
170
|
-
- PlsqlAfterProcedure
|
|
171
197
|
- PlsqlAlwaysDescribeProcedure
|
|
198
|
+
- PlsqlAfterProcedure
|
|
172
199
|
- PlsqlBeforeProcedure
|
|
173
200
|
- PlsqlCGIEnvironmentList
|
|
201
|
+
- PlsqlDocumentProcedure
|
|
174
202
|
- PlsqlDocumentPath
|
|
175
203
|
- PlsqlIdleSessionCleanupInterval
|
|
176
204
|
- PlsqlSessionCookieName
|
|
@@ -1,6 +1,39 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import {startServer} from '../src/index.js';
|
|
4
|
+
import oracledb from 'oracledb';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @param {import('oracledb').Connection} connection - Oracle DB connection.
|
|
8
|
+
* @returns {Promise<boolean>}
|
|
9
|
+
*/
|
|
10
|
+
const hasOpenTransaction = async (connection) => {
|
|
11
|
+
const sql = `SELECT COUNT(*) FROM sys.v_$transaction t JOIN sys.v_$session s ON t.ses_addr = s.saddr WHERE s.audsid = USERENV('SESSIONID')`;
|
|
12
|
+
/** @type {import('oracledb').Result<number>} */
|
|
13
|
+
let result;
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
result = await connection.execute(sql, [], {
|
|
17
|
+
outFormat: oracledb.OUT_FORMAT_ARRAY,
|
|
18
|
+
});
|
|
19
|
+
} catch (error) {
|
|
20
|
+
console.error('hasOpenTransaction: error', error, sql);
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (
|
|
25
|
+
!Array.isArray(result.rows) ||
|
|
26
|
+
result.rows.length !== 1 ||
|
|
27
|
+
!Array.isArray(result.rows[0]) ||
|
|
28
|
+
result.rows[0].length !== 1 ||
|
|
29
|
+
typeof result.rows[0][0] !== 'number'
|
|
30
|
+
) {
|
|
31
|
+
console.error('hasOpenTransaction: invalid result', result, sql);
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return result.rows[0] > 0;
|
|
36
|
+
};
|
|
4
37
|
|
|
5
38
|
void startServer({
|
|
6
39
|
port: 8888,
|
|
@@ -22,6 +55,14 @@ void startServer({
|
|
|
22
55
|
requestValidationFunction: 'sample_pkg.request_validation_function', // PlsqlRequestValidationFunction
|
|
23
56
|
pathAlias: 'myalias', // PlsqlPathAlias
|
|
24
57
|
pathAliasProcedure: 'sample_pkg.page_path_alias', // PlsqlPathAliasProcedure
|
|
58
|
+
transactionMode: 'commit',
|
|
59
|
+
/*
|
|
60
|
+
transactionMode: async (req, connection) => {
|
|
61
|
+
if (await hasOpenTransaction(connection)) {
|
|
62
|
+
console.log(`We detected an open transaction in "${req.originalUrl}"`);
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
*/
|
|
25
66
|
errorStyle: 'debug', // PlsqlErrorStyle
|
|
26
67
|
},
|
|
27
68
|
],
|
package/examples/sql/install.sql
CHANGED
|
@@ -15,6 +15,10 @@ GRANT create sequence TO &&SAMPLE_USER;
|
|
|
15
15
|
GRANT create procedure TO &&SAMPLE_USER;
|
|
16
16
|
GRANT execute on dbms_lob TO &&SAMPLE_USER;
|
|
17
17
|
|
|
18
|
+
-- only required if you want to test for open connections
|
|
19
|
+
GRANT select on sys.v_$transaction TO &&SAMPLE_USER;
|
|
20
|
+
GRANT select on sys.v_$session TO &&SAMPLE_USER;
|
|
21
|
+
|
|
18
22
|
-- change schema
|
|
19
23
|
ALTER SESSION SET CURRENT_SCHEMA=&&SAMPLE_USER;
|
|
20
24
|
|
|
@@ -18,7 +18,7 @@ PROCEDURE page_file_upload;
|
|
|
18
18
|
PROCEDURE page_file_uploaded(name_array IN owa.vc_arr, value_array IN owa.vc_arr);
|
|
19
19
|
PROCEDURE page_redirect;
|
|
20
20
|
PROCEDURE page_location;
|
|
21
|
-
PROCEDURE
|
|
21
|
+
PROCEDURE page_open_transaction;
|
|
22
22
|
PROCEDURE page_path_alias(p_path IN VARCHAR2);
|
|
23
23
|
PROCEDURE page_exclusion_list;
|
|
24
24
|
PROCEDURE page_request_validation_function;
|
|
@@ -32,6 +32,7 @@ BEGIN
|
|
|
32
32
|
htp.p('<li><a href="sample_pkg.page_file_upload">File upload</a></li>');
|
|
33
33
|
htp.p('<li><a href="sample_pkg.page_redirect">Redirect</a></li>');
|
|
34
34
|
htp.p('<li><a href="sample_pkg.page_location">Change location</a></li>');
|
|
35
|
+
htp.p('<li><a href="sample_pkg.page_open_transaction">Transaction mode: custom handler</a></li>');
|
|
35
36
|
htp.p('<li><a href="myalias">pathAlias configuration setting</a></li>');
|
|
36
37
|
htp.p('<li><a href="sample_pkg.page_exclusion_list">Custom exclusion list</a></li>');
|
|
37
38
|
htp.p('<li><a href="sample_pkg.page_request_validation_function">Request validation function</a></li>');
|
|
@@ -236,12 +237,17 @@ BEGIN
|
|
|
236
237
|
close_page();
|
|
237
238
|
END page_location;
|
|
238
239
|
|
|
239
|
-
PROCEDURE
|
|
240
|
+
PROCEDURE page_open_transaction
|
|
240
241
|
IS
|
|
242
|
+
l_name CONSTANT docTable.name%TYPE := 'page_open_transaction';
|
|
241
243
|
BEGIN
|
|
242
|
-
open_page('web_plsql -
|
|
244
|
+
open_page('web_plsql - Transaction mode: custom handler');
|
|
245
|
+
|
|
246
|
+
DELETE docTable WHERE name = l_name;
|
|
247
|
+
INSERT INTO docTable (name) VALUES (l_name);
|
|
248
|
+
|
|
243
249
|
close_page();
|
|
244
|
-
END
|
|
250
|
+
END page_open_transaction;
|
|
245
251
|
|
|
246
252
|
PROCEDURE page_path_alias(p_path IN VARCHAR2)
|
|
247
253
|
IS
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "web_plsql",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"author": "Dieter Oberkofler <dieter.oberkofler@gmail.com>",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "The Express Middleware for Oracle PL/SQL",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"image-save": "docker save web_plsql | gzip > web_plsql.tar.gz"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"oracledb": "6.
|
|
56
|
+
"oracledb": "6.10.0"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
59
|
"basic-auth": "2.0.1",
|
|
@@ -67,27 +67,27 @@
|
|
|
67
67
|
"morgan": "1.10.1",
|
|
68
68
|
"multer": "2.0.2",
|
|
69
69
|
"rotating-file-stream": "3.2.7",
|
|
70
|
-
"zod": "4.1.
|
|
70
|
+
"zod": "4.1.12"
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
|
73
73
|
"@types/basic-auth": "1.1.8",
|
|
74
74
|
"@types/compression": "1.8.1",
|
|
75
|
-
"@types/cookie-parser": "1.4.
|
|
75
|
+
"@types/cookie-parser": "1.4.10",
|
|
76
76
|
"@types/debug": "4.1.12",
|
|
77
77
|
"@types/escape-html": "1.0.4",
|
|
78
78
|
"@types/fs-extra": "11.0.4",
|
|
79
79
|
"@types/morgan": "1.9.10",
|
|
80
80
|
"@types/multer": "2.0.0",
|
|
81
|
-
"@types/node": "24.
|
|
82
|
-
"@types/oracledb": "6.
|
|
81
|
+
"@types/node": "24.9.2",
|
|
82
|
+
"@types/oracledb": "6.10.0",
|
|
83
83
|
"@types/supertest": "6.0.3",
|
|
84
|
-
"eslint": "9.
|
|
85
|
-
"eslint-plugin-jsdoc": "
|
|
84
|
+
"eslint": "9.39.0",
|
|
85
|
+
"eslint-plugin-jsdoc": "61.1.11",
|
|
86
86
|
"prettier": "3.6.2",
|
|
87
|
-
"rimraf": "6.0
|
|
87
|
+
"rimraf": "6.1.0",
|
|
88
88
|
"shx": "0.4.0",
|
|
89
89
|
"supertest": "7.1.4",
|
|
90
|
-
"typescript": "5.9.
|
|
91
|
-
"typescript-eslint": "8.
|
|
90
|
+
"typescript": "5.9.3",
|
|
91
|
+
"typescript-eslint": "8.46.2"
|
|
92
92
|
}
|
|
93
93
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
1
|
+
//import path from 'node:path';
|
|
2
2
|
import multer from 'multer';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -17,11 +17,13 @@ import multer from 'multer';
|
|
|
17
17
|
export const handlerUpload = () => {
|
|
18
18
|
const upload = multer({
|
|
19
19
|
storage: multer.diskStorage({
|
|
20
|
-
|
|
20
|
+
/*
|
|
21
|
+
destination: '/tmp/webplsql',
|
|
21
22
|
filename: (req, file, cb) => {
|
|
22
23
|
const uniqueSuffix = `${Date.now()}-${Math.round(Math.random() * 1e9)}`;
|
|
23
24
|
cb(null, `${file.fieldname}-${uniqueSuffix}${path.extname(file.originalname)}`);
|
|
24
25
|
},
|
|
26
|
+
*/
|
|
25
27
|
}),
|
|
26
28
|
limits: {
|
|
27
29
|
fileSize: 50 * 1024 * 1024, // 50MB limit
|
|
@@ -41,6 +41,7 @@ const requestHandler = async (req, res, next, connectionPool, options) => {
|
|
|
41
41
|
errorPage(req, res, options, new RequestError('No procedure name given and no default page has been specified'));
|
|
42
42
|
}
|
|
43
43
|
} else {
|
|
44
|
+
// request handler
|
|
44
45
|
await processRequest(req, res, options, connectionPool);
|
|
45
46
|
}
|
|
46
47
|
} catch (err) {
|
|
@@ -49,7 +49,7 @@ export const processRequest = async (req, res, options, connectionPool) => {
|
|
|
49
49
|
const argObj = {};
|
|
50
50
|
Object.assign(argObj, req.query);
|
|
51
51
|
|
|
52
|
-
// For add the files that must be uploaded, we now copy the actual filename to the appropriate
|
|
52
|
+
// For add the files that must be uploaded, we now copy the actual filename to the appropriate parameter to the invoked procedure.
|
|
53
53
|
filesToUpload.reduce((aggregator, file) => {
|
|
54
54
|
aggregator[file.fieldname] = file.filename;
|
|
55
55
|
return aggregator;
|
|
@@ -57,10 +57,27 @@ export const processRequest = async (req, res, options, connectionPool) => {
|
|
|
57
57
|
|
|
58
58
|
// Does the request contain a body
|
|
59
59
|
Object.assign(argObj, normalizeBody(req));
|
|
60
|
+
debug('executeRequest: argObj=', argObj);
|
|
60
61
|
|
|
61
62
|
// invoke the Oracle procedure and get the page contenst
|
|
62
63
|
await invokeProcedure(req, res, argObj, cgiObj, filesToUpload, options, connection);
|
|
63
64
|
|
|
65
|
+
// transaction mode
|
|
66
|
+
if (options.transactionMode === 'rollback') {
|
|
67
|
+
debug('transactionMode: rollback');
|
|
68
|
+
await connection.rollback();
|
|
69
|
+
} else if (typeof options.transactionMode === 'function') {
|
|
70
|
+
debug('transactionMode: callback');
|
|
71
|
+
const result = options.transactionMode(req, connection);
|
|
72
|
+
debug('transactionMode: callback restult', result);
|
|
73
|
+
if (result && typeof result.then === 'function') {
|
|
74
|
+
await result;
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
debug('transactionMode: commit');
|
|
78
|
+
await connection.commit();
|
|
79
|
+
}
|
|
80
|
+
|
|
64
81
|
// close database connection
|
|
65
82
|
await connection.release();
|
|
66
83
|
|
|
@@ -8,6 +8,7 @@ import debugModule from 'debug';
|
|
|
8
8
|
const debug = debugModule('webplsql:fileUpload');
|
|
9
9
|
|
|
10
10
|
import {readFile, removeFile} from '../../util/file.js';
|
|
11
|
+
import {errorToString} from '../../util/errorToString.js';
|
|
11
12
|
import oracledb from 'oracledb';
|
|
12
13
|
import z from 'zod';
|
|
13
14
|
|
|
@@ -37,20 +38,25 @@ const z$reqFiles = z.array(
|
|
|
37
38
|
* @returns {fileUploadType[]} - Promise that resolves with an array of files to be uploaded.
|
|
38
39
|
*/
|
|
39
40
|
export const getFiles = (req) => {
|
|
40
|
-
/** @type {fileUploadType[]} */
|
|
41
|
-
const files = [];
|
|
42
|
-
|
|
43
41
|
if (!('files' in req)) {
|
|
44
|
-
|
|
42
|
+
debug('getFiles: no files');
|
|
43
|
+
return [];
|
|
45
44
|
}
|
|
46
45
|
|
|
47
46
|
if (typeof req.files === 'object' && req.files !== null && Object.keys(req.files).length === 0) {
|
|
48
|
-
|
|
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}`;
|
|
49
55
|
}
|
|
50
56
|
|
|
51
|
-
debug('
|
|
57
|
+
debug('getFiles', files);
|
|
52
58
|
|
|
53
|
-
return
|
|
59
|
+
return files;
|
|
54
60
|
};
|
|
55
61
|
|
|
56
62
|
/**
|
|
@@ -62,12 +68,22 @@ export const getFiles = (req) => {
|
|
|
62
68
|
* @returns {Promise<void>} - Promise that resolves when uploaded.
|
|
63
69
|
*/
|
|
64
70
|
export const uploadFile = async (file, doctable, databaseConnection) => {
|
|
71
|
+
debug(`uploadFile`, file, doctable);
|
|
72
|
+
|
|
65
73
|
/* istanbul ignore next */
|
|
66
74
|
if (typeof doctable !== 'string' || doctable.length === 0) {
|
|
67
75
|
throw new Error(`Unable to upload file "${file.filename}" because the option ""doctable" has not been defined`);
|
|
68
76
|
}
|
|
69
77
|
|
|
70
|
-
|
|
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
|
|
71
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)`;
|
|
72
88
|
const bind = {
|
|
73
89
|
name: file.filename,
|
|
@@ -78,13 +94,16 @@ export const uploadFile = async (file, doctable, databaseConnection) => {
|
|
|
78
94
|
type: oracledb.BUFFER,
|
|
79
95
|
},
|
|
80
96
|
};
|
|
81
|
-
|
|
82
97
|
try {
|
|
83
98
|
await databaseConnection.execute(sql, bind, {autoCommit: true});
|
|
84
99
|
} catch (err) {
|
|
85
|
-
throw new Error(`Unable to insert file "${file.filename}"}`);
|
|
100
|
+
throw new Error(`Unable to insert file "${file.filename}".\n${errorToString(err)}`);
|
|
86
101
|
}
|
|
87
102
|
|
|
88
103
|
// remove the file
|
|
89
|
-
|
|
104
|
+
try {
|
|
105
|
+
await removeFile(file.path);
|
|
106
|
+
} catch (err) {
|
|
107
|
+
throw new Error(`Unable to remove file "${file.filename}".\n${errorToString(err)}`);
|
|
108
|
+
}
|
|
90
109
|
};
|
package/src/server/config.js
CHANGED
|
@@ -14,30 +14,38 @@ export const showConfig = (config) => {
|
|
|
14
14
|
console.log('NODE PL/SQL SERVER');
|
|
15
15
|
console.log(LINE);
|
|
16
16
|
|
|
17
|
-
console.log(`Server port:
|
|
18
|
-
console.log(`Access log:
|
|
17
|
+
console.log(`Server port: ${config.port}`);
|
|
18
|
+
console.log(`Access log: ${config.loggerFilename.length > 0 ? config.loggerFilename : ''}`);
|
|
19
19
|
|
|
20
20
|
if (config.routeStatic.length > 0) {
|
|
21
21
|
console.log(LINE);
|
|
22
22
|
config.routeStatic.forEach((e) => {
|
|
23
|
-
console.log(`Static route:
|
|
24
|
-
console.log(`Directory path:
|
|
23
|
+
console.log(`Static route: ${e.route}`);
|
|
24
|
+
console.log(`Directory path: ${e.directoryPath}`);
|
|
25
25
|
});
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
if (config.routePlSql.length > 0) {
|
|
29
29
|
console.log(LINE);
|
|
30
30
|
config.routePlSql.forEach((e) => {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
console.log(`
|
|
39
|
-
console.log(`
|
|
40
|
-
console.log(`
|
|
31
|
+
let transactionMode = '';
|
|
32
|
+
if (typeof e.transactionMode === 'string') {
|
|
33
|
+
transactionMode = e.transactionMode;
|
|
34
|
+
} else if (typeof e.transactionMode === 'function') {
|
|
35
|
+
transactionMode = 'custom callback';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
console.log(`Route: http://localhost:${config.port}${e.route}`);
|
|
39
|
+
console.log(`Oracle user: ${e.user}`);
|
|
40
|
+
console.log(`Oracle server: ${e.connectString}`);
|
|
41
|
+
console.log(`Oracle document table: ${e.documentTable}`);
|
|
42
|
+
console.log(`Default page: ${e.defaultPage}`);
|
|
43
|
+
console.log(`Path alias: ${e.pathAlias}`);
|
|
44
|
+
console.log(`Path alias procedure: ${e.pathAliasProcedure}`);
|
|
45
|
+
console.log(`Exclution list: ${e.exclusionList?.join(', ')}`);
|
|
46
|
+
console.log(`Validation function: ${e.requestValidationFunction}`);
|
|
47
|
+
console.log(`After request handler: ${transactionMode}`);
|
|
48
|
+
console.log(`Error style: ${e.errorStyle}`);
|
|
41
49
|
});
|
|
42
50
|
}
|
|
43
51
|
|
package/src/server/server.js
CHANGED
|
@@ -67,20 +67,20 @@ export const createServer = (app, ssl) => {
|
|
|
67
67
|
export const startServer = async (config, ssl) => {
|
|
68
68
|
debug('startServer: BEGIN', config, ssl);
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
const internalConfig = /** @type {configType} */ (z$configType.parse(config));
|
|
71
71
|
|
|
72
|
-
showConfig(
|
|
72
|
+
showConfig(internalConfig);
|
|
73
73
|
|
|
74
74
|
// Create express app
|
|
75
75
|
const app = express();
|
|
76
76
|
|
|
77
77
|
// Access log
|
|
78
|
-
if (
|
|
79
|
-
app.use(handlerLogger(
|
|
78
|
+
if (internalConfig.loggerFilename.length > 0) {
|
|
79
|
+
app.use(handlerLogger(internalConfig.loggerFilename));
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
// Serving static files
|
|
83
|
-
for (const i of
|
|
83
|
+
for (const i of internalConfig.routeStatic) {
|
|
84
84
|
app.use(i.route, express.static(i.directoryPath));
|
|
85
85
|
}
|
|
86
86
|
|
|
@@ -95,7 +95,7 @@ export const startServer = async (config, ssl) => {
|
|
|
95
95
|
const connectionPools = [];
|
|
96
96
|
|
|
97
97
|
// Oracle pl/sql express middleware
|
|
98
|
-
for (const i of
|
|
98
|
+
for (const i of internalConfig.routePlSql) {
|
|
99
99
|
// Allocate the Oracle database pool
|
|
100
100
|
const pool = await poolCreate(i.user, i.password, i.connectString);
|
|
101
101
|
connectionPools.push(pool);
|
|
@@ -145,7 +145,7 @@ export const startServer = async (config, ssl) => {
|
|
|
145
145
|
await /** @type {Promise<void>} */ (
|
|
146
146
|
new Promise((resolve, reject) => {
|
|
147
147
|
server
|
|
148
|
-
.listen(
|
|
148
|
+
.listen(internalConfig.port)
|
|
149
149
|
.on('listening', () => {
|
|
150
150
|
debug('startServer: listener running');
|
|
151
151
|
resolve();
|
|
@@ -160,7 +160,7 @@ export const startServer = async (config, ssl) => {
|
|
|
160
160
|
debug('startServer: END');
|
|
161
161
|
|
|
162
162
|
return {
|
|
163
|
-
config,
|
|
163
|
+
config: internalConfig,
|
|
164
164
|
connectionPools,
|
|
165
165
|
app,
|
|
166
166
|
server,
|
|
@@ -173,7 +173,7 @@ export const startServer = async (config, ssl) => {
|
|
|
173
173
|
* @param {string} [filename] - The configuration filename.
|
|
174
174
|
* @returns {configType} - Promise.
|
|
175
175
|
*/
|
|
176
|
-
export const loadConfig = (filename = 'config.json') => z$configType.parse(getJsonFile(filename));
|
|
176
|
+
export const loadConfig = (filename = 'config.json') => /** @type {configType} */ (z$configType.parse(getJsonFile(filename)));
|
|
177
177
|
|
|
178
178
|
/**
|
|
179
179
|
* Start server from config file.
|
package/src/types.js
CHANGED
|
@@ -3,6 +3,7 @@ import z from 'zod';
|
|
|
3
3
|
/**
|
|
4
4
|
* @typedef {import('oracledb').BindParameter} BindParameter
|
|
5
5
|
* @typedef {import('express').CookieOptions} CookieOptions
|
|
6
|
+
* @typedef {import('express').Request} Request
|
|
6
7
|
*/
|
|
7
8
|
|
|
8
9
|
/**
|
|
@@ -20,6 +21,17 @@ export const z$configStaticType = z.strictObject({
|
|
|
20
21
|
directoryPath: z.string(),
|
|
21
22
|
});
|
|
22
23
|
|
|
24
|
+
/**
|
|
25
|
+
* @callback transactionCallbackType
|
|
26
|
+
* @param {Request} req - Incoming request object.
|
|
27
|
+
* @param {import('oracledb').Connection} connection - Active database connection pool.
|
|
28
|
+
* @returns {void | Promise<void>}
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @typedef {'commit' | 'rollback' | transactionCallbackType | undefined | null} transactionModeType
|
|
33
|
+
*/
|
|
34
|
+
|
|
23
35
|
/**
|
|
24
36
|
* @typedef {object} configPlSqlHandlerType
|
|
25
37
|
* @property {string} defaultPage - The default page.
|
|
@@ -29,6 +41,10 @@ export const z$configStaticType = z.strictObject({
|
|
|
29
41
|
* @property {string[]} [exclusionList] - The exclusion list.
|
|
30
42
|
* @property {string} [requestValidationFunction] - The request validation function.
|
|
31
43
|
* @property {Record<string, string>} [cgi] - The additional CGI.
|
|
44
|
+
* @property {transactionModeType} [transactionMode='commit'] - Specifies an optional transaction mode.
|
|
45
|
+
* "commit" this automatically commits any open transaction after each request. This is the defaults because this is what mod_plsql and ohs are doing.
|
|
46
|
+
* "rollback" this automatically rolles back any open transaction after each request.
|
|
47
|
+
* "transactionCallbackType" this allows to defined a custom handler as a JavaScript function.
|
|
32
48
|
* @property {errorStyleType} errorStyle - The error style.
|
|
33
49
|
*/
|
|
34
50
|
export const z$configPlSqlHandlerType = z.strictObject({
|
|
@@ -38,6 +54,7 @@ export const z$configPlSqlHandlerType = z.strictObject({
|
|
|
38
54
|
documentTable: z.string(),
|
|
39
55
|
exclusionList: z.array(z.string()).optional(),
|
|
40
56
|
requestValidationFunction: z.string().optional(),
|
|
57
|
+
transactionMode: z.unknown().optional(),
|
|
41
58
|
errorStyle: z$errorStyleType,
|
|
42
59
|
});
|
|
43
60
|
|
package/src/util/type.js
CHANGED
package/types/types.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @typedef {import('oracledb').BindParameter} BindParameter
|
|
3
3
|
* @typedef {import('express').CookieOptions} CookieOptions
|
|
4
|
+
* @typedef {import('express').Request} Request
|
|
4
5
|
*/
|
|
5
6
|
/**
|
|
6
7
|
* @typedef {'basic' | 'debug'} errorStyleType
|
|
@@ -18,6 +19,15 @@ export const z$configStaticType: z.ZodObject<{
|
|
|
18
19
|
route: z.ZodString;
|
|
19
20
|
directoryPath: z.ZodString;
|
|
20
21
|
}, z.core.$strict>;
|
|
22
|
+
/**
|
|
23
|
+
* @callback transactionCallbackType
|
|
24
|
+
* @param {Request} req - Incoming request object.
|
|
25
|
+
* @param {import('oracledb').Connection} connection - Active database connection pool.
|
|
26
|
+
* @returns {void | Promise<void>}
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* @typedef {'commit' | 'rollback' | transactionCallbackType | undefined | null} transactionModeType
|
|
30
|
+
*/
|
|
21
31
|
/**
|
|
22
32
|
* @typedef {object} configPlSqlHandlerType
|
|
23
33
|
* @property {string} defaultPage - The default page.
|
|
@@ -27,6 +37,10 @@ export const z$configStaticType: z.ZodObject<{
|
|
|
27
37
|
* @property {string[]} [exclusionList] - The exclusion list.
|
|
28
38
|
* @property {string} [requestValidationFunction] - The request validation function.
|
|
29
39
|
* @property {Record<string, string>} [cgi] - The additional CGI.
|
|
40
|
+
* @property {transactionModeType} [transactionMode='commit'] - Specifies an optional transaction mode.
|
|
41
|
+
* "commit" this automatically commits any open transaction after each request. This is the defaults because this is what mod_plsql and ohs are doing.
|
|
42
|
+
* "rollback" this automatically rolles back any open transaction after each request.
|
|
43
|
+
* "transactionCallbackType" this allows to defined a custom handler as a JavaScript function.
|
|
30
44
|
* @property {errorStyleType} errorStyle - The error style.
|
|
31
45
|
*/
|
|
32
46
|
export const z$configPlSqlHandlerType: z.ZodObject<{
|
|
@@ -36,6 +50,7 @@ export const z$configPlSqlHandlerType: z.ZodObject<{
|
|
|
36
50
|
documentTable: z.ZodString;
|
|
37
51
|
exclusionList: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
38
52
|
requestValidationFunction: z.ZodOptional<z.ZodString>;
|
|
53
|
+
transactionMode: z.ZodOptional<z.ZodUnknown>;
|
|
39
54
|
errorStyle: z.ZodEnum<{
|
|
40
55
|
basic: "basic";
|
|
41
56
|
debug: "debug";
|
|
@@ -68,6 +83,7 @@ export const z$configPlSqlType: z.ZodObject<{
|
|
|
68
83
|
documentTable: z.ZodString;
|
|
69
84
|
exclusionList: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
70
85
|
requestValidationFunction: z.ZodOptional<z.ZodString>;
|
|
86
|
+
transactionMode: z.ZodOptional<z.ZodUnknown>;
|
|
71
87
|
errorStyle: z.ZodEnum<{
|
|
72
88
|
basic: "basic";
|
|
73
89
|
debug: "debug";
|
|
@@ -97,6 +113,7 @@ export const z$configType: z.ZodObject<{
|
|
|
97
113
|
documentTable: z.ZodString;
|
|
98
114
|
exclusionList: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
99
115
|
requestValidationFunction: z.ZodOptional<z.ZodString>;
|
|
116
|
+
transactionMode: z.ZodOptional<z.ZodUnknown>;
|
|
100
117
|
errorStyle: z.ZodEnum<{
|
|
101
118
|
basic: "basic";
|
|
102
119
|
debug: "debug";
|
|
@@ -106,6 +123,7 @@ export const z$configType: z.ZodObject<{
|
|
|
106
123
|
}, z.core.$strict>;
|
|
107
124
|
export type BindParameter = import("oracledb").BindParameter;
|
|
108
125
|
export type CookieOptions = import("express").CookieOptions;
|
|
126
|
+
export type Request = import("express").Request;
|
|
109
127
|
export type errorStyleType = "basic" | "debug";
|
|
110
128
|
export type configStaticType = {
|
|
111
129
|
/**
|
|
@@ -117,6 +135,8 @@ export type configStaticType = {
|
|
|
117
135
|
*/
|
|
118
136
|
directoryPath: string;
|
|
119
137
|
};
|
|
138
|
+
export type transactionCallbackType = (req: Request, connection: import("oracledb").Connection) => void | Promise<void>;
|
|
139
|
+
export type transactionModeType = "commit" | "rollback" | transactionCallbackType | undefined | null;
|
|
120
140
|
export type configPlSqlHandlerType = {
|
|
121
141
|
/**
|
|
122
142
|
* - The default page.
|
|
@@ -146,6 +166,13 @@ export type configPlSqlHandlerType = {
|
|
|
146
166
|
* - The additional CGI.
|
|
147
167
|
*/
|
|
148
168
|
cgi?: Record<string, string>;
|
|
169
|
+
/**
|
|
170
|
+
* - Specifies an optional transaction mode.
|
|
171
|
+
* "commit" this automatically commits any open transaction after each request. This is the defaults because this is what mod_plsql and ohs are doing.
|
|
172
|
+
* "rollback" this automatically rolles back any open transaction after each request.
|
|
173
|
+
* "transactionCallbackType" this allows to defined a custom handler as a JavaScript function.
|
|
174
|
+
*/
|
|
175
|
+
transactionMode?: transactionModeType;
|
|
149
176
|
/**
|
|
150
177
|
* - The error style.
|
|
151
178
|
*/
|