all
This commit is contained in:
299
Downloads/Voltaserve/webdav/src/client/api.ts
Normal file
299
Downloads/Voltaserve/webdav/src/client/api.ts
Normal file
@ -0,0 +1,299 @@
|
||||
import { API_URL } from '@/config'
|
||||
import { Token } from './idp'
|
||||
import { get } from 'http'
|
||||
import { createWriteStream, unlink } from 'fs'
|
||||
|
||||
export type APIErrorResponse = {
|
||||
code: string
|
||||
status: number
|
||||
message: string
|
||||
userMessage: string
|
||||
moreInfo: string
|
||||
}
|
||||
|
||||
export class APIError extends Error {
|
||||
constructor(readonly error: APIErrorResponse) {
|
||||
super(JSON.stringify(error, null, 2))
|
||||
}
|
||||
}
|
||||
|
||||
export enum FileType {
|
||||
File = 'file',
|
||||
Folder = 'folder',
|
||||
}
|
||||
|
||||
export type File = {
|
||||
id: string
|
||||
workspaceId: string
|
||||
name: string
|
||||
type: FileType
|
||||
parentId: string
|
||||
version: number
|
||||
original?: Download
|
||||
preview?: Download
|
||||
thumbnail?: Thumbnail
|
||||
snapshots: Snapshot[]
|
||||
permission: PermissionType
|
||||
isShared: boolean
|
||||
createTime: string
|
||||
updateTime?: string
|
||||
}
|
||||
|
||||
export type PermissionType = 'viewer' | 'editor' | 'owner'
|
||||
|
||||
export type Snapshot = {
|
||||
version: number
|
||||
original: Download
|
||||
preview?: Download
|
||||
ocr?: Download
|
||||
text?: Download
|
||||
thumbnail?: Thumbnail
|
||||
}
|
||||
|
||||
export type Download = {
|
||||
extension: string
|
||||
size: number
|
||||
image: ImageProps | undefined
|
||||
}
|
||||
|
||||
export type ImageProps = {
|
||||
width: number
|
||||
height: number
|
||||
}
|
||||
|
||||
export type Thumbnail = {
|
||||
base64: string
|
||||
width: number
|
||||
height: number
|
||||
}
|
||||
|
||||
export type FileCopyOptions = {
|
||||
ids: string[]
|
||||
}
|
||||
|
||||
export type FileRenameOptions = {
|
||||
name: string
|
||||
}
|
||||
|
||||
export type FileCreateFolderOptions = {
|
||||
workspaceId: string
|
||||
name: string
|
||||
parentId: string
|
||||
}
|
||||
|
||||
export type FileUploadOptions = {
|
||||
workspaceId: string
|
||||
parentId: string | null
|
||||
blob: Blob
|
||||
name: string
|
||||
}
|
||||
|
||||
export type FileMoveOptions = {
|
||||
ids: string[]
|
||||
}
|
||||
|
||||
export class FileAPI {
|
||||
constructor(private token: Token) {}
|
||||
|
||||
private async jsonResponseOrThrow<T>(response: Response): Promise<T> {
|
||||
if (response.headers.get('content-type')?.includes('application/json')) {
|
||||
const json = await response.json()
|
||||
if (response.status > 299) {
|
||||
throw new APIError(json)
|
||||
}
|
||||
return json
|
||||
} else {
|
||||
if (response.status > 299) {
|
||||
throw new Error(response.statusText)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async upload(options: FileUploadOptions): Promise<void> {
|
||||
const params = new URLSearchParams({
|
||||
workspace_id: options.workspaceId,
|
||||
})
|
||||
if (options.parentId) {
|
||||
params.append('parent_id', options.parentId)
|
||||
}
|
||||
const formData = new FormData()
|
||||
formData.set('file', options.blob, options.name)
|
||||
const response = await fetch(`${API_URL}/v1/files?${params}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${this.token.access_token}`,
|
||||
},
|
||||
body: formData,
|
||||
})
|
||||
return this.jsonResponseOrThrow(response)
|
||||
}
|
||||
|
||||
async getByPath(path: string): Promise<File> {
|
||||
const response = await fetch(
|
||||
`${API_URL}/v1/files/get?path=${encodeURIComponent(path)}`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${this.token.access_token}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}
|
||||
)
|
||||
return this.jsonResponseOrThrow(response)
|
||||
}
|
||||
|
||||
async listByPath(path: string): Promise<File[]> {
|
||||
const response = await fetch(
|
||||
`${API_URL}/v1/files/list?path=${encodeURIComponent(path)}`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${this.token.access_token}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}
|
||||
)
|
||||
return this.jsonResponseOrThrow(response)
|
||||
}
|
||||
|
||||
async createFolder(options: FileCreateFolderOptions): Promise<void> {
|
||||
const response = await fetch(`${API_URL}/v1/files/create_folder`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${this.token.access_token}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
workspaceId: options.workspaceId,
|
||||
parentId: options.parentId,
|
||||
name: options.name,
|
||||
}),
|
||||
})
|
||||
return this.jsonResponseOrThrow(response)
|
||||
}
|
||||
|
||||
async copy(id: string, options: FileCopyOptions): Promise<File[]> {
|
||||
const response = await fetch(`${API_URL}/v1/files/${id}/copy`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${this.token.access_token}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
ids: options.ids,
|
||||
}),
|
||||
})
|
||||
return this.jsonResponseOrThrow(response)
|
||||
}
|
||||
|
||||
async move(id: string, options: FileMoveOptions): Promise<void> {
|
||||
const response = await fetch(`${API_URL}/v1/files/${id}/move`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${this.token.access_token}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
ids: options.ids,
|
||||
}),
|
||||
})
|
||||
return this.jsonResponseOrThrow(response)
|
||||
}
|
||||
|
||||
async rename(id: string, options: FileRenameOptions): Promise<File> {
|
||||
const response = await fetch(`${API_URL}/v1/files/${id}/rename`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${this.token.access_token}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: options.name,
|
||||
}),
|
||||
})
|
||||
return this.jsonResponseOrThrow(response)
|
||||
}
|
||||
|
||||
async delete(id: string): Promise<void> {
|
||||
const response = await fetch(`${API_URL}/v1/files/${id}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${this.token.access_token}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
return this.jsonResponseOrThrow(response)
|
||||
}
|
||||
|
||||
downloadOriginal(file: File, outputPath: string): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const ws = createWriteStream(outputPath)
|
||||
const request = get(
|
||||
`${API_URL}/v1/files/${file.id}/original${file.original.extension}?access_token=${this.token.access_token}`,
|
||||
(response) => {
|
||||
response.pipe(ws)
|
||||
ws.on('finish', () => {
|
||||
ws.close()
|
||||
resolve()
|
||||
})
|
||||
}
|
||||
)
|
||||
request.on('error', (error) => {
|
||||
unlink(outputPath, () => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export const VIEWER_PERMISSION = 'viewer'
|
||||
export const EDITOR_PERMISSION = 'editor'
|
||||
export const OWNER_PERMISSION = 'owner'
|
||||
|
||||
export function geViewerPermission(permission: string): boolean {
|
||||
return (
|
||||
getPermissionWeight(permission) >= getPermissionWeight(VIEWER_PERMISSION)
|
||||
)
|
||||
}
|
||||
|
||||
export function geEditorPermission(permission: string) {
|
||||
return (
|
||||
getPermissionWeight(permission) >= getPermissionWeight(EDITOR_PERMISSION)
|
||||
)
|
||||
}
|
||||
|
||||
export function geOwnerPermission(permission: string) {
|
||||
return (
|
||||
getPermissionWeight(permission) >= getPermissionWeight(OWNER_PERMISSION)
|
||||
)
|
||||
}
|
||||
|
||||
export function ltViewerPermission(permission: string): boolean {
|
||||
return (
|
||||
getPermissionWeight(permission) < getPermissionWeight(VIEWER_PERMISSION)
|
||||
)
|
||||
}
|
||||
|
||||
export function ltEditorPermission(permission: string) {
|
||||
return (
|
||||
getPermissionWeight(permission) < getPermissionWeight(EDITOR_PERMISSION)
|
||||
)
|
||||
}
|
||||
|
||||
export function ltOwnerPermission(permission: string) {
|
||||
return getPermissionWeight(permission) < getPermissionWeight(OWNER_PERMISSION)
|
||||
}
|
||||
|
||||
export function getPermissionWeight(permission: string) {
|
||||
switch (permission) {
|
||||
case VIEWER_PERMISSION:
|
||||
return 1
|
||||
case EDITOR_PERMISSION:
|
||||
return 2
|
||||
case OWNER_PERMISSION:
|
||||
return 3
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
66
Downloads/Voltaserve/webdav/src/client/idp.ts
Normal file
66
Downloads/Voltaserve/webdav/src/client/idp.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import { IDP_URL } from '@/config'
|
||||
|
||||
export type IdPErrorResponse = {
|
||||
code: string
|
||||
status: number
|
||||
message: string
|
||||
userMessage: string
|
||||
moreInfo: string
|
||||
}
|
||||
|
||||
export class IdPError extends Error {
|
||||
constructor(readonly error: IdPErrorResponse) {
|
||||
super(JSON.stringify(error, null, 2))
|
||||
}
|
||||
}
|
||||
|
||||
export type Token = {
|
||||
access_token: string
|
||||
expires_in: number
|
||||
token_type: string
|
||||
refresh_token: string
|
||||
}
|
||||
|
||||
export type TokenGrantType = 'password' | 'refresh_token'
|
||||
|
||||
export type TokenExchangeOptions = {
|
||||
grant_type: TokenGrantType
|
||||
username?: string
|
||||
password?: string
|
||||
refresh_token?: string
|
||||
locale?: string
|
||||
}
|
||||
|
||||
export class TokenAPI {
|
||||
async exchange(options: TokenExchangeOptions): Promise<Token> {
|
||||
const formBody = []
|
||||
formBody.push(`grant_type=${options.grant_type}`)
|
||||
formBody.push(`username=${encodeURIComponent(options.username)}`)
|
||||
formBody.push(`password=${encodeURIComponent(options.password)}`)
|
||||
if (options.refresh_token) {
|
||||
formBody.push(`refresh_token=${options.refresh_token}`)
|
||||
}
|
||||
const response = await fetch(`${IDP_URL}/v1/token`, {
|
||||
method: 'POST',
|
||||
body: formBody.join('&'),
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
})
|
||||
return this.jsonResponseOrThrow(response)
|
||||
}
|
||||
|
||||
private async jsonResponseOrThrow<T>(response: Response): Promise<T> {
|
||||
if (response.headers.get('content-type')?.includes('application/json')) {
|
||||
const json = await response.json()
|
||||
if (response.status > 299) {
|
||||
throw new IdPError(json)
|
||||
}
|
||||
return json
|
||||
} else {
|
||||
if (response.status > 299) {
|
||||
throw new Error(response.statusText)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user