mirror of
https://github.com/sasjs/server.git
synced 2025-12-10 19:34:34 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
056a436e10 | ||
|
|
06d59c618c | ||
|
|
a0e7875ae6 | ||
|
|
24966e695a | ||
|
|
5c40d8a342 | ||
| 6f5566dabb | |||
| d93470d183 | |||
| 330c020933 | |||
|
|
a810f6c7cf | ||
|
|
5d6c6086b4 | ||
|
|
0edcbdcefc | ||
|
|
ea0222f218 | ||
| edc2e2a302 | |||
|
|
efd2e1450e | ||
|
|
1092a73c10 | ||
| 9977c9d161 |
22
CHANGELOG.md
22
CHANGELOG.md
@@ -1,3 +1,25 @@
|
|||||||
|
## [0.21.4](https://github.com/sasjs/server/compare/v0.21.3...v0.21.4) (2022-09-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* removing single quotes from _program value ([a0e7875](https://github.com/sasjs/server/commit/a0e7875ae61cbb6e7d3995d2e36e7300b0daec86))
|
||||||
|
|
||||||
|
## [0.21.3](https://github.com/sasjs/server/compare/v0.21.2...v0.21.3) (2022-09-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* return same tokens if not expired ([330c020](https://github.com/sasjs/server/commit/330c020933f1080261b38f07d6b627f6d7c62446))
|
||||||
|
|
||||||
|
## [0.21.2](https://github.com/sasjs/server/compare/v0.21.1...v0.21.2) (2022-09-20)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* default content-type for sas programs should be text/plain ([9977c9d](https://github.com/sasjs/server/commit/9977c9d161947b11d45ab2513f99a5320a3f5a06))
|
||||||
|
* **studio:** inject program path to code before sending for execution ([edc2e2a](https://github.com/sasjs/server/commit/edc2e2a302ccea4985f3d6b83ef8c23620ab82b6))
|
||||||
|
|
||||||
## [0.21.1](https://github.com/sasjs/server/compare/v0.21.0...v0.21.1) (2022-09-19)
|
## [0.21.1](https://github.com/sasjs/server/compare/v0.21.0...v0.21.1) (2022-09-19)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -660,10 +660,10 @@ paths:
|
|||||||
anyOf:
|
anyOf:
|
||||||
- {type: string}
|
- {type: string}
|
||||||
- {type: string, format: byte}
|
- {type: string, format: byte}
|
||||||
description: 'Execute SAS code.'
|
description: 'Execute Code on the Specified Runtime'
|
||||||
summary: 'Run SAS Code and returns log'
|
summary: 'Run Code and Return Webout Content and Log'
|
||||||
tags:
|
tags:
|
||||||
- CODE
|
- Code
|
||||||
security:
|
security:
|
||||||
-
|
-
|
||||||
bearerAuth: []
|
bearerAuth: []
|
||||||
@@ -1658,8 +1658,8 @@ paths:
|
|||||||
anyOf:
|
anyOf:
|
||||||
- {type: string}
|
- {type: string}
|
||||||
- {type: string, format: byte}
|
- {type: string, format: byte}
|
||||||
description: "Trigger a SAS or JS program using the _program URL parameter.\n\nAccepts URL parameters and file uploads. For more details, see docs:\n\nhttps://server.sasjs.io/storedprograms"
|
description: "Trigger a Stored Program using the _program URL parameter.\n\nAccepts URL parameters and file uploads. For more details, see docs:\n\nhttps://server.sasjs.io/storedprograms"
|
||||||
summary: 'Execute a Stored Program, returns raw _webout content.'
|
summary: 'Execute a Stored Program, returns _webout and (optionally) log.'
|
||||||
tags:
|
tags:
|
||||||
- STP
|
- STP
|
||||||
security:
|
security:
|
||||||
@@ -1667,7 +1667,7 @@ paths:
|
|||||||
bearerAuth: []
|
bearerAuth: []
|
||||||
parameters:
|
parameters:
|
||||||
-
|
-
|
||||||
description: 'Location of SAS or JS code'
|
description: 'Location of code in SASjs Drive'
|
||||||
in: query
|
in: query
|
||||||
name: _program
|
name: _program
|
||||||
required: true
|
required: true
|
||||||
@@ -1685,8 +1685,8 @@ paths:
|
|||||||
anyOf:
|
anyOf:
|
||||||
- {type: string}
|
- {type: string}
|
||||||
- {type: string, format: byte}
|
- {type: string, format: byte}
|
||||||
description: "Trigger a SAS or JS program using the _program URL parameter.\n\nAccepts URL parameters and file uploads. For more details, see docs:\n\nhttps://server.sasjs.io/storedprograms\n\nThe response will be a JSON object with the following root attributes:\nlog, webout, headers.\n\nThe webout attribute will be nested JSON ONLY if the response-header\ncontains a content-type of application/json AND it is valid JSON.\nOtherwise it will be a stringified version of the webout content."
|
description: "Trigger a Stored Program using the _program URL parameter.\n\nAccepts URL parameters and file uploads. For more details, see docs:\n\nhttps://server.sasjs.io/storedprograms"
|
||||||
summary: 'Execute a Stored Program, return a JSON object'
|
summary: 'Execute a Stored Program, returns _webout and (optionally) log.'
|
||||||
tags:
|
tags:
|
||||||
- STP
|
- STP
|
||||||
security:
|
security:
|
||||||
@@ -1694,7 +1694,7 @@ paths:
|
|||||||
bearerAuth: []
|
bearerAuth: []
|
||||||
parameters:
|
parameters:
|
||||||
-
|
-
|
||||||
description: 'Location of SAS or JS code'
|
description: 'Location of code in SASjs Drive'
|
||||||
in: query
|
in: query
|
||||||
name: _program
|
name: _program
|
||||||
required: false
|
required: false
|
||||||
@@ -1798,7 +1798,7 @@ tags:
|
|||||||
name: Client
|
name: Client
|
||||||
description: 'Operations about clients'
|
description: 'Operations about clients'
|
||||||
-
|
-
|
||||||
name: CODE
|
name: Code
|
||||||
description: 'Execution of code (various runtimes are supported)'
|
description: 'Execution of code (various runtimes are supported)'
|
||||||
-
|
-
|
||||||
name: Drive
|
name: Drive
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { InfoJWT } from '../types'
|
|||||||
import {
|
import {
|
||||||
generateAccessToken,
|
generateAccessToken,
|
||||||
generateRefreshToken,
|
generateRefreshToken,
|
||||||
|
getTokensFromDB,
|
||||||
removeTokensInDB,
|
removeTokensInDB,
|
||||||
saveTokensInDB
|
saveTokensInDB
|
||||||
} from '../utils'
|
} from '../utils'
|
||||||
@@ -73,6 +74,15 @@ const token = async (data: any): Promise<TokenResponse> => {
|
|||||||
|
|
||||||
AuthController.deleteCode(userInfo.userId, clientId)
|
AuthController.deleteCode(userInfo.userId, clientId)
|
||||||
|
|
||||||
|
// get tokens from DB
|
||||||
|
const existingTokens = await getTokensFromDB(userInfo.userId, clientId)
|
||||||
|
if (existingTokens) {
|
||||||
|
return {
|
||||||
|
accessToken: existingTokens.accessToken,
|
||||||
|
refreshToken: existingTokens.refreshToken
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const accessToken = generateAccessToken(userInfo)
|
const accessToken = generateAccessToken(userInfo)
|
||||||
const refreshToken = generateRefreshToken(userInfo)
|
const refreshToken = generateRefreshToken(userInfo)
|
||||||
|
|
||||||
|
|||||||
@@ -24,11 +24,11 @@ interface ExecuteCodePayload {
|
|||||||
|
|
||||||
@Security('bearerAuth')
|
@Security('bearerAuth')
|
||||||
@Route('SASjsApi/code')
|
@Route('SASjsApi/code')
|
||||||
@Tags('CODE')
|
@Tags('Code')
|
||||||
export class CodeController {
|
export class CodeController {
|
||||||
/**
|
/**
|
||||||
* Execute SAS code.
|
* Execute Code on the Specified Runtime
|
||||||
* @summary Run SAS Code and returns log
|
* @summary Run Code and Return Webout Content and Log
|
||||||
*/
|
*/
|
||||||
@Post('/execute')
|
@Post('/execute')
|
||||||
public async executeCode(
|
public async executeCode(
|
||||||
|
|||||||
@@ -92,6 +92,9 @@ export class SASSessionController extends SessionController {
|
|||||||
path: sessionFolder
|
path: sessionFolder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const headersPath = path.join(session.path, 'stpsrv_header.txt')
|
||||||
|
await createFile(headersPath, 'Content-type: text/plain')
|
||||||
|
|
||||||
// we do not want to leave sessions running forever
|
// we do not want to leave sessions running forever
|
||||||
// we clean them up after a predefined period, if unused
|
// we clean them up after a predefined period, if unused
|
||||||
this.scheduleSessionDestroy(session)
|
this.scheduleSessionDestroy(session)
|
||||||
@@ -170,7 +173,7 @@ ${autoExecContent}`
|
|||||||
session.ready = true
|
session.ready = true
|
||||||
}
|
}
|
||||||
|
|
||||||
public async deleteSession(session: Session) {
|
private async deleteSession(session: Session) {
|
||||||
// remove the temporary files, to avoid buildup
|
// remove the temporary files, to avoid buildup
|
||||||
await deleteFolder(session.path)
|
await deleteFolder(session.path)
|
||||||
|
|
||||||
|
|||||||
@@ -23,14 +23,14 @@ interface ExecutePostRequestPayload {
|
|||||||
@Tags('STP')
|
@Tags('STP')
|
||||||
export class STPController {
|
export class STPController {
|
||||||
/**
|
/**
|
||||||
* Trigger a SAS or JS program using the _program URL parameter.
|
* Trigger a Stored Program using the _program URL parameter.
|
||||||
*
|
*
|
||||||
* Accepts URL parameters and file uploads. For more details, see docs:
|
* Accepts URL parameters and file uploads. For more details, see docs:
|
||||||
*
|
*
|
||||||
* https://server.sasjs.io/storedprograms
|
* https://server.sasjs.io/storedprograms
|
||||||
*
|
*
|
||||||
* @summary Execute a Stored Program, returns raw _webout content.
|
* @summary Execute a Stored Program, returns _webout and (optionally) log.
|
||||||
* @param _program Location of SAS or JS code
|
* @param _program Location of code in SASjs Drive
|
||||||
* @example _program "/Projects/myApp/some/program"
|
* @example _program "/Projects/myApp/some/program"
|
||||||
*/
|
*/
|
||||||
@Get('/execute')
|
@Get('/execute')
|
||||||
@@ -43,21 +43,15 @@ export class STPController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trigger a SAS or JS program using the _program URL parameter.
|
* Trigger a Stored Program using the _program URL parameter.
|
||||||
*
|
*
|
||||||
* Accepts URL parameters and file uploads. For more details, see docs:
|
* Accepts URL parameters and file uploads. For more details, see docs:
|
||||||
*
|
*
|
||||||
* https://server.sasjs.io/storedprograms
|
* https://server.sasjs.io/storedprograms
|
||||||
*
|
*
|
||||||
* The response will be a JSON object with the following root attributes:
|
|
||||||
* log, webout, headers.
|
|
||||||
*
|
*
|
||||||
* The webout attribute will be nested JSON ONLY if the response-header
|
* @summary Execute a Stored Program, returns _webout and (optionally) log.
|
||||||
* contains a content-type of application/json AND it is valid JSON.
|
* @param _program Location of code in SASjs Drive
|
||||||
* Otherwise it will be a stringified version of the webout content.
|
|
||||||
*
|
|
||||||
* @summary Execute a Stored Program, return a JSON object
|
|
||||||
* @param _program Location of SAS or JS code
|
|
||||||
* @example _program "/Projects/myApp/some/program"
|
* @example _program "/Projects/myApp/some/program"
|
||||||
*/
|
*/
|
||||||
@Post('/execute')
|
@Post('/execute')
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
authenticateRefreshToken
|
authenticateRefreshToken
|
||||||
} from '../../middlewares'
|
} from '../../middlewares'
|
||||||
|
|
||||||
import { authorizeValidation, tokenValidation } from '../../utils'
|
import { tokenValidation } from '../../utils'
|
||||||
import { InfoJWT } from '../../types'
|
import { InfoJWT } from '../../types'
|
||||||
|
|
||||||
const authRouter = express.Router()
|
const authRouter = express.Router()
|
||||||
|
|||||||
34
api/src/utils/getTokensFromDB.ts
Normal file
34
api/src/utils/getTokensFromDB.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import jwt from 'jsonwebtoken'
|
||||||
|
import User from '../model/User'
|
||||||
|
|
||||||
|
export const getTokensFromDB = async (userId: number, clientId: string) => {
|
||||||
|
const user = await User.findOne({ id: userId })
|
||||||
|
if (!user) return
|
||||||
|
|
||||||
|
const currentTokenObj = user.tokens.find(
|
||||||
|
(tokenObj: any) => tokenObj.clientId === clientId
|
||||||
|
)
|
||||||
|
|
||||||
|
if (currentTokenObj) {
|
||||||
|
const accessToken = currentTokenObj.accessToken
|
||||||
|
const refreshToken = currentTokenObj.refreshToken
|
||||||
|
|
||||||
|
const verifiedAccessToken: any = jwt.verify(
|
||||||
|
accessToken,
|
||||||
|
process.secrets.ACCESS_TOKEN_SECRET
|
||||||
|
)
|
||||||
|
|
||||||
|
const verifiedRefreshToken: any = jwt.verify(
|
||||||
|
refreshToken,
|
||||||
|
process.secrets.REFRESH_TOKEN_SECRET
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
verifiedAccessToken?.userId === userId &&
|
||||||
|
verifiedAccessToken?.clientId === clientId &&
|
||||||
|
verifiedRefreshToken?.userId === userId &&
|
||||||
|
verifiedRefreshToken?.clientId === clientId
|
||||||
|
)
|
||||||
|
return { accessToken, refreshToken }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ export * from './getDesktopFields'
|
|||||||
export * from './getPreProgramVariables'
|
export * from './getPreProgramVariables'
|
||||||
export * from './getRunTimeAndFilePath'
|
export * from './getRunTimeAndFilePath'
|
||||||
export * from './getServerUrl'
|
export * from './getServerUrl'
|
||||||
|
export * from './getTokensFromDB'
|
||||||
export * from './instantiateLogger'
|
export * from './instantiateLogger'
|
||||||
export * from './isDebugOn'
|
export * from './isDebugOn'
|
||||||
export * from './isPublicRoute'
|
export * from './isPublicRoute'
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
"description": "Operations about clients"
|
"description": "Operations about clients"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "CODE",
|
"name": "Code",
|
||||||
"description": "Execution of code (various runtimes are supported)"
|
"description": "Execution of code (various runtimes are supported)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { RunTimeType } from '../../../context/appContext'
|
||||||
|
|
||||||
export const getLanguageFromExtension = (extension: string) => {
|
export const getLanguageFromExtension = (extension: string) => {
|
||||||
if (extension === 'js') return 'javascript'
|
if (extension === 'js') return 'javascript'
|
||||||
|
|
||||||
@@ -12,3 +14,26 @@ export const getSelection = (editor: any) => {
|
|||||||
const selection = editor?.getModel().getValueInRange(editor?.getSelection())
|
const selection = editor?.getModel().getValueInRange(editor?.getSelection())
|
||||||
return selection ?? ''
|
return selection ?? ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const programPathInjection = (
|
||||||
|
code: string,
|
||||||
|
path: string,
|
||||||
|
runtime: RunTimeType
|
||||||
|
) => {
|
||||||
|
if (path) {
|
||||||
|
if (runtime === RunTimeType.JS) {
|
||||||
|
return `const _PROGRAM = '${path}';\n${code}`
|
||||||
|
}
|
||||||
|
if (runtime === RunTimeType.PY) {
|
||||||
|
return `_PROGRAM = '${path}';\n${code}`
|
||||||
|
}
|
||||||
|
if (runtime === RunTimeType.R) {
|
||||||
|
return `._PROGRAM = '${path}';\n${code}`
|
||||||
|
}
|
||||||
|
if (runtime === RunTimeType.SAS) {
|
||||||
|
return `%let _program = ${path};\n${code}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
} from 'react'
|
} from 'react'
|
||||||
import { DiffEditorDidMount, EditorDidMount, monaco } from 'react-monaco-editor'
|
import { DiffEditorDidMount, EditorDidMount, monaco } from 'react-monaco-editor'
|
||||||
import { SelectChangeEvent } from '@mui/material'
|
import { SelectChangeEvent } from '@mui/material'
|
||||||
import { getSelection } from '../helper'
|
import { getSelection, programPathInjection } from '../helper'
|
||||||
import { AppContext, RunTimeType } from '../../../../context/appContext'
|
import { AppContext, RunTimeType } from '../../../../context/appContext'
|
||||||
import { AlertSeverityType } from '../../../../components/snackbar'
|
import { AlertSeverityType } from '../../../../components/snackbar'
|
||||||
import {
|
import {
|
||||||
@@ -151,7 +151,14 @@ const useEditor = ({
|
|||||||
const runCode = (code: string) => {
|
const runCode = (code: string) => {
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
axios
|
axios
|
||||||
.post(`/SASjsApi/code/execute`, { code, runTime: selectedRunTime })
|
.post(`/SASjsApi/code/execute`, {
|
||||||
|
code: programPathInjection(
|
||||||
|
code,
|
||||||
|
selectedFilePath,
|
||||||
|
selectedRunTime as RunTimeType
|
||||||
|
),
|
||||||
|
runTime: selectedRunTime
|
||||||
|
})
|
||||||
.then((res: any) => {
|
.then((res: any) => {
|
||||||
setWebout(res.data.split(SASJS_LOGS_SEPARATOR)[0] ?? '')
|
setWebout(res.data.split(SASJS_LOGS_SEPARATOR)[0] ?? '')
|
||||||
setLog(res.data.split(SASJS_LOGS_SEPARATOR)[1] ?? '')
|
setLog(res.data.split(SASJS_LOGS_SEPARATOR)[1] ?? '')
|
||||||
|
|||||||
Reference in New Issue
Block a user