diff --git a/README.md b/README.md index 523bda9..d3d209a 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,9 @@ MODE= # Path to SAS executable (sas.exe / sas.sh) SAS_PATH=/path/to/sas/executable.exe +# Path to Node.js executable +NODE_PATH=node + # Path to working directory # This location is for SAS WORK, staged files, DRIVE, configuration etc SASJS_ROOT=./sasjs_root @@ -131,10 +134,10 @@ HELMET_CSP_CONFIG_PATH=./csp.config.json LOG_FORMAT_MORGAN= # A comma separated string that defines the available runTimes. -# Priority is given to the runtime that comes first in string. +# Priority is given to the runtime that cSAS_PATHomes first in string. # Possible options at the moment are sas and js -# options: [sas,js|js,sas|sas|js] default:sas +# options: [sas,js|js,sas|sas|js] default:sas,js RUN_TIMES= ``` diff --git a/api/.env.example b/api/.env.example index 126b6ef..c1640bb 100644 --- a/api/.env.example +++ b/api/.env.example @@ -19,6 +19,7 @@ AUTH_CODE_SECRET= SESSION_SECRET= DB_CONNECT=mongodb+srv://:@/?retryWrites=true&w=majority +NODE_PATH=node SAS_PATH=/opt/sas/sas9/SASHome/SASFoundation/9.4/sas SASJS_ROOT=./sasjs_root diff --git a/api/src/controllers/internal/Execution.ts b/api/src/controllers/internal/Execution.ts index e6d6b34..7a02275 100644 --- a/api/src/controllers/internal/Execution.ts +++ b/api/src/controllers/internal/Execution.ts @@ -349,7 +349,7 @@ const processProgram = async ( // waiting for the open event so that we can have underlying file descriptor await once(writeStream, 'open') - execFileSync('node', [codePath], { + execFileSync(process.nodeLoc, [codePath], { stdio: ['ignore', writeStream, writeStream] }) diff --git a/api/src/types/system/process.d.ts b/api/src/types/system/process.d.ts index e8e83b4..45f9572 100644 --- a/api/src/types/system/process.d.ts +++ b/api/src/types/system/process.d.ts @@ -1,6 +1,7 @@ declare namespace NodeJS { export interface Process { sasLoc: string + nodeLoc: string driveLoc: string sasSessionController?: import('../../controllers/internal').SASSessionController jsSessionController?: import('../../controllers/internal').JSSessionController diff --git a/api/src/utils/getDesktopFields.ts b/api/src/utils/getDesktopFields.ts index af499a7..74999fa 100644 --- a/api/src/utils/getDesktopFields.ts +++ b/api/src/utils/getDesktopFields.ts @@ -5,12 +5,13 @@ import { createFolder, fileExists, folderExists } from '@sasjs/utils' const isWindows = () => process.platform === 'win32' export const getDesktopFields = async () => { - const { SAS_PATH } = process.env + const { SAS_PATH, NODE_PATH } = process.env const sasLoc = SAS_PATH ?? (await getSASLocation()) + const nodeLoc = NODE_PATH ?? (await getNodeLocation()) // const driveLoc = DRIVE_PATH ?? (await getDriveLocation()) - return { sasLoc } + return { sasLoc, nodeLoc } } const getDriveLocation = async (): Promise => { @@ -61,3 +62,27 @@ const getSASLocation = async (): Promise => { return targetName } + +const getNodeLocation = async (): Promise => { + const validator = async (filePath: string) => { + if (!filePath) return 'Path to NodeJS executable is required.' + + if (!(await fileExists(filePath))) { + return 'No file found at provided path.' + } + + return true + } + + const defaultLocation = isWindows() + ? 'C:\\Program Files\\nodejs\\' + : '/usr/local/nodejs/bin' + + const targetName = await getString( + 'Please enter path to nodejs executable (absolute path): ', + validator, + defaultLocation + ) + + return targetName +} diff --git a/api/src/utils/setProcessVariables.ts b/api/src/utils/setProcessVariables.ts index 43725a3..d365ec1 100644 --- a/api/src/utils/setProcessVariables.ts +++ b/api/src/utils/setProcessVariables.ts @@ -13,10 +13,12 @@ export const setProcessVariables = async () => { if (MODE === ModeType.Server) { process.sasLoc = process.env.SAS_PATH as string + process.nodeLoc = process.env.NODE_PATH as string } else { - const { sasLoc } = await getDesktopFields() + const { sasLoc, nodeLoc } = await getDesktopFields() process.sasLoc = sasLoc + process.nodeLoc = nodeLoc } const { SASJS_ROOT } = process.env diff --git a/api/src/utils/verifyEnvVariables.ts b/api/src/utils/verifyEnvVariables.ts index 5019d65..3c51363 100644 --- a/api/src/utils/verifyEnvVariables.ts +++ b/api/src/utils/verifyEnvVariables.ts @@ -53,6 +53,8 @@ export const verifyEnvVariables = (): ReturnCode => { errors.push(...verifyRUN_TIMES()) + errors.push(...verifyExecutablePaths()) + if (errors.length) { process.logger?.error( `Invalid environment variable(s) provided: \n${errors.join('\n')}` @@ -231,6 +233,25 @@ const verifyRUN_TIMES = (): string[] => { return errors } +const verifyExecutablePaths = () => { + const errors: string[] = [] + const { RUN_TIMES, SAS_PATH, NODE_PATH, MODE } = process.env + + if (MODE === ModeType.Server) { + const runTimes = RUN_TIMES?.split(',') + + if (runTimes?.includes(RunTimeType.SAS) && !SAS_PATH) { + errors.push(`- SAS_PATH is required for ${RunTimeType.SAS} run time`) + } + + if (runTimes?.includes(RunTimeType.JS) && !NODE_PATH) { + errors.push(`- NODE_PATH is required for ${RunTimeType.JS} run time`) + } + } + + return errors +} + const DEFAULTS = { MODE: ModeType.Desktop, PROTOCOL: ProtocolType.HTTP,