Skip to content

Commit 8bacf44

Browse files
authored
Merge pull request iflytek#1163 from lyj715824/bugfix/superteam
fix: Extend model to add form field length
2 parents f73d179 + c5d9746 commit 8bacf44

6 files changed

Lines changed: 117 additions & 89 deletions

File tree

PR.md

Lines changed: 0 additions & 71 deletions
This file was deleted.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
ALTER TABLE `model`
2+
MODIFY COLUMN `url` varchar(1024) DEFAULT NULL COMMENT 'Model call address',
3+
MODIFY COLUMN `api_key` varchar(1024) DEFAULT NULL;

console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/biz/modelconfig/ModelValidationRequest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,18 @@
22

33
import com.iflytek.astron.console.toolkit.entity.vo.ModelCategoryReq;
44
import jakarta.validation.constraints.NotNull;
5+
import jakarta.validation.constraints.Size;
56
import lombok.Data;
67

78
import java.util.List;
89

910
@Data
1011
public class ModelValidationRequest {
1112
@NotNull(message = "Model endpoint cannot be empty")
13+
@Size(max = 1024, message = "Model endpoint length cannot exceed 1024")
1214
private String endpoint;
1315
@NotNull(message = "API key cannot be empty")
16+
@Size(max = 1024, message = "API key length cannot exceed 1024")
1417
private String apiKey;
1518
private String modelName;
1619
/**

console/frontend/src/pages/model-management/components/modal-component.tsx

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import {
1616
ConfigProvider,
1717
Switch,
1818
} from 'antd';
19-
import JSEncrypt from 'jsencrypt';
2019
import {
2120
modelCreate,
2221
deleteModelAPI,
@@ -55,6 +54,7 @@ import {
5554
normalizeModelProvider,
5655
} from '../utils/provider';
5756
import { mapProviderToVendor } from '../utils/provider-group';
57+
import { encryptApiKey } from '../utils/encrypt-api-key';
5858

5959
const { TextArea } = Input;
6060

@@ -64,16 +64,6 @@ const checkNameConventions = (string: string): boolean => {
6464
return regex.test(string);
6565
};
6666

67-
const encryptApiKey = (publicKey: string, apiKey: string): string => {
68-
const encrypt = new JSEncrypt();
69-
encrypt.setPublicKey(publicKey);
70-
const encrypted = encrypt.encrypt(apiKey);
71-
if (!encrypted) {
72-
throw new Error(i18next.t('model.encryptionFailed'));
73-
}
74-
return encrypted;
75-
};
76-
7767
const checkParamsTable = (modelParams: ModelConfigParam[]): boolean => {
7868
let flag = true;
7969
modelParams.forEach(item => {
@@ -808,7 +798,7 @@ const ModelBasicForm = ({
808798
</div>
809799
</div>
810800
<Input
811-
maxLength={255}
801+
maxLength={1024}
812802
showCount
813803
placeholder={endpointPlaceholder}
814804
className="global-input w-full"
@@ -825,7 +815,7 @@ const ModelBasicForm = ({
825815
</div>
826816
</div>
827817
<Input
828-
maxLength={255}
818+
maxLength={1024}
829819
showCount
830820
placeholder={t('common.inputPlaceholder')}
831821
className="global-input w-full"

console/frontend/src/pages/model-management/components/model-card.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { useState, useMemo, useCallback, JSX, Fragment } from 'react';
22
import { useTranslation } from 'react-i18next';
33
import { useNavigate } from 'react-router-dom';
44
import { Switch, message, Button } from 'antd';
5-
import JSEncrypt from 'jsencrypt';
65

76
import { DeleteModal, CreateModal } from './modal-component';
87
import StatusTag from './status-tag';
@@ -27,16 +26,15 @@ import i18next from 'i18next';
2726
import styles from './model-card.module.scss';
2827
import classNames from 'classnames';
2928
import { getModelProviderLabel } from '../utils/provider';
29+
import { encryptApiKey as encryptLongApiKey } from '../utils/encrypt-api-key';
3030

3131
// 加密API密钥工具函数
3232
const encryptApiKey = (publicKey: string, apiKey: string): string => {
33-
const encrypt = new JSEncrypt();
34-
encrypt.setPublicKey(publicKey);
35-
const encrypted = encrypt.encrypt(apiKey);
36-
if (!encrypted) {
33+
return encryptLongApiKey(publicKey, apiKey); /*
3734
throw new Error('API密钥加密失败');
3835
}
3936
return encrypted;
37+
*/
4038
};
4139

