1
0
mirror of https://github.com/sasjs/server.git synced 2025-12-12 03:54:34 +00:00

Compare commits

...

17 Commits

Author SHA1 Message Date
Saad Jutt
a07f47a1ba chore(release): 0.0.68 2022-05-02 05:57:10 +05:00
Saad Jutt
2548c82dfe fix: using monaco editor locally 2022-05-02 05:57:03 +05:00
Saad Jutt
238aa1006f chore(release): 0.0.67 2022-05-02 03:41:07 +05:00
Saad Jutt
35cba97611 chore: commented helmet middleware 2022-05-02 03:40:14 +05:00
Saad Jutt
5f29dec16f chore(release): 0.0.66 2022-05-01 23:31:59 +05:00
Saad Jutt
e2a97fcb7c fix: added swagger ui init file manually 2022-05-01 23:31:48 +05:00
Allan Bowe
6adeeefcf5 chore(release): 0.0.65 2022-05-01 11:36:26 +00:00
Allan Bowe
c9d66b8576 Merge pull request #156 from sasjs/fix-swagger-api-with-csrf
fix: consume swagger api with CSRF
2022-05-01 14:35:23 +03:00
Saad Jutt
5aaac24080 fix: consume swagger api with CSRF 2022-05-01 06:07:17 +05:00
Saad Jutt
6d34206bbc chore(release): 0.0.64 2022-05-01 02:28:57 +05:00
Saad Jutt
7b39cc06d3 fix: removed fileExists for serving web 2022-05-01 02:28:50 +05:00
Saad Jutt
6e7f28a6f8 chore(release): 0.0.63 2022-05-01 02:10:24 +05:00
Saad Jutt
5689169ce4 chore: syntax fix for workflow 2022-05-01 02:10:17 +05:00
Saad Jutt
6139e7bff6 chore(release): 0.0.62 2022-05-01 02:08:03 +05:00
Saad Jutt
2c77317bb9 chore: release using node LTS 2022-05-01 02:07:55 +05:00
Saad Jutt
57b63db9cb chore(release): 0.0.61 2022-05-01 01:59:12 +05:00
Saad Jutt
60a2a4fe32 chore: bumped pkg version 2022-05-01 01:59:04 +05:00
16 changed files with 323 additions and 312 deletions

View File

