From 80b33c7a18c1b7727316ffeca71658346733e935 Mon Sep 17 00:00:00 2001 From: Saad Jutt Date: Mon, 23 May 2022 19:24:56 +0500 Subject: [PATCH] fix: issue174 + issue175 + issue146 --- README.md | 4 +- api/.env.example | 2 +- api/package.json | 3 - api/src/app.ts | 32 ++-- api/src/controllers/drive.ts | 22 +-- api/src/controllers/internal/Execution.ts | 8 +- api/src/controllers/internal/Session.ts | 4 +- api/src/controllers/internal/deploy.ts | 4 +- api/src/controllers/stp.ts | 6 +- api/src/controllers/web.ts | 4 +- api/src/middlewares/multer.ts | 4 +- api/src/routes/api/spec/drive.spec.ts | 30 ++-- api/src/routes/appStream/index.ts | 4 +- api/src/types/system/process.d.ts | 1 + api/src/utils/appStreamConfig.ts | 6 +- api/src/utils/copySASjsCore.ts | 4 +- api/src/utils/file.ts | 26 ++-- api/src/utils/getDesktopFields.ts | 6 +- api/src/utils/index.ts | 2 + api/src/utils/instantiateLogger.ts | 7 + api/src/utils/setProcessVariables.ts | 23 ++- api/src/utils/setupFolders.ts | 4 +- api/src/utils/verifyEnvVariables.ts | 181 ++++++++++++++++++++++ 23 files changed, 289 insertions(+), 98 deletions(-) create mode 100644 api/src/utils/instantiateLogger.ts create mode 100644 api/src/utils/verifyEnvVariables.ts diff --git a/README.md b/README.md index 8caa85c..87aa620 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ SAS_PATH=/path/to/sas/executable.exe # Path to working directory # This location is for SAS WORK, staged files, DRIVE, configuration etc -DRIVE_PATH=/tmp +SASJS_ROOT=./sasjs_root # options: [http|https] default: http PROTOCOL= @@ -147,7 +147,7 @@ Install the npm package [pm2](https://www.npmjs.com/package/pm2) (`npm install p ```bash export SAS_PATH=/opt/sas9/SASHome/SASFoundation/9.4/sasexe/sas export PORT=5001 -export DRIVE_PATH=./tmp +export SASJS_ROOT=./sasjs_root pm2 start api-linux ``` diff --git a/api/.env.example b/api/.env.example index 577112f..163af41 100644 --- a/api/.env.example +++ b/api/.env.example @@ -18,4 +18,4 @@ SESSION_SECRET= DB_CONNECT=mongodb+srv://:@/?retryWrites=true&w=majority SAS_PATH=/opt/sas/sas9/SASHome/SASFoundation/9.4/sas -DRIVE_PATH=./tmp +SASJS_ROOT=./sasjs_root diff --git a/api/package.json b/api/package.json index 663da85..cf8b3a6 100644 --- a/api/package.json +++ b/api/package.json @@ -94,9 +94,6 @@ "tsoa": "3.14.1", "typescript": "^4.3.2" }, - "configuration": { - "sasPath": "/opt/sas/sas9/SASHome/SASFoundation/9.4/sas" - }, "nodemonConfig": { "ignore": [ "tmp/**/*" diff --git a/api/src/app.ts b/api/src/app.ts index ce828dd..a15b08b 100644 --- a/api/src/app.ts +++ b/api/src/app.ts @@ -12,15 +12,28 @@ import helmet from 'helmet' import { connectDB, copySASjsCore, - getWebBuildFolderPath, + CorsType, + getWebBuildFolder, + HelmetCoepType, + instantiateLogger, loadAppStreamConfig, + ModeType, + ProtocolType, + ReturnCode, setProcessVariables, - setupFolders + setupFolders, + verifyEnvVariables } from './utils' import { getEnvCSPDirectives } from './utils/parseHelmetConfig' dotenv.config() +instantiateLogger() + +if (verifyEnvVariables()) { + process.exit(ReturnCode.InvalidEnv) +} + const app = express() app.use(cookieParser()) @@ -30,7 +43,7 @@ const { MODE, CORS, WHITELIST, PROTOCOL, HELMET_CSP_CONFIG_PATH, HELMET_COEP } = process.env export const cookieOptions = { - secure: PROTOCOL === 'https', + secure: PROTOCOL === ProtocolType.HTTPS, httpOnly: true, maxAge: 24 * 60 * 60 * 1000 // 24 hours } @@ -38,9 +51,8 @@ export const cookieOptions = { const cspConfigJson: { [key: string]: string[] | null } = getEnvCSPDirectives( HELMET_CSP_CONFIG_PATH ) -const coepFlag = - HELMET_COEP === 'true' || HELMET_COEP === undefined ? true : false -if (PROTOCOL === 'http') cspConfigJson['upgrade-insecure-requests'] = null +if (PROTOCOL === ProtocolType.HTTP) + cspConfigJson['upgrade-insecure-requests'] = null /*********************************** * CSRF Protection * @@ -58,14 +70,14 @@ app.use( ...cspConfigJson } }, - crossOriginEmbedderPolicy: coepFlag + crossOriginEmbedderPolicy: HELMET_COEP === HelmetCoepType.TRUE }) ) /*********************************** * Enabling CORS * ***********************************/ -if (MODE?.trim() !== 'server' || CORS?.trim() === 'enable') { +if (MODE === ModeType.Server || CORS === CorsType.ENABLED) { const whiteList: string[] = [] WHITELIST?.split(' ') ?.filter((url) => !!url) @@ -84,7 +96,7 @@ if (MODE?.trim() !== 'server' || CORS?.trim() === 'enable') { * Express Sessions * * With Mongo Store * ***********************************/ -if (MODE?.trim() === 'server') { +if (MODE === ModeType.Server) { let store: MongoStore | undefined // NOTE: when exporting app.js as agent for supertest @@ -129,7 +141,7 @@ export default setProcessVariables().then(async () => { // should be served after setting up web route // index.html needs to be injected with some js script. - app.use(express.static(getWebBuildFolderPath())) + app.use(express.static(getWebBuildFolder())) app.use(onError) diff --git a/api/src/controllers/drive.ts b/api/src/controllers/drive.ts index 7f22522..d4eee88 100644 --- a/api/src/controllers/drive.ts +++ b/api/src/controllers/drive.ts @@ -32,7 +32,7 @@ import { import { createFileTree, ExecutionController, getTreeExample } from './internal' import { TreeNode } from '../types' -import { getTmpFilesFolderPath } from '../utils' +import { getFilesFolder } from '../utils' interface DeployPayload { appLoc: string @@ -214,12 +214,12 @@ const getFileTree = () => { } const deploy = async (data: DeployPayload) => { - const driveFilesPath = getTmpFilesFolderPath() + const driveFilesPath = getFilesFolder() const appLocParts = data.appLoc.replace(/^\//, '').split('/') const appLocPath = path - .join(getTmpFilesFolderPath(), ...appLocParts) + .join(getFilesFolder(), ...appLocParts) .replace(new RegExp('/', 'g'), path.sep) if (!appLocPath.includes(driveFilesPath)) { @@ -238,10 +238,10 @@ const deploy = async (data: DeployPayload) => { } const getFile = async (req: express.Request, filePath: string) => { - const driveFilesPath = getTmpFilesFolderPath() + const driveFilesPath = getFilesFolder() const filePathFull = path - .join(getTmpFilesFolderPath(), filePath) + .join(getFilesFolder(), filePath) .replace(new RegExp('/', 'g'), path.sep) if (!filePathFull.includes(driveFilesPath)) { @@ -261,11 +261,11 @@ const getFile = async (req: express.Request, filePath: string) => { } const getFolder = async (folderPath?: string) => { - const driveFilesPath = getTmpFilesFolderPath() + const driveFilesPath = getFilesFolder() if (folderPath) { const folderPathFull = path - .join(getTmpFilesFolderPath(), folderPath) + .join(getFilesFolder(), folderPath) .replace(new RegExp('/', 'g'), path.sep) if (!folderPathFull.includes(driveFilesPath)) { @@ -291,10 +291,10 @@ const getFolder = async (folderPath?: string) => { } const deleteFile = async (filePath: string) => { - const driveFilesPath = getTmpFilesFolderPath() + const driveFilesPath = getFilesFolder() const filePathFull = path - .join(getTmpFilesFolderPath(), filePath) + .join(getFilesFolder(), filePath) .replace(new RegExp('/', 'g'), path.sep) if (!filePathFull.includes(driveFilesPath)) { @@ -314,7 +314,7 @@ const saveFile = async ( filePath: string, multerFile: Express.Multer.File ): Promise => { - const driveFilesPath = getTmpFilesFolderPath() + const driveFilesPath = getFilesFolder() const filePathFull = path .join(driveFilesPath, filePath) @@ -339,7 +339,7 @@ const updateFile = async ( filePath: string, multerFile: Express.Multer.File ): Promise => { - const driveFilesPath = getTmpFilesFolderPath() + const driveFilesPath = getFilesFolder() const filePathFull = path .join(driveFilesPath, filePath) diff --git a/api/src/controllers/internal/Execution.ts b/api/src/controllers/internal/Execution.ts index c7fde8e..4d4df15 100644 --- a/api/src/controllers/internal/Execution.ts +++ b/api/src/controllers/internal/Execution.ts @@ -12,8 +12,8 @@ import { PreProgramVars, Session, TreeNode } from '../../types' import { extractHeaders, generateFileUploadSasCode, - getTmpFilesFolderPath, - getTmpMacrosPath, + getFilesFolder, + getMacrosFolder, HTTPHeaders, isDebugOn } from '../../utils' @@ -110,7 +110,7 @@ export class ExecutionController { ` program = ` -options insert=(SASAUTOS="${getTmpMacrosPath()}"); +options insert=(SASAUTOS="${getMacrosFolder()}"); /* runtime vars */ ${varStatments} @@ -191,7 +191,7 @@ ${program}` const root: TreeNode = { name: 'files', relativePath: '', - absolutePath: getTmpFilesFolderPath(), + absolutePath: getFilesFolder(), children: [] } diff --git a/api/src/controllers/internal/Session.ts b/api/src/controllers/internal/Session.ts index e26d0c7..b83d70e 100644 --- a/api/src/controllers/internal/Session.ts +++ b/api/src/controllers/internal/Session.ts @@ -3,7 +3,7 @@ import { Session } from '../../types' import { promisify } from 'util' import { execFile } from 'child_process' import { - getTmpSessionsFolderPath, + getSessionsFolder, generateUniqueFileName, sysInitCompiledPath } from '../../utils' @@ -37,7 +37,7 @@ export class SessionController { private async createSession(): Promise { const sessionId = generateUniqueFileName(generateTimestamp()) - const sessionFolder = path.join(getTmpSessionsFolderPath(), sessionId) + const sessionFolder = path.join(getSessionsFolder(), sessionId) const creationTimeStamp = sessionId.split('-').pop() as string // death time of session is 15 mins from creation diff --git a/api/src/controllers/internal/deploy.ts b/api/src/controllers/internal/deploy.ts index 05a1996..d061018 100644 --- a/api/src/controllers/internal/deploy.ts +++ b/api/src/controllers/internal/deploy.ts @@ -1,5 +1,5 @@ import path from 'path' -import { getTmpFilesFolderPath } from '../../utils/file' +import { getFilesFolder } from '../../utils/file' import { createFolder, createFile, @@ -17,7 +17,7 @@ export const createFileTree = async ( parentFolders: string[] = [] ) => { const destinationPath = path.join( - getTmpFilesFolderPath(), + getFilesFolder(), path.join(...parentFolders) ) diff --git a/api/src/controllers/stp.ts b/api/src/controllers/stp.ts index 0d25ff0..b938629 100644 --- a/api/src/controllers/stp.ts +++ b/api/src/controllers/stp.ts @@ -19,7 +19,7 @@ import { } from './internal' import { getPreProgramVariables, - getTmpFilesFolderPath, + getFilesFolder, HTTPHeaders, isDebugOn, LogLine, @@ -132,7 +132,7 @@ const executeReturnRaw = async ( const query = req.query as ExecutionVars const sasCodePath = path - .join(getTmpFilesFolderPath(), _program) + .join(getFilesFolder(), _program) .replace(new RegExp('/', 'g'), path.sep) + '.sas' try { @@ -172,7 +172,7 @@ const executeReturnJson = async ( ): Promise => { const sasCodePath = path - .join(getTmpFilesFolderPath(), _program) + .join(getFilesFolder(), _program) .replace(new RegExp('/', 'g'), path.sep) + '.sas' const filesNamesMap = req.files?.length ? makeFilesNamesMap(req.files) : null diff --git a/api/src/controllers/web.ts b/api/src/controllers/web.ts index 70ccf6a..350eef2 100644 --- a/api/src/controllers/web.ts +++ b/api/src/controllers/web.ts @@ -5,7 +5,7 @@ import { readFile } from '@sasjs/utils' import User from '../model/User' import Client from '../model/Client' -import { getWebBuildFolderPath, generateAuthCode } from '../utils' +import { getWebBuildFolder, generateAuthCode } from '../utils' import { InfoJWT } from '../types' import { AuthController } from './auth' @@ -63,7 +63,7 @@ export class WebController { } const home = async () => { - const indexHtmlPath = path.join(getWebBuildFolderPath(), 'index.html') + const indexHtmlPath = path.join(getWebBuildFolder(), 'index.html') // Attention! Cannot use fileExists here, // due to limitation after building executable diff --git a/api/src/middlewares/multer.ts b/api/src/middlewares/multer.ts index 21ab913..7daf339 100644 --- a/api/src/middlewares/multer.ts +++ b/api/src/middlewares/multer.ts @@ -1,13 +1,13 @@ import path from 'path' import { Request } from 'express' import multer, { FileFilterCallback, Options } from 'multer' -import { blockFileRegex, getTmpUploadsPath } from '../utils' +import { blockFileRegex, getUploadsFolder } from '../utils' const fieldNameSize = 300 const fileSize = 104857600 // 100 MB const storage = multer.diskStorage({ - destination: getTmpUploadsPath(), + destination: getUploadsFolder(), filename: function ( _req: Request, file: Express.Multer.File, diff --git a/api/src/routes/api/spec/drive.spec.ts b/api/src/routes/api/spec/drive.spec.ts index dba7098..341bd02 100644 --- a/api/src/routes/api/spec/drive.spec.ts +++ b/api/src/routes/api/spec/drive.spec.ts @@ -21,17 +21,17 @@ import * as fileUtilModules from '../../../utils/file' const timestamp = generateTimestamp() const tmpFolder = path.join(process.cwd(), `tmp-${timestamp}`) jest - .spyOn(fileUtilModules, 'getTmpFolderPath') + .spyOn(fileUtilModules, 'getSasjsRootFolder') .mockImplementation(() => tmpFolder) jest - .spyOn(fileUtilModules, 'getTmpUploadsPath') + .spyOn(fileUtilModules, 'getUploadsFolder') .mockImplementation(() => path.join(tmpFolder, 'uploads')) import appPromise from '../../../app' import { UserController } from '../../../controllers/' import { getTreeExample } from '../../../controllers/internal' import { generateAccessToken, saveTokensInDB } from '../../../utils/' -const { getTmpFilesFolderPath } = fileUtilModules +const { getFilesFolder } = fileUtilModules const clientId = 'someclientID' const user = { @@ -157,10 +157,10 @@ describe('drive', () => { expect(res.text).toEqual( '{"status":"success","message":"Files deployed successfully to @sasjs/server."}' ) - await expect(folderExists(getTmpFilesFolderPath())).resolves.toEqual(true) + await expect(folderExists(getFilesFolder())).resolves.toEqual(true) const testJobFolder = path.join( - getTmpFilesFolderPath(), + getFilesFolder(), 'public', 'jobs', 'extract' @@ -174,7 +174,7 @@ describe('drive', () => { await expect(readFile(testJobFile)).resolves.toEqual(exampleService.code) - await deleteFolder(path.join(getTmpFilesFolderPath(), 'public')) + await deleteFolder(path.join(getFilesFolder(), 'public')) }) }) @@ -192,7 +192,7 @@ describe('drive', () => { }) it('should get a SAS folder on drive having _folderPath as query param', async () => { - const pathToDrive = fileUtilModules.getTmpFilesFolderPath() + const pathToDrive = fileUtilModules.getFilesFolder() const dirLevel1 = 'level1' const dirLevel2 = 'level2' @@ -267,10 +267,7 @@ describe('drive', () => { const fileToCopyPath = path.join(__dirname, 'files', 'sample.sas') const filePath = '/my/path/code.sas' - const pathToCopy = path.join( - fileUtilModules.getTmpFilesFolderPath(), - filePath - ) + const pathToCopy = path.join(fileUtilModules.getFilesFolder(), filePath) await copy(fileToCopyPath, pathToCopy) const res = await request(app) @@ -333,7 +330,7 @@ describe('drive', () => { const pathToUpload = `/my/path/code-${generateTimestamp()}.sas` const pathToCopy = path.join( - fileUtilModules.getTmpFilesFolderPath(), + fileUtilModules.getFilesFolder(), pathToUpload ) await copy(fileToAttachPath, pathToCopy) @@ -445,7 +442,7 @@ describe('drive', () => { const pathToUpload = '/my/path/code.sas' const pathToCopy = path.join( - fileUtilModules.getTmpFilesFolderPath(), + fileUtilModules.getFilesFolder(), pathToUpload ) await copy(fileToAttachPath, pathToCopy) @@ -467,7 +464,7 @@ describe('drive', () => { const pathToUpload = '/my/path/code.sas' const pathToCopy = path.join( - fileUtilModules.getTmpFilesFolderPath(), + fileUtilModules.getFilesFolder(), pathToUpload ) await copy(fileToAttachPath, pathToCopy) @@ -603,10 +600,7 @@ describe('drive', () => { const fileToCopyContent = await readFile(fileToCopyPath) const filePath = '/my/path/code.sas' - const pathToCopy = path.join( - fileUtilModules.getTmpFilesFolderPath(), - filePath - ) + const pathToCopy = path.join(fileUtilModules.getFilesFolder(), filePath) await copy(fileToCopyPath, pathToCopy) const res = await request(app) diff --git a/api/src/routes/appStream/index.ts b/api/src/routes/appStream/index.ts index d0f4249..cf52017 100644 --- a/api/src/routes/appStream/index.ts +++ b/api/src/routes/appStream/index.ts @@ -2,7 +2,7 @@ import path from 'path' import express from 'express' import { folderExists } from '@sasjs/utils' -import { addEntryToAppStreamConfig, getTmpFilesFolderPath } from '../../utils' +import { addEntryToAppStreamConfig, getFilesFolder } from '../../utils' import { appStreamHtml } from './appStreamHtml' const router = express.Router() @@ -22,7 +22,7 @@ export const publishAppStream = async ( streamLogo?: string, addEntryToFile: boolean = true ) => { - const driveFilesPath = getTmpFilesFolderPath() + const driveFilesPath = getFilesFolder() const appLocParts = appLoc.replace(/^\//, '')?.split('/') const appLocPath = path.join(driveFilesPath, ...appLocParts) diff --git a/api/src/types/system/process.d.ts b/api/src/types/system/process.d.ts index 9b03ec6..f7845bc 100644 --- a/api/src/types/system/process.d.ts +++ b/api/src/types/system/process.d.ts @@ -4,5 +4,6 @@ declare namespace NodeJS { driveLoc: string sessionController?: import('../../controllers/internal').SessionController appStreamConfig: import('../').AppStreamConfig + logger: import('@sasjs/utils/logger').Logger } } diff --git a/api/src/utils/appStreamConfig.ts b/api/src/utils/appStreamConfig.ts index 867964d..c293b35 100644 --- a/api/src/utils/appStreamConfig.ts +++ b/api/src/utils/appStreamConfig.ts @@ -2,12 +2,12 @@ import { createFile, fileExists, readFile } from '@sasjs/utils' import { publishAppStream } from '../routes/appStream' import { AppStreamConfig } from '../types' -import { getTmpAppStreamConfigPath } from './file' +import { getAppStreamConfigPath } from './file' export const loadAppStreamConfig = async () => { if (process.env.NODE_ENV === 'test') return - const appStreamConfigPath = getTmpAppStreamConfigPath() + const appStreamConfigPath = getAppStreamConfigPath() const content = (await fileExists(appStreamConfigPath)) ? await readFile(appStreamConfigPath) @@ -63,7 +63,7 @@ export const removeEntryFromAppStreamConfig = (streamServiceName: string) => { } const saveAppStreamConfig = async () => { - const appStreamConfigPath = getTmpAppStreamConfigPath() + const appStreamConfigPath = getAppStreamConfigPath() try { await createFile( diff --git a/api/src/utils/copySASjsCore.ts b/api/src/utils/copySASjsCore.ts index 0352dd0..42e040e 100644 --- a/api/src/utils/copySASjsCore.ts +++ b/api/src/utils/copySASjsCore.ts @@ -7,14 +7,14 @@ import { readFile } from '@sasjs/utils' -import { getTmpMacrosPath, sasJSCoreMacros, sasJSCoreMacrosInfo } from '.' +import { getMacrosFolder, sasJSCoreMacros, sasJSCoreMacrosInfo } from '.' export const copySASjsCore = async () => { if (process.env.NODE_ENV === 'test') return console.log('Copying Macros from container to drive(tmp).') - const macrosDrivePath = getTmpMacrosPath() + const macrosDrivePath = getMacrosFolder() await deleteFolder(macrosDrivePath) await createFolder(macrosDrivePath) diff --git a/api/src/utils/file.ts b/api/src/utils/file.ts index 764be4a..007ad6f 100644 --- a/api/src/utils/file.ts +++ b/api/src/utils/file.ts @@ -11,28 +11,26 @@ export const sysInitCompiledPath = path.join( export const sasJSCoreMacros = path.join(apiRoot, 'sasjscore') export const sasJSCoreMacrosInfo = path.join(sasJSCoreMacros, '.macrolist') -export const getWebBuildFolderPath = () => - path.join(codebaseRoot, 'web', 'build') +export const getWebBuildFolder = () => path.join(codebaseRoot, 'web', 'build') -export const getTmpFolderPath = () => process.driveLoc +export const getSasjsRootFolder = () => process.driveLoc -export const getTmpAppStreamConfigPath = () => - path.join(getTmpFolderPath(), 'appStreamConfig.json') +export const getAppStreamConfigPath = () => + path.join(getSasjsRootFolder(), 'appStreamConfig.json') -export const getTmpMacrosPath = () => path.join(getTmpFolderPath(), 'sasjscore') +export const getMacrosFolder = () => + path.join(getSasjsRootFolder(), 'sasjscore') -export const getTmpUploadsPath = () => path.join(getTmpFolderPath(), 'uploads') +export const getUploadsFolder = () => path.join(getSasjsRootFolder(), 'uploads') -export const getTmpFilesFolderPath = () => - path.join(getTmpFolderPath(), 'files') +export const getFilesFolder = () => path.join(getSasjsRootFolder(), 'files') -export const getTmpLogFolderPath = () => path.join(getTmpFolderPath(), 'logs') +export const getLogFolder = () => path.join(getSasjsRootFolder(), 'logs') -export const getTmpWeboutFolderPath = () => - path.join(getTmpFolderPath(), 'webouts') +export const getWeboutFolder = () => path.join(getSasjsRootFolder(), 'webouts') -export const getTmpSessionsFolderPath = () => - path.join(getTmpFolderPath(), 'sessions') +export const getSessionsFolder = () => + path.join(getSasjsRootFolder(), 'sessions') export const generateUniqueFileName = (fileName: string, extension = '') => [ diff --git a/api/src/utils/getDesktopFields.ts b/api/src/utils/getDesktopFields.ts index 2700145..af499a7 100644 --- a/api/src/utils/getDesktopFields.ts +++ b/api/src/utils/getDesktopFields.ts @@ -5,12 +5,12 @@ import { createFolder, fileExists, folderExists } from '@sasjs/utils' const isWindows = () => process.platform === 'win32' export const getDesktopFields = async () => { - const { SAS_PATH, DRIVE_PATH } = process.env + const { SAS_PATH } = process.env const sasLoc = SAS_PATH ?? (await getSASLocation()) - const driveLoc = DRIVE_PATH ?? (await getDriveLocation()) + // const driveLoc = DRIVE_PATH ?? (await getDriveLocation()) - return { sasLoc, driveLoc } + return { sasLoc } } const getDriveLocation = async (): Promise => { diff --git a/api/src/utils/index.ts b/api/src/utils/index.ts index 77dfc05..16980ec 100644 --- a/api/src/utils/index.ts +++ b/api/src/utils/index.ts @@ -9,6 +9,7 @@ export * from './generateRefreshToken' export * from './getCertificates' export * from './getDesktopFields' export * from './getPreProgramVariables' +export * from './instantiateLogger' export * from './isDebugOn' export * from './parseLogToArray' export * from './removeTokensInDB' @@ -18,4 +19,5 @@ export * from './setProcessVariables' export * from './setupFolders' export * from './upload' export * from './validation' +export * from './verifyEnvVariables' export * from './verifyTokenInDB' diff --git a/api/src/utils/instantiateLogger.ts b/api/src/utils/instantiateLogger.ts new file mode 100644 index 0000000..0317e47 --- /dev/null +++ b/api/src/utils/instantiateLogger.ts @@ -0,0 +1,7 @@ +import { LogLevel, Logger } from '@sasjs/utils/logger' + +export const instantiateLogger = () => { + const logLevel = (process.env.LOG_LEVEL || LogLevel.Info) as LogLevel + const logger = new Logger(logLevel) + process.logger = logger +} diff --git a/api/src/utils/setProcessVariables.ts b/api/src/utils/setProcessVariables.ts index b44f1bd..4edac7c 100644 --- a/api/src/utils/setProcessVariables.ts +++ b/api/src/utils/setProcessVariables.ts @@ -1,30 +1,29 @@ import path from 'path' -import { getAbsolutePath, getRealPath } from '@sasjs/utils' +import { createFolder, getAbsolutePath, getRealPath } from '@sasjs/utils' -import { configuration } from '../../package.json' -import { getDesktopFields } from '.' +import { getDesktopFields, ModeType } from '.' export const setProcessVariables = async () => { if (process.env.NODE_ENV === 'test') { - process.driveLoc = path.join(process.cwd(), 'tmp') + process.driveLoc = path.join(process.cwd(), 'sasjs_root') return } const { MODE } = process.env - if (MODE?.trim() === 'server') { - const { SAS_PATH, DRIVE_PATH } = process.env - - process.sasLoc = SAS_PATH ?? configuration.sasPath - const absPath = getAbsolutePath(DRIVE_PATH ?? 'tmp', process.cwd()) - process.driveLoc = getRealPath(absPath) + if (MODE === ModeType.Server) { + process.sasLoc = process.env.SAS_PATH as string } else { - const { sasLoc, driveLoc } = await getDesktopFields() + const { sasLoc } = await getDesktopFields() process.sasLoc = sasLoc - process.driveLoc = driveLoc } + const { SASJS_ROOT } = process.env + const absPath = getAbsolutePath(SASJS_ROOT ?? 'sasjs_root', process.cwd()) + await createFolder(absPath) + process.driveLoc = getRealPath(absPath) + console.log('sasLoc: ', process.sasLoc) console.log('sasDrive: ', process.driveLoc) } diff --git a/api/src/utils/setupFolders.ts b/api/src/utils/setupFolders.ts index cfe0872..2b71f63 100644 --- a/api/src/utils/setupFolders.ts +++ b/api/src/utils/setupFolders.ts @@ -1,7 +1,7 @@ import { createFolder } from '@sasjs/utils' -import { getTmpFilesFolderPath } from './file' +import { getFilesFolder } from './file' export const setupFolders = async () => { - const drivePath = getTmpFilesFolderPath() + const drivePath = getFilesFolder() await createFolder(drivePath) } diff --git a/api/src/utils/verifyEnvVariables.ts b/api/src/utils/verifyEnvVariables.ts new file mode 100644 index 0000000..d4eb91e --- /dev/null +++ b/api/src/utils/verifyEnvVariables.ts @@ -0,0 +1,181 @@ +export enum ModeType { + Server = 'server', + Desktop = 'desktop' +} + +export enum ProtocolType { + HTTP = 'http', + HTTPS = 'https' +} + +export enum CorsType { + ENABLED = 'enable', + DISABLED = 'disable' +} + +export enum HelmetCoepType { + TRUE = 'true', + FALSE = 'false' +} + +export enum ReturnCode { + Success, + InvalidEnv +} + +export const verifyEnvVariables = (): ReturnCode => { + const errors: string[] = [] + + errors.push(...verifyMODE()) + + errors.push(...verifyPROTOCOL()) + + errors.push(...verifyPORT()) + + errors.push(...verifyCORS()) + + errors.push(...verifyHELMET_COEP()) + + if (errors.length) { + process.logger?.error( + `Invalid environment variable(s) provided: \n${errors.join('\n')}` + ) + return ReturnCode.InvalidEnv + } + + return ReturnCode.Success +} + +const verifyMODE = (): string[] => { + const errors: string[] = [] + const { MODE } = process.env + + if (MODE) { + const modeTypes = Object.values(ModeType) + if (!modeTypes.includes(MODE as ModeType)) + errors.push(`- MODE '${MODE}'\n - valid options ${modeTypes}`) + } else { + process.env.MODE = DEFAULTS.MODE + } + + if (process.env.MODE === ModeType.Server) { + const { + ACCESS_TOKEN_SECRET, + REFRESH_TOKEN_SECRET, + AUTH_CODE_SECRET, + SESSION_SECRET, + DB_CONNECT + } = process.env + + if (!ACCESS_TOKEN_SECRET) + errors.push( + `- ACCESS_TOKEN_SECRET is required for PROTOCOL '${ModeType.Server}'` + ) + + if (!REFRESH_TOKEN_SECRET) + errors.push( + `- REFRESH_TOKEN_SECRET is required for PROTOCOL '${ModeType.Server}'` + ) + + if (!AUTH_CODE_SECRET) + errors.push( + `- AUTH_CODE_SECRET is required for PROTOCOL '${ModeType.Server}'` + ) + + if (!SESSION_SECRET) + errors.push( + `- SESSION_SECRET is required for PROTOCOL '${ModeType.Server}'` + ) + + if (process.env.NODE_ENV !== 'test') + if (!DB_CONNECT) + errors.push( + `- DB_CONNECT is required for PROTOCOL '${ModeType.Server}'` + ) + } + + return errors +} + +const verifyPROTOCOL = (): string[] => { + const errors: string[] = [] + const { PROTOCOL } = process.env + + if (PROTOCOL) { + const protocolTypes = Object.values(ProtocolType) + if (!protocolTypes.includes(PROTOCOL as ProtocolType)) + errors.push(`- PROTOCOL '${PROTOCOL}'\n - valid options ${protocolTypes}`) + } else { + process.env.PROTOCOL = DEFAULTS.PROTOCOL + } + + if (process.env.PROTOCOL === ProtocolType.HTTPS) { + const { PRIVATE_KEY, FULL_CHAIN } = process.env + + if (!PRIVATE_KEY) + errors.push( + `- PRIVATE_KEY is required for PROTOCOL '${ProtocolType.HTTPS}'` + ) + + if (!FULL_CHAIN) + errors.push( + `- FULL_CHAIN is required for PROTOCOL '${ProtocolType.HTTPS}'` + ) + } + + return errors +} + +const verifyCORS = (): string[] => { + const errors: string[] = [] + const { CORS } = process.env + + if (CORS) { + const corsTypes = Object.values(CorsType) + if (!corsTypes.includes(CORS as CorsType)) + errors.push(`- CORS '${CORS}'\n - valid options ${corsTypes}`) + } else { + const { MODE } = process.env + process.env.CORS = + MODE === ModeType.Server ? CorsType.DISABLED : CorsType.ENABLED + } + + return errors +} + +const verifyPORT = (): string[] => { + const errors: string[] = [] + const { PORT } = process.env + + if (PORT) { + if (Number.isNaN(parseInt(PORT))) + errors.push(`- PORT '${PORT}'\n - should be a valid number`) + } else { + process.env.PORT = DEFAULTS.PORT + } + return errors +} + +const verifyHELMET_COEP = (): string[] => { + const errors: string[] = [] + const { HELMET_COEP } = process.env + + if (HELMET_COEP) { + const helmetCoepTypes = Object.values(HelmetCoepType) + if (!helmetCoepTypes.includes(HELMET_COEP as HelmetCoepType)) + errors.push( + `- HELMET_COEP '${HELMET_COEP}'\n - valid options ${helmetCoepTypes}` + ) + HELMET_COEP + } else { + process.env.HELMET_COEP = DEFAULTS.HELMET_COEP + } + return errors +} + +const DEFAULTS = { + MODE: 'desktop', + PROTOCOL: 'http', + PORT: '5000', + HELMET_COEP: 'true' +}