增加Rate Limit功能
This commit is contained in:
36
src/api.ts
36
src/api.ts
@@ -35,7 +35,13 @@ export const voteArticle = async (
|
|||||||
},
|
},
|
||||||
body: JSON.stringify({ id }),
|
body: JSON.stringify({ id }),
|
||||||
});
|
});
|
||||||
|
if (response.status === 403) {
|
||||||
|
const data = await response.json().catch(() => null);
|
||||||
|
if (data?.reason === 'Rate Limit Exceeded') {
|
||||||
|
toast.error('Rate Limit Exceeded');
|
||||||
|
throw new Error('Rate Limit Exceeded');
|
||||||
|
}
|
||||||
|
}
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
if (data.status !== 'OK') {
|
if (data.status !== 'OK') {
|
||||||
throw new Error(`Vote ${type} failed`);
|
throw new Error(`Vote ${type} failed`);
|
||||||
@@ -63,6 +69,11 @@ export const submitPost = async (postData: { content: string }): Promise<SubmitP
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (response.status === 403) {
|
if (response.status === 403) {
|
||||||
|
const data = await response.json().catch(() => null);
|
||||||
|
if (data?.reason === 'Rate Limit Exceeded') {
|
||||||
|
toast.error('Rate Limit Exceeded');
|
||||||
|
throw new Error('Rate Limit Exceeded');
|
||||||
|
}
|
||||||
return { status: 'Deny', message: '投稿中包含违禁词', id: 'null'};
|
return { status: 'Deny', message: '投稿中包含违禁词', id: 'null'};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +94,13 @@ export const uploadImage = async (formData: FormData): Promise<{ status: 'OK' |
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: formData,
|
body: formData,
|
||||||
});
|
});
|
||||||
|
if (response.status === 403) {
|
||||||
|
const data = await response.json().catch(() => null);
|
||||||
|
if (data?.reason === 'Rate Limit Exceeded') {
|
||||||
|
toast.error('Rate Limit Exceeded');
|
||||||
|
throw new Error('Rate Limit Exceeded');
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`HTTP error! status: ${response.status}`);
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
}
|
}
|
||||||
@@ -113,7 +130,13 @@ export const reportPost = async (reportData: { id: number; title: string; conten
|
|||||||
},
|
},
|
||||||
body: JSON.stringify(reportData),
|
body: JSON.stringify(reportData),
|
||||||
});
|
});
|
||||||
|
if (response.status === 403) {
|
||||||
|
const data = await response.json().catch(() => null);
|
||||||
|
if (data?.reason === 'Rate Limit Exceeded') {
|
||||||
|
toast.error('Rate Limit Exceeded');
|
||||||
|
throw new Error('Rate Limit Exceeded');
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`HTTP error! status: ${response.status}`);
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
}
|
}
|
||||||
@@ -181,6 +204,11 @@ export const postComment = async (commentData: PostCommentRequest): Promise<Post
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (response.status === 403) {
|
if (response.status === 403) {
|
||||||
|
const data = await response.json().catch(() => null);
|
||||||
|
if (data?.reason === 'Rate Limit Exceeded') {
|
||||||
|
toast.error('Rate Limit Exceeded');
|
||||||
|
throw new Error('Rate Limit Exceeded');
|
||||||
|
}
|
||||||
throw new Error('评论包含违禁词');
|
throw new Error('评论包含违禁词');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,6 +230,7 @@ export interface InitPayload {
|
|||||||
allowedExtensions: string[];
|
allowedExtensions: string[];
|
||||||
maxFileSize: number;
|
maxFileSize: number;
|
||||||
bannedKeywords?: string[];
|
bannedKeywords?: string[];
|
||||||
|
rateLimit: number; // 次/分钟,0为无限制
|
||||||
}
|
}
|
||||||
|
|
||||||
export const initBackend = async (payload: InitPayload): Promise<{ status: string; reason?: string }> => {
|
export const initBackend = async (payload: InitPayload): Promise<{ status: string; reason?: string }> => {
|
||||||
@@ -211,6 +240,7 @@ export const initBackend = async (payload: InitPayload): Promise<{ status: strin
|
|||||||
ALLOWED_EXTENSIONS: payload.allowedExtensions,
|
ALLOWED_EXTENSIONS: payload.allowedExtensions,
|
||||||
MAX_FILE_SIZE: payload.maxFileSize,
|
MAX_FILE_SIZE: payload.maxFileSize,
|
||||||
...(payload.bannedKeywords ? { BANNED_KEYWORDS: payload.bannedKeywords } : {}),
|
...(payload.bannedKeywords ? { BANNED_KEYWORDS: payload.bannedKeywords } : {}),
|
||||||
|
RATE_LIMIT: payload.rateLimit,
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await fetch(`${API_CONFIG.BASE_URL}/init`, {
|
const response = await fetch(`${API_CONFIG.BASE_URL}/init`, {
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ const InitPage: React.FC = () => {
|
|||||||
const [allowedExtensions, setAllowedExtensions] = useState('png,jpg,jpeg,gif,webp');
|
const [allowedExtensions, setAllowedExtensions] = useState('png,jpg,jpeg,gif,webp');
|
||||||
const [maxFileSizeMB, setMaxFileSizeMB] = useState(10); // 以MB为单位,默认10MB
|
const [maxFileSizeMB, setMaxFileSizeMB] = useState(10); // 以MB为单位,默认10MB
|
||||||
const [bannedKeywords, setBannedKeywords] = useState('');
|
const [bannedKeywords, setBannedKeywords] = useState('');
|
||||||
|
const [rateLimit, setRateLimit] = useState<number>(10); // 次/分钟,0为无限制
|
||||||
const [initializing, setInitializing] = useState(false);
|
const [initializing, setInitializing] = useState(false);
|
||||||
|
|
||||||
const onInit = async () => {
|
const onInit = async () => {
|
||||||
@@ -57,6 +58,7 @@ const InitPage: React.FC = () => {
|
|||||||
bannedKeywords: bannedKeywords
|
bannedKeywords: bannedKeywords
|
||||||
? bannedKeywords.split(',').map(s => s.trim()).filter(Boolean)
|
? bannedKeywords.split(',').map(s => s.trim()).filter(Boolean)
|
||||||
: undefined,
|
: undefined,
|
||||||
|
rateLimit: Number(rateLimit) || 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
const res = await initBackend(payload);
|
const res = await initBackend(payload);
|
||||||
@@ -100,6 +102,14 @@ const InitPage: React.FC = () => {
|
|||||||
<Field label="违禁词 (可选,逗号分隔)">
|
<Field label="违禁词 (可选,逗号分隔)">
|
||||||
<Textarea value={bannedKeywords} onChange={(_, v) => setBannedKeywords(v?.value || '')} resize="vertical" placeholder="例如:spam,广告,违禁词" />
|
<Textarea value={bannedKeywords} onChange={(_, v) => setBannedKeywords(v?.value || '')} resize="vertical" placeholder="例如:spam,广告,违禁词" />
|
||||||
</Field>
|
</Field>
|
||||||
|
<Field label="提交内容 Rate Limit (次/分钟)">
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
value={String(rateLimit)}
|
||||||
|
onChange={(_, v) => setRateLimit(Number(v?.value ?? rateLimit))}
|
||||||
|
placeholder="0次为无限制"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
|
||||||
<Button style={{ marginTop: tokens.spacingVerticalXL }} appearance="primary" onClick={onInit} disabled={initializing}>
|
<Button style={{ marginTop: tokens.spacingVerticalXL }} appearance="primary" onClick={onInit} disabled={initializing}>
|
||||||
{initializing ? '正在初始化...' : '开始初始化'}
|
{initializing ? '正在初始化...' : '开始初始化'}
|
||||||
|
|||||||
Reference in New Issue
Block a user