mirror of
https://github.com/sasjs/server.git
synced 2025-12-10 11:24:35 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a6b972c65 | ||
|
|
be11707042 | ||
| 2412622367 | |||
|
|
de3a190a8d | ||
|
|
d5daafc6ed | ||
|
|
b1a2677b8c | ||
|
|
94072c3d24 | ||
|
|
b64c0c12da | ||
|
|
79bc7b0e28 | ||
|
|
fda0e0b57d | ||
|
|
14731e8824 | ||
|
|
258cc35f14 |
23
CHANGELOG.md
23
CHANGELOG.md
@@ -1,3 +1,26 @@
|
||||
## [0.23.4](https://github.com/sasjs/server/compare/v0.23.3...v0.23.4) (2022-10-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add action to editor ref for running code ([2412622](https://github.com/sasjs/server/commit/2412622367eb46c40f388e988ae4606a7ec239b2))
|
||||
|
||||
## [0.23.3](https://github.com/sasjs/server/compare/v0.23.2...v0.23.3) (2022-10-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* added domain for session cookies ([94072c3](https://github.com/sasjs/server/commit/94072c3d24a4d0d4c97900dc31bfbf1c9d2559b7))
|
||||
|
||||
## [0.23.2](https://github.com/sasjs/server/compare/v0.23.1...v0.23.2) (2022-10-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* bump in correct place ([14731e8](https://github.com/sasjs/server/commit/14731e8824fa9f3d1daf89fd62f9916d5e3fcae4))
|
||||
* bumping sasjs/score ([258cc35](https://github.com/sasjs/server/commit/258cc35f14cf50f2160f607000c60de27593fd79))
|
||||
* reverting commit ([fda0e0b](https://github.com/sasjs/server/commit/fda0e0b57d56e3b5231e626a8d933343ac0c5cdc))
|
||||
|
||||
## [0.23.1](https://github.com/sasjs/server/compare/v0.23.0...v0.23.1) (2022-10-04)
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
MODE=[desktop|server] default considered as desktop
|
||||
CORS=[disable|enable] default considered as disable for server MODE & enable for desktop MODE
|
||||
ALLOWED_DOMAIN=<just domain e.g. example.com >
|
||||
WHITELIST=<space separated urls, each starting with protocol `http` or `https`>
|
||||
|
||||
PROTOCOL=[http|https] default considered as http
|
||||
|
||||
14
api/package-lock.json
generated
14
api/package-lock.json
generated
@@ -8,7 +8,7 @@
|
||||
"name": "api",
|
||||
"version": "0.0.2",
|
||||
"dependencies": {
|
||||
"@sasjs/core": "^4.31.3",
|
||||
"@sasjs/core": "^4.40.1",
|
||||
"@sasjs/utils": "2.48.1",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"connect-mongo": "^4.6.0",
|
||||
@@ -1394,9 +1394,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sasjs/core": {
|
||||
"version": "4.31.3",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.31.3.tgz",
|
||||
"integrity": "sha512-TpVqWl5bqp3JTQjIg0r4WiQg7Ima5f17eAJILJbdYDdXsnLXlA/Csbb95G7eDPhzWpM3C0NrzKek3yvCMGzXIA=="
|
||||
"version": "4.40.1",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.40.1.tgz",
|
||||
"integrity": "sha512-hVEVnH8tej57Cran/X/iUoDms7EoL+2fwAPvjQMgHBHh8ynsF8aqYBreiRCwbrvdrjBsnmayOVh2RiQLtfHhoQ=="
|
||||
},
|
||||
"node_modules/@sasjs/utils": {
|
||||
"version": "2.48.1",
|
||||
@@ -11135,9 +11135,9 @@
|
||||
}
|
||||
},
|
||||
"@sasjs/core": {
|
||||
"version": "4.31.3",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.31.3.tgz",
|
||||
"integrity": "sha512-TpVqWl5bqp3JTQjIg0r4WiQg7Ima5f17eAJILJbdYDdXsnLXlA/Csbb95G7eDPhzWpM3C0NrzKek3yvCMGzXIA=="
|
||||
"version": "4.40.1",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.40.1.tgz",
|
||||
"integrity": "sha512-hVEVnH8tej57Cran/X/iUoDms7EoL+2fwAPvjQMgHBHh8ynsF8aqYBreiRCwbrvdrjBsnmayOVh2RiQLtfHhoQ=="
|
||||
},
|
||||
"@sasjs/utils": {
|
||||
"version": "2.48.1",
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
},
|
||||
"author": "4GL Ltd",
|
||||
"dependencies": {
|
||||
"@sasjs/core": "^4.31.3",
|
||||
"@sasjs/core": "^4.40.1",
|
||||
"@sasjs/utils": "2.48.1",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"connect-mongo": "^4.6.0",
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { Express } from 'express'
|
||||
import { Express, CookieOptions } from 'express'
|
||||
import mongoose from 'mongoose'
|
||||
import session from 'express-session'
|
||||
import MongoStore from 'connect-mongo'
|
||||
|
||||
import { ModeType } from '../utils'
|
||||
import { cookieOptions } from '../app'
|
||||
import { ModeType, ProtocolType } from '../utils'
|
||||
|
||||
export const configureExpressSession = (app: Express) => {
|
||||
const { MODE } = process.env
|
||||
@@ -19,6 +18,15 @@ export const configureExpressSession = (app: Express) => {
|
||||
})
|
||||
}
|
||||
|
||||
const { PROTOCOL, ALLOWED_DOMAIN } = process.env
|
||||
const cookieOptions: CookieOptions = {
|
||||
secure: PROTOCOL === ProtocolType.HTTPS,
|
||||
httpOnly: true,
|
||||
sameSite: PROTOCOL === ProtocolType.HTTPS ? 'none' : undefined,
|
||||
maxAge: 24 * 60 * 60 * 1000, // 24 hours
|
||||
domain: ALLOWED_DOMAIN?.trim() || undefined
|
||||
}
|
||||
|
||||
app.use(
|
||||
session({
|
||||
secret: process.secrets.SESSION_SECRET,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import path from 'path'
|
||||
import express, { ErrorRequestHandler, CookieOptions } from 'express'
|
||||
import express, { ErrorRequestHandler } from 'express'
|
||||
import cookieParser from 'cookie-parser'
|
||||
import dotenv from 'dotenv'
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
getWebBuildFolder,
|
||||
instantiateLogger,
|
||||
loadAppStreamConfig,
|
||||
ProtocolType,
|
||||
ReturnCode,
|
||||
setProcessVariables,
|
||||
setupFolders,
|
||||
@@ -29,15 +28,6 @@ if (verifyEnvVariables()) process.exit(ReturnCode.InvalidEnv)
|
||||
|
||||
const app = express()
|
||||
|
||||
const { PROTOCOL } = process.env
|
||||
|
||||
export const cookieOptions: CookieOptions = {
|
||||
secure: PROTOCOL === ProtocolType.HTTPS,
|
||||
httpOnly: true,
|
||||
sameSite: PROTOCOL === ProtocolType.HTTPS ? 'none' : undefined,
|
||||
maxAge: 24 * 60 * 60 * 1000 // 24 hours
|
||||
}
|
||||
|
||||
const onError: ErrorRequestHandler = (err, req, res, next) => {
|
||||
console.error(err.stack)
|
||||
res.status(500).send('Something broke!')
|
||||
|
||||
@@ -14,7 +14,10 @@ webRouter.get('/', async (req, res) => {
|
||||
} catch (_) {
|
||||
response = '<html><head></head><body>Web Build is not present</body></html>'
|
||||
} finally {
|
||||
const codeToInject = `<script>document.cookie = 'XSRF-TOKEN=${generateCSRFToken()}; Max-Age=86400; SameSite=Strict; Path=/;'</script>`
|
||||
const { ALLOWED_DOMAIN } = process.env
|
||||
const allowedDomain = ALLOWED_DOMAIN?.trim()
|
||||
const domain = allowedDomain ? ` Domain=${allowedDomain};` : ''
|
||||
const codeToInject = `<script>document.cookie = 'XSRF-TOKEN=${generateCSRFToken()};${domain} Max-Age=86400; SameSite=Strict; Path=/;'</script>`
|
||||
const injectedContent = response?.replace(
|
||||
'</head>',
|
||||
`${codeToInject}</head>`
|
||||
|
||||
@@ -267,7 +267,7 @@ const verifyRUN_TIMES = (): string[] => {
|
||||
return errors
|
||||
}
|
||||
|
||||
const verifyExecutablePaths = () => {
|
||||
const verifyExecutablePaths = (): string[] => {
|
||||
const errors: string[] = []
|
||||
const { RUN_TIMES, SAS_PATH, NODE_PATH, PYTHON_PATH, R_PATH, MODE } =
|
||||
process.env
|
||||
|
||||
@@ -48,7 +48,6 @@ const SASjsEditor = ({
|
||||
setTab
|
||||
}: SASjsEditorProps) => {
|
||||
const {
|
||||
ctrlPressed,
|
||||
fileContent,
|
||||
isLoading,
|
||||
log,
|
||||
@@ -64,8 +63,6 @@ const SASjsEditor = ({
|
||||
handleDiffEditorDidMount,
|
||||
handleEditorDidMount,
|
||||
handleFilePathInput,
|
||||
handleKeyDown,
|
||||
handleKeyUp,
|
||||
handleRunBtnClick,
|
||||
handleTabChange,
|
||||
saveFile,
|
||||
@@ -99,7 +96,6 @@ const SASjsEditor = ({
|
||||
original={prevFileContent}
|
||||
value={fileContent}
|
||||
editorDidMount={handleDiffEditorDidMount}
|
||||
options={{ readOnly: ctrlPressed }}
|
||||
onChange={(val) => setFileContent(val)}
|
||||
/>
|
||||
) : (
|
||||
@@ -108,7 +104,6 @@ const SASjsEditor = ({
|
||||
language={getLanguageFromExtension(selectedFileExtension)}
|
||||
value={fileContent}
|
||||
editorDidMount={handleEditorDidMount}
|
||||
options={{ readOnly: ctrlPressed }}
|
||||
onChange={(val) => setFileContent(val)}
|
||||
/>
|
||||
)
|
||||
@@ -176,8 +171,6 @@ const SASjsEditor = ({
|
||||
{fileMenu}
|
||||
</Box>
|
||||
<Paper
|
||||
onKeyUp={handleKeyUp}
|
||||
onKeyDown={handleKeyDown}
|
||||
sx={{
|
||||
height: 'calc(100vh - 170px)',
|
||||
padding: '10px',
|
||||
|
||||
@@ -42,7 +42,6 @@ const useEditor = ({
|
||||
const [prevFileContent, setPrevFileContent] = useStateWithCallback('')
|
||||
const [fileContent, setFileContent] = useState('')
|
||||
const [log, setLog] = useState('')
|
||||
const [ctrlPressed, setCtrlPressed] = useState(false)
|
||||
const [webout, setWebout] = useState('')
|
||||
const [runTimes, setRunTimes] = useState<string[]>([])
|
||||
const [selectedRunTime, setSelectedRunTime] = useState('')
|
||||
@@ -148,53 +147,47 @@ const useEditor = ({
|
||||
const handleRunBtnClick = () =>
|
||||
runCode(getSelection(editorRef.current as any) || fileContent)
|
||||
|
||||
const runCode = (code: string) => {
|
||||
setIsLoading(true)
|
||||
axios
|
||||
.post(`/SASjsApi/code/execute`, {
|
||||
code: programPathInjection(
|
||||
code,
|
||||
selectedFilePath,
|
||||
selectedRunTime as RunTimeType
|
||||
),
|
||||
runTime: selectedRunTime
|
||||
})
|
||||
.then((res: any) => {
|
||||
setWebout(res.data.split(SASJS_LOGS_SEPARATOR)[0] ?? '')
|
||||
setLog(res.data.split(SASJS_LOGS_SEPARATOR)[1] ?? '')
|
||||
setTab('log')
|
||||
const runCode = useCallback(
|
||||
(code: string) => {
|
||||
setIsLoading(true)
|
||||
axios
|
||||
.post(`/SASjsApi/code/execute`, {
|
||||
code: programPathInjection(
|
||||
code,
|
||||
selectedFilePath,
|
||||
selectedRunTime as RunTimeType
|
||||
),
|
||||
runTime: selectedRunTime
|
||||
})
|
||||
.then((res: any) => {
|
||||
setWebout(res.data.split(SASJS_LOGS_SEPARATOR)[0] ?? '')
|
||||
setLog(res.data.split(SASJS_LOGS_SEPARATOR)[1] ?? '')
|
||||
setTab('log')
|
||||
|
||||
// Scroll to bottom of log
|
||||
const logElement = document.getElementById('log')
|
||||
if (logElement) logElement.scrollTop = logElement.scrollHeight
|
||||
})
|
||||
.catch((err) => {
|
||||
setModalTitle('Abort')
|
||||
setModalPayload(
|
||||
typeof err.response.data === 'object'
|
||||
? JSON.stringify(err.response.data)
|
||||
: err.response.data
|
||||
)
|
||||
setOpenModal(true)
|
||||
})
|
||||
.finally(() => setIsLoading(false))
|
||||
}
|
||||
|
||||
const handleKeyDown = (event: any) => {
|
||||
if (event.ctrlKey) {
|
||||
if (event.key === 'v') {
|
||||
setCtrlPressed(false)
|
||||
}
|
||||
|
||||
if (event.key === 'Enter')
|
||||
runCode(getSelection(editorRef.current as any) || fileContent)
|
||||
if (!ctrlPressed) setCtrlPressed(true)
|
||||
}
|
||||
}
|
||||
|
||||
const handleKeyUp = (event: any) => {
|
||||
if (!event.ctrlKey && ctrlPressed) setCtrlPressed(false)
|
||||
}
|
||||
// Scroll to bottom of log
|
||||
const logElement = document.getElementById('log')
|
||||
if (logElement) logElement.scrollTop = logElement.scrollHeight
|
||||
})
|
||||
.catch((err) => {
|
||||
setModalTitle('Abort')
|
||||
setModalPayload(
|
||||
typeof err.response.data === 'object'
|
||||
? JSON.stringify(err.response.data)
|
||||
: err.response.data
|
||||
)
|
||||
setOpenModal(true)
|
||||
})
|
||||
.finally(() => setIsLoading(false))
|
||||
},
|
||||
[
|
||||
selectedFilePath,
|
||||
selectedRunTime,
|
||||
setModalPayload,
|
||||
setModalTitle,
|
||||
setOpenModal,
|
||||
setTab
|
||||
]
|
||||
)
|
||||
|
||||
const handleChangeRunTime = (event: SelectChangeEvent) => {
|
||||
setSelectedRunTime(event.target.value as RunTimeType)
|
||||
@@ -223,7 +216,28 @@ const useEditor = ({
|
||||
if (prevFileContent !== fileContent) return saveFile()
|
||||
}
|
||||
})
|
||||
}, [fileContent, prevFileContent, selectedFilePath, saveFile])
|
||||
|
||||
editorRef.current.addAction({
|
||||
// An unique identifier of the contributed action.
|
||||
id: 'run-code',
|
||||
|
||||
// A label of the action that will be presented to the user.
|
||||
label: 'Run Code',
|
||||
|
||||
// An optional array of keybindings for the action.
|
||||
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter],
|
||||
|
||||
contextMenuGroupId: 'navigation',
|
||||
|
||||
contextMenuOrder: 1,
|
||||
|
||||
// Method that will be executed when the action is triggered.
|
||||
// @param editor The editor instance is passed in as a convenience
|
||||
run: function () {
|
||||
runCode(getSelection(editorRef.current as any) || fileContent)
|
||||
}
|
||||
})
|
||||
}, [fileContent, prevFileContent, selectedFilePath, saveFile, runCode])
|
||||
|
||||
useEffect(() => {
|
||||
setRunTimes(Object.values(appContext.runTimes))
|
||||
@@ -277,7 +291,6 @@ const useEditor = ({
|
||||
}, [selectedFileExtension, runTimes])
|
||||
|
||||
return {
|
||||
ctrlPressed,
|
||||
fileContent,
|
||||
isLoading,
|
||||
log,
|
||||
@@ -293,8 +306,6 @@ const useEditor = ({
|
||||
handleDiffEditorDidMount,
|
||||
handleEditorDidMount,
|
||||
handleFilePathInput,
|
||||
handleKeyDown,
|
||||
handleKeyUp,
|
||||
handleRunBtnClick,
|
||||
handleTabChange,
|
||||
saveFile,
|
||||
|
||||
Reference in New Issue
Block a user