@shencom/api
Version:
shencom api group
423 lines (356 loc) • 12.9 kB
text/typescript
import type { Dictionary, Unfurl } from '@shencom/typing';
import { getInitializedApiConfig } from '../config';
import type { RequestConfig } from '../type';
function isFormData(val: any): val is FormData {
return Object.prototype.toString.call(val) === '[object FormData]';
}
interface ManagementSingOptions {
/** `0:私有`,`1:开放`,`默认: 1` */
open?: 0 | 1;
/** 上传到的位置 `0: aliyunoss`,`1: minio`,`默认: 0` */
target?: 0 | 1;
}
interface ManagementUploadOptions extends ManagementSingOptions {
/** 音频文件时长,单位:秒 */
fileLength?: number;
// 临时链接的过期时间,单位:秒。当open为false时传才有效。注意:慎用阿里云的过期时间,ServerTime 可能比本地块,待调整。
expiry?: number;
}
type Ids = string | string[];
type FileInfo = File | { path: string; size: number };
/** 文件详情 */
export const ApiFileShow = (body: { ids: Ids }, headers?: Record<string, any>) => {
const { url, http } = getInitializedApiConfig();
const api = `${url}/service-file/file/show`;
return http.post<SC.File.Info[]>(api, body, { headers });
};
/**
* 获取oss直传所需Policy和签名
* @param {string} open `0:私有`,`1:开放`,`默认: 1`
* @param {string} target 上传到的位置 上传到的位置 `0: aliyunoss`,`1: minio`,`默认: 0`
*/
export const ApiFileOssSign = (
body: Unfurl<ManagementSingOptions> = {},
headers?: Record<string, any>,
) => {
const { url, http } = getInitializedApiConfig();
const api = `${url}/service-file/file/management/sign`;
return http.get<SC.OSS.Sign>(api, { target: 0, open: 1, ...body }, { headers });
};
/** 和 sign 的区别是上传指定的文件 */
type ReqFileOssSignV2 = Unfurl<
ManagementSingOptions & {
/** 文件名, 后端主要用于校验文件类型等 */
name: string;
}
>;
/**
* 获取oss直传所需Policy和签名 V2
* @param {string} open `0:私有`,`1:开放`,`默认: 1`
* @param {string} target 上传到的位置 上传到的位置 `0: aliyunoss`,`1: minio`,`默认: 0`
* @param {string} name 文件名,需要带上文件后缀,eg:`test.png`
*/
export const ApiFileOssSignV2 = (body: ReqFileOssSignV2, headers?: Record<string, any>) => {
const { url, http } = getInitializedApiConfig();
const api = `${url}/service-file/file/management/signV2`;
return http.post<SC.OSS.SignV2>(api, { target: 0, open: 1, ...body }, { headers });
};
type ReqFileOssBatchSignV2 = Unfurl<ManagementSingOptions & { names: string[] }>;
/**
* 批量获取oss直传所需Policy和签名 V2
* @param {string} open `0:私有`,`1:开放`,`默认: 1`
* @param {string} target 上传到的位置 上传到的位置 `0: aliyunoss`,`1: minio`,`默认: 0`
* @param {string[]} names 文件名,需要带上文件后缀,eg:`["test.png", "211.jpeg"]`
*/
export const ApiFileOssBatchSignV2 = (
body: ReqFileOssBatchSignV2,
headers?: Record<string, any>,
) => {
const { url, http } = getInitializedApiConfig();
const api = `${url}/service-file/file/management/batchSignV2`;
return http.post<SC.OSS.SignV2[]>(api, { target: 0, open: 1, ...body }, { headers });
};
function HandleFileETag(headers: Dictionary<any> = {}) {
let etag = headers.etag || headers.ETag || headers.Etag;
if (!etag) return;
if (Array.isArray(etag)) etag = etag[0];
try {
etag = JSON.parse(etag) as string;
} catch (error) {
//
}
return etag;
}
interface ReqFileOssUpload extends SC.OSS.Sign {
/** 文件随机名称 */
fileName: string;
/** 文件 */
file: FileInfo;
}
type ResFileOssUpload = Omit<SC.File.Info, 'createdAt' | 'remark' | 'id' | 'memo'>;
/**
* oss签名上传文件
* @param {object} file 文件对象
* @param {object} fileName 文件随机名
* @param {string} accessid OSS 的 `AccessKeyId`
* @param {string} host 上传存储空间的访问域名
* @param {string} signature 签名信息
* @param {string} policy 上传所需policy信息
* @param {string} dir 用户上传文件时指定的前缀
* @link https://help.aliyun.com/document_detail/31988.htm?spm=a2c4g.31925.0.0.67fe5918MHHN8l#reference-smp-nsw-wdb
*/
export const ApiFileOssUpload = async (body: Unfurl<ReqFileOssUpload>, custom?: RequestConfig) => {
const { http } = getInitializedApiConfig();
const { file, fileName, accessid, host, signature, policy, dir } = body;
const key = `${dir}${fileName}`;
let name;
if ('name' in file) name = file.name; // 文件真实名称
const param: Dictionary = {
key,
file,
policy,
signature,
OSSAccessKeyId: accessid,
success_action_status: '200',
// 下载时的文件名
'Content-Disposition': name ? `attachment;filename="${encodeURIComponent(name)}"` : undefined,
};
let formData: FormData | Dictionary;
if (typeof FormData === 'undefined') {
formData = param;
} else {
formData = new FormData();
const formDataKey = Object.keys(param) as (keyof typeof param)[];
formDataKey.forEach((key) => {
if (key === 'file') return;
if (param[key] === undefined || param[key] === null) return;
formData.append(key as string, `${param[key]}`);
});
formData.append('file', param.file as Blob); // 必须放在最后一个
}
const res = await http.upload<any, typeof formData, true>(host, formData, custom);
const etag = HandleFileETag(res.headers);
const data = {
sign: 1,
etag,
fileName,
signature,
fileSize: file.size,
name: name || fileName,
remoteUrl: `${host.replace(/(?=\/)\/$/, '')}/${key}`, // 处理 host 可能后面添加 / 的情况
};
return data as ResFileOssUpload;
};
interface ReqFileOssUploadV2 extends SC.OSS.SignV2 {
/** 文件随机名称 */
fileName: string;
/** 文件 */
file: FileInfo;
}
/**
* oss签名上传文件 V2
* @param {object} file 文件对象
* @param {object} fileName 文件随机名
* @param {string} accessid OSS 的 `AccessKeyId`
* @param {string} host 上传存储空间的访问域名
* @param {string} signature 签名信息
* @param {string} policy 上传所需policy信息
* @param {string} objectName 上传至 oss 的完整文件路径
* @link https://help.aliyun.com/document_detail/31988.htm?spm=a2c4g.31925.0.0.67fe5918MHHN8l#reference-smp-nsw-wdb
*/
export const ApiFileOssUploadV2 = async (
body: Unfurl<ReqFileOssUploadV2>,
custom?: RequestConfig,
) => {
const { http } = getInitializedApiConfig();
const { file, fileName, accessid, host, signature, policy, objectName } = body;
let name;
if ('name' in file) name = file.name; // 文件真实名称
const param: Dictionary = {
key: objectName,
file,
policy,
signature,
OSSAccessKeyId: accessid,
success_action_status: '200',
// 下载时的文件名
'Content-Disposition': name ? `attachment;filename="${encodeURIComponent(name)}"` : undefined,
};
let formData: FormData | Dictionary;
if (typeof FormData === 'undefined') {
formData = param;
} else {
formData = new FormData();
const formDataKey = Object.keys(param) as (keyof typeof param)[];
formDataKey.forEach((key) => {
if (key === 'file') return;
if (param[key] === undefined || param[key] === null) return;
formData.append(key as string, `${param[key]}`);
});
formData.append('file', param.file as Blob); // 必须放在最后一个
}
const res = await http.upload<any, typeof formData, true>(host, formData, custom);
const etag = HandleFileETag(res.headers);
const data = {
sign: 1,
etag,
fileName,
signature,
fileSize: file.size,
name: name || fileName,
remoteUrl: `${host.replace(/(?=\/)\/$/, '')}/${objectName}`, // 处理 host 可能后面添加 / 的情况
};
return data as ResFileOssUpload;
};
type ReqFileUpdate = Pick<SC.File.Info, 'fileName' | 'name' | 'remoteUrl'> & {
/** etag字段 */
remark?: string;
etag?: string;
fileSize?: number;
};
/** 将 oss 文件信息更新到数据库 */
export const ApiFileUpdate = (body: Unfurl<ReqFileUpdate>, headers?: Record<string, any>) => {
const { http, url } = getInitializedApiConfig();
const api = `${url}/service-file/file/update`;
return http.post<SC.File.Info>(api, body, { headers });
};
type ReqFileUpload = ManagementSingOptions & {
/** 文件信息 */
file: FileInfo;
/** 访问链接是否下载 */
isDownload?: boolean;
/** 通过 isDownload 属性控制 */
disposition?: string;
};
function HandleFileUploadBody(defaultBody: Dictionary, body: FormData | Unfurl<ReqFileUpload>) {
const param = body;
// 小程序情况下
if (typeof FormData === 'undefined' && !isFormData(param)) {
if (param.isDownload === true) {
param.disposition = `attachment;filename=`;
}
const data = { ...defaultBody, ...param };
delete data.isDownload;
return data;
}
if (!isFormData(param)) {
const formData = new FormData();
const data = { ...defaultBody, ...param };
const formDataKey = Object.keys(data) as (keyof typeof data)[];
formDataKey.forEach((key) => {
if (key === 'file') return;
if (data[key] === undefined || data[key] === null) return;
if (key === 'isDownload' && data[key]) {
formData.append('disposition', `attachment;filename=`);
return;
}
formData.append(key, `${data[key]}`);
});
formData.append('file', data.file as Blob); // 必须放在最后一个
return formData;
} else {
if (param.has('isDownload')) {
param.delete('isDownload');
param.append('disposition', `attachment;filename=`);
}
Object.keys(defaultBody).forEach((key) => {
const value = param.get(key);
if (value === undefined) param.append(key, `${defaultBody[key]}`);
});
}
return param;
}
export interface ResFileUpload {
id: string;
etag: string;
name: string;
fileName: string;
fileSize: string;
url: string;
}
/** 服务器上传 */
export const ApiFileUpload = (body: FormData | Unfurl<ReqFileUpload>, custom?: RequestConfig) => {
const { http, url } = getInitializedApiConfig();
const api = `${url}/service-file/file/management/upload/single`;
const formData = HandleFileUploadBody({ target: 0, open: 1, isDownload: true }, body);
return http.upload<ResFileUpload>(api, formData, custom);
};
export interface ResStsToken {
/** oss accessKeyId */
accessid: string;
/** oss accessKeySecret */
accessSecret: string;
/** 过期时间,秒 */
expire: number;
bucket: string;
region: string;
securityToken: string;
}
/**
* 获取临时sts-token【全功能】
*
* @param target 文件源 aliyunoss: 0, minio: 1, 默认: 0
* @param open 文件权限 开放: 0, 私有文件: 1, 默认: 1
*/
export const ApiFileStsToken = (
body?: Unfurl<Partial<ManagementSingOptions & { bucketName?: string }>>,
headers?: Record<string, any>,
) => {
const { http, url } = getInitializedApiConfig();
const api = `${url}/service-file/file/management/tempCrudStsToken`;
return http.post<ResStsToken>(api, { target: 0, open: 1, ...body }, { headers });
};
export interface PresignedResponse {
id: string;
/** 有效时间 */
expiry: number;
/** 文件名 */
name: string;
/** 文件链接 */
url: string;
}
/**
* 获取私有临时文件访问链接
*
* @param ids id
* @param expiry 有效时间单位秒
* @param target 文件源 aliyunoss: 0, minio: 1, 默认: 0
* @param open 文件权限 开放: 0, 私有文件: 1, 默认: 1
*/
export const ApiFilePresigned = (
body: Unfurl<Omit<ManagementUploadOptions, 'open'>>,
headers?: Record<string, any>,
) => {
const { http, url } = getInitializedApiConfig();
const api = `${url}/service-file/file/management/presigned`;
return http.post<PresignedResponse[]>(
api,
{ target: 0, ...body, open: 0 },
{ headers: { Authorization: null, ...headers } },
);
};
/**
* 判断是否重复照片
* @param {string} etag - 文件返回的etag
*/
export const ApiFileExist = (body: { etag: string }, headers?: Record<string, any>) => {
const { http, url } = getInitializedApiConfig();
const api = `${url}/service-file/file/exist`;
return http.get<SC.File.Info[]>(`${api}/${body.etag}`, {}, { headers });
};
export interface ReqCheckFile {
origin: string;
files: { id: string; etag: string }[];
}
/**
* 判断重复照片
* @param origin - 业务标识符,若不传则当前检测租户下所有文件
* @param files - 重复的文件列表
* @param files.id - 重复的文件id
* @param files.etag - 重复的文件etag
*/
export const ApiFileRepeat = (body: ReqCheckFile, headers?: Record<string, any>) => {
const { http, url } = getInitializedApiConfig();
const api = `${url}/service-file/file/management/repeatpic/check`;
return http.post<ReqCheckFile>(api, body, { headers });
};