@@ -8,10 +8,20 @@ on:
jobs: jobs:
release: release:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy:
matrix:
node-version: [lts/*]
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
- name: Install Dependencies WEB - name: Install Dependencies WEB
working-directory: ./web working-directory: ./web
run: npm ci run: npm ci

View File

@@ -2,6 +2,49 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
### [0.0.68](https://github.com/sasjs/server/compare/v0.0.67...v0.0.68) (2022-05-02)
### Bug Fixes
* using monaco editor locally ([2548c82](https://github.com/sasjs/server/commit/2548c82dfe1149e62a570a00546dddd9e30049b1))
### [0.0.67](https://github.com/sasjs/server/compare/v0.0.66...v0.0.67) (2022-05-01)
### [0.0.66](https://github.com/sasjs/server/compare/v0.0.64...v0.0.66) (2022-05-01)
### Bug Fixes
* added swagger ui init file manually ([e2a97fc](https://github.com/sasjs/server/commit/e2a97fcb7c54a57a7ca118677cfce93fe9430d8f))
* consume swagger api with CSRF ([5aaac24](https://github.com/sasjs/server/commit/5aaac24080362d6ce0c5d1157798a9343f40ae2a))
### [0.0.65](https://github.com/sasjs/server/compare/v0.0.64...v0.0.65) (2022-05-01)
### Bug Fixes
* consume swagger api with CSRF ([5aaac24](https://github.com/sasjs/server/commit/5aaac24080362d6ce0c5d1157798a9343f40ae2a))
### [0.0.64](https://github.com/sasjs/server/compare/v0.0.63...v0.0.64) (2022-04-30)
### Bug Fixes
* removed fileExists for serving web ([7b39cc0](https://github.com/sasjs/server/commit/7b39cc06d358f5ffecb87955040c4eb0fcc7469e))
### [0.0.63](https://github.com/sasjs/server/compare/v0.0.62...v0.0.63) (2022-04-30)
### [0.0.62](https://github.com/sasjs/server/compare/v0.0.61...v0.0.62) (2022-04-30)
### [0.0.61](https://github.com/sasjs/server/compare/v0.0.59...v0.0.61) (2022-04-30)
### Bug Fixes
* added CSRF check for granting access via session authentication ([b060ad1](https://github.com/sasjs/server/commit/b060ad1b8e0bbc61c20dc25be553bba4cc4d2716))
* setting CSRF Token for only rendering SPA ([b4b60c6](https://github.com/sasjs/server/commit/b4b60c69cf67a42f4797f7f1afe68b7a5eec2998))
### [0.0.60](https://github.com/sasjs/server/compare/v0.0.59...v0.0.60) (2022-04-30) ### [0.0.60](https://github.com/sasjs/server/compare/v0.0.59...v0.0.60) (2022-04-30)

60
api/package-lock.json generated
View File

@@ -24,7 +24,7 @@
"mongoose-sequence": "^5.3.1", "mongoose-sequence": "^5.3.1",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"multer": "^1.4.3", "multer": "^1.4.3",
"swagger-ui-express": "^4.1.6" "swagger-ui-express": "4.3.0"
}, },
"bin": { "bin": {
"api": "build/src/server.js" "api": "build/src/server.js"
@@ -49,7 +49,7 @@
"jest": "^27.0.6", "jest": "^27.0.6",
"mongodb-memory-server": "^8.0.0", "mongodb-memory-server": "^8.0.0",
"nodemon": "^2.0.7", "nodemon": "^2.0.7",
"pkg": "5.5.2", "pkg": "5.6.0",
"prettier": "^2.3.1", "prettier": "^2.3.1",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"supertest": "^6.1.3", "supertest": "^6.1.3",
@@ -8170,9 +8170,9 @@
} }
}, },
"node_modules/pkg": { "node_modules/pkg": {
"version": "5.5.2", "version": "5.6.0",
"resolved": "https://registry.npmjs.org/pkg/-/pkg-5.5.2.tgz", "resolved": "https://registry.npmjs.org/pkg/-/pkg-5.6.0.tgz",
"integrity": "sha512-pD0UB2ud01C6pVv2wpGsTYJrXI/bnvGRYvMLd44wFzA1p+A2jrlTGFPAYa7YEYzmitXhx23PqalaG1eUEnSwcA==", "integrity": "sha512-mHrAVSQWmHA41RnUmRpC7pK9lNnMfdA16CF3cqOI22a8LZxOQzF7M8YWtA2nfs+d7I0MTDXOtkDsAsFXeCpYjg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@babel/parser": "7.16.2", "@babel/parser": "7.16.2",
@@ -8184,7 +8184,7 @@
"into-stream": "^6.0.0", "into-stream": "^6.0.0",
"minimist": "^1.2.5", "minimist": "^1.2.5",
"multistream": "^4.1.0", "multistream": "^4.1.0",
"pkg-fetch": "3.2.6", "pkg-fetch": "3.3.0",
"prebuild-install": "6.1.4", "prebuild-install": "6.1.4",
"progress": "^2.0.3", "progress": "^2.0.3",
"resolve": "^1.20.0", "resolve": "^1.20.0",
@@ -8216,9 +8216,9 @@
} }
}, },
"node_modules/pkg-fetch": { "node_modules/pkg-fetch": {
"version": "3.2.6", "version": "3.3.0",
"resolved": "https://registry.npmjs.org/pkg-fetch/-/pkg-fetch-3.2.6.tgz", "resolved": "https://registry.npmjs.org/pkg-fetch/-/pkg-fetch-3.3.0.tgz",
"integrity": "sha512-Q8fx6SIT022g0cdSE4Axv/xpfHeltspo2gg1KsWRinLQZOTRRAtOOaEFghA1F3jJ8FVsh8hGrL/Pb6Ea5XHIFw==", "integrity": "sha512-xJnIZ1KP+8rNN+VLafwu4tEeV4m8IkFBDdCFqmAJz9K1aiXEtbARmdbEe6HlXWGSVuShSHjFXpfkKRkDBQ5kiA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"chalk": "^4.1.2", "chalk": "^4.1.2",
@@ -8275,9 +8275,9 @@
} }
}, },
"node_modules/pkg-fetch/node_modules/semver": { "node_modules/pkg-fetch/node_modules/semver": {
"version": "7.3.5", "version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"lru-cache": "^6.0.0" "lru-cache": "^6.0.0"
@@ -9434,11 +9434,11 @@
"integrity": "sha512-WvfPSfAAMlE/sKS6YkW47nX/hA7StmhYnAHc6wWCXNL0oclwLj6UXv0hQCkLnDgvebi0MEV40SJJpVjKUgH1IQ==" "integrity": "sha512-WvfPSfAAMlE/sKS6YkW47nX/hA7StmhYnAHc6wWCXNL0oclwLj6UXv0hQCkLnDgvebi0MEV40SJJpVjKUgH1IQ=="
}, },
"node_modules/swagger-ui-express": { "node_modules/swagger-ui-express": {
"version": "4.2.0", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.2.0.tgz", "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.3.0.tgz",
"integrity": "sha512-znrHTwh9UpvsjqgWopA4noIet7mi7UGuIYZ465YfUDKQ5Dpas0jxnkfUKCo+0aB17YCBv26AhIjiQYDV4uvJFA==", "integrity": "sha512-jN46SEEe9EoXa3ZgZoKgnSF6z0w3tnM1yqhO4Y+Q4iZVc8JOQB960EZpIAz6rNROrDApVDwcMHR0mhlnc/5Omw==",
"dependencies": { "dependencies": {
"swagger-ui-dist": ">3.52.5" "swagger-ui-dist": ">=4.1.3"
}, },
"engines": { "engines": {
"node": ">= v0.10.32" "node": ">= v0.10.32"
@@ -16649,9 +16649,9 @@
} }
}, },
"pkg": { "pkg": {
"version": "5.5.2", "version": "5.6.0",
"resolved": "https://registry.npmjs.org/pkg/-/pkg-5.5.2.tgz", "resolved": "https://registry.npmjs.org/pkg/-/pkg-5.6.0.tgz",
"integrity": "sha512-pD0UB2ud01C6pVv2wpGsTYJrXI/bnvGRYvMLd44wFzA1p+A2jrlTGFPAYa7YEYzmitXhx23PqalaG1eUEnSwcA==", "integrity": "sha512-mHrAVSQWmHA41RnUmRpC7pK9lNnMfdA16CF3cqOI22a8LZxOQzF7M8YWtA2nfs+d7I0MTDXOtkDsAsFXeCpYjg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/parser": "7.16.2", "@babel/parser": "7.16.2",
@@ -16663,7 +16663,7 @@
"into-stream": "^6.0.0", "into-stream": "^6.0.0",
"minimist": "^1.2.5", "minimist": "^1.2.5",
"multistream": "^4.1.0", "multistream": "^4.1.0",
"pkg-fetch": "3.2.6", "pkg-fetch": "3.3.0",
"prebuild-install": "6.1.4", "prebuild-install": "6.1.4",
"progress": "^2.0.3", "progress": "^2.0.3",
"resolve": "^1.20.0", "resolve": "^1.20.0",
@@ -16720,9 +16720,9 @@
} }
}, },
"pkg-fetch": { "pkg-fetch": {
"version": "3.2.6", "version": "3.3.0",
"resolved": "https://registry.npmjs.org/pkg-fetch/-/pkg-fetch-3.2.6.tgz", "resolved": "https://registry.npmjs.org/pkg-fetch/-/pkg-fetch-3.3.0.tgz",
"integrity": "sha512-Q8fx6SIT022g0cdSE4Axv/xpfHeltspo2gg1KsWRinLQZOTRRAtOOaEFghA1F3jJ8FVsh8hGrL/Pb6Ea5XHIFw==", "integrity": "sha512-xJnIZ1KP+8rNN+VLafwu4tEeV4m8IkFBDdCFqmAJz9K1aiXEtbARmdbEe6HlXWGSVuShSHjFXpfkKRkDBQ5kiA==",
"dev": true, "dev": true,
"requires": { "requires": {
"chalk": "^4.1.2", "chalk": "^4.1.2",
@@ -16764,9 +16764,9 @@
"dev": true "dev": true
}, },
"semver": { "semver": {
"version": "7.3.5", "version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"dev": true, "dev": true,
"requires": { "requires": {
"lru-cache": "^6.0.0" "lru-cache": "^6.0.0"
@@ -17601,11 +17601,11 @@
"integrity": "sha512-WvfPSfAAMlE/sKS6YkW47nX/hA7StmhYnAHc6wWCXNL0oclwLj6UXv0hQCkLnDgvebi0MEV40SJJpVjKUgH1IQ==" "integrity": "sha512-WvfPSfAAMlE/sKS6YkW47nX/hA7StmhYnAHc6wWCXNL0oclwLj6UXv0hQCkLnDgvebi0MEV40SJJpVjKUgH1IQ=="
}, },
"swagger-ui-express": { "swagger-ui-express": {
"version": "4.2.0", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.2.0.tgz", "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.3.0.tgz",
"integrity": "sha512-znrHTwh9UpvsjqgWopA4noIet7mi7UGuIYZ465YfUDKQ5Dpas0jxnkfUKCo+0aB17YCBv26AhIjiQYDV4uvJFA==", "integrity": "sha512-jN46SEEe9EoXa3ZgZoKgnSF6z0w3tnM1yqhO4Y+Q4iZVc8JOQB960EZpIAz6rNROrDApVDwcMHR0mhlnc/5Omw==",
"requires": { "requires": {
"swagger-ui-dist": ">3.52.5" "swagger-ui-dist": ">=4.1.3"
} }
}, },
"symbol-tree": { "symbol-tree": {

View File

@@ -63,7 +63,7 @@
"mongoose-sequence": "^5.3.1", "mongoose-sequence": "^5.3.1",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"multer": "^1.4.3", "multer": "^1.4.3",
"swagger-ui-express": "^4.1.6" "swagger-ui-express": "4.3.0"
}, },
"devDependencies": { "devDependencies": {
"@types/bcryptjs": "^2.4.2", "@types/bcryptjs": "^2.4.2",
@@ -85,7 +85,7 @@
"jest": "^27.0.6", "jest": "^27.0.6",
"mongodb-memory-server": "^8.0.0", "mongodb-memory-server": "^8.0.0",
"nodemon": "^2.0.7", "nodemon": "^2.0.7",
"pkg": "5.5.2", "pkg": "5.6.0",
"prettier": "^2.3.1", "prettier": "^2.3.1",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"supertest": "^6.1.3", "supertest": "^6.1.3",

View File

@@ -0,0 +1,50 @@
window.onload = function () {
// Build a system
var url = window.location.search.match(/url=([^&]+)/)
if (url && url.length > 1) {
url = decodeURIComponent(url[1])
} else {
url = window.location.origin
}
var options = {
customOptions: {
url: '/swagger.yaml',
requestInterceptor: function (request) {
request.credentials = 'include'
var cookie = document.cookie
var startIndex = cookie.indexOf('XSRF-TOKEN')
var csrf = cookie.slice(startIndex + 11).split('; ')[0]
request.headers['X-XSRF-TOKEN'] = csrf
return request
}
}
}
url = options.swaggerUrl || url
var urls = options.swaggerUrls
var customOptions = options.customOptions
var spec1 = options.swaggerDoc
var swaggerOptions = {
spec: spec1,
url: url,
urls: urls,
dom_id: '#swagger-ui',
deepLinking: true,
presets: [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset],
plugins: [SwaggerUIBundle.plugins.DownloadUrl],
layout: 'StandaloneLayout'
}
for (var attrname in customOptions) {
swaggerOptions[attrname] = customOptions[attrname]
}
var ui = SwaggerUIBundle(swaggerOptions)
if (customOptions.oauth) {
ui.initOAuth(customOptions.oauth)
}
if (customOptions.authAction) {
ui.authActions.authorize(customOptions.authAction)
}
window.ui = ui
}

View File

@@ -465,6 +465,21 @@ info:
name: '4GL Ltd' name: '4GL Ltd'
openapi: 3.0.0 openapi: 3.0.0
paths: paths:
/:
get:
operationId: Home
responses:
'200':
description: Ok
content:
application/json:
schema:
type: string
summary: 'Render index.html'
tags:
- Web
security: []
parameters: []
/login: /login:
post: post:
operationId: Login operationId: Login

View File

@@ -1,10 +1,23 @@
import path from 'path'
import express from 'express' import express from 'express'
import { Request, Route, Tags, Post, Body, Get } from 'tsoa' import { Request, Route, Tags, Post, Body, Get } from 'tsoa'
import { readFile } from '@sasjs/utils'
import User from '../model/User' import User from '../model/User'
import { getWebBuildFolderPath } from '../utils'
@Route('/') @Route('/')
@Tags('Web') @Tags('Web')
export class WebController { export class WebController {
/**
* @summary Render index.html
*
*/
@Get('/')
public async home(@Request() req: express.Request) {
return home(req)
}
/** /**
* @summary Accept a valid username/password * @summary Accept a valid username/password
* *
@@ -31,6 +44,19 @@ export class WebController {
} }
} }
const home = async (req: express.Request) => {
const indexHtmlPath = path.join(getWebBuildFolderPath(), 'index.html')
// Attention! Cannot use fileExists here,
// due to limitation after building executable
const content = await readFile(indexHtmlPath)
req.res?.cookie('XSRF-TOKEN', req.csrfToken())
req.res?.setHeader('Content-Type', 'text/html')
return content
}
const login = async ( const login = async (
req: express.Request, req: express.Request,
{ username, password }: LoginPayload { username, password }: LoginPayload

View File

@@ -36,12 +36,22 @@ router.use('/group', desktopRestrict, groupRouter)
router.use('/stp', authenticateAccessToken, stpRouter) router.use('/stp', authenticateAccessToken, stpRouter)
router.use('/code', authenticateAccessToken, codeRouter) router.use('/code', authenticateAccessToken, codeRouter)
router.use('/user', desktopRestrict, userRouter) router.use('/user', desktopRestrict, userRouter)
router.use( router.use(
'/', '/',
swaggerUi.serve, swaggerUi.serve,
swaggerUi.setup(undefined, { swaggerUi.setup(undefined, {
swaggerOptions: { swaggerOptions: {
url: '/swagger.yaml' url: '/swagger.yaml',
requestInterceptor: (request: any) => {
request.credentials = 'include'
const cookie = document.cookie
const startIndex = cookie.indexOf('XSRF-TOKEN')
const csrf = cookie.slice(startIndex + 11).split('; ')[0]
request.headers['X-XSRF-TOKEN'] = csrf
return request
}
} }
}) })
) )

View File

@@ -1,27 +1,23 @@
import path from 'path'
import express from 'express' import express from 'express'
import { fileExists } from '@sasjs/utils'
import { WebController } from '../../controllers/web' import { WebController } from '../../controllers/web'
import { getWebBuildFolderPath, loginWebValidation } from '../../utils' import { loginWebValidation } from '../../utils'
const webRouter = express.Router() const webRouter = express.Router()
const controller = new WebController()
webRouter.get('/', async (req, res) => { webRouter.get('/', async (req, res) => {
const indexHtmlPath = path.join(getWebBuildFolderPath(), 'index.html') try {
const response = await controller.home(req)
if (await fileExists(indexHtmlPath)) { return res.send(response)
res.cookie('XSRF-TOKEN', req.csrfToken()) } catch (_) {
return res.sendFile(indexHtmlPath)
}
return res.send('Web Build is not present') return res.send('Web Build is not present')
}
}) })
webRouter.post('/login', async (req, res) => { webRouter.post('/login', async (req, res) => {
const { error, value: body } = loginWebValidation(req.body) const { error, value: body } = loginWebValidation(req.body)
if (error) return res.status(400).send(error.details[0].message) if (error) return res.status(400).send(error.details[0].message)
const controller = new WebController()
try { try {
const response = await controller.login(req, body) const response = await controller.login(req, body)
res.send(response) res.send(response)
@@ -31,10 +27,9 @@ webRouter.post('/login', async (req, res) => {
}) })
webRouter.get('/logout', async (req, res) => { webRouter.get('/logout', async (req, res) => {
const controller = new WebController()
try { try {
await controller.logout(req) await controller.logout(req)
res.status(200).send() res.status(200).send('OK!')
} catch (err: any) { } catch (err: any) {
res.status(400).send(err.toString()) res.status(400).send(err.toString())
} }

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "server", "name": "server",
"version": "0.0.60", "version": "0.0.68",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "server", "name": "server",
"version": "0.0.60", "version": "0.0.68",
"devDependencies": { "devDependencies": {
"prettier": "^2.3.1", "prettier": "^2.3.1",
"standard-version": "^9.3.2" "standard-version": "^9.3.2"

View File

@@ -1,6 +1,6 @@
{ {
"name": "server", "name": "server",
"version": "0.0.60", "version": "0.0.68",
"description": "NodeJS wrapper for calling the SAS binary executable", "description": "NodeJS wrapper for calling the SAS binary executable",
"repository": "https://github.com/sasjs/server", "repository": "https://github.com/sasjs/server",
"scripts": { "scripts": {

370
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,6 @@
"dependencies": { "dependencies": {
"@emotion/react": "^11.4.1", "@emotion/react": "^11.4.1",
"@emotion/styled": "^11.3.0", "@emotion/styled": "^11.3.0",
"@monaco-editor/react": "^4.3.1",
"@mui/icons-material": "^5.0.3", "@mui/icons-material": "^5.0.3",
"@mui/lab": "^5.0.0-alpha.50", "@mui/lab": "^5.0.0-alpha.50",
"@mui/material": "^5.0.3", "@mui/material": "^5.0.3",
@@ -21,8 +20,10 @@
"@types/node": "^12.20.28", "@types/node": "^12.20.28",
"@types/react": "^17.0.27", "@types/react": "^17.0.27",
"axios": "^0.24.0", "axios": "^0.24.0",
"monaco-editor-webpack-plugin": "^7.0.1",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-monaco-editor": "^0.48.0",
"react-router-dom": "^5.3.0" "react-router-dom": "^5.3.0"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import axios from 'axios' import axios from 'axios'
import Editor from '@monaco-editor/react' import Editor from 'react-monaco-editor'
import Box from '@mui/material/Box' import Box from '@mui/material/Box'
import Paper from '@mui/material/Paper' import Paper from '@mui/material/Paper'
@@ -125,6 +125,7 @@ const Main = (props: Props) => {
{!isLoading && props?.selectedFilePath && editMode && ( {!isLoading && props?.selectedFilePath && editMode && (
<Editor <Editor
height="95%" height="95%"
language="sas"
value={fileContent} value={fileContent}
onChange={(val) => { onChange={(val) => {
if (val) setFileContent(val) if (val) setFileContent(val)

View File

@@ -4,7 +4,7 @@ import axios from 'axios'
import Box from '@mui/material/Box' import Box from '@mui/material/Box'
import { Button, Paper, Stack, Tab, Tooltip } from '@mui/material' import { Button, Paper, Stack, Tab, Tooltip } from '@mui/material'
import { makeStyles } from '@mui/styles' import { makeStyles } from '@mui/styles'
import Editor, { OnMount } from '@monaco-editor/react' import Editor, { EditorDidMount } from 'react-monaco-editor'
import { useLocation } from 'react-router-dom' import { useLocation } from 'react-router-dom'
import { TabContext, TabList, TabPanel } from '@mui/lab' import { TabContext, TabList, TabPanel } from '@mui/lab'
@@ -42,7 +42,7 @@ const Studio = () => {
} }
const editorRef = useRef(null as any) const editorRef = useRef(null as any)
const handleEditorDidMount: OnMount = (editor) => { const handleEditorDidMount: EditorDidMount = (editor) => {
editor.focus() editor.focus()
editorRef.current = editor editorRef.current = editor
} }
@@ -141,6 +141,7 @@ const Studio = () => {
<Tooltip title="CTRL+ENTER will also run SAS code"> <Tooltip title="CTRL+ENTER will also run SAS code">
<Button onClick={handleRunBtnClick} className={classes.runButton}> <Button onClick={handleRunBtnClick} className={classes.runButton}>
<img <img
alt=""
draggable="false" draggable="false"
style={{ width: '25px' }} style={{ width: '25px' }}
src="/running-sas.png" src="/running-sas.png"
@@ -161,8 +162,9 @@ const Studio = () => {
> >
<Editor <Editor
height="98%" height="98%"
language="sas"
value={fileContent} value={fileContent}
onMount={handleEditorDidMount} editorDidMount={handleEditorDidMount}
options={{ readOnly: ctrlPressed }} options={{ readOnly: ctrlPressed }}
onChange={(val) => { onChange={(val) => {
if (val) setFileContent(val) if (val) setFileContent(val)

View File

@@ -1,4 +1,5 @@
import path from 'path' import path from 'path'
import MonacoWebpackPlugin from 'monaco-editor-webpack-plugin'
import { Configuration } from 'webpack' import { Configuration } from 'webpack'
import HtmlWebpackPlugin from 'html-webpack-plugin' import HtmlWebpackPlugin from 'html-webpack-plugin'
import CopyPlugin from 'copy-webpack-plugin' import CopyPlugin from 'copy-webpack-plugin'
@@ -53,7 +54,8 @@ const config: Configuration = {
new CopyPlugin({ new CopyPlugin({
patterns: [{ from: 'public' }] patterns: [{ from: 'public' }]
}), }),
new dotenv() new dotenv(),
new MonacoWebpackPlugin()
] ]
} }