4240
// 重新发布模型函数
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import JSEncrypt from 'jsencrypt';
2+
import i18next from 'i18next';
3+
4+
const RSA_PKCS1_PADDING_OVERHEAD = 11;
5+
const DEFAULT_MAX_CHUNK_BYTES = 245;
6+
7+
const getMaxChunkBytes = (publicKey: string): number => {
8+
const encrypt = new JSEncrypt();
9+
encrypt.setPublicKey(publicKey);
10+
11+
const key = encrypt.getKey() as unknown as { n?: { bitLength?: () => number } };
12+
const bitLength = key?.n?.bitLength?.();
13+
14+
if (typeof bitLength === 'number' && bitLength > RSA_PKCS1_PADDING_OVERHEAD) {
15+
return Math.floor((bitLength + 7) / 8) - RSA_PKCS1_PADDING_OVERHEAD;
16+
}
17+
18+
return DEFAULT_MAX_CHUNK_BYTES;
19+
};
20+
21+
const splitTextByUtf8ByteLength = (
22+
text: string,
23+
maxChunkBytes: number
24+
): string[] => {
25+
if (!text) {
26+
return [''];
27+
}
28+
29+
const encoder = new TextEncoder();
30+
const chunks: string[] = [];
31+
let currentChunk = '';
32+
let currentChunkBytes = 0;
33+
34+
for (const char of text) {
35+
const charBytes = encoder.encode(char).length;
36+
37+
if (charBytes > maxChunkBytes) {
38+
throw new Error(i18next.t('model.encryptionFailed'));
39+
}
40+
41+
if (currentChunkBytes + charBytes > maxChunkBytes) {
42+
chunks.push(currentChunk);
43+
currentChunk = char;
44+
currentChunkBytes = charBytes;
45+
continue;
46+
}
47+
48+
currentChunk += char;
49+
currentChunkBytes += charBytes;
50+
}
51+
52+
if (currentChunk) {
53+
chunks.push(currentChunk);
54+
}
55+
56+
return chunks;
57+
};
58+
59+
const decodeBase64ToBytes = (base64: string): Uint8Array => {
60+
const binary = atob(base64);
61+
const bytes = new Uint8Array(binary.length);
62+
63+
for (let i = 0; i < binary.length; i += 1) {
64+
bytes[i] = binary.charCodeAt(i);
65+
}
66+
67+
return bytes;
68+
};
69+
70+
const encodeBytesToBase64 = (bytes: Uint8Array): string => {
71+
let binary = '';
72+
73+
for (let i = 0; i < bytes.length; i += 1) {
74+
binary += String.fromCharCode(bytes[i] ?? 0);
75+
}
76+
77+
return btoa(binary);
78+
};
79+
80+
export const encryptApiKey = (publicKey: string, apiKey: string): string => {
81+
const maxChunkBytes = getMaxChunkBytes(publicKey);
82+
const chunks = splitTextByUtf8ByteLength(apiKey, maxChunkBytes);
83+
const encrypt = new JSEncrypt();
84+
encrypt.setPublicKey(publicKey);
85+
86+
const encryptedBlocks = chunks.map(chunk => {
87+
const encrypted = encrypt.encrypt(chunk);
88+
if (!encrypted) {
89+
throw new Error(i18next.t('model.encryptionFailed'));
90+
}
91+
92+
return decodeBase64ToBytes(encrypted);
93+
});
94+
95+
const totalLength = encryptedBlocks.reduce((sum, block) => sum + block.length, 0);
96+
const mergedBytes = new Uint8Array(totalLength);
97+
let offset = 0;
98+
99+
encryptedBlocks.forEach(block => {
100+
mergedBytes.set(block, offset);
101+
offset += block.length;
102+
});
103+
104+
return encodeBytesToBase64(mergedBytes);
105+
};

0 commit comments

Comments
 (0)