mirror of
https://github.com/sasjs/server.git
synced 2025-12-10 11:24:35 +00:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66232aefd2 | ||
|
|
bf35791655 | ||
|
|
2dc11630e4 | ||
|
|
1473925896 | ||
|
|
b472f1bd61 | ||
|
|
1fc1431442 | ||
|
|
3387efbb9a | ||
|
|
e2996b495f | ||
|
|
41c627f93a | ||
|
|
49f5dc7555 | ||
|
|
f6e77f99a4 | ||
|
|
b57dfa429b | ||
| 9586dbb2d0 | |||
|
|
a4f78ab48d | ||
|
|
2f47a2213b | ||
|
|
0f91395fbb | ||
|
|
167b14fed0 | ||
|
|
8940f4dc47 |
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -56,4 +56,4 @@ jobs:
|
||||
|
||||
- name: Release
|
||||
run: |
|
||||
GITHUB_TOKEN=${{ secrets.GH_TOKEN }} semantic-release
|
||||
GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} semantic-release
|
||||
|
||||
14
CHANGELOG.md
14
CHANGELOG.md
@@ -1,3 +1,17 @@
|
||||
## [0.35.2](https://github.com/sasjs/server/compare/v0.35.1...v0.35.2) (2023-08-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add _debug as optional query param in swagger apis for GET stp/execute ([9586dbb](https://github.com/sasjs/server/commit/9586dbb2d0d6611061c9efdfb84030144f62c2ee))
|
||||
|
||||
## [0.35.1](https://github.com/sasjs/server/compare/v0.35.0...v0.35.1) (2023-07-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **log-separator:** log separator should always wrap log ([8940f4d](https://github.com/sasjs/server/commit/8940f4dc47abae2036b4fcdeb772c31a0ca07cca))
|
||||
|
||||
# [0.35.0](https://github.com/sasjs/server/compare/v0.34.2...v0.35.0) (2023-05-03)
|
||||
|
||||
|
||||
|
||||
4643
api/package-lock.json
generated
4643
api/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -18,10 +18,10 @@
|
||||
"lint": "npx prettier --check \"src/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\"",
|
||||
"exe": "npm run build && pkg .",
|
||||
"copy:files": "npm run public:copy && npm run sasjsbuild:copy && npm run sas:copy && npm run web:copy",
|
||||
"public:copy": "cp -r ./public/ ./build/public/",
|
||||
"sasjsbuild:copy": "cp -r ./sasjsbuild/ ./build/sasjsbuild/",
|
||||
"sas:copy": "cp -r ./sas/ ./build/sas/",
|
||||
"web:copy": "rimraf web && mkdir web && cp -r ../web/build/ ./web/build/",
|
||||
"public:copy": "cpr ./public/ ./build/public/",
|
||||
"sasjsbuild:copy": "cpr ./sasjsbuild/ ./build/sasjsbuild/",
|
||||
"sas:copy": "cpr ./sas/ ./build/sas/",
|
||||
"web:copy": "rimraf web && mkdir web && cpr ../web/build/ ./web/build/",
|
||||
"compileSysInit": "ts-node ./scripts/compileSysInit.ts",
|
||||
"copySASjsCore": "ts-node ./scripts/copySASjsCore.ts",
|
||||
"downloadMacros": "ts-node ./scripts/downloadMacros.ts"
|
||||
@@ -58,7 +58,7 @@
|
||||
"express-session": "^1.17.2",
|
||||
"helmet": "^5.0.2",
|
||||
"joi": "^17.4.2",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"ldapjs": "2.3.3",
|
||||
"mongoose": "^6.0.12",
|
||||
"morgan": "^1.10.0",
|
||||
@@ -87,6 +87,7 @@
|
||||
"@types/unzipper": "^0.10.5",
|
||||
"adm-zip": "^0.5.9",
|
||||
"axios": "0.27.2",
|
||||
"cpr": "^3.0.1",
|
||||
"csrf": "^3.1.0",
|
||||
"dotenv": "^10.0.0",
|
||||
"http-headers-validation": "^0.0.1",
|
||||
@@ -95,7 +96,7 @@
|
||||
"nodejs-file-downloader": "4.10.2",
|
||||
"nodemon": "^2.0.7",
|
||||
"pkg": "5.6.0",
|
||||
"prettier": "^2.3.1",
|
||||
"prettier": "^3.0.3",
|
||||
"rimraf": "^3.0.2",
|
||||
"supertest": "^6.1.3",
|
||||
"ts-jest": "^27.0.3",
|
||||
|
||||
@@ -792,7 +792,7 @@ paths:
|
||||
- {type: string}
|
||||
- {type: string, format: byte}
|
||||
description: 'Execute Code on the Specified Runtime'
|
||||
summary: 'Run Code and Return Webout Content and Log'
|
||||
summary: "Run Code and Return Webout Content, Log and Print output\nThe order of returned parts of the payload is:\n1. Webout (if present)\n2. Logs UUID (used as separator)\n3. Log\n4. Logs UUID (used as separator)\n5. Print (if present and if the runtime is SAS)\nPlease see"
|
||||
tags:
|
||||
- Code
|
||||
security:
|
||||
@@ -1789,7 +1789,7 @@ paths:
|
||||
anyOf:
|
||||
- {type: string}
|
||||
- {type: string, format: byte}
|
||||
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"
|
||||
description: "Trigger a Stored Program using the _program URL parameter.\n\nAccepts additional URL parameters (converted to session variables)\nand file uploads. For more details, see docs:\n\nhttps://server.sasjs.io/storedprograms"
|
||||
summary: 'Execute a Stored Program, returns _webout and (optionally) log.'
|
||||
tags:
|
||||
- STP
|
||||
@@ -1805,6 +1805,15 @@ paths:
|
||||
schema:
|
||||
type: string
|
||||
example: /Projects/myApp/some/program
|
||||
-
|
||||
description: 'Optional query param for setting debug mode, which will return the session log.'
|
||||
in: query
|
||||
name: _debug
|
||||
required: false
|
||||
schema:
|
||||
format: double
|
||||
type: number
|
||||
example: 131
|
||||
post:
|
||||
operationId: ExecutePostRequest
|
||||
responses:
|
||||
|
||||
@@ -128,8 +128,10 @@ export class ExecutionController {
|
||||
// INFO: webout can be a Buffer, that is why it's length should be checked to determine if it is empty
|
||||
if (webout && webout.length !== 0) resultParts.push(webout)
|
||||
|
||||
// INFO: log separator wraps the log from the beginning and the end
|
||||
resultParts.push(process.logsUUID)
|
||||
resultParts.push(log)
|
||||
resultParts.push(process.logsUUID)
|
||||
|
||||
if (includePrintOutput && runTime === RunTimeType.SAS) {
|
||||
const printOutputPath = path.join(session.path, 'output.lst')
|
||||
@@ -137,10 +139,7 @@ export class ExecutionController {
|
||||
? await readFile(printOutputPath)
|
||||
: ''
|
||||
|
||||
if (printOutput) {
|
||||
resultParts.push(process.logsUUID)
|
||||
resultParts.push(printOutput)
|
||||
}
|
||||
if (printOutput) resultParts.push(printOutput)
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -190,17 +190,20 @@ ${autoExecContent}`
|
||||
}
|
||||
|
||||
private scheduleSessionDestroy(session: Session) {
|
||||
setTimeout(async () => {
|
||||
if (session.inUse) {
|
||||
// adding 10 more minutes
|
||||
const newDeathTimeStamp = parseInt(session.deathTimeStamp) + 10 * 1000
|
||||
session.deathTimeStamp = newDeathTimeStamp.toString()
|
||||
setTimeout(
|
||||
async () => {
|
||||
if (session.inUse) {
|
||||
// adding 10 more minutes
|
||||
const newDeathTimeStamp = parseInt(session.deathTimeStamp) + 10 * 1000
|
||||
session.deathTimeStamp = newDeathTimeStamp.toString()
|
||||
|
||||
this.scheduleSessionDestroy(session)
|
||||
} else {
|
||||
await this.deleteSession(session)
|
||||
}
|
||||
}, parseInt(session.deathTimeStamp) - new Date().getTime() - 100)
|
||||
this.scheduleSessionDestroy(session)
|
||||
} else {
|
||||
await this.deleteSession(session)
|
||||
}
|
||||
},
|
||||
parseInt(session.deathTimeStamp) - new Date().getTime() - 100
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
getRunTimeAndFilePath
|
||||
} from '../utils'
|
||||
import { MulterFile } from '../types/Upload'
|
||||
import { debug } from 'console'
|
||||
|
||||
interface ExecutePostRequestPayload {
|
||||
/**
|
||||
@@ -23,20 +24,30 @@ export class STPController {
|
||||
/**
|
||||
* Trigger a Stored Program using the _program URL parameter.
|
||||
*
|
||||
* Accepts URL parameters and file uploads. For more details, see docs:
|
||||
* Accepts additional URL parameters (converted to session variables)
|
||||
* and file uploads. For more details, see docs:
|
||||
*
|
||||
* https://server.sasjs.io/storedprograms
|
||||
*
|
||||
* @summary Execute a Stored Program, returns _webout and (optionally) log.
|
||||
* @param _program Location of code in SASjs Drive
|
||||
* @param _debug Optional query param for setting debug mode, which will return the session log.
|
||||
* @example _program "/Projects/myApp/some/program"
|
||||
* @example _debug 131
|
||||
*/
|
||||
@Get('/execute')
|
||||
public async executeGetRequest(
|
||||
@Request() request: express.Request,
|
||||
@Query() _program: string
|
||||
@Query() _program: string,
|
||||
@Query() _debug?: number
|
||||
): Promise<string | Buffer> {
|
||||
const vars = request.query as ExecutionVars
|
||||
let vars = request.query as ExecutionVars
|
||||
if (_debug) {
|
||||
vars = {
|
||||
...vars,
|
||||
_debug
|
||||
}
|
||||
}
|
||||
|
||||
return execute(request, _program, vars)
|
||||
}
|
||||
|
||||
@@ -13,7 +13,11 @@ stpRouter.get('/execute', async (req, res) => {
|
||||
if (error) return res.status(400).send(error.details[0].message)
|
||||
|
||||
try {
|
||||
const response = await controller.executeGetRequest(req, query._program)
|
||||
const response = await controller.executeGetRequest(
|
||||
req,
|
||||
query._program,
|
||||
query._debug
|
||||
)
|
||||
|
||||
if (response instanceof Buffer) {
|
||||
res.writeHead(200, (req as any).sasHeaders)
|
||||
|
||||
@@ -51,9 +51,8 @@ export const generateFileUploadSasCode = async (
|
||||
let fileCount = 0
|
||||
const uploadedFiles: UploadedFiles[] = []
|
||||
|
||||
const sasSessionFolderList: string[] = await listFilesInFolder(
|
||||
sasSessionFolder
|
||||
)
|
||||
const sasSessionFolderList: string[] =
|
||||
await listFilesInFolder(sasSessionFolder)
|
||||
sasSessionFolderList.forEach((fileName) => {
|
||||
let fileCountString = fileCount < 100 ? '0' + fileCount : fileCount
|
||||
fileCountString = fileCount < 10 ? '00' + fileCount : fileCount
|
||||
|
||||
@@ -180,7 +180,8 @@ export const runCodeValidation = (data: any): Joi.ValidationResult =>
|
||||
|
||||
export const executeProgramRawValidation = (data: any): Joi.ValidationResult =>
|
||||
Joi.object({
|
||||
_program: Joi.string().required()
|
||||
_program: Joi.string().required(),
|
||||
_debug: Joi.number()
|
||||
})
|
||||
.pattern(/^/, Joi.alternatives(Joi.string(), Joi.number()))
|
||||
.validate(data)
|
||||
|
||||
21
web/package-lock.json
generated
21
web/package-lock.json
generated
@@ -56,7 +56,7 @@
|
||||
"file-loader": "^6.2.0",
|
||||
"html-webpack-plugin": "5.5.0",
|
||||
"path": "0.12.7",
|
||||
"prettier": "^2.4.1",
|
||||
"prettier": "^3.0.3",
|
||||
"sass": "^1.44.0",
|
||||
"sass-loader": "^12.3.0",
|
||||
"style-loader": "^3.3.1",
|
||||
@@ -9413,15 +9413,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz",
|
||||
"integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==",
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz",
|
||||
"integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"prettier": "bin-prettier.js"
|
||||
"prettier": "bin/prettier.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/pretty-error": {
|
||||
@@ -18615,9 +18618,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"prettier": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz",
|
||||
"integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==",
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz",
|
||||
"integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==",
|
||||
"dev": true
|
||||
},
|
||||
"pretty-error": {
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "webpack-dev-server --config webpack.dev.ts --hot",
|
||||
"build": "webpack --config webpack.prod.ts"
|
||||
"build": "webpack --config webpack.prod.ts",
|
||||
"lint": "npx prettier --check \"src/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\"",
|
||||
"lint:fix": "npx prettier --write \"src/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.4.1",
|
||||
@@ -55,7 +57,7 @@
|
||||
"file-loader": "^6.2.0",
|
||||
"html-webpack-plugin": "5.5.0",
|
||||
"path": "0.12.7",
|
||||
"prettier": "^2.4.1",
|
||||
"prettier": "^3.0.3",
|
||||
"sass": "^1.44.0",
|
||||
"sass-loader": "^12.3.0",
|
||||
"style-loader": "^3.3.1",
|
||||
|
||||
@@ -3,12 +3,11 @@ import Snackbar from '@mui/material/Snackbar'
|
||||
import MuiAlert, { AlertProps } from '@mui/material/Alert'
|
||||
import Slide, { SlideProps } from '@mui/material/Slide'
|
||||
|
||||
const Alert = React.forwardRef<HTMLDivElement, AlertProps>(function Alert(
|
||||
props,
|
||||
ref
|
||||
) {
|
||||
return <MuiAlert elevation={6} ref={ref} variant="filled" {...props} />
|
||||
})
|
||||
const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
|
||||
function Alert(props, ref) {
|
||||
return <MuiAlert elevation={6} ref={ref} variant="filled" {...props} />
|
||||
}
|
||||
)
|
||||
|
||||
const Transition = (props: SlideProps) => {
|
||||
return <Slide {...props} direction="up" />
|
||||
|
||||
@@ -7,8 +7,10 @@
|
||||
border: none;
|
||||
outline: none;
|
||||
transition: 0.4s;
|
||||
box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 1px -1px,
|
||||
rgba(0, 0, 0, 0.14) 0px 1px 1px 0px, rgba(0, 0, 0, 0.12) 0px 1px 3px 0px;
|
||||
box-shadow:
|
||||
rgba(0, 0, 0, 0.2) 0px 2px 1px -1px,
|
||||
rgba(0, 0, 0, 0.14) 0px 1px 1px 0px,
|
||||
rgba(0, 0, 0, 0.12) 0px 1px 3px 0px;
|
||||
}
|
||||
|
||||
.ChunkDetails {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
|
||||
Reference in New Issue
Block a user