mirror of
https://github.com/sasjs/server.git
synced 2025-12-22 16:21:19 +00:00
Compare commits
2 Commits
mock-viya-
...
v0.0.11
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e11a4b66e7 | ||
|
|
d0a1457f44 |
@@ -2,6 +2,13 @@
|
|||||||
|
|
||||||
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.11](https://github.com/sasjs/server/compare/v0.0.10...v0.0.11) (2021-12-15)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* added authorization route for web ([#37](https://github.com/sasjs/server/issues/37)) ([d0a1457](https://github.com/sasjs/server/commit/d0a1457f44a3d8993b57106e5e681c4e51fe8e7d))
|
||||||
|
|
||||||
### [0.0.10](https://github.com/sasjs/server/compare/v0.0.9...v0.0.10) (2021-12-07)
|
### [0.0.10](https://github.com/sasjs/server/compare/v0.0.9...v0.0.10) (2021-12-07)
|
||||||
|
|
||||||
### [0.0.9](https://github.com/sasjs/server/compare/v0.0.3...v0.0.9) (2021-12-07)
|
### [0.0.9](https://github.com/sasjs/server/compare/v0.0.3...v0.0.9) (2021-12-07)
|
||||||
|
|||||||
@@ -59,12 +59,12 @@ It will build following images if running first time:
|
|||||||
|
|
||||||
### Using node:
|
### Using node:
|
||||||
|
|
||||||
#### Development (running api and web separately):
|
#### Development (running api and web seperately):
|
||||||
|
|
||||||
##### API
|
##### API
|
||||||
|
|
||||||
Navigate to `./api`
|
Navigate to `./api`
|
||||||
There is `.env.example` file present at `./api` directory. Remember to provide environment variables else default values will be used mentioned in `.env.example` files
|
There is `.env.example` file present at `./api` directory. Remember to provide enviornment variables else default values will be used mentioned in `.env.example` files
|
||||||
Command to install and run api server.
|
Command to install and run api server.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ import webRouter from './routes/web'
|
|||||||
import apiRouter from './routes/api'
|
import apiRouter from './routes/api'
|
||||||
import { connectDB, getWebBuildFolderPath } from './utils'
|
import { connectDB, getWebBuildFolderPath } from './utils'
|
||||||
|
|
||||||
import { FolderController } from './controllers'
|
|
||||||
|
|
||||||
dotenv.config()
|
dotenv.config()
|
||||||
|
|
||||||
const app = express()
|
const app = express()
|
||||||
@@ -32,8 +30,4 @@ app.use(express.json({ limit: '50mb' }))
|
|||||||
|
|
||||||
app.use(express.static(getWebBuildFolderPath()))
|
app.use(express.static(getWebBuildFolderPath()))
|
||||||
|
|
||||||
const folderController = new FolderController()
|
|
||||||
|
|
||||||
folderController.addRootFolder()
|
|
||||||
|
|
||||||
export default connectDB().then(() => app)
|
export default connectDB().then(() => app)
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
import { Body } from 'tsoa'
|
|
||||||
import Folder, { FolderPayload, MemberType } from '../model/Folder'
|
|
||||||
|
|
||||||
export class FolderController {
|
|
||||||
public async createFolder(@Body() body: FolderPayload) {
|
|
||||||
return createFolder(body)
|
|
||||||
}
|
|
||||||
|
|
||||||
public async addRootFolder() {
|
|
||||||
await addRootFolder()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FolderDetailsResponse {
|
|
||||||
name: string
|
|
||||||
parentFolderUri: string
|
|
||||||
children: []
|
|
||||||
}
|
|
||||||
|
|
||||||
const createFolder = async ({
|
|
||||||
name,
|
|
||||||
parentFolderUri,
|
|
||||||
type
|
|
||||||
}: FolderPayload): Promise<FolderDetailsResponse> => {
|
|
||||||
parentFolderUri = parentFolderUri.replace(/\/folders\/folders\//i, '')
|
|
||||||
const parentFolder = await Folder.findById(parentFolderUri).catch(
|
|
||||||
(_: any) => {
|
|
||||||
throw new Error(
|
|
||||||
`No folder with an URI '${parentFolderUri}' has been found.`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const folder = new Folder({
|
|
||||||
name,
|
|
||||||
parentFolderUri,
|
|
||||||
type
|
|
||||||
})
|
|
||||||
|
|
||||||
const savedFolder = await folder.save().catch((err: any) => {
|
|
||||||
// TODO: log error
|
|
||||||
throw new Error(`Error while saving folder.`)
|
|
||||||
})
|
|
||||||
|
|
||||||
await parentFolder?.addMember(savedFolder._id)
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: savedFolder.name,
|
|
||||||
parentFolderUri: savedFolder.parentFolderUri,
|
|
||||||
children: []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const addRootFolder = async () => {
|
|
||||||
let folder = await Folder.findOne({ name: '/' })
|
|
||||||
|
|
||||||
if (folder) return
|
|
||||||
|
|
||||||
folder = new Folder({
|
|
||||||
name: '/',
|
|
||||||
parentFolderUri: '',
|
|
||||||
type: MemberType.Folder
|
|
||||||
})
|
|
||||||
folder.parentFolderUri = folder._id
|
|
||||||
|
|
||||||
return await folder.save()
|
|
||||||
}
|
|
||||||
|
|
||||||
const getItem = async({ path })
|
|
||||||
@@ -5,4 +5,3 @@ export * from './group'
|
|||||||
export * from './stp'
|
export * from './stp'
|
||||||
export * from './user'
|
export * from './user'
|
||||||
export * from './session'
|
export * from './session'
|
||||||
export * from './folder'
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import jwt from 'jsonwebtoken'
|
import jwt from 'jsonwebtoken'
|
||||||
import { Request, Response } from 'express'
|
|
||||||
import { verifyTokenInDB } from '../utils'
|
import { verifyTokenInDB } from '../utils'
|
||||||
import { headerIsNotPresentMessage, headerIsNotValidMessage } from './header'
|
|
||||||
|
|
||||||
export const authenticateAccessToken = (req: any, res: any, next: any) => {
|
export const authenticateAccessToken = (req: any, res: any, next: any) => {
|
||||||
authenticateToken(
|
authenticateToken(
|
||||||
@@ -23,18 +21,6 @@ export const authenticateRefreshToken = (req: any, res: any, next: any) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const verifyAuthHeaderIsPresent = (req: Request, res: Response) => {
|
|
||||||
console.log(`🤖[verifyAuthHeaderIsPresent]🤖`)
|
|
||||||
|
|
||||||
const authHeader = req.headers.authorization
|
|
||||||
|
|
||||||
if (!authHeader) {
|
|
||||||
return res.status(401).json(headerIsNotPresentMessage('Authorization'))
|
|
||||||
} else if (!/^Bearer\s.{1}/.test(authHeader)) {
|
|
||||||
return res.status(401).json(headerIsNotValidMessage('Authorization'))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const authenticateToken = (
|
const authenticateToken = (
|
||||||
req: any,
|
req: any,
|
||||||
res: any,
|
res: any,
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
import { Request, Response } from 'express'
|
|
||||||
|
|
||||||
export const verifyAcceptHeader = (req: Request, res: Response) => {
|
|
||||||
const acceptHeader = req.headers.accept
|
|
||||||
|
|
||||||
if (!acceptHeader) {
|
|
||||||
return res.status(406).json(headerIsNotPresentMessage('Accept'))
|
|
||||||
} else if (acceptHeader !== 'application/json') {
|
|
||||||
return res.status(406).json(headerIsNotValidMessage('Accept'))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const verifyContentTypeHeader = (req: Request, res: Response) => {
|
|
||||||
const contentTypeHeader = req.headers['content-type']
|
|
||||||
|
|
||||||
if (!contentTypeHeader) {
|
|
||||||
return res.status(406).json(headerIsNotPresentMessage('Content-Type'))
|
|
||||||
} else if (contentTypeHeader !== 'application/json') {
|
|
||||||
return res.status(406).json(headerIsNotValidMessage('Content-Type'))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const headerIsNotPresentMessage = (header: string) => ({
|
|
||||||
message: `${header} header is not present.`
|
|
||||||
})
|
|
||||||
|
|
||||||
export const headerIsNotValidMessage = (header: string) => ({
|
|
||||||
message: `${header} header is not valid.`
|
|
||||||
})
|
|
||||||
@@ -2,5 +2,3 @@ export * from './authenticateToken'
|
|||||||
export * from './desktop'
|
export * from './desktop'
|
||||||
export * from './verifyAdmin'
|
export * from './verifyAdmin'
|
||||||
export * from './verifyAdminIfNeeded'
|
export * from './verifyAdminIfNeeded'
|
||||||
export * from './header'
|
|
||||||
export * from './mock'
|
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
import {
|
|
||||||
verifyAuthHeaderIsPresent,
|
|
||||||
verifyAcceptHeader,
|
|
||||||
verifyContentTypeHeader
|
|
||||||
} from './'
|
|
||||||
import { Request, Response, NextFunction } from 'express'
|
|
||||||
|
|
||||||
export const verifyHeaders = (
|
|
||||||
req: Request,
|
|
||||||
res: Response,
|
|
||||||
next: NextFunction
|
|
||||||
) => {
|
|
||||||
switch (true) {
|
|
||||||
case verifyAuthHeaderIsPresent(req, res) !== undefined:
|
|
||||||
break
|
|
||||||
case verifyAcceptHeader(req, res) !== undefined:
|
|
||||||
break
|
|
||||||
case verifyContentTypeHeader(req, res) !== undefined:
|
|
||||||
break
|
|
||||||
|
|
||||||
default:
|
|
||||||
return next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
import { Document, Schema, Model, model } from 'mongoose'
|
|
||||||
import {} from '@sasjs/utils'
|
|
||||||
|
|
||||||
export interface FolderPayload {
|
|
||||||
parentFolderUri: string
|
|
||||||
name: string
|
|
||||||
type: MemberType
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum MemberType {
|
|
||||||
Folder = 'Folder',
|
|
||||||
File = 'File'
|
|
||||||
}
|
|
||||||
|
|
||||||
const isMemberType = (value: string) => value in MemberType
|
|
||||||
|
|
||||||
export const getMemberType = (value: string) => {
|
|
||||||
value
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IFolderDocument extends FolderPayload, Document {
|
|
||||||
members: Schema.Types.ObjectId[]
|
|
||||||
type: MemberType
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IFolder extends IFolderDocument {
|
|
||||||
addMember(memberId: Schema.Types.ObjectId): Promise<IFolder>
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IFolderModel extends Model<IFolder> {}
|
|
||||||
|
|
||||||
const folderSchema = new Schema({
|
|
||||||
name: { type: String, required: true },
|
|
||||||
parentFolderUri: { type: String, required: true },
|
|
||||||
members: [{ type: Schema.Types.ObjectId, refPath: 'member' }],
|
|
||||||
type: { type: String, required: true }
|
|
||||||
})
|
|
||||||
|
|
||||||
folderSchema.post('save', (folder: IFolder, next: Function) => {
|
|
||||||
folder.populate('members', '').then(() => next())
|
|
||||||
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
|
|
||||||
// folderSchema.get('item', (folder: IFolder, next: Function) => {
|
|
||||||
|
|
||||||
// next()
|
|
||||||
// })
|
|
||||||
|
|
||||||
folderSchema.method(
|
|
||||||
'addMember',
|
|
||||||
async function (memberId: Schema.Types.ObjectId) {
|
|
||||||
const folderIdIndex = this.members.indexOf(memberId)
|
|
||||||
|
|
||||||
if (folderIdIndex === -1) this.members.push(memberId)
|
|
||||||
|
|
||||||
this.markModified('folders')
|
|
||||||
|
|
||||||
return this.save()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
folderSchema.method('getItem', async function (path: string) {
|
|
||||||
console.log(`🤖[getItem]🤖`)
|
|
||||||
console.log(`🤖[path]🤖`, path)
|
|
||||||
// const folderIdIndex = this.members.indexOf(memberId)
|
|
||||||
|
|
||||||
// if (folderIdIndex === -1) this.members.push(memberId)
|
|
||||||
|
|
||||||
// this.markModified('folders')
|
|
||||||
|
|
||||||
// return this.save()
|
|
||||||
})
|
|
||||||
|
|
||||||
export const Folder: IFolderModel = model<IFolder, IFolderModel>(
|
|
||||||
'Folder',
|
|
||||||
folderSchema
|
|
||||||
)
|
|
||||||
|
|
||||||
export default Folder
|
|
||||||
@@ -8,7 +8,11 @@ import {
|
|||||||
authenticateRefreshToken
|
authenticateRefreshToken
|
||||||
} from '../../middlewares'
|
} from '../../middlewares'
|
||||||
|
|
||||||
import { authorizeValidation, tokenValidation } from '../../utils'
|
import {
|
||||||
|
authorizeValidation,
|
||||||
|
getDesktopFields,
|
||||||
|
tokenValidation
|
||||||
|
} from '../../utils'
|
||||||
import { InfoJWT } from '../../types'
|
import { InfoJWT } from '../../types'
|
||||||
|
|
||||||
const authRouter = express.Router()
|
const authRouter = express.Router()
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
import express from 'express'
|
|
||||||
import { verifyHeaders } from '../../middlewares'
|
|
||||||
import { verifyQuery, setHeaders } from '../../utils'
|
|
||||||
import { FolderController } from '../../controllers'
|
|
||||||
|
|
||||||
const foldersRouter = express.Router()
|
|
||||||
const controller = new FolderController()
|
|
||||||
|
|
||||||
// https://sas.analytium.co.uk/folders/folders?parentFolderUri=/folders/folders/9e442a90-2c5b-40bb-982a-5fe3ff8a66b7
|
|
||||||
foldersRouter.post('/folders', verifyHeaders, async (req, res) => {
|
|
||||||
console.log(`🤖[req.query]🤖`, req.query)
|
|
||||||
console.log(`🤖[req.body]🤖`, req.body)
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await controller.createFolder({
|
|
||||||
...req.query,
|
|
||||||
...req.body
|
|
||||||
})
|
|
||||||
|
|
||||||
console.log(`🤖[response]🤖`, response)
|
|
||||||
|
|
||||||
res.send(response)
|
|
||||||
} catch (err: any) {
|
|
||||||
console.log(`🤖[error]🤖`, err)
|
|
||||||
|
|
||||||
res.status(403).send(err.toString())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
foldersRouter.get('/folders/@item', verifyHeaders, async (req, res) => {
|
|
||||||
const queryParam = 'path'
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await controller.getItem({
|
|
||||||
...req.query,
|
|
||||||
...req.body
|
|
||||||
})
|
|
||||||
|
|
||||||
console.log(`🤖[response]🤖`, response)
|
|
||||||
|
|
||||||
res.send(response)
|
|
||||||
} catch (err: any) {
|
|
||||||
console.log(`🤖[error]🤖`, err)
|
|
||||||
|
|
||||||
res.status(403).send(err.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
// if (verifyQuery(req, res, [queryParam])) {
|
|
||||||
// const folderExist = Math.random() > 0.5
|
|
||||||
|
|
||||||
// setHeaders(res, folderExist)
|
|
||||||
|
|
||||||
// if (folderExist) {
|
|
||||||
// res.status(200).json({ message: 'Folder exists!' })
|
|
||||||
// } else {
|
|
||||||
// res.status(404).json({
|
|
||||||
// errorCode: 11512,
|
|
||||||
// message: 'No folders match the search criteria.',
|
|
||||||
// details: [`${queryParam}: ${req.query[queryParam]}`],
|
|
||||||
// links: [],
|
|
||||||
// version: 2
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
})
|
|
||||||
|
|
||||||
export default foldersRouter
|
|
||||||
@@ -16,7 +16,6 @@ import groupRouter from './group'
|
|||||||
import clientRouter from './client'
|
import clientRouter from './client'
|
||||||
import authRouter from './auth'
|
import authRouter from './auth'
|
||||||
import sessionRouter from './session'
|
import sessionRouter from './session'
|
||||||
import foldersRouter from './folders'
|
|
||||||
|
|
||||||
const router = express.Router()
|
const router = express.Router()
|
||||||
|
|
||||||
@@ -33,7 +32,6 @@ router.use('/drive', authenticateAccessToken, driveRouter)
|
|||||||
router.use('/group', desktopRestrict, groupRouter)
|
router.use('/group', desktopRestrict, groupRouter)
|
||||||
router.use('/stp', authenticateAccessToken, stpRouter)
|
router.use('/stp', authenticateAccessToken, stpRouter)
|
||||||
router.use('/user', desktopRestrict, userRouter)
|
router.use('/user', desktopRestrict, userRouter)
|
||||||
router.use('/folders', foldersRouter)
|
|
||||||
router.use(
|
router.use(
|
||||||
'/',
|
'/',
|
||||||
swaggerUi.serve,
|
swaggerUi.serve,
|
||||||
|
|||||||
@@ -10,4 +10,3 @@ export * from './sleep'
|
|||||||
export * from './upload'
|
export * from './upload'
|
||||||
export * from './validation'
|
export * from './validation'
|
||||||
export * from './verifyTokenInDB'
|
export * from './verifyTokenInDB'
|
||||||
export * from './mock'
|
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
import { Response } from 'express'
|
|
||||||
import { uuidv4 } from '@sasjs/utils'
|
|
||||||
|
|
||||||
export const setHeaders = (res: Response, isSuccess: boolean) => {
|
|
||||||
res.setHeader(
|
|
||||||
'cache-control',
|
|
||||||
`no-cache, no-store, max-age=0, must-revalidate`
|
|
||||||
)
|
|
||||||
res.setHeader(
|
|
||||||
'content-security-policy',
|
|
||||||
`default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; img-src 'self' *.sas.com blob: data:; style-src 'self' 'unsafe-inline'; child-src 'self' blob: data: mailto:;`
|
|
||||||
)
|
|
||||||
res.setHeader(
|
|
||||||
'content-type',
|
|
||||||
`application/vnd.sas.${isSuccess ? 'content.folder' : 'error'}+json${
|
|
||||||
isSuccess ? '' : '; version=2;charset=UTF-8'
|
|
||||||
}`
|
|
||||||
)
|
|
||||||
res.setHeader('pragma', `no-cache`)
|
|
||||||
res.setHeader('server', `Apache/2.4`)
|
|
||||||
res.setHeader('strict-transport-security', `max-age=31536000`)
|
|
||||||
res.setHeader('Transfer-Encoding', `chunked`)
|
|
||||||
res.setHeader('vary', `User-Agent`)
|
|
||||||
res.setHeader('x-content-type-options', `nosniff`)
|
|
||||||
res.setHeader('x-frame-options', `SAMEORIGIN`)
|
|
||||||
res.setHeader('x-xss-protection', `1; mode=block`)
|
|
||||||
|
|
||||||
if (isSuccess) {
|
|
||||||
const uuid = uuidv4()
|
|
||||||
|
|
||||||
res.setHeader('content-location', `/folders/folders/${uuid}`)
|
|
||||||
res.setHeader('etag', `-2066812946`)
|
|
||||||
res.setHeader('last-modified', `${new Date(Date.now()).toUTCString()}`)
|
|
||||||
res.setHeader('location', `/folders/folders/${uuid}`)
|
|
||||||
} else {
|
|
||||||
res.setHeader('sas-service-response-flag', `true`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
export * from './query'
|
|
||||||
export * from './header'
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import { Request, Response } from 'express'
|
|
||||||
|
|
||||||
export const verifyQuery = (req: Request, res: Response, args: string[]) => {
|
|
||||||
let isValid = true
|
|
||||||
const { query } = req
|
|
||||||
|
|
||||||
args.forEach((arg: string) => {
|
|
||||||
if (!Object.keys(query).includes(arg)) {
|
|
||||||
res.status(400).json({ message: `${arg} query argument is not present.` })
|
|
||||||
isValid = false
|
|
||||||
} else if (!query[arg]) {
|
|
||||||
res.status(400).json({ message: `${arg} query argument is not valid.` })
|
|
||||||
isValid = false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return isValid
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,7 @@ services:
|
|||||||
context: .
|
context: .
|
||||||
dockerfile: DockerfileApi
|
dockerfile: DockerfileApi
|
||||||
environment:
|
environment:
|
||||||
MODE: ${MODE}
|
MODE: 'server'
|
||||||
CORS: ${CORS}
|
CORS: ${CORS}
|
||||||
PORT: ${PORT_API}
|
PORT: ${PORT_API}
|
||||||
PORT_WEB: ${PORT_WEB}
|
PORT_WEB: ${PORT_WEB}
|
||||||
|
|||||||
16
package-lock.json
generated
16
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "server",
|
"name": "server",
|
||||||
"version": "0.0.10",
|
"version": "0.0.11",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "server",
|
"name": "server",
|
||||||
"version": "0.0.10",
|
"version": "0.0.11",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"prettier": "^2.3.1",
|
"prettier": "^2.3.1",
|
||||||
"standard-version": "^9.3.2"
|
"standard-version": "^9.3.2"
|
||||||
@@ -1350,9 +1350,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/minimist": {
|
"node_modules/minimist": {
|
||||||
"version": "1.2.6",
|
"version": "1.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
|
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/minimist-options": {
|
"node_modules/minimist-options": {
|
||||||
@@ -3158,9 +3158,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"minimist": {
|
"minimist": {
|
||||||
"version": "1.2.6",
|
"version": "1.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
|
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"minimist-options": {
|
"minimist-options": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "server",
|
"name": "server",
|
||||||
"version": "0.0.10",
|
"version": "0.0.11",
|
||||||
"description": "NodeJS wrapper for calling the SAS binary executable",
|
"description": "NodeJS wrapper for calling the SAS binary executable",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"server": "npm run server:prepare && npm run server:start",
|
"server": "npm run server:prepare && npm run server:start",
|
||||||
|
|||||||
@@ -19,7 +19,14 @@ function App() {
|
|||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
<HashRouter>
|
<HashRouter>
|
||||||
<Header />
|
<Header />
|
||||||
|
<Switch>
|
||||||
|
<Route exact path="/SASjsLogon">
|
||||||
|
<Login getCodeOnly />
|
||||||
|
</Route>
|
||||||
|
<Route path="/">
|
||||||
<Login setTokens={setTokens} />
|
<Login setTokens={setTokens} />
|
||||||
|
</Route>
|
||||||
|
</Switch>
|
||||||
</HashRouter>
|
</HashRouter>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
)
|
)
|
||||||
@@ -39,6 +46,9 @@ function App() {
|
|||||||
<Route exact path="/SASjsStudio">
|
<Route exact path="/SASjsStudio">
|
||||||
<Studio />
|
<Studio />
|
||||||
</Route>
|
</Route>
|
||||||
|
<Route exact path="/SASjsLogon">
|
||||||
|
<Login getCodeOnly />
|
||||||
|
</Route>
|
||||||
</Switch>
|
</Switch>
|
||||||
</HashRouter>
|
</HashRouter>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
import { useLocation } from 'react-router-dom'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
import { CssBaseline, Box, TextField, Button } from '@mui/material'
|
import { CssBaseline, Box, TextField, Button, Typography } from '@mui/material'
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
Accept: 'application/json',
|
Accept: 'application/json',
|
||||||
@@ -18,7 +19,12 @@ const getAuthCode = async (credentials: any) => {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers,
|
headers,
|
||||||
body: JSON.stringify(credentials)
|
body: JSON.stringify(credentials)
|
||||||
}).then((data) => data.json())
|
}).then(async (response) => {
|
||||||
|
const resText = await response.text()
|
||||||
|
if (response.status !== 200) throw resText
|
||||||
|
|
||||||
|
return JSON.parse(resText)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
const getTokens = async (payload: any) => {
|
const getTokens = async (payload: any) => {
|
||||||
return fetch(`${baseUrl}/SASjsApi/auth/token`, {
|
return fetch(`${baseUrl}/SASjsApi/auth/token`, {
|
||||||
@@ -28,20 +34,40 @@ const getTokens = async (payload: any) => {
|
|||||||
}).then((data) => data.json())
|
}).then((data) => data.json())
|
||||||
}
|
}
|
||||||
|
|
||||||
const Login = ({ setTokens }: any) => {
|
const Login = ({ setTokens, getCodeOnly }: any) => {
|
||||||
const [username, setUserName] = useState()
|
const location = useLocation()
|
||||||
const [password, setPassword] = useState()
|
const [username, setUserName] = useState('')
|
||||||
|
const [password, setPassword] = useState('')
|
||||||
|
const [errorMessage, setErrorMessage] = useState('')
|
||||||
|
let error: boolean
|
||||||
|
const [displayCode, setDisplayCode] = useState(null)
|
||||||
|
|
||||||
const handleSubmit = async (e: any) => {
|
const handleSubmit = async (e: any) => {
|
||||||
|
error = false
|
||||||
|
setErrorMessage('')
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
const { REACT_APP_CLIENT_ID: clientId } = process.env
|
let { REACT_APP_CLIENT_ID: clientId } = process.env
|
||||||
|
|
||||||
|
if (getCodeOnly) {
|
||||||
|
const params = new URLSearchParams(location.search)
|
||||||
|
const responseType = params.get('response_type')
|
||||||
|
if (responseType === 'code')
|
||||||
|
clientId = params.get('client_id') ?? undefined
|
||||||
|
}
|
||||||
|
|
||||||
const { code } = await getAuthCode({
|
const { code } = await getAuthCode({
|
||||||
clientId,
|
clientId,
|
||||||
username,
|
username,
|
||||||
password
|
password
|
||||||
|
}).catch((err: string) => {
|
||||||
|
error = true
|
||||||
|
setErrorMessage(err)
|
||||||
|
return {}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (!error) {
|
||||||
|
if (getCodeOnly) return setDisplayCode(code)
|
||||||
|
|
||||||
const { accessToken, refreshToken } = await getTokens({
|
const { accessToken, refreshToken } = await getTokens({
|
||||||
clientId,
|
clientId,
|
||||||
code
|
code
|
||||||
@@ -49,6 +75,22 @@ const Login = ({ setTokens }: any) => {
|
|||||||
|
|
||||||
setTokens(accessToken, refreshToken)
|
setTokens(accessToken, refreshToken)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (displayCode) {
|
||||||
|
return (
|
||||||
|
<Box className="main">
|
||||||
|
<CssBaseline />
|
||||||
|
<br />
|
||||||
|
<h2>Authorization Code</h2>
|
||||||
|
<Typography m={2} p={3} style={{ overflowWrap: 'anywhere' }}>
|
||||||
|
{displayCode}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@@ -61,7 +103,12 @@ const Login = ({ setTokens }: any) => {
|
|||||||
>
|
>
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
<br />
|
<br />
|
||||||
<h2>Welcome to SASjs Server!</h2>
|
<h2 style={{ width: 'auto' }}>Welcome to SASjs Server!</h2>
|
||||||
|
{getCodeOnly && (
|
||||||
|
<p style={{ width: 'auto' }}>
|
||||||
|
Provide credentials to get authorization code.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<TextField
|
<TextField
|
||||||
@@ -80,6 +127,7 @@ const Login = ({ setTokens }: any) => {
|
|||||||
onChange={(e: any) => setPassword(e.target.value)}
|
onChange={(e: any) => setPassword(e.target.value)}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
{errorMessage && <span>{errorMessage}</span>}
|
||||||
<Button type="submit" variant="outlined">
|
<Button type="submit" variant="outlined">
|
||||||
Submit
|
Submit
|
||||||
</Button>
|
</Button>
|
||||||
@@ -88,7 +136,8 @@ const Login = ({ setTokens }: any) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Login.propTypes = {
|
Login.propTypes = {
|
||||||
setTokens: PropTypes.func.isRequired
|
setTokens: PropTypes.func,
|
||||||
|
getCodeOnly: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Login
|
export default Login
|
||||||
|
|||||||
@@ -3,15 +3,8 @@ import { useEffect, useState } from 'react'
|
|||||||
|
|
||||||
export default function useTokens() {
|
export default function useTokens() {
|
||||||
const getTokens = () => {
|
const getTokens = () => {
|
||||||
const accessTokenString = localStorage.getItem('accessToken')
|
const accessToken = localStorage.getItem('accessToken')
|
||||||
const accessToken: string = accessTokenString
|
const refreshToken = localStorage.getItem('refreshToken')
|
||||||
? JSON.parse(accessTokenString)
|
|
||||||
: undefined
|
|
||||||
|
|
||||||
const refreshTokenString = localStorage.getItem('refreshToken')
|
|
||||||
const refreshToken: string = refreshTokenString
|
|
||||||
? JSON.parse(refreshTokenString)
|
|
||||||
: undefined
|
|
||||||
|
|
||||||
if (accessToken && refreshToken) {
|
if (accessToken && refreshToken) {
|
||||||
setAxiosRequestHeader(accessToken)
|
setAxiosRequestHeader(accessToken)
|
||||||
@@ -31,8 +24,8 @@ export default function useTokens() {
|
|||||||
setAxiosResponse(setTokens)
|
setAxiosResponse(setTokens)
|
||||||
|
|
||||||
const saveTokens = (accessToken: string, refreshToken: string) => {
|
const saveTokens = (accessToken: string, refreshToken: string) => {
|
||||||
localStorage.setItem('accessToken', JSON.stringify(accessToken))
|
localStorage.setItem('accessToken', accessToken)
|
||||||
localStorage.setItem('refreshToken', JSON.stringify(refreshToken))
|
localStorage.setItem('refreshToken', refreshToken)
|
||||||
setAxiosRequestHeader(accessToken)
|
setAxiosRequestHeader(accessToken)
|
||||||
setTokens({ accessToken, refreshToken })
|
setTokens({ accessToken, refreshToken })
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user