@ctil/gql 1.0.5 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +15 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -1
- package/dist/index.d.ts +23 -1
- package/dist/index.js +15 -5
- package/dist/index.js.map +1 -1
- package/package.json +8 -3
- package/.vscode/launch.json +0 -18
- package/ctil-gql-1.0.5.tgz +0 -0
- package/src/builders/auth.ts +0 -182
- package/src/builders/baseType.ts +0 -194
- package/src/builders/index.ts +0 -6
- package/src/builders/mutation.ts +0 -341
- package/src/builders/oss.ts +0 -140
- package/src/builders/query.ts +0 -180
- package/src/builders/sms.ts +0 -59
- package/src/cache/memoryCache.ts +0 -34
- package/src/core/api/auth.ts +0 -86
- package/src/core/api/gql.ts +0 -22
- package/src/core/api/mutation.ts +0 -100
- package/src/core/api/oss.ts +0 -216
- package/src/core/api/query.ts +0 -82
- package/src/core/api/sms.ts +0 -18
- package/src/core/client.ts +0 -47
- package/src/core/core.ts +0 -284
- package/src/core/executor.ts +0 -19
- package/src/core/type.ts +0 -76
- package/src/device/index.ts +0 -116
- package/src/index.ts +0 -60
- package/src/rateLimit/rateLimit.ts +0 -51
- package/src/rateLimit/rateLimitConfig.ts +0 -12
- package/src/test.ts +0 -109
- package/tsconfig.json +0 -17
- package/tsup.config.ts +0 -10
package/src/builders/sms.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { buildFields, buildCommonResultSelection,buildDataValue } from './baseType.ts';
|
|
2
|
-
import type { FieldInput } from './baseType.ts';
|
|
3
|
-
|
|
4
|
-
// ------------------- sendCode -------------------
|
|
5
|
-
export interface SendCodeInput {
|
|
6
|
-
phone: string;
|
|
7
|
-
dataFields?: FieldInput[]; // SmsSendResult 字段
|
|
8
|
-
variables?: Record<string, any>;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function buildGraphQLMutationSendCode(input: SendCodeInput) {
|
|
12
|
-
const { phone } = input;
|
|
13
|
-
const finalVariables: Record<string, any> = { ...(input.variables || {}) };
|
|
14
|
-
if (finalVariables.phone === undefined) finalVariables.phone = phone;
|
|
15
|
-
|
|
16
|
-
const varDefs = Object.keys(finalVariables).length
|
|
17
|
-
? Object.keys(finalVariables).map(k => `$${k}: String!`).join(', ')
|
|
18
|
-
: '';
|
|
19
|
-
|
|
20
|
-
const selection = buildCommonResultSelection(input.dataFields);
|
|
21
|
-
|
|
22
|
-
const query = `mutation sendCode${varDefs ? `(${varDefs})` : ''} {
|
|
23
|
-
sendCode(phone: $phone) {
|
|
24
|
-
${selection}
|
|
25
|
-
}
|
|
26
|
-
}`;
|
|
27
|
-
|
|
28
|
-
return { query, variables: finalVariables };
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// ------------------- verifyCode -------------------
|
|
32
|
-
export interface VerifyCodeInput {
|
|
33
|
-
code: string;
|
|
34
|
-
phone: string;
|
|
35
|
-
variables?: Record<string, any>;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function buildGraphQLMutationVerifyCode(input: VerifyCodeInput) {
|
|
39
|
-
const { code, phone } = input;
|
|
40
|
-
const finalVariables: Record<string, any> = { ...(input.variables || {}) };
|
|
41
|
-
if (finalVariables.code === undefined) finalVariables.code = code;
|
|
42
|
-
if (finalVariables.phone === undefined) finalVariables.phone = phone;
|
|
43
|
-
|
|
44
|
-
const varDefs = Object.keys(finalVariables).length
|
|
45
|
-
? Object.keys(finalVariables).map(k => `$${k}: String!`).join(', ')
|
|
46
|
-
: '';
|
|
47
|
-
|
|
48
|
-
const query = `mutation verifyCode${varDefs ? `(${varDefs})` : ''} {
|
|
49
|
-
verifyCode(code: $code, phone: $phone) {
|
|
50
|
-
${buildCommonResultSelection()}
|
|
51
|
-
}
|
|
52
|
-
}`;
|
|
53
|
-
|
|
54
|
-
return { query, variables: finalVariables };
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
package/src/cache/memoryCache.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
interface CacheItem<T> {
|
|
2
|
-
data: T;
|
|
3
|
-
expire: number;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export const DEFAULT_TTL = 5 * 60 * 1000; // 5 分钟
|
|
7
|
-
const cache = new Map<string, CacheItem<any>>();
|
|
8
|
-
|
|
9
|
-
export function getCacheKey(key: string, variables?: any): string {
|
|
10
|
-
return variables ? `${key}::${JSON.stringify(variables)}` : key;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function getFromCache<T>(key: string): T | null {
|
|
14
|
-
const item = cache.get(key);
|
|
15
|
-
if (!item) return null;
|
|
16
|
-
if (Date.now() > item.expire) {
|
|
17
|
-
cache.delete(key);
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
|
-
return item.data;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function setCache<T>(key: string, data: T, ttl = DEFAULT_TTL) {
|
|
24
|
-
cache.set(key, { data, expire: Date.now() + ttl });
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function deleteCache(key: string) {
|
|
28
|
-
cache.delete(key);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function clearCache() {
|
|
32
|
-
cache.clear();
|
|
33
|
-
}
|
|
34
|
-
|
package/src/core/api/auth.ts
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { execute } from '../executor.ts';
|
|
2
|
-
import {
|
|
3
|
-
buildGraphQLMutationLogin,
|
|
4
|
-
buildGraphQLMutationRegisterUser,
|
|
5
|
-
buildGraphQLMutationLogout,
|
|
6
|
-
buildGraphQLMutationLogoutAllDevices,
|
|
7
|
-
buildGraphQLMutationLogoutDevice,
|
|
8
|
-
buildGraphQLMutationRefreshToken,
|
|
9
|
-
} from '../../builders/index.ts';
|
|
10
|
-
import type {
|
|
11
|
-
LoginInput,
|
|
12
|
-
RegisterUserInput,
|
|
13
|
-
RefreshTokenInput,
|
|
14
|
-
} from '../../builders/index.ts';
|
|
15
|
-
import type { UserToken, requestResult } from '../type.ts';
|
|
16
|
-
import { rateLimit } from '../../rateLimit/rateLimit.ts';
|
|
17
|
-
import { rateLimitConfig } from '../../rateLimit/rateLimitConfig.ts';
|
|
18
|
-
import { clearCache } from '../../cache/memoryCache.ts';
|
|
19
|
-
import {
|
|
20
|
-
setLoginInfo,
|
|
21
|
-
removeLoginInfo,
|
|
22
|
-
getLoginInfo
|
|
23
|
-
} from '../../core/client.js';
|
|
24
|
-
|
|
25
|
-
export const auth = {
|
|
26
|
-
async login<T = requestResult<UserToken>>(input: LoginInput) {
|
|
27
|
-
return rateLimit('login', 'mutation', async () => {
|
|
28
|
-
const { query, variables } = buildGraphQLMutationLogin(input);
|
|
29
|
-
const result = await execute<T>({ query, variables });
|
|
30
|
-
setLoginInfo((result as any).login,input.remember)
|
|
31
|
-
clearCache(); // 登录后刷新缓存
|
|
32
|
-
return result;
|
|
33
|
-
}, rateLimitConfig);
|
|
34
|
-
},
|
|
35
|
-
|
|
36
|
-
async refreshToken<T = requestResult<UserToken>>(input: RefreshTokenInput) {
|
|
37
|
-
return rateLimit('refreshToken', 'mutation', async () => {
|
|
38
|
-
const { query, variables } = buildGraphQLMutationRefreshToken(input);
|
|
39
|
-
const result = await execute<T>({ query, variables });
|
|
40
|
-
setLoginInfo((result as any).refreshToken,input.remember)
|
|
41
|
-
clearCache();
|
|
42
|
-
return result;
|
|
43
|
-
}, rateLimitConfig);
|
|
44
|
-
},
|
|
45
|
-
|
|
46
|
-
async register<T = requestResult<any>>(input: RegisterUserInput) {
|
|
47
|
-
return rateLimit('registerUser', 'mutation', async () => {
|
|
48
|
-
const { query, variables } = buildGraphQLMutationRegisterUser(input);
|
|
49
|
-
const result = await execute<T>({ query, variables });
|
|
50
|
-
clearCache();
|
|
51
|
-
return result;
|
|
52
|
-
}, rateLimitConfig);
|
|
53
|
-
},
|
|
54
|
-
|
|
55
|
-
async logout<T = requestResult<any>>() {
|
|
56
|
-
return rateLimit('logout', 'mutation', async () => {
|
|
57
|
-
const { query, variables } = buildGraphQLMutationLogout();
|
|
58
|
-
const result = await execute<T>({ query, variables });
|
|
59
|
-
removeLoginInfo()
|
|
60
|
-
clearCache();
|
|
61
|
-
return result;
|
|
62
|
-
}, rateLimitConfig);
|
|
63
|
-
},
|
|
64
|
-
|
|
65
|
-
async logoutAllDevices<T = requestResult<any>>() {
|
|
66
|
-
return rateLimit('logoutAllDevices', 'mutation', async () => {
|
|
67
|
-
const { query, variables } = buildGraphQLMutationLogoutAllDevices();
|
|
68
|
-
const result = await execute<T>({ query, variables });
|
|
69
|
-
removeLoginInfo()
|
|
70
|
-
clearCache();
|
|
71
|
-
return result;
|
|
72
|
-
}, rateLimitConfig);
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
async logoutDevice<T = requestResult<any>>(deviceId: string) {
|
|
76
|
-
return rateLimit('logoutDevice', 'mutation', async () => {
|
|
77
|
-
const { query, variables } = buildGraphQLMutationLogoutDevice({ deviceId });
|
|
78
|
-
const result = await execute<T>({ query, variables });
|
|
79
|
-
if (getLoginInfo()?.deviceId==deviceId) {
|
|
80
|
-
removeLoginInfo()
|
|
81
|
-
}
|
|
82
|
-
clearCache();
|
|
83
|
-
return result;
|
|
84
|
-
}, rateLimitConfig);
|
|
85
|
-
},
|
|
86
|
-
};
|
package/src/core/api/gql.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
// src/core/api/gql.ts
|
|
2
|
-
import { execute } from '../executor.js';
|
|
3
|
-
import type { DocumentNode } from 'graphql';
|
|
4
|
-
import type { requestResult } from '../type.ts';
|
|
5
|
-
/**
|
|
6
|
-
* 原生 GraphQL 执行模块
|
|
7
|
-
* 可执行任意 query / mutation / subscription
|
|
8
|
-
*/
|
|
9
|
-
export const gql = {
|
|
10
|
-
/**
|
|
11
|
-
* 执行任意 GraphQL 文本或 DocumentNode
|
|
12
|
-
* @param query 原生 GQL 文本或 DocumentNode
|
|
13
|
-
* @param variables 可选变量
|
|
14
|
-
* @returns Promise<T>
|
|
15
|
-
*/
|
|
16
|
-
async execute<T = requestResult<any>>(
|
|
17
|
-
query: string | DocumentNode,
|
|
18
|
-
variables?: Record<string, any>
|
|
19
|
-
): Promise<T> {
|
|
20
|
-
return execute<T>({ query, variables });
|
|
21
|
-
},
|
|
22
|
-
};
|
package/src/core/api/mutation.ts
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import { execute } from '../executor.ts';
|
|
2
|
-
import {
|
|
3
|
-
buildGraphQLMutationInsertOne,
|
|
4
|
-
buildGraphQLMutationBatchInsert,
|
|
5
|
-
|
|
6
|
-
buildGraphQLMutationUpdate,
|
|
7
|
-
buildGraphQLMutationBatchUpdate,
|
|
8
|
-
buildGraphQLMutationUpdateByPk,
|
|
9
|
-
|
|
10
|
-
buildGraphQLMutationDelete,
|
|
11
|
-
buildGraphQLMutationDeleteById,
|
|
12
|
-
} from '../../builders/index.ts';
|
|
13
|
-
import type {
|
|
14
|
-
InsertOneInput,
|
|
15
|
-
BatchInsertInput,
|
|
16
|
-
|
|
17
|
-
DeleteInput,
|
|
18
|
-
DeleteByIdInput,
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
BatchUpdateInput,
|
|
22
|
-
UpdateByPkInput,
|
|
23
|
-
UpdateInput,
|
|
24
|
-
} from '../../builders/index.ts';
|
|
25
|
-
import type { requestResult } from '../type.ts';
|
|
26
|
-
import { clearCache } from '../../cache/memoryCache.ts';
|
|
27
|
-
import { rateLimit } from '../../rateLimit/rateLimit.ts';
|
|
28
|
-
|
|
29
|
-
export const mutation = {
|
|
30
|
-
async insertOne<T = requestResult<any>>(input: InsertOneInput) {
|
|
31
|
-
return rateLimit('insertOne', 'mutation', async () => {
|
|
32
|
-
const { query, variables } = buildGraphQLMutationInsertOne(input);
|
|
33
|
-
const result = await execute<T>({ query, variables });
|
|
34
|
-
|
|
35
|
-
// 清空缓存,保证数据一致
|
|
36
|
-
clearCache();
|
|
37
|
-
return result;
|
|
38
|
-
});
|
|
39
|
-
},
|
|
40
|
-
|
|
41
|
-
async batchInsert<T = requestResult<any>>(input: BatchInsertInput) {
|
|
42
|
-
return rateLimit('batchInsert', 'mutation', async () => {
|
|
43
|
-
const { query, variables } = buildGraphQLMutationBatchInsert(input);
|
|
44
|
-
const result = await execute<T>({ query, variables });
|
|
45
|
-
|
|
46
|
-
clearCache();
|
|
47
|
-
return result;
|
|
48
|
-
});
|
|
49
|
-
},
|
|
50
|
-
|
|
51
|
-
async update<T = requestResult<any>>(input: UpdateInput) {
|
|
52
|
-
return rateLimit('update', 'mutation', async () => {
|
|
53
|
-
const { query, variables } = buildGraphQLMutationUpdate(input);
|
|
54
|
-
const result = await execute<T>({ query, variables });
|
|
55
|
-
|
|
56
|
-
clearCache();
|
|
57
|
-
return result;
|
|
58
|
-
});
|
|
59
|
-
},
|
|
60
|
-
|
|
61
|
-
async batchUpdate<T = requestResult<any>>(input: BatchUpdateInput) {
|
|
62
|
-
return rateLimit('batchUpdate', 'mutation', async () => {
|
|
63
|
-
const { query, variables } = buildGraphQLMutationBatchUpdate(input);
|
|
64
|
-
const result = await execute<T>({ query, variables });
|
|
65
|
-
|
|
66
|
-
clearCache();
|
|
67
|
-
return result;
|
|
68
|
-
});
|
|
69
|
-
},
|
|
70
|
-
|
|
71
|
-
async updateByPk<T = requestResult<any>>(input: UpdateByPkInput) {
|
|
72
|
-
return rateLimit('updateByPk', 'mutation', async () => {
|
|
73
|
-
const { query, variables } = buildGraphQLMutationUpdateByPk(input);
|
|
74
|
-
const result = await execute<T>({ query, variables });
|
|
75
|
-
|
|
76
|
-
clearCache();
|
|
77
|
-
return result;
|
|
78
|
-
});
|
|
79
|
-
},
|
|
80
|
-
|
|
81
|
-
async delete<T = requestResult<any>>(input: DeleteInput) {
|
|
82
|
-
return rateLimit('delete', 'mutation', async () => {
|
|
83
|
-
const { query, variables } = buildGraphQLMutationDelete(input);
|
|
84
|
-
const result = await execute<T>({ query, variables });
|
|
85
|
-
|
|
86
|
-
clearCache();
|
|
87
|
-
return result;
|
|
88
|
-
});
|
|
89
|
-
},
|
|
90
|
-
|
|
91
|
-
async deleteById<T = requestResult<any>>(input: DeleteByIdInput) {
|
|
92
|
-
return rateLimit('deleteById', 'mutation', async () => {
|
|
93
|
-
const { query, variables } = buildGraphQLMutationDeleteById(input);
|
|
94
|
-
const result = await execute<T>({ query, variables });
|
|
95
|
-
|
|
96
|
-
clearCache();
|
|
97
|
-
return result;
|
|
98
|
-
});
|
|
99
|
-
},
|
|
100
|
-
};
|
package/src/core/api/oss.ts
DELETED
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
import { execute } from '../executor.ts';
|
|
2
|
-
import { buildGraphQLUploadPresignedUrl, buildGraphQLGetFilePreview } from '../../builders/index.ts';
|
|
3
|
-
import type { UploadPresignedUrlInput, PresignedUrlResult, FilePreview } from '../../builders/index.ts';
|
|
4
|
-
import type { requestResult } from '../type.ts';
|
|
5
|
-
import { rateLimit } from '../../rateLimit/rateLimit.ts';
|
|
6
|
-
import { rateLimitConfig } from '../../rateLimit/rateLimitConfig.ts';
|
|
7
|
-
import crypto from 'crypto';
|
|
8
|
-
import fs from 'fs';
|
|
9
|
-
import path from 'path';
|
|
10
|
-
|
|
11
|
-
async function computeFileMd5Base64(file: File | string): Promise<string> {
|
|
12
|
-
if (typeof window === 'undefined') {
|
|
13
|
-
// Node.js
|
|
14
|
-
let buffer: Buffer;
|
|
15
|
-
if (typeof file === 'string') {
|
|
16
|
-
// file 是路径
|
|
17
|
-
buffer = fs.readFileSync(path.resolve(file));
|
|
18
|
-
} else {
|
|
19
|
-
buffer = Buffer.from(await file.arrayBuffer());
|
|
20
|
-
}
|
|
21
|
-
return crypto.createHash('md5').update(buffer).digest('base64');
|
|
22
|
-
} else {
|
|
23
|
-
// 浏览器
|
|
24
|
-
const arrayBuffer = await (file as File).arrayBuffer();
|
|
25
|
-
const SparkMD5 = (await import('spark-md5')).default;
|
|
26
|
-
return SparkMD5.ArrayBuffer.hash(arrayBuffer, true); // true 表示 base64
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
function parseFileNameAndSuffix(url: string) {
|
|
30
|
-
try {
|
|
31
|
-
const urlObj = new URL(url);
|
|
32
|
-
// 获取路径最后一部分
|
|
33
|
-
const pathname = urlObj.pathname;
|
|
34
|
-
const decodedName = decodeURIComponent(pathname.split('/').pop() || 'file');
|
|
35
|
-
const suffix = decodedName.includes('.') ? decodedName.split('.').pop()! : '';
|
|
36
|
-
return { fileName: decodedName, fileSuffix: suffix };
|
|
37
|
-
} catch (e) {
|
|
38
|
-
// 如果不是标准 URL,fallback
|
|
39
|
-
const decodedName = decodeURIComponent(url.split('/').pop() || 'file');
|
|
40
|
-
const suffix = decodedName.includes('.') ? decodedName.split('.').pop()! : '';
|
|
41
|
-
return { fileName: decodedName, fileSuffix: suffix };
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
export const oss = {
|
|
47
|
-
/**
|
|
48
|
-
* 上传文件到 OSS(封装预签名 + fetch 上传 + 返回 FilePreview)
|
|
49
|
-
* @param params.file 必传
|
|
50
|
-
* @param params.folder 可选,可为空
|
|
51
|
-
* @param params.acl 可选,默认 'private'
|
|
52
|
-
*/
|
|
53
|
-
async uploadFile(params: {
|
|
54
|
-
file: File;
|
|
55
|
-
folder?: string | null;
|
|
56
|
-
acl?: 'private' | 'public_read';
|
|
57
|
-
}): Promise<FilePreview> {
|
|
58
|
-
const { file, folder, acl = 'private' } = params;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
// 1. 计算 MD5 Base64
|
|
62
|
-
const md5Base64 = await computeFileMd5Base64(file);
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
// 2. 构建上传参数
|
|
66
|
-
const presignedInput: UploadPresignedUrlInput = {
|
|
67
|
-
fileMd5Base64: md5Base64,
|
|
68
|
-
fileSuffix: file.name.split('.').pop() || '',
|
|
69
|
-
originalFileName: file.name,
|
|
70
|
-
fileSize: file.size,
|
|
71
|
-
acl
|
|
72
|
-
};
|
|
73
|
-
if (folder) presignedInput.folder = folder;
|
|
74
|
-
|
|
75
|
-
// 3. 获取预签名 URL
|
|
76
|
-
const presignedResult = await rateLimit('uploadPresignedUrl', 'mutation', async () => {
|
|
77
|
-
const { query, variables } = buildGraphQLUploadPresignedUrl(presignedInput);
|
|
78
|
-
const result = await execute<requestResult<PresignedUrlResult>>({ query, variables });
|
|
79
|
-
return result;
|
|
80
|
-
}, rateLimitConfig);
|
|
81
|
-
console.log("presignedResult====>", presignedResult);
|
|
82
|
-
|
|
83
|
-
if (!presignedResult.uploadPresignedUrl) throw new Error('获取预签名 URL 失败');
|
|
84
|
-
|
|
85
|
-
const { uploadUrl, uploadHeaders, downloadUrl, resourceId, contentType, originalFileName } = presignedResult.uploadPresignedUrl;
|
|
86
|
-
// 4. 上传文件
|
|
87
|
-
const headers: Record<string, string> = {};
|
|
88
|
-
if (uploadHeaders) {
|
|
89
|
-
if (uploadHeaders.contentMD5) headers['Content-MD5'] = uploadHeaders.contentMD5;
|
|
90
|
-
if (uploadHeaders.contentType) headers['Content-Type'] = uploadHeaders.contentType;
|
|
91
|
-
if (uploadHeaders.date) headers['Date'] = uploadHeaders.date;
|
|
92
|
-
}
|
|
93
|
-
const res = await fetch(uploadUrl, {
|
|
94
|
-
method: 'PUT',
|
|
95
|
-
body: file,
|
|
96
|
-
headers
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
if (!res.ok) {
|
|
100
|
-
throw new Error(`上传失败: ${res.status} ${res.statusText}`);
|
|
101
|
-
}
|
|
102
|
-
// 5. 返回 FilePreview
|
|
103
|
-
return {
|
|
104
|
-
id: resourceId!, // 非空断言,确保 TS 类型通过
|
|
105
|
-
originalFileName: originalFileName ?? file.name,
|
|
106
|
-
contentType: contentType ?? file.type,
|
|
107
|
-
fileSize: file.size,
|
|
108
|
-
url: downloadUrl ?? uploadUrl // downloadUrl 可能为 undefined,fallback 使用 uploadUrl
|
|
109
|
-
};
|
|
110
|
-
},
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* 从网络 URL 上传文件到 OSS
|
|
120
|
-
* @param url 必传 网络文件 URL
|
|
121
|
-
* @param params.folder 可选
|
|
122
|
-
* @param params.acl 可选
|
|
123
|
-
*/
|
|
124
|
-
async uploadFromUrl(params: {
|
|
125
|
-
url: string;
|
|
126
|
-
folder?: string | null;
|
|
127
|
-
acl?: 'private' | 'public_read';
|
|
128
|
-
}): Promise<FilePreview> {
|
|
129
|
-
const { url, folder, acl = 'private' } = params;
|
|
130
|
-
|
|
131
|
-
// 1. 下载网络文件
|
|
132
|
-
const res = await fetch(url);
|
|
133
|
-
if (!res.ok) throw new Error(`下载网络文件失败: ${res.status} ${res.statusText}`);
|
|
134
|
-
const arrayBuffer = await res.arrayBuffer();
|
|
135
|
-
|
|
136
|
-
const { fileName, fileSuffix } = parseFileNameAndSuffix(url);
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const fileSize = arrayBuffer.byteLength;
|
|
141
|
-
const fileType = res.headers.get('Content-Type') || 'application/octet-stream';
|
|
142
|
-
|
|
143
|
-
// 2. 计算 MD5 Base64
|
|
144
|
-
let md5Base64: string;
|
|
145
|
-
if (typeof window === 'undefined') {
|
|
146
|
-
md5Base64 = crypto.createHash('md5').update(Buffer.from(arrayBuffer)).digest('base64');
|
|
147
|
-
} else {
|
|
148
|
-
const SparkMD5 = (await import('spark-md5')).default;
|
|
149
|
-
md5Base64 = SparkMD5.ArrayBuffer.hash(arrayBuffer, true);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// 3. 构建上传参数
|
|
153
|
-
const presignedInput: UploadPresignedUrlInput = {
|
|
154
|
-
fileMd5Base64: md5Base64,
|
|
155
|
-
fileSuffix,
|
|
156
|
-
originalFileName: fileName,
|
|
157
|
-
fileSize,
|
|
158
|
-
acl
|
|
159
|
-
};
|
|
160
|
-
if (folder) presignedInput.folder = folder;
|
|
161
|
-
|
|
162
|
-
// 4. 获取预签名 URL
|
|
163
|
-
const presignedResult = await rateLimit('uploadPresignedUrl', 'mutation', async () => {
|
|
164
|
-
const { query, variables } = buildGraphQLUploadPresignedUrl(presignedInput);
|
|
165
|
-
const result = await execute<requestResult<PresignedUrlResult>>({ query, variables });
|
|
166
|
-
return result;
|
|
167
|
-
}, rateLimitConfig);
|
|
168
|
-
|
|
169
|
-
if (!presignedResult.uploadPresignedUrl) throw new Error('获取预签名 URL 失败');
|
|
170
|
-
|
|
171
|
-
const { uploadUrl, uploadHeaders, downloadUrl, resourceId, contentType, originalFileName } = presignedResult.uploadPresignedUrl;
|
|
172
|
-
|
|
173
|
-
// 5. 上传文件
|
|
174
|
-
const headers: Record<string, string> = {};
|
|
175
|
-
if (uploadHeaders) {
|
|
176
|
-
if (uploadHeaders.contentMD5) headers['Content-MD5'] = uploadHeaders.contentMD5;
|
|
177
|
-
if (uploadHeaders.contentType) headers['Content-Type'] = uploadHeaders.contentType;
|
|
178
|
-
if (uploadHeaders.date) headers['Date'] = uploadHeaders.date;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const uploadBody = typeof window === 'undefined' ? Buffer.from(arrayBuffer) : new Blob([arrayBuffer]);
|
|
182
|
-
const uploadRes = await fetch(uploadUrl, { method: 'PUT', body: uploadBody, headers });
|
|
183
|
-
if (!uploadRes.ok) throw new Error(`上传失败: ${uploadRes.status} ${uploadRes.statusText}`);
|
|
184
|
-
|
|
185
|
-
// 6. 返回 FilePreview
|
|
186
|
-
return {
|
|
187
|
-
id: resourceId!,
|
|
188
|
-
originalFileName: originalFileName ?? fileName,
|
|
189
|
-
contentType: contentType ?? fileType,
|
|
190
|
-
fileSize,
|
|
191
|
-
url: downloadUrl ?? uploadUrl
|
|
192
|
-
};
|
|
193
|
-
},
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* 获取文件预览信息
|
|
203
|
-
*/
|
|
204
|
-
async getFilePreview<T = requestResult<FilePreview>>(resourceId: number | string) {
|
|
205
|
-
return rateLimit('getFilePreview', 'query', async () => {
|
|
206
|
-
const { query, variables } = buildGraphQLGetFilePreview(resourceId);
|
|
207
|
-
const result = await execute<T>({ query, variables });
|
|
208
|
-
return result;
|
|
209
|
-
}, rateLimitConfig);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
};
|
package/src/core/api/query.ts
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { execute } from '../executor.ts';
|
|
2
|
-
import {
|
|
3
|
-
buildGraphQLQueryList,
|
|
4
|
-
buildGraphQLQueryByIdFixed,
|
|
5
|
-
buildGraphQLQueryPageList,
|
|
6
|
-
buildGraphQLQueryAggregate,
|
|
7
|
-
} from '../../builders/index.ts';
|
|
8
|
-
import type {
|
|
9
|
-
QueryInput,
|
|
10
|
-
QueryByIdInput,
|
|
11
|
-
QueryPageListInput,
|
|
12
|
-
QueryAggregateInput,
|
|
13
|
-
} from '../../builders/index.ts';
|
|
14
|
-
import type { requestResult } from '../type.ts';
|
|
15
|
-
import { getCacheKey, getFromCache, setCache, DEFAULT_TTL } from '../../cache/memoryCache.ts';
|
|
16
|
-
import { rateLimit } from '../../rateLimit/rateLimit.ts';
|
|
17
|
-
|
|
18
|
-
export const query = {
|
|
19
|
-
async list<T = requestResult<any>>(input: QueryInput, useCache = true, ttl?: number) {
|
|
20
|
-
return rateLimit('list', 'query', async () => {
|
|
21
|
-
const { query: q, variables } = buildGraphQLQueryList(input);
|
|
22
|
-
const key = getCacheKey(q, variables);
|
|
23
|
-
|
|
24
|
-
if (useCache) {
|
|
25
|
-
const cached = getFromCache<T>(key);
|
|
26
|
-
if (cached) return cached;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const result = await execute<T>({ query: q, variables });
|
|
30
|
-
if (useCache) setCache(key, result, ttl ?? DEFAULT_TTL);
|
|
31
|
-
return result;
|
|
32
|
-
});
|
|
33
|
-
},
|
|
34
|
-
|
|
35
|
-
async byId<T = requestResult<any>>(input: QueryByIdInput, useCache = true, ttl?: number) {
|
|
36
|
-
return rateLimit('byId', 'query', async () => {
|
|
37
|
-
const { query: q, variables } = buildGraphQLQueryByIdFixed(input);
|
|
38
|
-
const key = getCacheKey(q, variables);
|
|
39
|
-
|
|
40
|
-
if (useCache) {
|
|
41
|
-
const cached = getFromCache<T>(key);
|
|
42
|
-
if (cached) return cached;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const result = await execute<T>({ query: q, variables });
|
|
46
|
-
if (useCache) setCache(key, result, ttl ?? DEFAULT_TTL);
|
|
47
|
-
return result;
|
|
48
|
-
});
|
|
49
|
-
},
|
|
50
|
-
|
|
51
|
-
async page<T = requestResult<any>>(input: QueryPageListInput, useCache = true, ttl?: number) {
|
|
52
|
-
return rateLimit('page', 'query', async () => {
|
|
53
|
-
const { query: q, variables } = buildGraphQLQueryPageList(input);
|
|
54
|
-
const key = getCacheKey(q, variables);
|
|
55
|
-
|
|
56
|
-
if (useCache) {
|
|
57
|
-
const cached = getFromCache<T>(key);
|
|
58
|
-
if (cached) return cached;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const result = await execute<T>({ query: q, variables });
|
|
62
|
-
if (useCache) setCache(key, result, ttl ?? DEFAULT_TTL);
|
|
63
|
-
return result;
|
|
64
|
-
});
|
|
65
|
-
},
|
|
66
|
-
|
|
67
|
-
async aggregate<T = requestResult<any>>(input: QueryAggregateInput, useCache = true, ttl?: number) {
|
|
68
|
-
return rateLimit('aggregate', 'query', async () => {
|
|
69
|
-
const { query: q, variables } = buildGraphQLQueryAggregate(input);
|
|
70
|
-
const key = getCacheKey(q, variables);
|
|
71
|
-
|
|
72
|
-
if (useCache) {
|
|
73
|
-
const cached = getFromCache<T>(key);
|
|
74
|
-
if (cached) return cached;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const result = await execute<T>({ query: q, variables });
|
|
78
|
-
if (useCache) setCache(key, result, ttl ?? DEFAULT_TTL);
|
|
79
|
-
return result;
|
|
80
|
-
});
|
|
81
|
-
},
|
|
82
|
-
};
|
package/src/core/api/sms.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { execute } from '../executor.ts';
|
|
2
|
-
import {
|
|
3
|
-
buildGraphQLMutationSendCode,
|
|
4
|
-
buildGraphQLMutationVerifyCode,
|
|
5
|
-
} from '../../builders/index.ts';
|
|
6
|
-
import type { SendCodeInput, VerifyCodeInput } from '../../builders/index.ts';
|
|
7
|
-
import type { requestResult } from '../type.ts';
|
|
8
|
-
export const sms = {
|
|
9
|
-
async send<T = requestResult<any>>(input: SendCodeInput) {
|
|
10
|
-
const { query, variables } = buildGraphQLMutationSendCode(input);
|
|
11
|
-
return execute<T>({ query, variables });
|
|
12
|
-
},
|
|
13
|
-
|
|
14
|
-
async verify<T = requestResult<any>>(input: VerifyCodeInput) {
|
|
15
|
-
const { query, variables } = buildGraphQLMutationVerifyCode(input);
|
|
16
|
-
return execute<T>({ query, variables });
|
|
17
|
-
},
|
|
18
|
-
};
|
package/src/core/client.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
// src/core/client.ts
|
|
2
|
-
import { CCRequest } from './core.ts';
|
|
3
|
-
import type { RequestConfig, RequestInterceptor, UserToken } from './type.ts';
|
|
4
|
-
|
|
5
|
-
let client: CCRequest | null = null;
|
|
6
|
-
|
|
7
|
-
/** 初始化全局客户端 */
|
|
8
|
-
export function initGraphQLClient(config: RequestConfig): CCRequest {
|
|
9
|
-
client = new CCRequest(config);
|
|
10
|
-
return client;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/** 获取全局客户端实例 */
|
|
14
|
-
export function getClient(): CCRequest {
|
|
15
|
-
if (!client) throw new Error('GraphQL client not initialized. Call initGraphQLClient() first.');
|
|
16
|
-
return client;
|
|
17
|
-
}
|
|
18
|
-
/** 注册全局拦截器 */
|
|
19
|
-
export const useInterceptor = (interceptor: RequestInterceptor) =>
|
|
20
|
-
getClient().use(interceptor);
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
// ===== 便捷操作函数 =====
|
|
25
|
-
// token 修改 → 重建实例
|
|
26
|
-
export const setToken = (token: string) => getClient().setToken(token);
|
|
27
|
-
export const removeToken = () => getClient().removeToken();
|
|
28
|
-
|
|
29
|
-
// 登录信息
|
|
30
|
-
export const setLoginInfo = (loginInfo: UserToken, remember: boolean = false) => getClient().setLoginInfo(loginInfo,remember)
|
|
31
|
-
export const removeLoginInfo = () => getClient().removeLoginInfo()
|
|
32
|
-
export const getLoginInfo = () => getClient().getLoginInfo()
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
// endpoint 修改 → 重建实例
|
|
39
|
-
export const setEndpoint = (endpoint: string) => getClient().setEndpoint(endpoint);
|
|
40
|
-
|
|
41
|
-
// 普通 header 操作 → 不重建实例
|
|
42
|
-
export const setHeader = (key: string, value: string) => getClient().setHeader(key, value);
|
|
43
|
-
export const setHeaders = (headers: Record<string, string>) => getClient().setHeaders(headers);
|
|
44
|
-
export const removeHeader = (key: string) => getClient().removeHeader(key);
|
|
45
|
-
export const clearHeaders = () => getClient().clearHeaders();
|
|
46
|
-
|
|
47
|
-
|