mirror of
https://github.com/sasjs/server.git
synced 2026-01-14 17:30:05 +00:00
feat(drive): new route delete file api
This commit is contained in:
@@ -585,6 +585,7 @@ paths:
|
|||||||
responses:
|
responses:
|
||||||
'204':
|
'204':
|
||||||
description: 'No content'
|
description: 'No content'
|
||||||
|
description: "It's optional to either provide `_filePath` in url as query parameter\nOr provide `filePath` in body as form field.\nBut it's required to provide else API will respond with Bad Request."
|
||||||
summary: 'Get file from SASjs Drive'
|
summary: 'Get file from SASjs Drive'
|
||||||
tags:
|
tags:
|
||||||
- Drive
|
- Drive
|
||||||
@@ -594,11 +595,57 @@ paths:
|
|||||||
parameters:
|
parameters:
|
||||||
-
|
-
|
||||||
in: query
|
in: query
|
||||||
name: filePath
|
name: _filePath
|
||||||
required: true
|
required: false
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
example: /Public/somefolder/some.file
|
example: /Public/somefolder/some.file
|
||||||
|
requestBody:
|
||||||
|
required: false
|
||||||
|
content:
|
||||||
|
multipart/form-data:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
filePath:
|
||||||
|
type: string
|
||||||
|
delete:
|
||||||
|
operationId: DeleteFile
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Ok
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
properties:
|
||||||
|
status: {type: string}
|
||||||
|
required:
|
||||||
|
- status
|
||||||
|
type: object
|
||||||
|
description: "It's optional to either provide `_filePath` in url as query parameter\nOr provide `filePath` in body as form field.\nBut it's required to provide else API will respond with Bad Request."
|
||||||
|
summary: 'Delete file from SASjs Drive'
|
||||||
|
tags:
|
||||||
|
- Drive
|
||||||
|
security:
|
||||||
|
-
|
||||||
|
bearerAuth: []
|
||||||
|
parameters:
|
||||||
|
-
|
||||||
|
in: query
|
||||||
|
name: _filePath
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
example: /Public/somefolder/some.file
|
||||||
|
requestBody:
|
||||||
|
required: false
|
||||||
|
content:
|
||||||
|
multipart/form-data:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
filePath:
|
||||||
|
type: string
|
||||||
post:
|
post:
|
||||||
operationId: SaveFile
|
operationId: SaveFile
|
||||||
responses:
|
responses:
|
||||||
|
|||||||
@@ -13,9 +13,15 @@ import {
|
|||||||
Get,
|
Get,
|
||||||
Patch,
|
Patch,
|
||||||
UploadedFile,
|
UploadedFile,
|
||||||
FormField
|
FormField,
|
||||||
|
Delete
|
||||||
} from 'tsoa'
|
} from 'tsoa'
|
||||||
import { fileExists, createFile, moveFile, createFolder } from '@sasjs/utils'
|
import {
|
||||||
|
fileExists,
|
||||||
|
moveFile,
|
||||||
|
createFolder,
|
||||||
|
deleteFile as deleteFileOnSystem
|
||||||
|
} from '@sasjs/utils'
|
||||||
import { createFileTree, ExecutionController, getTreeExample } from './internal'
|
import { createFileTree, ExecutionController, getTreeExample } from './internal'
|
||||||
|
|
||||||
import { FileTree, isFileTree, TreeNode } from '../types'
|
import { FileTree, isFileTree, TreeNode } from '../types'
|
||||||
@@ -25,18 +31,6 @@ interface DeployPayload {
|
|||||||
appLoc?: string
|
appLoc?: string
|
||||||
fileTree: FileTree
|
fileTree: FileTree
|
||||||
}
|
}
|
||||||
interface FilePayload {
|
|
||||||
/**
|
|
||||||
* Path of the file
|
|
||||||
* @example "/Public/somefolder/some.file"
|
|
||||||
*/
|
|
||||||
filePath: string
|
|
||||||
/**
|
|
||||||
* Contents of the file
|
|
||||||
* @example "Contents of the File"
|
|
||||||
*/
|
|
||||||
fileContent: string
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DeployResponse {
|
interface DeployResponse {
|
||||||
status: string
|
status: string
|
||||||
@@ -93,16 +87,39 @@ export class DriveController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* It's optional to either provide `_filePath` in url as query parameter
|
||||||
|
* Or provide `filePath` in body as form field.
|
||||||
|
* But it's required to provide else API will respond with Bad Request.
|
||||||
|
*
|
||||||
* @summary Get file from SASjs Drive
|
* @summary Get file from SASjs Drive
|
||||||
* @query filePath Location of SAS program
|
* @query _filePath Location of SAS program
|
||||||
* @example filePath "/Public/somefolder/some.file"
|
* @example _filePath "/Public/somefolder/some.file"
|
||||||
*/
|
*/
|
||||||
@Get('/file')
|
@Get('/file')
|
||||||
public async getFile(
|
public async getFile(
|
||||||
@Request() request: express.Request,
|
@Request() request: express.Request,
|
||||||
@Query() filePath: string
|
|
||||||
|
@Query() _filePath?: string,
|
||||||
|
@FormField() filePath?: string
|
||||||
) {
|
) {
|
||||||
return getFile(request, filePath)
|
return getFile(request, (_filePath ?? filePath)!)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* It's optional to either provide `_filePath` in url as query parameter
|
||||||
|
* Or provide `filePath` in body as form field.
|
||||||
|
* But it's required to provide else API will respond with Bad Request.
|
||||||
|
*
|
||||||
|
* @summary Delete file from SASjs Drive
|
||||||
|
* @query _filePath Location of SAS program
|
||||||
|
* @example _filePath "/Public/somefolder/some.file"
|
||||||
|
*/
|
||||||
|
@Delete('/file')
|
||||||
|
public async deleteFile(
|
||||||
|
@Query() _filePath?: string,
|
||||||
|
@FormField() filePath?: string
|
||||||
|
) {
|
||||||
|
return deleteFile((_filePath ?? filePath)!)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -210,6 +227,26 @@ const getFile = async (req: express.Request, filePath: string) => {
|
|||||||
req.res?.sendFile(path.resolve(filePathFull))
|
req.res?.sendFile(path.resolve(filePathFull))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const deleteFile = async (filePath: string) => {
|
||||||
|
const driveFilesPath = getTmpFilesFolderPath()
|
||||||
|
|
||||||
|
const filePathFull = path
|
||||||
|
.join(getTmpFilesFolderPath(), filePath)
|
||||||
|
.replace(new RegExp('/', 'g'), path.sep)
|
||||||
|
|
||||||
|
if (!filePathFull.includes(driveFilesPath)) {
|
||||||
|
throw new Error('Cannot delete file outside drive.')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(await fileExists(filePathFull))) {
|
||||||
|
throw new Error('File does not exist.')
|
||||||
|
}
|
||||||
|
|
||||||
|
await deleteFileOnSystem(filePathFull)
|
||||||
|
|
||||||
|
return { status: 'success' }
|
||||||
|
}
|
||||||
|
|
||||||
const saveFile = async (
|
const saveFile = async (
|
||||||
filePath: string,
|
filePath: string,
|
||||||
multerFile: Express.Multer.File
|
multerFile: Express.Multer.File
|
||||||
|
|||||||
@@ -3,12 +3,7 @@ import { deleteFile } from '@sasjs/utils'
|
|||||||
|
|
||||||
import { multerSingle } from '../../middlewares/multer'
|
import { multerSingle } from '../../middlewares/multer'
|
||||||
import { DriveController } from '../../controllers/'
|
import { DriveController } from '../../controllers/'
|
||||||
import {
|
import { fileBodyValidation, fileParamValidation } from '../../utils'
|
||||||
getFileDriveValidation,
|
|
||||||
updateFileDriveValidation,
|
|
||||||
uploadFileBodyValidation,
|
|
||||||
uploadFileParamValidation
|
|
||||||
} from '../../utils'
|
|
||||||
|
|
||||||
const controller = new DriveController()
|
const controller = new DriveController()
|
||||||
|
|
||||||
@@ -28,11 +23,27 @@ driveRouter.post('/deploy', async (req, res) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
driveRouter.get('/file', async (req, res) => {
|
driveRouter.get('/file', async (req, res) => {
|
||||||
const { error, value: query } = getFileDriveValidation(req.query)
|
const { error: errQ, value: query } = fileParamValidation(req.query)
|
||||||
if (error) return res.status(400).send(error.details[0].message)
|
const { error: errB, value: body } = fileBodyValidation(req.body)
|
||||||
|
|
||||||
|
if (errQ && errB) return res.status(400).send(errB.details[0].message)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await controller.getFile(req, query.filePath)
|
await controller.getFile(req, query._filePath, body.filePath)
|
||||||
|
} catch (err: any) {
|
||||||
|
res.status(403).send(err.toString())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
driveRouter.delete('/file', async (req, res) => {
|
||||||
|
const { error: errQ, value: query } = fileParamValidation(req.query)
|
||||||
|
const { error: errB, value: body } = fileBodyValidation(req.body)
|
||||||
|
|
||||||
|
if (errQ && errB) return res.status(400).send(errB.details[0].message)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await controller.deleteFile(query._filePath, body.filePath)
|
||||||
|
res.send(response)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
res.status(403).send(err.toString())
|
res.status(403).send(err.toString())
|
||||||
}
|
}
|
||||||
@@ -42,8 +53,8 @@ driveRouter.post(
|
|||||||
'/file',
|
'/file',
|
||||||
(...arg) => multerSingle('file', arg),
|
(...arg) => multerSingle('file', arg),
|
||||||
async (req, res) => {
|
async (req, res) => {
|
||||||
const { error: errQ, value: query } = uploadFileParamValidation(req.query)
|
const { error: errQ, value: query } = fileParamValidation(req.query)
|
||||||
const { error: errB, value: body } = uploadFileBodyValidation(req.body)
|
const { error: errB, value: body } = fileBodyValidation(req.body)
|
||||||
|
|
||||||
if (errQ && errB) {
|
if (errQ && errB) {
|
||||||
if (req.file) await deleteFile(req.file.path)
|
if (req.file) await deleteFile(req.file.path)
|
||||||
@@ -70,8 +81,8 @@ driveRouter.patch(
|
|||||||
'/file',
|
'/file',
|
||||||
(...arg) => multerSingle('file', arg),
|
(...arg) => multerSingle('file', arg),
|
||||||
async (req, res) => {
|
async (req, res) => {
|
||||||
const { error: errQ, value: query } = uploadFileParamValidation(req.query)
|
const { error: errQ, value: query } = fileParamValidation(req.query)
|
||||||
const { error: errB, value: body } = uploadFileBodyValidation(req.body)
|
const { error: errB, value: body } = fileBodyValidation(req.body)
|
||||||
|
|
||||||
if (errQ && errB) {
|
if (errQ && errB) {
|
||||||
if (req.file) await deleteFile(req.file.path)
|
if (req.file) await deleteFile(req.file.path)
|
||||||
|
|||||||
@@ -66,25 +66,14 @@ export const registerClientValidation = (data: any): Joi.ValidationResult =>
|
|||||||
clientSecret: Joi.string().required()
|
clientSecret: Joi.string().required()
|
||||||
}).validate(data)
|
}).validate(data)
|
||||||
|
|
||||||
export const getFileDriveValidation = (data: any): Joi.ValidationResult =>
|
export const fileBodyValidation = (data: any): Joi.ValidationResult =>
|
||||||
Joi.object({
|
|
||||||
filePath: Joi.string().required()
|
|
||||||
}).validate(data)
|
|
||||||
|
|
||||||
export const updateFileDriveValidation = (data: any): Joi.ValidationResult =>
|
|
||||||
Joi.object({
|
|
||||||
filePath: Joi.string().required(),
|
|
||||||
fileContent: Joi.string().required()
|
|
||||||
}).validate(data)
|
|
||||||
|
|
||||||
export const uploadFileBodyValidation = (data: any): Joi.ValidationResult =>
|
|
||||||
Joi.object({
|
Joi.object({
|
||||||
filePath: Joi.string().pattern(/.sas$/).required().messages({
|
filePath: Joi.string().pattern(/.sas$/).required().messages({
|
||||||
'string.pattern.base': `Valid extensions for filePath: .sas`
|
'string.pattern.base': `Valid extensions for filePath: .sas`
|
||||||
})
|
})
|
||||||
}).validate(data)
|
}).validate(data)
|
||||||
|
|
||||||
export const uploadFileParamValidation = (data: any): Joi.ValidationResult =>
|
export const fileParamValidation = (data: any): Joi.ValidationResult =>
|
||||||
Joi.object({
|
Joi.object({
|
||||||
_filePath: Joi.string().pattern(/.sas$/).required().messages({
|
_filePath: Joi.string().pattern(/.sas$/).required().messages({
|
||||||
'string.pattern.base': `Valid extensions for filePath: .sas`
|
'string.pattern.base': `Valid extensions for filePath: .sas`
|
||||||
|
|||||||
Reference in New Issue
Block a user