web_plsql 1.0.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +108 -13
- package/dist/chunk-DQk6qfdC.mjs +18 -0
- package/dist/frontend/assets/main-0oxRTIkL.js +147 -0
- package/dist/frontend/assets/main-0oxRTIkL.js.br +0 -0
- package/dist/frontend/assets/main-0oxRTIkL.js.gz +0 -0
- package/dist/frontend/assets/main-TYgLak6O.css +1 -0
- package/dist/frontend/assets/main-TYgLak6O.css.br +0 -0
- package/dist/frontend/assets/main-TYgLak6O.css.gz +0 -0
- package/dist/frontend/index.html +516 -0
- 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 +3548 -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 +45 -32
- package/examples/config-native.js +0 -68
- package/src/admin/client/charts.ts +0 -299
- package/src/admin/client/main.js +0 -3
- package/src/admin/client/tailwind.css +0 -41
- package/src/admin/index.html +0 -315
- package/src/admin/js/api.ts +0 -95
- package/src/admin/js/app.ts +0 -306
- package/src/admin/js/eslint.config.js +0 -74
- package/src/admin/js/schemas.ts +0 -153
- package/src/admin/js/templates/config.ts +0 -146
- package/src/admin/js/templates/errorRow.ts +0 -18
- package/src/admin/js/templates/index.ts +0 -3
- package/src/admin/js/templates/poolCard.ts +0 -61
- package/src/admin/js/tsconfig.json +0 -24
- package/src/admin/js/types.ts +0 -223
- package/src/admin/js/ui/theme.ts +0 -93
- package/src/admin/js/ui/views.ts +0 -164
- package/src/admin/js/util/format.ts +0 -27
- package/src/admin/lib/assets/main-zpdhQ1gD.css +0 -1
- package/src/admin/lib/chart.bundle.js +0 -139
- package/src/admin/style.css +0 -1321
- package/src/bin/load-test.js +0 -202
- package/src/handler/handlerAdmin.js +0 -198
- package/src/handler/handlerLogger.js +0 -21
- package/src/handler/handlerMetrics.js +0 -68
- package/src/handler/handlerUpload.js +0 -26
- package/src/handler/plsql/cgi.js +0 -131
- package/src/handler/plsql/errorPage.js +0 -117
- package/src/handler/plsql/handlerPlSql.js +0 -90
- package/src/handler/plsql/parsePage.js +0 -213
- package/src/handler/plsql/procedure.js +0 -356
- package/src/handler/plsql/procedureError.js +0 -40
- package/src/handler/plsql/procedureNamed.js +0 -268
- package/src/handler/plsql/procedureSanitize.js +0 -262
- package/src/handler/plsql/procedureVariable.js +0 -54
- package/src/handler/plsql/request.js +0 -124
- package/src/handler/plsql/requestError.js +0 -23
- package/src/handler/plsql/sendResponse.js +0 -136
- package/src/handler/plsql/stream.js +0 -28
- package/src/handler/plsql/upload.js +0 -109
- package/src/index.js +0 -20
- package/src/server/config.js +0 -57
- package/src/server/connections.js +0 -40
- package/src/server/server.js +0 -289
- 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/trace.js +0 -325
- package/src/util/type.js +0 -6
- package/src/util/util.js +0 -66
- package/src/version.js +0 -5
- package/types/handler/handlerAdmin.d.ts +0 -9
- package/types/handler/handlerLogger.d.ts +0 -2
- package/types/handler/handlerMetrics.d.ts +0 -25
- 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/parsePage.d.ts +0 -3
- package/types/handler/plsql/procedure.d.ts +0 -12
- 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 -11
- package/types/server/config.d.ts +0 -2
- package/types/server/server.d.ts +0 -74
- 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/trace.d.ts +0 -22
- 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,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,20 +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
|
-
export {handlerMetrics} from './handler/handlerMetrics.js';
|
|
15
|
-
|
|
16
|
-
// oracle
|
|
17
|
-
export * from './util/oracle.js';
|
|
18
|
-
|
|
19
|
-
// util
|
|
20
|
-
export * from './util/file.js';
|
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
|
-
};
|
package/src/server/server.js
DELETED
|
@@ -1,289 +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 {Cache} from '../util/cache.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: Cache<string>;
|
|
39
|
-
* argumentCache: Cache<argsType>;
|
|
40
|
-
* }} ExtendedRequestHandler
|
|
41
|
-
*/
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Global Admin Context
|
|
45
|
-
*/
|
|
46
|
-
export const AdminContext = {
|
|
47
|
-
startTime: new Date(),
|
|
48
|
-
/** @type {configType | null} */
|
|
49
|
-
config: null,
|
|
50
|
-
/** @type {Pool[]} */
|
|
51
|
-
pools: [],
|
|
52
|
-
/** @type {Array<{poolName: string, procedureNameCache: Cache<string>, argumentCache: Cache<argsType>}>} */
|
|
53
|
-
caches: [],
|
|
54
|
-
paused: false,
|
|
55
|
-
metrics: {
|
|
56
|
-
requestCount: 0,
|
|
57
|
-
errorCount: 0,
|
|
58
|
-
totalDuration: 0,
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* @typedef {object} webServer - Web server interface.
|
|
64
|
-
* @property {configType} config - Configuration object.
|
|
65
|
-
* @property {Pool[]} connectionPools - Oracle connection pools.
|
|
66
|
-
* @property {Express} app - Express app.
|
|
67
|
-
* @property {http.Server | https.Server} server - Native Node http(s) server instance.
|
|
68
|
-
* @property {() => Promise<void>} shutdown - Shutdown function.
|
|
69
|
-
*/
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* @typedef {object} sslConfig - SSL configuration.
|
|
73
|
-
* @property {string} keyFilename - key filename.
|
|
74
|
-
* @property {string} certFilename - cert filename.
|
|
75
|
-
*/
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Admin basic auth middleware
|
|
79
|
-
* @param {Request} req - The request.
|
|
80
|
-
* @param {Response} res - The response.
|
|
81
|
-
* @param {NextFunction} next - The next function.
|
|
82
|
-
*/
|
|
83
|
-
const adminAuth = (req, res, next) => {
|
|
84
|
-
const adminRoute = AdminContext.config?.adminRoute ?? '/admin';
|
|
85
|
-
|
|
86
|
-
// Simple pause check for all PL/SQL routes (not admin)
|
|
87
|
-
if (AdminContext.paused && !req.path.startsWith(adminRoute)) {
|
|
88
|
-
res.status(503).send('Server Paused');
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Basic Auth for Admin Route
|
|
93
|
-
if (req.path.startsWith(adminRoute)) {
|
|
94
|
-
const user = AdminContext.config?.adminUser;
|
|
95
|
-
const pass = AdminContext.config?.adminPassword;
|
|
96
|
-
|
|
97
|
-
if (user && pass) {
|
|
98
|
-
const auth = {login: user, password: pass};
|
|
99
|
-
const b64auth = (req.headers.authorization ?? '').split(' ')[1] ?? '';
|
|
100
|
-
const [login, password] = Buffer.from(b64auth, 'base64').toString().split(':');
|
|
101
|
-
|
|
102
|
-
if (login !== auth.login || password !== auth.password) {
|
|
103
|
-
res.set('WWW-Authenticate', 'Basic realm="Admin Console"');
|
|
104
|
-
res.status(401).send('Authentication required.');
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
next();
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Create HTTPS server.
|
|
115
|
-
* @param {Express} app - express application
|
|
116
|
-
* @param {sslConfig} [ssl] - ssl configuration.
|
|
117
|
-
* @returns {http.Server | https.Server} - server
|
|
118
|
-
*/
|
|
119
|
-
export const createServer = (app, ssl) => {
|
|
120
|
-
if (ssl) {
|
|
121
|
-
const key = readFileSyncUtf8(ssl.keyFilename);
|
|
122
|
-
const cert = readFileSyncUtf8(ssl.certFilename);
|
|
123
|
-
|
|
124
|
-
return https.createServer({key, cert}, app);
|
|
125
|
-
} else {
|
|
126
|
-
return http.createServer(app);
|
|
127
|
-
}
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Start server.
|
|
132
|
-
* @param {configType} config - The config.
|
|
133
|
-
* @param {sslConfig} [ssl] - ssl configuration.
|
|
134
|
-
* @returns {Promise<webServer>} - Promise resolving to the web server object.
|
|
135
|
-
*/
|
|
136
|
-
export const startServer = async (config, ssl) => {
|
|
137
|
-
debug('startServer: BEGIN', config, ssl);
|
|
138
|
-
|
|
139
|
-
const internalConfig = /** @type {configType} */ (z$configType.parse(config));
|
|
140
|
-
AdminContext.config = internalConfig;
|
|
141
|
-
|
|
142
|
-
showConfig(internalConfig);
|
|
143
|
-
|
|
144
|
-
// Create express app
|
|
145
|
-
const app = express();
|
|
146
|
-
|
|
147
|
-
// Pause & Admin Auth middleware
|
|
148
|
-
app.use(adminAuth);
|
|
149
|
-
|
|
150
|
-
// Access log
|
|
151
|
-
if (internalConfig.loggerFilename.length > 0) {
|
|
152
|
-
app.use(handlerLogger(internalConfig.loggerFilename));
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Admin console
|
|
156
|
-
const adminRoute = internalConfig.adminRoute ?? '/admin';
|
|
157
|
-
const adminDirectory = path.resolve(__dirname, '../admin');
|
|
158
|
-
app.use(adminRoute, handlerAdmin);
|
|
159
|
-
app.use(adminRoute, express.static(adminDirectory));
|
|
160
|
-
|
|
161
|
-
// Serving static files
|
|
162
|
-
for (const i of internalConfig.routeStatic) {
|
|
163
|
-
app.use(i.route, express.static(i.directoryPath));
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Default middleware
|
|
167
|
-
app.use(handlerUpload(internalConfig.uploadFileSizeLimit));
|
|
168
|
-
app.use(express.json({limit: '50mb'}));
|
|
169
|
-
app.use(express.urlencoded({limit: '50mb', extended: true}));
|
|
170
|
-
app.use(cookieParser());
|
|
171
|
-
app.use(compression());
|
|
172
|
-
|
|
173
|
-
/** @type {Pool[]} */
|
|
174
|
-
const connectionPools = [];
|
|
175
|
-
AdminContext.pools = connectionPools;
|
|
176
|
-
AdminContext.caches = [];
|
|
177
|
-
|
|
178
|
-
// Oracle pl/sql express middleware
|
|
179
|
-
for (const i of internalConfig.routePlSql) {
|
|
180
|
-
// Allocate the Oracle database pool
|
|
181
|
-
const pool = await poolCreate(i.user, i.password, i.connectString);
|
|
182
|
-
connectionPools.push(pool);
|
|
183
|
-
|
|
184
|
-
const handler = handlerWebPlSql(pool, i);
|
|
185
|
-
|
|
186
|
-
// Capture caches for admin console
|
|
187
|
-
AdminContext.caches.push({
|
|
188
|
-
poolName: i.route,
|
|
189
|
-
procedureNameCache: handler.procedureNameCache,
|
|
190
|
-
argumentCache: handler.argumentCache,
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
app.use([`${i.route}/:name`, i.route], (req, res, next) => {
|
|
194
|
-
AdminContext.metrics.requestCount++;
|
|
195
|
-
const start = process.hrtime();
|
|
196
|
-
res.on('finish', () => {
|
|
197
|
-
const diff = process.hrtime(start);
|
|
198
|
-
const duration = diff[0] * 1000 + diff[1] / 1_000_000;
|
|
199
|
-
AdminContext.metrics.totalDuration += duration;
|
|
200
|
-
});
|
|
201
|
-
handler(req, res, next);
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// create server
|
|
206
|
-
debug('startServer: createServer');
|
|
207
|
-
const server = createServer(app, ssl);
|
|
208
|
-
|
|
209
|
-
// Track open connections
|
|
210
|
-
/** @type {Set<Socket>} */
|
|
211
|
-
const connections = new Set();
|
|
212
|
-
server.on('connection', (/** @type {Socket} */ socket) => {
|
|
213
|
-
connections.add(socket);
|
|
214
|
-
socket.on('close', () => {
|
|
215
|
-
connections.delete(socket);
|
|
216
|
-
});
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
const closeAllConnections = () => {
|
|
220
|
-
for (const socket of connections) {
|
|
221
|
-
socket.destroy(); // forcibly closes the connection
|
|
222
|
-
connections.delete(socket);
|
|
223
|
-
}
|
|
224
|
-
};
|
|
225
|
-
|
|
226
|
-
const shutdown = async () => {
|
|
227
|
-
debug('startServer: onShutdown');
|
|
228
|
-
|
|
229
|
-
await poolsClose(connectionPools);
|
|
230
|
-
|
|
231
|
-
server.close(() => {
|
|
232
|
-
console.log('Server has closed');
|
|
233
|
-
process.exit(0);
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
closeAllConnections();
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
// Install shutdown handler
|
|
240
|
-
installShutdown(shutdown);
|
|
241
|
-
|
|
242
|
-
// Listen
|
|
243
|
-
debug('startServer: start listener');
|
|
244
|
-
await /** @type {Promise<void>} */ (
|
|
245
|
-
new Promise((resolve, reject) => {
|
|
246
|
-
server
|
|
247
|
-
.listen(internalConfig.port)
|
|
248
|
-
.on('listening', () => {
|
|
249
|
-
debug('startServer: listener running');
|
|
250
|
-
resolve();
|
|
251
|
-
})
|
|
252
|
-
.on('error', (err) => {
|
|
253
|
-
if ('code' in err) {
|
|
254
|
-
if (err.code === 'EADDRINUSE') {
|
|
255
|
-
err.message = `Port ${internalConfig.port} is already in use`;
|
|
256
|
-
} else if (err.code === 'EACCES') {
|
|
257
|
-
err.message = `Port ${internalConfig.port} requires elevated privileges`;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
reject(err);
|
|
261
|
-
});
|
|
262
|
-
})
|
|
263
|
-
);
|
|
264
|
-
|
|
265
|
-
debug('startServer: END');
|
|
266
|
-
|
|
267
|
-
return {
|
|
268
|
-
config: internalConfig,
|
|
269
|
-
connectionPools,
|
|
270
|
-
app,
|
|
271
|
-
server,
|
|
272
|
-
shutdown,
|
|
273
|
-
};
|
|
274
|
-
};
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* Load configuration.
|
|
278
|
-
* @param {string} [filename] - The configuration filename.
|
|
279
|
-
* @returns {configType} - Promise.
|
|
280
|
-
*/
|
|
281
|
-
export const loadConfig = (filename = 'config.json') => /** @type {configType} */ (z$configType.parse(getJsonFile(filename)));
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* Start server from config file.
|
|
285
|
-
* @param {string} [filename] - The configuration filename.
|
|
286
|
-
* @param {sslConfig} [ssl] - ssl configuration.
|
|
287
|
-
* @returns {Promise<webServer>} - Promise resolving to the web server object.
|
|
288
|
-
*/
|
|
289
|
-
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} 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
|
-
*/
|