mirror of
https://github.com/sasjs/server.git
synced 2025-12-10 19:34:34 +00:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e684b54a6 | ||
|
|
aafda2922b | ||
| 418bf41e38 | |||
| 81f0b03b09 | |||
| fe5ae44aab | |||
| 36be3a7d5e | |||
| 6434123401 | |||
|
|
0a6b972c65 | ||
|
|
be11707042 | ||
| 2412622367 | |||
|
|
de3a190a8d | ||
|
|
d5daafc6ed | ||
|
|
b1a2677b8c | ||
|
|
94072c3d24 | ||
|
|
b64c0c12da | ||
|
|
79bc7b0e28 | ||
|
|
fda0e0b57d | ||
|
|
14731e8824 | ||
|
|
258cc35f14 | ||
|
|
2295a518f0 | ||
|
|
1e5d621817 | ||
| 4d64420c45 | |||
|
|
799339de30 | ||
|
|
042ed41189 | ||
| 424f0fc1fa |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -5,8 +5,6 @@ node_modules/
|
|||||||
.env*
|
.env*
|
||||||
sas/
|
sas/
|
||||||
sasjs_root/
|
sasjs_root/
|
||||||
api/mocks/custom/*
|
|
||||||
!api/mocks/custom/.keep
|
|
||||||
tmp/
|
tmp/
|
||||||
build/
|
build/
|
||||||
sasjsbuild/
|
sasjsbuild/
|
||||||
|
|||||||
45
CHANGELOG.md
45
CHANGELOG.md
@@ -1,3 +1,48 @@
|
|||||||
|
# [0.24.0](https://github.com/sasjs/server/compare/v0.23.4...v0.24.0) (2022-10-28)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* cli mock testing ([6434123](https://github.com/sasjs/server/commit/643412340162e854f31fba2f162d83b7ab1751d8))
|
||||||
|
* mocking sas9 responses with JS STP ([36be3a7](https://github.com/sasjs/server/commit/36be3a7d5e7df79f9a1f3f00c3661b925f462383))
|
||||||
|
|
||||||
|
## [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)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* ldap issues ([4d64420](https://github.com/sasjs/server/commit/4d64420c45424134b4d2014a2d5dd6e846ed03b3))
|
||||||
|
|
||||||
|
# [0.23.0](https://github.com/sasjs/server/compare/v0.22.1...v0.23.0) (2022-10-03)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Enable SAS_PACKAGES in SASjs Server ([424f0fc](https://github.com/sasjs/server/commit/424f0fc1faec765eb7a14619584e649454105b70))
|
||||||
|
|
||||||
## [0.22.1](https://github.com/sasjs/server/compare/v0.22.0...v0.22.1) (2022-10-03)
|
## [0.22.1](https://github.com/sasjs/server/compare/v0.22.0...v0.22.1) (2022-10-03)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -103,6 +103,11 @@ PORT=
|
|||||||
# If not present, mocking function is disabled
|
# If not present, mocking function is disabled
|
||||||
MOCK_SERVERTYPE=
|
MOCK_SERVERTYPE=
|
||||||
|
|
||||||
|
# default: /api/mocks
|
||||||
|
# Path to mocking folder, for generic responses, it's sub directories should be: sas9, viya, sasjs
|
||||||
|
# Server will automatically use subdirectory accordingly
|
||||||
|
STATIC_MOCK_LOCATION=
|
||||||
|
|
||||||
#
|
#
|
||||||
## Additional SAS Options
|
## Additional SAS Options
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
MODE=[desktop|server] default considered as desktop
|
MODE=[desktop|server] default considered as desktop
|
||||||
CORS=[disable|enable] default considered as disable for server MODE & enable for desktop MODE
|
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`>
|
WHITELIST=<space separated urls, each starting with protocol `http` or `https`>
|
||||||
|
|
||||||
PROTOCOL=[http|https] default considered as http
|
PROTOCOL=[http|https] default considered as http
|
||||||
@@ -14,7 +15,7 @@ HELMET_COEP=[true|false] if omitted HELMET default will be used
|
|||||||
|
|
||||||
DB_CONNECT=mongodb+srv://<DB_USERNAME>:<DB_PASSWORD>@<CLUSTER>/<DB_NAME>?retryWrites=true&w=majority
|
DB_CONNECT=mongodb+srv://<DB_USERNAME>:<DB_PASSWORD>@<CLUSTER>/<DB_NAME>?retryWrites=true&w=majority
|
||||||
|
|
||||||
AUTH_PROVIDERS=[ldap|internal] default considered as internal
|
AUTH_PROVIDERS=[ldap]
|
||||||
|
|
||||||
LDAP_URL= <LDAP_SERVER_URL>
|
LDAP_URL= <LDAP_SERVER_URL>
|
||||||
LDAP_BIND_DN= <cn=admin,ou=system,dc=cloudron>
|
LDAP_BIND_DN= <cn=admin,ou=system,dc=cloudron>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<div class="content">
|
<div class="content">
|
||||||
<form id="credentials" class="minimal" action="/SASLogon/login?service=http%3A%2F%2Flocalhost:5004%2FSASStoredProcess%2Fj_spring_cas_security_check" method="post">
|
<form id="credentials" class="minimal" action="/SASLogon/login?service=http%3A%2F%2Flocalhost:5004%2FSASStoredProcess%2Fj_spring_cas_security_check" method="post">
|
||||||
<!--form container-->
|
<!--form container-->
|
||||||
<input type="hidden" name="lt" value="LT-8-WGkt9EXwICBihaVbxGc92opjufTK1D" aria-hidden="true" />
|
<input type="hidden" name="lt" value="validtoken" aria-hidden="true" />
|
||||||
<input type="hidden" name="execution" value="e2s1" aria-hidden="true" />
|
<input type="hidden" name="execution" value="e2s1" aria-hidden="true" />
|
||||||
<input type="hidden" name="_eventId" value="submit" aria-hidden="true" />
|
<input type="hidden" name="_eventId" value="submit" aria-hidden="true" />
|
||||||
|
|
||||||
161
api/package-lock.json
generated
161
api/package-lock.json
generated
@@ -8,7 +8,7 @@
|
|||||||
"name": "api",
|
"name": "api",
|
||||||
"version": "0.0.2",
|
"version": "0.0.2",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sasjs/core": "^4.31.3",
|
"@sasjs/core": "^4.40.1",
|
||||||
"@sasjs/utils": "2.48.1",
|
"@sasjs/utils": "2.48.1",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"connect-mongo": "^4.6.0",
|
"connect-mongo": "^4.6.0",
|
||||||
@@ -50,11 +50,13 @@
|
|||||||
"@types/swagger-ui-express": "^4.1.3",
|
"@types/swagger-ui-express": "^4.1.3",
|
||||||
"@types/unzipper": "^0.10.5",
|
"@types/unzipper": "^0.10.5",
|
||||||
"adm-zip": "^0.5.9",
|
"adm-zip": "^0.5.9",
|
||||||
|
"axios": "0.27.2",
|
||||||
"csrf": "^3.1.0",
|
"csrf": "^3.1.0",
|
||||||
"dotenv": "^10.0.0",
|
"dotenv": "^10.0.0",
|
||||||
"http-headers-validation": "^0.0.1",
|
"http-headers-validation": "^0.0.1",
|
||||||
"jest": "^27.0.6",
|
"jest": "^27.0.6",
|
||||||
"mongodb-memory-server": "^8.0.0",
|
"mongodb-memory-server": "^8.0.0",
|
||||||
|
"nodejs-file-downloader": "4.10.2",
|
||||||
"nodemon": "^2.0.7",
|
"nodemon": "^2.0.7",
|
||||||
"pkg": "5.6.0",
|
"pkg": "5.6.0",
|
||||||
"prettier": "^2.3.1",
|
"prettier": "^2.3.1",
|
||||||
@@ -1392,9 +1394,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sasjs/core": {
|
"node_modules/@sasjs/core": {
|
||||||
"version": "4.31.3",
|
"version": "4.40.1",
|
||||||
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.31.3.tgz",
|
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.40.1.tgz",
|
||||||
"integrity": "sha512-TpVqWl5bqp3JTQjIg0r4WiQg7Ima5f17eAJILJbdYDdXsnLXlA/Csbb95G7eDPhzWpM3C0NrzKek3yvCMGzXIA=="
|
"integrity": "sha512-hVEVnH8tej57Cran/X/iUoDms7EoL+2fwAPvjQMgHBHh8ynsF8aqYBreiRCwbrvdrjBsnmayOVh2RiQLtfHhoQ=="
|
||||||
},
|
},
|
||||||
"node_modules/@sasjs/utils": {
|
"node_modules/@sasjs/utils": {
|
||||||
"version": "2.48.1",
|
"version": "2.48.1",
|
||||||
@@ -2539,6 +2541,30 @@
|
|||||||
"node": ">= 4.0.0"
|
"node": ">= 4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/axios": {
|
||||||
|
"version": "0.27.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
|
||||||
|
"integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"follow-redirects": "^1.14.9",
|
||||||
|
"form-data": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/axios/node_modules/form-data": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"asynckit": "^0.4.0",
|
||||||
|
"combined-stream": "^1.0.8",
|
||||||
|
"mime-types": "^2.1.12"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/babel-jest": {
|
"node_modules/babel-jest": {
|
||||||
"version": "27.0.6",
|
"version": "27.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.0.6.tgz",
|
||||||
@@ -4185,6 +4211,26 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/follow-redirects": {
|
||||||
|
"version": "1.15.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
||||||
|
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"debug": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/form-data": {
|
"node_modules/form-data": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
|
||||||
@@ -7519,6 +7565,18 @@
|
|||||||
"integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==",
|
"integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/nodejs-file-downloader": {
|
||||||
|
"version": "4.10.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/nodejs-file-downloader/-/nodejs-file-downloader-4.10.2.tgz",
|
||||||
|
"integrity": "sha512-pTVlytER/4wxcIpEhLXoqhuJ7WH1+xSFNLbI0wPmbwH3pWlJRRebb1Kbu91mz1CyOJmO4sj6YLH1wkF1B6efrQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"follow-redirects": "^1.15.1",
|
||||||
|
"https-proxy-agent": "^5.0.0",
|
||||||
|
"mime-types": "^2.1.27",
|
||||||
|
"sanitize-filename": "^1.6.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/nodemon": {
|
"node_modules/nodemon": {
|
||||||
"version": "2.0.19",
|
"version": "2.0.19",
|
||||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.19.tgz",
|
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.19.tgz",
|
||||||
@@ -8469,6 +8527,15 @@
|
|||||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/sanitize-filename": {
|
||||||
|
"version": "1.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz",
|
||||||
|
"integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"truncate-utf8-bytes": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/saslprep": {
|
"node_modules/saslprep": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
|
||||||
@@ -9323,6 +9390,15 @@
|
|||||||
"resolved": "https://registry.npmjs.org/traverse-chain/-/traverse-chain-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/traverse-chain/-/traverse-chain-0.1.0.tgz",
|
||||||
"integrity": "sha1-YdvC1Ttp/2CRoSoWj9fUMxB+QPE="
|
"integrity": "sha1-YdvC1Ttp/2CRoSoWj9fUMxB+QPE="
|
||||||
},
|
},
|
||||||
|
"node_modules/truncate-utf8-bytes": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"utf8-byte-length": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ts-jest": {
|
"node_modules/ts-jest": {
|
||||||
"version": "27.0.3",
|
"version": "27.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.0.3.tgz",
|
||||||
@@ -9626,6 +9702,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
|
||||||
"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
|
"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
|
||||||
},
|
},
|
||||||
|
"node_modules/utf8-byte-length": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-4+wkEYLBbWxqTahEsWrhxepcoVOJ+1z5PGIjPZxRkytcdSUaNjIjBM7Xn8E+pdSuV7SzvWovBFA54FO0JSoqhA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/util-deprecate": {
|
"node_modules/util-deprecate": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
@@ -11053,9 +11135,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@sasjs/core": {
|
"@sasjs/core": {
|
||||||
"version": "4.31.3",
|
"version": "4.40.1",
|
||||||
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.31.3.tgz",
|
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.40.1.tgz",
|
||||||
"integrity": "sha512-TpVqWl5bqp3JTQjIg0r4WiQg7Ima5f17eAJILJbdYDdXsnLXlA/Csbb95G7eDPhzWpM3C0NrzKek3yvCMGzXIA=="
|
"integrity": "sha512-hVEVnH8tej57Cran/X/iUoDms7EoL+2fwAPvjQMgHBHh8ynsF8aqYBreiRCwbrvdrjBsnmayOVh2RiQLtfHhoQ=="
|
||||||
},
|
},
|
||||||
"@sasjs/utils": {
|
"@sasjs/utils": {
|
||||||
"version": "2.48.1",
|
"version": "2.48.1",
|
||||||
@@ -12061,6 +12143,29 @@
|
|||||||
"integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
|
"integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"axios": {
|
||||||
|
"version": "0.27.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
|
||||||
|
"integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"follow-redirects": "^1.14.9",
|
||||||
|
"form-data": "^4.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"form-data": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"asynckit": "^0.4.0",
|
||||||
|
"combined-stream": "^1.0.8",
|
||||||
|
"mime-types": "^2.1.12"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"babel-jest": {
|
"babel-jest": {
|
||||||
"version": "27.0.6",
|
"version": "27.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.0.6.tgz",
|
||||||
@@ -13332,6 +13437,12 @@
|
|||||||
"path-exists": "^4.0.0"
|
"path-exists": "^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"follow-redirects": {
|
||||||
|
"version": "1.15.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
||||||
|
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"form-data": {
|
"form-data": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
|
||||||
@@ -15842,6 +15953,18 @@
|
|||||||
"integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==",
|
"integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"nodejs-file-downloader": {
|
||||||
|
"version": "4.10.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/nodejs-file-downloader/-/nodejs-file-downloader-4.10.2.tgz",
|
||||||
|
"integrity": "sha512-pTVlytER/4wxcIpEhLXoqhuJ7WH1+xSFNLbI0wPmbwH3pWlJRRebb1Kbu91mz1CyOJmO4sj6YLH1wkF1B6efrQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"follow-redirects": "^1.15.1",
|
||||||
|
"https-proxy-agent": "^5.0.0",
|
||||||
|
"mime-types": "^2.1.27",
|
||||||
|
"sanitize-filename": "^1.6.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nodemon": {
|
"nodemon": {
|
||||||
"version": "2.0.19",
|
"version": "2.0.19",
|
||||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.19.tgz",
|
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.19.tgz",
|
||||||
@@ -16520,6 +16643,15 @@
|
|||||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||||
},
|
},
|
||||||
|
"sanitize-filename": {
|
||||||
|
"version": "1.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz",
|
||||||
|
"integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"truncate-utf8-bytes": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"saslprep": {
|
"saslprep": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
|
||||||
@@ -17183,6 +17315,15 @@
|
|||||||
"resolved": "https://registry.npmjs.org/traverse-chain/-/traverse-chain-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/traverse-chain/-/traverse-chain-0.1.0.tgz",
|
||||||
"integrity": "sha1-YdvC1Ttp/2CRoSoWj9fUMxB+QPE="
|
"integrity": "sha1-YdvC1Ttp/2CRoSoWj9fUMxB+QPE="
|
||||||
},
|
},
|
||||||
|
"truncate-utf8-bytes": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"utf8-byte-length": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"ts-jest": {
|
"ts-jest": {
|
||||||
"version": "27.0.3",
|
"version": "27.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.0.3.tgz",
|
||||||
@@ -17405,6 +17546,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"utf8-byte-length": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-4+wkEYLBbWxqTahEsWrhxepcoVOJ+1z5PGIjPZxRkytcdSUaNjIjBM7Xn8E+pdSuV7SzvWovBFA54FO0JSoqhA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"util-deprecate": {
|
"util-deprecate": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"description": "Api of SASjs server",
|
"description": "Api of SASjs server",
|
||||||
"main": "./src/server.ts",
|
"main": "./src/server.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"initial": "npm run swagger && npm run compileSysInit && npm run copySASjsCore",
|
"initial": "npm run swagger && npm run compileSysInit && npm run copySASjsCore && npm run downloadMacros",
|
||||||
"prestart": "npm run initial",
|
"prestart": "npm run initial",
|
||||||
"prebuild": "npm run initial",
|
"prebuild": "npm run initial",
|
||||||
"start": "NODE_ENV=development nodemon ./src/server.ts",
|
"start": "NODE_ENV=development nodemon ./src/server.ts",
|
||||||
@@ -17,20 +17,21 @@
|
|||||||
"lint:fix": "npx prettier --write \"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}\"",
|
||||||
"lint": "npx prettier --check \"src/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\"",
|
"lint": "npx prettier --check \"src/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\"",
|
||||||
"exe": "npm run build && pkg .",
|
"exe": "npm run build && pkg .",
|
||||||
"copy:files": "npm run public:copy && npm run sasjsbuild:copy && npm run sasjscore:copy && npm run web:copy",
|
"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/",
|
"public:copy": "cp -r ./public/ ./build/public/",
|
||||||
"sasjsbuild:copy": "cp -r ./sasjsbuild/ ./build/sasjsbuild/",
|
"sasjsbuild:copy": "cp -r ./sasjsbuild/ ./build/sasjsbuild/",
|
||||||
"sasjscore:copy": "cp -r ./sasjscore/ ./build/sasjscore/",
|
"sas:copy": "cp -r ./sas/ ./build/sas/",
|
||||||
"web:copy": "rimraf web && mkdir web && cp -r ../web/build/ ./web/build/",
|
"web:copy": "rimraf web && mkdir web && cp -r ../web/build/ ./web/build/",
|
||||||
"compileSysInit": "ts-node ./scripts/compileSysInit.ts",
|
"compileSysInit": "ts-node ./scripts/compileSysInit.ts",
|
||||||
"copySASjsCore": "ts-node ./scripts/copySASjsCore.ts"
|
"copySASjsCore": "ts-node ./scripts/copySASjsCore.ts",
|
||||||
|
"downloadMacros": "ts-node ./scripts/downloadMacros.ts"
|
||||||
},
|
},
|
||||||
"bin": "./build/src/server.js",
|
"bin": "./build/src/server.js",
|
||||||
"pkg": {
|
"pkg": {
|
||||||
"assets": [
|
"assets": [
|
||||||
"./build/public/**/*",
|
"./build/public/**/*",
|
||||||
"./build/sasjsbuild/**/*",
|
"./build/sasjsbuild/**/*",
|
||||||
"./build/sasjscore/**/*",
|
"./build/sas/**/*",
|
||||||
"./web/build/**/*"
|
"./web/build/**/*"
|
||||||
],
|
],
|
||||||
"targets": [
|
"targets": [
|
||||||
@@ -47,7 +48,7 @@
|
|||||||
},
|
},
|
||||||
"author": "4GL Ltd",
|
"author": "4GL Ltd",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sasjs/core": "^4.31.3",
|
"@sasjs/core": "^4.40.1",
|
||||||
"@sasjs/utils": "2.48.1",
|
"@sasjs/utils": "2.48.1",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"connect-mongo": "^4.6.0",
|
"connect-mongo": "^4.6.0",
|
||||||
@@ -86,11 +87,13 @@
|
|||||||
"@types/swagger-ui-express": "^4.1.3",
|
"@types/swagger-ui-express": "^4.1.3",
|
||||||
"@types/unzipper": "^0.10.5",
|
"@types/unzipper": "^0.10.5",
|
||||||
"adm-zip": "^0.5.9",
|
"adm-zip": "^0.5.9",
|
||||||
|
"axios": "0.27.2",
|
||||||
"csrf": "^3.1.0",
|
"csrf": "^3.1.0",
|
||||||
"dotenv": "^10.0.0",
|
"dotenv": "^10.0.0",
|
||||||
"http-headers-validation": "^0.0.1",
|
"http-headers-validation": "^0.0.1",
|
||||||
"jest": "^27.0.6",
|
"jest": "^27.0.6",
|
||||||
"mongodb-memory-server": "^8.0.0",
|
"mongodb-memory-server": "^8.0.0",
|
||||||
|
"nodejs-file-downloader": "4.10.2",
|
||||||
"nodemon": "^2.0.7",
|
"nodemon": "^2.0.7",
|
||||||
"pkg": "5.6.0",
|
"pkg": "5.6.0",
|
||||||
"prettier": "^2.3.1",
|
"prettier": "^2.3.1",
|
||||||
|
|||||||
39
api/scripts/downloadMacros.ts
Normal file
39
api/scripts/downloadMacros.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
import Downloader from 'nodejs-file-downloader'
|
||||||
|
import { createFile, listFilesInFolder } from '@sasjs/utils'
|
||||||
|
|
||||||
|
import { sasJSCoreMacros, sasJSCoreMacrosInfo } from '../src/utils/file'
|
||||||
|
|
||||||
|
export const downloadMacros = async () => {
|
||||||
|
const url =
|
||||||
|
'https://api.github.com/repos/yabwon/SAS_PACKAGES/contents/SPF/Macros'
|
||||||
|
|
||||||
|
console.info(`Downloading macros from ${url}`)
|
||||||
|
|
||||||
|
await axios
|
||||||
|
.get(url)
|
||||||
|
.then(async (res) => {
|
||||||
|
await downloadFiles(res.data)
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
throw new Error(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const downloadFiles = async function (fileList: any) {
|
||||||
|
for (const file of fileList) {
|
||||||
|
const downloader = new Downloader({
|
||||||
|
url: file.download_url,
|
||||||
|
directory: sasJSCoreMacros,
|
||||||
|
fileName: file.path.replace(/^SPF\/Macros/, ''),
|
||||||
|
cloneFiles: false
|
||||||
|
})
|
||||||
|
await downloader.download()
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileNames = await listFilesInFolder(sasJSCoreMacros)
|
||||||
|
|
||||||
|
await createFile(sasJSCoreMacrosInfo, fileNames.join('\n'))
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadMacros()
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
import { Express } from 'express'
|
import { Express, CookieOptions } from 'express'
|
||||||
import mongoose from 'mongoose'
|
import mongoose from 'mongoose'
|
||||||
import session from 'express-session'
|
import session from 'express-session'
|
||||||
import MongoStore from 'connect-mongo'
|
import MongoStore from 'connect-mongo'
|
||||||
|
|
||||||
import { ModeType } from '../utils'
|
import { ModeType, ProtocolType } from '../utils'
|
||||||
import { cookieOptions } from '../app'
|
|
||||||
|
|
||||||
export const configureExpressSession = (app: Express) => {
|
export const configureExpressSession = (app: Express) => {
|
||||||
const { MODE } = process.env
|
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(
|
app.use(
|
||||||
session({
|
session({
|
||||||
secret: process.secrets.SESSION_SECRET,
|
secret: process.secrets.SESSION_SECRET,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import path from 'path'
|
import path from 'path'
|
||||||
import express, { ErrorRequestHandler, CookieOptions } from 'express'
|
import express, { ErrorRequestHandler } from 'express'
|
||||||
import cookieParser from 'cookie-parser'
|
import cookieParser from 'cookie-parser'
|
||||||
import dotenv from 'dotenv'
|
import dotenv from 'dotenv'
|
||||||
|
|
||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
getWebBuildFolder,
|
getWebBuildFolder,
|
||||||
instantiateLogger,
|
instantiateLogger,
|
||||||
loadAppStreamConfig,
|
loadAppStreamConfig,
|
||||||
ProtocolType,
|
|
||||||
ReturnCode,
|
ReturnCode,
|
||||||
setProcessVariables,
|
setProcessVariables,
|
||||||
setupFolders,
|
setupFolders,
|
||||||
@@ -29,15 +28,6 @@ if (verifyEnvVariables()) process.exit(ReturnCode.InvalidEnv)
|
|||||||
|
|
||||||
const app = express()
|
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) => {
|
const onError: ErrorRequestHandler = (err, req, res, next) => {
|
||||||
console.error(err.stack)
|
console.error(err.stack)
|
||||||
res.status(500).send('Something broke!')
|
res.status(500).send('Something broke!')
|
||||||
|
|||||||
@@ -251,7 +251,7 @@ const updateUsersListInGroup = async (
|
|||||||
message: `Can't add/remove user to '${PUBLIC_GROUP_NAME}' group.`
|
message: `Can't add/remove user to '${PUBLIC_GROUP_NAME}' group.`
|
||||||
}
|
}
|
||||||
|
|
||||||
if (group.authProvider !== AuthProviderType.Internal)
|
if (group.authProvider)
|
||||||
throw {
|
throw {
|
||||||
code: 405,
|
code: 405,
|
||||||
status: 'Method Not Allowed',
|
status: 'Method Not Allowed',
|
||||||
@@ -266,7 +266,7 @@ const updateUsersListInGroup = async (
|
|||||||
message: 'User not found.'
|
message: 'User not found.'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user.authProvider !== AuthProviderType.Internal)
|
if (user.authProvider)
|
||||||
throw {
|
throw {
|
||||||
code: 405,
|
code: 405,
|
||||||
status: 'Method Not Allowed',
|
status: 'Method Not Allowed',
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ interface ExecuteFileParams {
|
|||||||
returnJson?: boolean
|
returnJson?: boolean
|
||||||
session?: Session
|
session?: Session
|
||||||
runTime: RunTimeType
|
runTime: RunTimeType
|
||||||
|
forceStringResult?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ExecuteProgramParams extends Omit<ExecuteFileParams, 'programPath'> {
|
interface ExecuteProgramParams extends Omit<ExecuteFileParams, 'programPath'> {
|
||||||
@@ -42,7 +43,8 @@ export class ExecutionController {
|
|||||||
otherArgs,
|
otherArgs,
|
||||||
returnJson,
|
returnJson,
|
||||||
session,
|
session,
|
||||||
runTime
|
runTime,
|
||||||
|
forceStringResult
|
||||||
}: ExecuteFileParams) {
|
}: ExecuteFileParams) {
|
||||||
const program = await readFile(programPath)
|
const program = await readFile(programPath)
|
||||||
|
|
||||||
@@ -53,7 +55,8 @@ export class ExecutionController {
|
|||||||
otherArgs,
|
otherArgs,
|
||||||
returnJson,
|
returnJson,
|
||||||
session,
|
session,
|
||||||
runTime
|
runTime,
|
||||||
|
forceStringResult
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +66,8 @@ export class ExecutionController {
|
|||||||
vars,
|
vars,
|
||||||
otherArgs,
|
otherArgs,
|
||||||
session: sessionByFileUpload,
|
session: sessionByFileUpload,
|
||||||
runTime
|
runTime,
|
||||||
|
forceStringResult
|
||||||
}: ExecuteProgramParams): Promise<ExecuteReturnRaw> {
|
}: ExecuteProgramParams): Promise<ExecuteReturnRaw> {
|
||||||
const sessionController = getSessionController(runTime)
|
const sessionController = getSessionController(runTime)
|
||||||
|
|
||||||
@@ -104,7 +108,7 @@ export class ExecutionController {
|
|||||||
const fileResponse: boolean = httpHeaders.hasOwnProperty('content-type')
|
const fileResponse: boolean = httpHeaders.hasOwnProperty('content-type')
|
||||||
|
|
||||||
const webout = (await fileExists(weboutPath))
|
const webout = (await fileExists(weboutPath))
|
||||||
? fileResponse
|
? fileResponse && !forceStringResult
|
||||||
? await readFileBinary(weboutPath)
|
? await readFileBinary(weboutPath)
|
||||||
: await readFile(weboutPath)
|
: await readFile(weboutPath)
|
||||||
: ''
|
: ''
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Session } from '../../types'
|
|||||||
import { promisify } from 'util'
|
import { promisify } from 'util'
|
||||||
import { execFile } from 'child_process'
|
import { execFile } from 'child_process'
|
||||||
import {
|
import {
|
||||||
|
getPackagesFolder,
|
||||||
getSessionsFolder,
|
getSessionsFolder,
|
||||||
generateUniqueFileName,
|
generateUniqueFileName,
|
||||||
sysInitCompiledPath,
|
sysInitCompiledPath,
|
||||||
@@ -104,7 +105,8 @@ export class SASSessionController extends SessionController {
|
|||||||
|
|
||||||
// the autoexec file is executed on SAS startup
|
// the autoexec file is executed on SAS startup
|
||||||
const autoExecPath = path.join(sessionFolder, 'autoexec.sas')
|
const autoExecPath = path.join(sessionFolder, 'autoexec.sas')
|
||||||
const contentForAutoExec = `/* compiled systemInit */
|
const contentForAutoExec = `filename packages "${getPackagesFolder()}";
|
||||||
|
/* compiled systemInit */
|
||||||
${compiledSystemInitContent}
|
${compiledSystemInitContent}
|
||||||
/* autoexec */
|
/* autoexec */
|
||||||
${autoExecContent}`
|
${autoExecContent}`
|
||||||
|
|||||||
@@ -110,17 +110,13 @@ export const processProgram = async (
|
|||||||
|
|
||||||
// create a stream that will write to console outputs to log file
|
// create a stream that will write to console outputs to log file
|
||||||
const writeStream = fs.createWriteStream(logPath)
|
const writeStream = fs.createWriteStream(logPath)
|
||||||
|
|
||||||
// waiting for the open event so that we can have underlying file descriptor
|
// waiting for the open event so that we can have underlying file descriptor
|
||||||
await once(writeStream, 'open')
|
await once(writeStream, 'open')
|
||||||
|
|
||||||
execFileSync(executablePath, [codePath], {
|
execFileSync(executablePath, [codePath], {
|
||||||
stdio: ['ignore', writeStream, writeStream]
|
stdio: ['ignore', writeStream, writeStream]
|
||||||
})
|
})
|
||||||
|
|
||||||
// copy the code file to log and end write stream
|
// copy the code file to log and end write stream
|
||||||
writeStream.end(program)
|
writeStream.end(program)
|
||||||
|
|
||||||
session.completed = true
|
session.completed = true
|
||||||
console.log('session completed', session)
|
console.log('session completed', session)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
|
|||||||
@@ -2,6 +2,16 @@ import { readFile } from '@sasjs/utils'
|
|||||||
import express from 'express'
|
import express from 'express'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { Request, Post, Get } from 'tsoa'
|
import { Request, Post, Get } from 'tsoa'
|
||||||
|
import dotenv from 'dotenv'
|
||||||
|
import { ExecutionController } from './internal'
|
||||||
|
import {
|
||||||
|
getPreProgramVariables,
|
||||||
|
getRunTimeAndFilePath,
|
||||||
|
makeFilesNamesMap
|
||||||
|
} from '../utils'
|
||||||
|
import { MulterFile } from '../types/Upload'
|
||||||
|
|
||||||
|
dotenv.config()
|
||||||
|
|
||||||
export interface Sas9Response {
|
export interface Sas9Response {
|
||||||
content: string
|
content: string
|
||||||
@@ -16,9 +26,17 @@ export interface MockFileRead {
|
|||||||
|
|
||||||
export class MockSas9Controller {
|
export class MockSas9Controller {
|
||||||
private loggedIn: string | undefined
|
private loggedIn: string | undefined
|
||||||
|
private mocksPath = process.env.STATIC_MOCK_LOCATION || 'mocks'
|
||||||
|
|
||||||
@Get('/SASStoredProcess')
|
@Get('/SASStoredProcess')
|
||||||
public async sasStoredProcess(): Promise<Sas9Response> {
|
public async sasStoredProcess(
|
||||||
|
@Request() req: express.Request
|
||||||
|
): Promise<Sas9Response> {
|
||||||
|
const username = req.query._username?.toString() || undefined
|
||||||
|
const password = req.query._password?.toString() || undefined
|
||||||
|
|
||||||
|
if (username && password) this.loggedIn = req.body.username
|
||||||
|
|
||||||
if (!this.loggedIn) {
|
if (!this.loggedIn) {
|
||||||
return {
|
return {
|
||||||
content: '',
|
content: '',
|
||||||
@@ -26,17 +44,87 @@ export class MockSas9Controller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let program = req.query._program?.toString() || undefined
|
||||||
|
const filePath: string[] = program
|
||||||
|
? program.replace('/', '').split('/')
|
||||||
|
: ['generic', 'sas-stored-process']
|
||||||
|
|
||||||
|
if (program) {
|
||||||
|
return await getMockResponseFromFile([
|
||||||
|
process.cwd(),
|
||||||
|
this.mocksPath,
|
||||||
|
'sas9',
|
||||||
|
...filePath
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
return await getMockResponseFromFile([
|
return await getMockResponseFromFile([
|
||||||
process.cwd(),
|
process.cwd(),
|
||||||
'mocks',
|
'mocks',
|
||||||
'generic',
|
|
||||||
'sas9',
|
'sas9',
|
||||||
'sas-stored-process'
|
...filePath
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('/SASStoredProcess/do')
|
||||||
|
public async sasStoredProcessDoGet(
|
||||||
|
@Request() req: express.Request
|
||||||
|
): Promise<Sas9Response> {
|
||||||
|
const username = req.query._username?.toString() || undefined
|
||||||
|
const password = req.query._password?.toString() || undefined
|
||||||
|
|
||||||
|
if (username && password) this.loggedIn = username
|
||||||
|
|
||||||
|
if (!this.loggedIn) {
|
||||||
|
return {
|
||||||
|
content: '',
|
||||||
|
redirect: '/SASLogon/login'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const program = req.query._program ?? req.body?._program
|
||||||
|
const filePath: string[] = ['generic', 'sas-stored-process']
|
||||||
|
|
||||||
|
if (program) {
|
||||||
|
const vars = { ...req.query, ...req.body, _requestMethod: req.method }
|
||||||
|
const otherArgs = {}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { codePath, runTime } = await getRunTimeAndFilePath(
|
||||||
|
program + '.js'
|
||||||
|
)
|
||||||
|
|
||||||
|
const result = await new ExecutionController().executeFile({
|
||||||
|
programPath: codePath,
|
||||||
|
preProgramVariables: getPreProgramVariables(req),
|
||||||
|
vars: vars,
|
||||||
|
otherArgs: otherArgs,
|
||||||
|
runTime,
|
||||||
|
forceStringResult: true
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
content: result.result as string
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log('err', err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
content: 'No webout returned.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return await getMockResponseFromFile([
|
||||||
|
process.cwd(),
|
||||||
|
'mocks',
|
||||||
|
'sas9',
|
||||||
|
...filePath
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('/SASStoredProcess/do/')
|
@Post('/SASStoredProcess/do/')
|
||||||
public async sasStoredProcessDo(
|
public async sasStoredProcessDoPost(
|
||||||
@Request() req: express.Request
|
@Request() req: express.Request
|
||||||
): Promise<Sas9Response> {
|
): Promise<Sas9Response> {
|
||||||
if (!this.loggedIn) {
|
if (!this.loggedIn) {
|
||||||
@@ -53,23 +141,38 @@ export class MockSas9Controller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let program = req.query._program?.toString() || ''
|
const program = req.query._program ?? req.body?._program
|
||||||
program = program.replace('/', '')
|
const vars = {
|
||||||
|
...req.query,
|
||||||
|
...req.body,
|
||||||
|
_requestMethod: req.method,
|
||||||
|
_driveLoc: process.driveLoc
|
||||||
|
}
|
||||||
|
const filesNamesMap = req.files?.length
|
||||||
|
? makeFilesNamesMap(req.files as MulterFile[])
|
||||||
|
: null
|
||||||
|
const otherArgs = { filesNamesMap: filesNamesMap }
|
||||||
|
const { codePath, runTime } = await getRunTimeAndFilePath(program + '.js')
|
||||||
|
try {
|
||||||
|
const result = await new ExecutionController().executeFile({
|
||||||
|
programPath: codePath,
|
||||||
|
preProgramVariables: getPreProgramVariables(req),
|
||||||
|
vars: vars,
|
||||||
|
otherArgs: otherArgs,
|
||||||
|
runTime,
|
||||||
|
session: req.sasjsSession,
|
||||||
|
forceStringResult: true
|
||||||
|
})
|
||||||
|
|
||||||
const content = await getMockResponseFromFile([
|
return {
|
||||||
process.cwd(),
|
content: result.result as string
|
||||||
'mocks',
|
}
|
||||||
...program.split('/')
|
} catch (err) {
|
||||||
])
|
console.log('err', err)
|
||||||
|
|
||||||
if (content.error) {
|
|
||||||
return content
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsedContent = parseJsonIfValid(content.content)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: parsedContent
|
content: 'No webout returned.'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,8 +188,8 @@ export class MockSas9Controller {
|
|||||||
return await getMockResponseFromFile([
|
return await getMockResponseFromFile([
|
||||||
process.cwd(),
|
process.cwd(),
|
||||||
'mocks',
|
'mocks',
|
||||||
'generic',
|
|
||||||
'sas9',
|
'sas9',
|
||||||
|
'generic',
|
||||||
'logged-in'
|
'logged-in'
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
@@ -95,21 +198,27 @@ export class MockSas9Controller {
|
|||||||
return await getMockResponseFromFile([
|
return await getMockResponseFromFile([
|
||||||
process.cwd(),
|
process.cwd(),
|
||||||
'mocks',
|
'mocks',
|
||||||
'generic',
|
|
||||||
'sas9',
|
'sas9',
|
||||||
|
'generic',
|
||||||
'login'
|
'login'
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('/SASLogon/login')
|
@Post('/SASLogon/login')
|
||||||
public async loginPost(req: express.Request): Promise<Sas9Response> {
|
public async loginPost(req: express.Request): Promise<Sas9Response> {
|
||||||
|
if (req.body.lt && req.body.lt !== 'validtoken')
|
||||||
|
return {
|
||||||
|
content: '',
|
||||||
|
redirect: '/SASLogon/login'
|
||||||
|
}
|
||||||
|
|
||||||
this.loggedIn = req.body.username
|
this.loggedIn = req.body.username
|
||||||
|
|
||||||
return await getMockResponseFromFile([
|
return await getMockResponseFromFile([
|
||||||
process.cwd(),
|
process.cwd(),
|
||||||
'mocks',
|
'mocks',
|
||||||
'generic',
|
|
||||||
'sas9',
|
'sas9',
|
||||||
|
'generic',
|
||||||
'logged-in'
|
'logged-in'
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
@@ -122,8 +231,8 @@ export class MockSas9Controller {
|
|||||||
return await getMockResponseFromFile([
|
return await getMockResponseFromFile([
|
||||||
process.cwd(),
|
process.cwd(),
|
||||||
'mocks',
|
'mocks',
|
||||||
'generic',
|
|
||||||
'sas9',
|
'sas9',
|
||||||
|
'generic',
|
||||||
'public-access-denied'
|
'public-access-denied'
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
@@ -131,8 +240,8 @@ export class MockSas9Controller {
|
|||||||
return await getMockResponseFromFile([
|
return await getMockResponseFromFile([
|
||||||
process.cwd(),
|
process.cwd(),
|
||||||
'mocks',
|
'mocks',
|
||||||
'generic',
|
|
||||||
'sas9',
|
'sas9',
|
||||||
|
'generic',
|
||||||
'logged-out'
|
'logged-out'
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
@@ -152,23 +261,6 @@ export class MockSas9Controller {
|
|||||||
private isPublicAccount = () => this.loggedIn?.toLowerCase() === 'public'
|
private isPublicAccount = () => this.loggedIn?.toLowerCase() === 'public'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* If JSON is valid it will be parsed otherwise will return text unaltered
|
|
||||||
* @param content string to be parsed
|
|
||||||
* @returns JSON or string
|
|
||||||
*/
|
|
||||||
const parseJsonIfValid = (content: string) => {
|
|
||||||
let fileContent = ''
|
|
||||||
|
|
||||||
try {
|
|
||||||
fileContent = JSON.parse(content)
|
|
||||||
} catch (err: any) {
|
|
||||||
fileContent = content
|
|
||||||
}
|
|
||||||
|
|
||||||
return fileContent
|
|
||||||
}
|
|
||||||
|
|
||||||
const getMockResponseFromFile = async (
|
const getMockResponseFromFile = async (
|
||||||
filePath: string[]
|
filePath: string[]
|
||||||
): Promise<MockFileRead> => {
|
): Promise<MockFileRead> => {
|
||||||
|
|||||||
@@ -299,14 +299,19 @@ const updateUser = async (
|
|||||||
|
|
||||||
const user = await User.findOne(findBy)
|
const user = await User.findOne(findBy)
|
||||||
|
|
||||||
if (
|
if (username && username !== user?.username && user?.authProvider) {
|
||||||
user?.authProvider !== AuthProviderType.Internal &&
|
|
||||||
(username !== user?.username || displayName !== user?.displayName)
|
|
||||||
) {
|
|
||||||
throw {
|
throw {
|
||||||
code: 405,
|
code: 405,
|
||||||
message:
|
message:
|
||||||
'Can not update username and display name of user that is created by an external auth provider.'
|
'Can not update username of user that is created by an external auth provider.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (displayName && displayName !== user?.displayName && user?.authProvider) {
|
||||||
|
throw {
|
||||||
|
code: 405,
|
||||||
|
message:
|
||||||
|
'Can not update display name of user that is created by an external auth provider.'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,8 +50,7 @@ const groupSchema = new Schema<IGroupDocument>({
|
|||||||
},
|
},
|
||||||
authProvider: {
|
authProvider: {
|
||||||
type: String,
|
type: String,
|
||||||
enum: AuthProviderType,
|
enum: AuthProviderType
|
||||||
default: 'internal'
|
|
||||||
},
|
},
|
||||||
isActive: {
|
isActive: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
|||||||
@@ -71,8 +71,7 @@ const userSchema = new Schema<IUserDocument>({
|
|||||||
},
|
},
|
||||||
authProvider: {
|
authProvider: {
|
||||||
type: String,
|
type: String,
|
||||||
enum: AuthProviderType,
|
enum: AuthProviderType
|
||||||
default: 'internal'
|
|
||||||
},
|
},
|
||||||
isAdmin: {
|
isAdmin: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
|||||||
@@ -15,5 +15,5 @@ export const setupRoutes = (app: Express) => {
|
|||||||
appStreamRouter(req, res, next)
|
appStreamRouter(req, res, next)
|
||||||
})
|
})
|
||||||
|
|
||||||
app.use('/', csrfProtection, webRouter)
|
app.use('/', webRouter)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import sas9WebRouter from './sas9-web'
|
|||||||
import sasViyaWebRouter from './sasviya-web'
|
import sasViyaWebRouter from './sasviya-web'
|
||||||
import webRouter from './web'
|
import webRouter from './web'
|
||||||
import { MOCK_SERVERTYPEType } from '../../utils'
|
import { MOCK_SERVERTYPEType } from '../../utils'
|
||||||
|
import { csrfProtection } from '../../middlewares'
|
||||||
|
|
||||||
const router = express.Router()
|
const router = express.Router()
|
||||||
|
|
||||||
@@ -18,7 +19,7 @@ switch (MOCK_SERVERTYPE) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
router.use('/', webRouter)
|
router.use('/', csrfProtection, webRouter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,12 +2,25 @@ import express from 'express'
|
|||||||
import { generateCSRFToken } from '../../middlewares'
|
import { generateCSRFToken } from '../../middlewares'
|
||||||
import { WebController } from '../../controllers'
|
import { WebController } from '../../controllers'
|
||||||
import { MockSas9Controller } from '../../controllers/mock-sas9'
|
import { MockSas9Controller } from '../../controllers/mock-sas9'
|
||||||
|
import multer from 'multer'
|
||||||
|
import path from 'path'
|
||||||
|
import dotenv from 'dotenv'
|
||||||
|
import { FileUploadController } from '../../controllers/internal'
|
||||||
|
|
||||||
|
dotenv.config()
|
||||||
|
|
||||||
const sas9WebRouter = express.Router()
|
const sas9WebRouter = express.Router()
|
||||||
const webController = new WebController()
|
const webController = new WebController()
|
||||||
// Mock controller must be singleton because it keeps the states
|
// Mock controller must be singleton because it keeps the states
|
||||||
// for example `isLoggedIn` and potentially more in future mocks
|
// for example `isLoggedIn` and potentially more in future mocks
|
||||||
const controller = new MockSas9Controller()
|
const controller = new MockSas9Controller()
|
||||||
|
const fileUploadController = new FileUploadController()
|
||||||
|
|
||||||
|
const mockPath = process.env.STATIC_MOCK_LOCATION || 'mocks'
|
||||||
|
|
||||||
|
const upload = multer({
|
||||||
|
dest: path.join(process.cwd(), mockPath, 'sas9', 'files-received')
|
||||||
|
})
|
||||||
|
|
||||||
sas9WebRouter.get('/', async (req, res) => {
|
sas9WebRouter.get('/', async (req, res) => {
|
||||||
let response
|
let response
|
||||||
@@ -27,7 +40,7 @@ sas9WebRouter.get('/', async (req, res) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
sas9WebRouter.get('/SASStoredProcess', async (req, res) => {
|
sas9WebRouter.get('/SASStoredProcess', async (req, res) => {
|
||||||
const response = await controller.sasStoredProcess()
|
const response = await controller.sasStoredProcess(req)
|
||||||
|
|
||||||
if (response.redirect) {
|
if (response.redirect) {
|
||||||
res.redirect(response.redirect)
|
res.redirect(response.redirect)
|
||||||
@@ -41,8 +54,8 @@ sas9WebRouter.get('/SASStoredProcess', async (req, res) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
sas9WebRouter.post('/SASStoredProcess/do/', async (req, res) => {
|
sas9WebRouter.get('/SASStoredProcess/do/', async (req, res) => {
|
||||||
const response = await controller.sasStoredProcessDo(req)
|
const response = await controller.sasStoredProcessDoGet(req)
|
||||||
|
|
||||||
if (response.redirect) {
|
if (response.redirect) {
|
||||||
res.redirect(response.redirect)
|
res.redirect(response.redirect)
|
||||||
@@ -56,6 +69,26 @@ sas9WebRouter.post('/SASStoredProcess/do/', async (req, res) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
sas9WebRouter.post(
|
||||||
|
'/SASStoredProcess/do/',
|
||||||
|
fileUploadController.preUploadMiddleware,
|
||||||
|
fileUploadController.getMulterUploadObject().any(),
|
||||||
|
async (req, res) => {
|
||||||
|
const response = await controller.sasStoredProcessDoPost(req)
|
||||||
|
|
||||||
|
if (response.redirect) {
|
||||||
|
res.redirect(response.redirect)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
res.send(response.content)
|
||||||
|
} catch (err: any) {
|
||||||
|
res.status(403).send(err.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
sas9WebRouter.get('/SASLogon/login', async (req, res) => {
|
sas9WebRouter.get('/SASLogon/login', async (req, res) => {
|
||||||
const response = await controller.loginGet()
|
const response = await controller.loginGet()
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,10 @@ webRouter.get('/', async (req, res) => {
|
|||||||
} catch (_) {
|
} catch (_) {
|
||||||
response = '<html><head></head><body>Web Build is not present</body></html>'
|
response = '<html><head></head><body>Web Build is not present</body></html>'
|
||||||
} finally {
|
} 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(
|
const injectedContent = response?.replace(
|
||||||
'</head>',
|
'</head>',
|
||||||
`${codeToInject}</head>`
|
`${codeToInject}</head>`
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export const sysInitCompiledPath = path.join(
|
|||||||
'systemInitCompiled.sas'
|
'systemInitCompiled.sas'
|
||||||
)
|
)
|
||||||
|
|
||||||
export const sasJSCoreMacros = path.join(apiRoot, 'sasjscore')
|
export const sasJSCoreMacros = path.join(apiRoot, 'sas', 'sasautos')
|
||||||
export const sasJSCoreMacrosInfo = path.join(sasJSCoreMacros, '.macrolist')
|
export const sasJSCoreMacrosInfo = path.join(sasJSCoreMacros, '.macrolist')
|
||||||
|
|
||||||
export const getWebBuildFolder = () => path.join(codebaseRoot, 'web', 'build')
|
export const getWebBuildFolder = () => path.join(codebaseRoot, 'web', 'build')
|
||||||
@@ -28,7 +28,10 @@ export const getAppStreamConfigPath = () =>
|
|||||||
path.join(getSasjsRootFolder(), 'appStreamConfig.json')
|
path.join(getSasjsRootFolder(), 'appStreamConfig.json')
|
||||||
|
|
||||||
export const getMacrosFolder = () =>
|
export const getMacrosFolder = () =>
|
||||||
path.join(getSasjsRootFolder(), 'sasjscore')
|
path.join(getSasjsRootFolder(), 'sas', 'sasautos')
|
||||||
|
|
||||||
|
export const getPackagesFolder = () =>
|
||||||
|
path.join(getSasjsRootFolder(), 'sas', 'sas_packages')
|
||||||
|
|
||||||
export const getUploadsFolder = () => path.join(getSasjsRootFolder(), 'uploads')
|
export const getUploadsFolder = () => path.join(getSasjsRootFolder(), 'uploads')
|
||||||
|
|
||||||
|
|||||||
@@ -18,10 +18,12 @@ export const getPreProgramVariables = (req: Request): PreProgramVars => {
|
|||||||
|
|
||||||
if (cookies.length) httpHeaders.push(`cookie: ${cookies.join('; ')}`)
|
if (cookies.length) httpHeaders.push(`cookie: ${cookies.join('; ')}`)
|
||||||
|
|
||||||
|
//In desktop mode when mocking mode is enabled, user was undefined.
|
||||||
|
//So this is workaround.
|
||||||
return {
|
return {
|
||||||
username: user!.username,
|
username: user ? user.username : 'demo',
|
||||||
userId: user!.userId,
|
userId: user ? user.userId : 0,
|
||||||
displayName: user!.displayName,
|
displayName: user ? user.displayName : 'demo',
|
||||||
serverUrl: protocol + host,
|
serverUrl: protocol + host,
|
||||||
httpHeaders
|
httpHeaders
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ export const setProcessVariables = async () => {
|
|||||||
process.rLoc = process.env.R_PATH
|
process.rLoc = process.env.R_PATH
|
||||||
} else {
|
} else {
|
||||||
const { sasLoc, nodeLoc, pythonLoc, rLoc } = await getDesktopFields()
|
const { sasLoc, nodeLoc, pythonLoc, rLoc } = await getDesktopFields()
|
||||||
|
|
||||||
process.sasLoc = sasLoc
|
process.sasLoc = sasLoc
|
||||||
process.nodeLoc = nodeLoc
|
process.nodeLoc = nodeLoc
|
||||||
process.pythonLoc = pythonLoc
|
process.pythonLoc = pythonLoc
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
import { createFile, createFolder, fileExists } from '@sasjs/utils'
|
import { createFile, createFolder, fileExists } from '@sasjs/utils'
|
||||||
import { getDesktopUserAutoExecPath, getFilesFolder } from './file'
|
import {
|
||||||
|
getDesktopUserAutoExecPath,
|
||||||
|
getFilesFolder,
|
||||||
|
getPackagesFolder
|
||||||
|
} from './file'
|
||||||
import { ModeType } from './verifyEnvVariables'
|
import { ModeType } from './verifyEnvVariables'
|
||||||
|
|
||||||
export const setupFolders = async () => {
|
export const setupFolders = async () => {
|
||||||
const drivePath = getFilesFolder()
|
const drivePath = getFilesFolder()
|
||||||
await createFolder(drivePath)
|
await createFolder(drivePath)
|
||||||
|
await createFolder(getPackagesFolder())
|
||||||
|
|
||||||
if (process.env.MODE === ModeType.Desktop) {
|
if (process.env.MODE === ModeType.Desktop) {
|
||||||
if (!(await fileExists(getDesktopUserAutoExecPath()))) {
|
if (!(await fileExists(getDesktopUserAutoExecPath()))) {
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ export enum ModeType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum AuthProviderType {
|
export enum AuthProviderType {
|
||||||
LDAP = 'ldap',
|
LDAP = 'ldap'
|
||||||
Internal = 'internal'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ProtocolType {
|
export enum ProtocolType {
|
||||||
@@ -111,7 +110,7 @@ const verifyMODE = (): string[] => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (process.env.MODE === ModeType.Server) {
|
if (process.env.MODE === ModeType.Server) {
|
||||||
const { DB_CONNECT, AUTH_MECHANISM } = process.env
|
const { DB_CONNECT, AUTH_PROVIDERS } = process.env
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'test') {
|
if (process.env.NODE_ENV !== 'test') {
|
||||||
if (!DB_CONNECT)
|
if (!DB_CONNECT)
|
||||||
@@ -119,14 +118,12 @@ const verifyMODE = (): string[] => {
|
|||||||
`- DB_CONNECT is required for PROTOCOL '${ModeType.Server}'`
|
`- DB_CONNECT is required for PROTOCOL '${ModeType.Server}'`
|
||||||
)
|
)
|
||||||
|
|
||||||
if (AUTH_MECHANISM) {
|
if (AUTH_PROVIDERS) {
|
||||||
const authMechanismTypes = Object.values(AuthProviderType)
|
const authProvidersType = Object.values(AuthProviderType)
|
||||||
if (!authMechanismTypes.includes(AUTH_MECHANISM as AuthProviderType))
|
if (!authProvidersType.includes(AUTH_PROVIDERS as AuthProviderType))
|
||||||
errors.push(
|
errors.push(
|
||||||
`- AUTH_MECHANISM '${AUTH_MECHANISM}'\n - valid options ${authMechanismTypes}`
|
`- AUTH_PROVIDERS '${AUTH_PROVIDERS}'\n - valid options ${authProvidersType}`
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
process.env.AUTH_MECHANISM = DEFAULTS.AUTH_MECHANISM
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -270,7 +267,7 @@ const verifyRUN_TIMES = (): string[] => {
|
|||||||
return errors
|
return errors
|
||||||
}
|
}
|
||||||
|
|
||||||
const verifyExecutablePaths = () => {
|
const verifyExecutablePaths = (): string[] => {
|
||||||
const errors: string[] = []
|
const errors: string[] = []
|
||||||
const { RUN_TIMES, SAS_PATH, NODE_PATH, PYTHON_PATH, R_PATH, MODE } =
|
const { RUN_TIMES, SAS_PATH, NODE_PATH, PYTHON_PATH, R_PATH, MODE } =
|
||||||
process.env
|
process.env
|
||||||
@@ -307,37 +304,37 @@ const verifyLDAPVariables = () => {
|
|||||||
LDAP_USERS_BASE_DN,
|
LDAP_USERS_BASE_DN,
|
||||||
LDAP_GROUPS_BASE_DN,
|
LDAP_GROUPS_BASE_DN,
|
||||||
MODE,
|
MODE,
|
||||||
AUTH_MECHANISM
|
AUTH_PROVIDERS
|
||||||
} = process.env
|
} = process.env
|
||||||
|
|
||||||
if (MODE === ModeType.Server && AUTH_MECHANISM === AuthProviderType.LDAP) {
|
if (MODE === ModeType.Server && AUTH_PROVIDERS === AuthProviderType.LDAP) {
|
||||||
if (!LDAP_URL) {
|
if (!LDAP_URL) {
|
||||||
errors.push(
|
errors.push(
|
||||||
`- LDAP_URL is required for AUTH_MECHANISM '${AuthProviderType.LDAP}'`
|
`- LDAP_URL is required for AUTH_PROVIDER '${AuthProviderType.LDAP}'`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!LDAP_BIND_DN) {
|
if (!LDAP_BIND_DN) {
|
||||||
errors.push(
|
errors.push(
|
||||||
`- LDAP_BIND_DN is required for AUTH_MECHANISM '${AuthProviderType.LDAP}'`
|
`- LDAP_BIND_DN is required for AUTH_PROVIDER '${AuthProviderType.LDAP}'`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!LDAP_BIND_PASSWORD) {
|
if (!LDAP_BIND_PASSWORD) {
|
||||||
errors.push(
|
errors.push(
|
||||||
`- LDAP_BIND_PASSWORD is required for AUTH_MECHANISM '${AuthProviderType.LDAP}'`
|
`- LDAP_BIND_PASSWORD is required for AUTH_PROVIDER '${AuthProviderType.LDAP}'`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!LDAP_USERS_BASE_DN) {
|
if (!LDAP_USERS_BASE_DN) {
|
||||||
errors.push(
|
errors.push(
|
||||||
`- LDAP_USERS_BASE_DN is required for AUTH_MECHANISM '${AuthProviderType.LDAP}'`
|
`- LDAP_USERS_BASE_DN is required for AUTH_PROVIDER '${AuthProviderType.LDAP}'`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!LDAP_GROUPS_BASE_DN) {
|
if (!LDAP_GROUPS_BASE_DN) {
|
||||||
errors.push(
|
errors.push(
|
||||||
`- LDAP_GROUPS_BASE_DN is required for AUTH_MECHANISM '${AuthProviderType.LDAP}'`
|
`- LDAP_GROUPS_BASE_DN is required for AUTH_PROVIDER '${AuthProviderType.LDAP}'`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -347,7 +344,6 @@ const verifyLDAPVariables = () => {
|
|||||||
|
|
||||||
const DEFAULTS = {
|
const DEFAULTS = {
|
||||||
MODE: ModeType.Desktop,
|
MODE: ModeType.Desktop,
|
||||||
AUTH_MECHANISM: AuthProviderType.Internal,
|
|
||||||
PROTOCOL: ProtocolType.HTTP,
|
PROTOCOL: ProtocolType.HTTP,
|
||||||
PORT: '5000',
|
PORT: '5000',
|
||||||
HELMET_COEP: HelmetCoepType.TRUE,
|
HELMET_COEP: HelmetCoepType.TRUE,
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ const SASjsEditor = ({
|
|||||||
setTab
|
setTab
|
||||||
}: SASjsEditorProps) => {
|
}: SASjsEditorProps) => {
|
||||||
const {
|
const {
|
||||||
ctrlPressed,
|
|
||||||
fileContent,
|
fileContent,
|
||||||
isLoading,
|
isLoading,
|
||||||
log,
|
log,
|
||||||
@@ -64,8 +63,6 @@ const SASjsEditor = ({
|
|||||||
handleDiffEditorDidMount,
|
handleDiffEditorDidMount,
|
||||||
handleEditorDidMount,
|
handleEditorDidMount,
|
||||||
handleFilePathInput,
|
handleFilePathInput,
|
||||||
handleKeyDown,
|
|
||||||
handleKeyUp,
|
|
||||||
handleRunBtnClick,
|
handleRunBtnClick,
|
||||||
handleTabChange,
|
handleTabChange,
|
||||||
saveFile,
|
saveFile,
|
||||||
@@ -99,7 +96,6 @@ const SASjsEditor = ({
|
|||||||
original={prevFileContent}
|
original={prevFileContent}
|
||||||
value={fileContent}
|
value={fileContent}
|
||||||
editorDidMount={handleDiffEditorDidMount}
|
editorDidMount={handleDiffEditorDidMount}
|
||||||
options={{ readOnly: ctrlPressed }}
|
|
||||||
onChange={(val) => setFileContent(val)}
|
onChange={(val) => setFileContent(val)}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
@@ -108,7 +104,6 @@ const SASjsEditor = ({
|
|||||||
language={getLanguageFromExtension(selectedFileExtension)}
|
language={getLanguageFromExtension(selectedFileExtension)}
|
||||||
value={fileContent}
|
value={fileContent}
|
||||||
editorDidMount={handleEditorDidMount}
|
editorDidMount={handleEditorDidMount}
|
||||||
options={{ readOnly: ctrlPressed }}
|
|
||||||
onChange={(val) => setFileContent(val)}
|
onChange={(val) => setFileContent(val)}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
@@ -176,8 +171,6 @@ const SASjsEditor = ({
|
|||||||
{fileMenu}
|
{fileMenu}
|
||||||
</Box>
|
</Box>
|
||||||
<Paper
|
<Paper
|
||||||
onKeyUp={handleKeyUp}
|
|
||||||
onKeyDown={handleKeyDown}
|
|
||||||
sx={{
|
sx={{
|
||||||
height: 'calc(100vh - 170px)',
|
height: 'calc(100vh - 170px)',
|
||||||
padding: '10px',
|
padding: '10px',
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ const useEditor = ({
|
|||||||
const [prevFileContent, setPrevFileContent] = useStateWithCallback('')
|
const [prevFileContent, setPrevFileContent] = useStateWithCallback('')
|
||||||
const [fileContent, setFileContent] = useState('')
|
const [fileContent, setFileContent] = useState('')
|
||||||
const [log, setLog] = useState('')
|
const [log, setLog] = useState('')
|
||||||
const [ctrlPressed, setCtrlPressed] = useState(false)
|
|
||||||
const [webout, setWebout] = useState('')
|
const [webout, setWebout] = useState('')
|
||||||
const [runTimes, setRunTimes] = useState<string[]>([])
|
const [runTimes, setRunTimes] = useState<string[]>([])
|
||||||
const [selectedRunTime, setSelectedRunTime] = useState('')
|
const [selectedRunTime, setSelectedRunTime] = useState('')
|
||||||
@@ -148,53 +147,47 @@ const useEditor = ({
|
|||||||
const handleRunBtnClick = () =>
|
const handleRunBtnClick = () =>
|
||||||
runCode(getSelection(editorRef.current as any) || fileContent)
|
runCode(getSelection(editorRef.current as any) || fileContent)
|
||||||
|
|
||||||
const runCode = (code: string) => {
|
const runCode = useCallback(
|
||||||
setIsLoading(true)
|
(code: string) => {
|
||||||
axios
|
setIsLoading(true)
|
||||||
.post(`/SASjsApi/code/execute`, {
|
axios
|
||||||
code: programPathInjection(
|
.post(`/SASjsApi/code/execute`, {
|
||||||
code,
|
code: programPathInjection(
|
||||||
selectedFilePath,
|
code,
|
||||||
selectedRunTime as RunTimeType
|
selectedFilePath,
|
||||||
),
|
selectedRunTime as RunTimeType
|
||||||
runTime: selectedRunTime
|
),
|
||||||
})
|
runTime: selectedRunTime
|
||||||
.then((res: any) => {
|
})
|
||||||
setWebout(res.data.split(SASJS_LOGS_SEPARATOR)[0] ?? '')
|
.then((res: any) => {
|
||||||
setLog(res.data.split(SASJS_LOGS_SEPARATOR)[1] ?? '')
|
setWebout(res.data.split(SASJS_LOGS_SEPARATOR)[0] ?? '')
|
||||||
setTab('log')
|
setLog(res.data.split(SASJS_LOGS_SEPARATOR)[1] ?? '')
|
||||||
|
setTab('log')
|
||||||
|
|
||||||
// Scroll to bottom of log
|
// Scroll to bottom of log
|
||||||
const logElement = document.getElementById('log')
|
const logElement = document.getElementById('log')
|
||||||
if (logElement) logElement.scrollTop = logElement.scrollHeight
|
if (logElement) logElement.scrollTop = logElement.scrollHeight
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
setModalTitle('Abort')
|
setModalTitle('Abort')
|
||||||
setModalPayload(
|
setModalPayload(
|
||||||
typeof err.response.data === 'object'
|
typeof err.response.data === 'object'
|
||||||
? JSON.stringify(err.response.data)
|
? JSON.stringify(err.response.data)
|
||||||
: err.response.data
|
: err.response.data
|
||||||
)
|
)
|
||||||
setOpenModal(true)
|
setOpenModal(true)
|
||||||
})
|
})
|
||||||
.finally(() => setIsLoading(false))
|
.finally(() => setIsLoading(false))
|
||||||
}
|
},
|
||||||
|
[
|
||||||
const handleKeyDown = (event: any) => {
|
selectedFilePath,
|
||||||
if (event.ctrlKey) {
|
selectedRunTime,
|
||||||
if (event.key === 'v') {
|
setModalPayload,
|
||||||
setCtrlPressed(false)
|
setModalTitle,
|
||||||
}
|
setOpenModal,
|
||||||
|
setTab
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleChangeRunTime = (event: SelectChangeEvent) => {
|
const handleChangeRunTime = (event: SelectChangeEvent) => {
|
||||||
setSelectedRunTime(event.target.value as RunTimeType)
|
setSelectedRunTime(event.target.value as RunTimeType)
|
||||||
@@ -223,7 +216,28 @@ const useEditor = ({
|
|||||||
if (prevFileContent !== fileContent) return saveFile()
|
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(() => {
|
useEffect(() => {
|
||||||
setRunTimes(Object.values(appContext.runTimes))
|
setRunTimes(Object.values(appContext.runTimes))
|
||||||
@@ -277,7 +291,6 @@ const useEditor = ({
|
|||||||
}, [selectedFileExtension, runTimes])
|
}, [selectedFileExtension, runTimes])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ctrlPressed,
|
|
||||||
fileContent,
|
fileContent,
|
||||||
isLoading,
|
isLoading,
|
||||||
log,
|
log,
|
||||||
@@ -293,8 +306,6 @@ const useEditor = ({
|
|||||||
handleDiffEditorDidMount,
|
handleDiffEditorDidMount,
|
||||||
handleEditorDidMount,
|
handleEditorDidMount,
|
||||||
handleFilePathInput,
|
handleFilePathInput,
|
||||||
handleKeyDown,
|
|
||||||
handleKeyUp,
|
|
||||||
handleRunBtnClick,
|
handleRunBtnClick,
|
||||||
handleTabChange,
|
handleTabChange,
|
||||||
saveFile,
|
saveFile,
|
||||||
|
|||||||
Reference in New Issue
Block a user