381 lines
10 KiB
TypeScript
381 lines
10 KiB
TypeScript
export interface SiteSettings {
|
|
siteTitle: string;
|
|
siteFooter: string;
|
|
enableCodeIcon: boolean;
|
|
repoUrl: string;
|
|
favicon: string;
|
|
}
|
|
|
|
export const getSettings = async (): Promise<SiteSettings> => {
|
|
try {
|
|
const response = await fetch('/api/settings');
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
const json = await response.json();
|
|
if (json.code === 1000 && json.data) {
|
|
const data = json.data;
|
|
return {
|
|
siteTitle: data.title,
|
|
siteFooter: data.footer_text,
|
|
enableCodeIcon: data.enable_repo_button ?? true,
|
|
repoUrl: data.repo_link,
|
|
favicon: data.icon,
|
|
};
|
|
} else {
|
|
throw new Error('Invalid response code or missing data');
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to fetch settings:', error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
export const getAbout = async (): Promise<string> => {
|
|
try {
|
|
const response = await fetch('/api/about');
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
const json = await response.json();
|
|
if (json.code === 1000) {
|
|
return json.data;
|
|
} else {
|
|
throw new Error('Invalid response code');
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to fetch about content:', error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
export interface StaticsData {
|
|
posts: number;
|
|
comments: number;
|
|
images: number;
|
|
}
|
|
|
|
export const testApiStatus = async (): Promise<boolean> => {
|
|
try {
|
|
const response = await fetch('/api/test');
|
|
return response.ok;
|
|
} catch (error) {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
export const getStatics = async (): Promise<StaticsData> => {
|
|
try {
|
|
const response = await fetch('/api/statics');
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
const json = await response.json();
|
|
if (json.code === 1000) {
|
|
return json.data;
|
|
} else {
|
|
throw new Error('Invalid response code');
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to fetch statics:', error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
export const saveDraft = (content: string): void => {
|
|
localStorage.setItem('draft', content);
|
|
};
|
|
|
|
export const getDraft = (): string | null => {
|
|
return localStorage.getItem('draft');
|
|
};
|
|
|
|
export const fetch_id_token = async (): Promise<string> => {
|
|
try {
|
|
const response = await fetch('/api/get_id_token');
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
const json = await response.json();
|
|
if (json.code === 1000) {
|
|
return json.data;
|
|
} else {
|
|
throw new Error(json.data || 'Failed to fetch identity token');
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to fetch identity token:', error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
export const get_id_token = async (): Promise<string> => {
|
|
let token = localStorage.getItem('identity_token');
|
|
if (!token) {
|
|
token = await fetch_id_token();
|
|
localStorage.setItem('identity_token', token);
|
|
}
|
|
return token;
|
|
};
|
|
|
|
export interface CreatePostResponse {
|
|
code: number;
|
|
data: any;
|
|
}
|
|
|
|
export const createPost = async (content: string): Promise<CreatePostResponse> => {
|
|
try {
|
|
const identity = await get_id_token();
|
|
|
|
// 解析标签:#开头,后跟非空白字符
|
|
const hashtopic: string[] = [];
|
|
const regex = /#\S+/g;
|
|
const matches = content.match(regex);
|
|
if (matches) {
|
|
matches.forEach(tag => {
|
|
const cleanTag = tag.substring(1);
|
|
// 去重添加
|
|
if (!hashtopic.includes(cleanTag)) {
|
|
hashtopic.push(cleanTag);
|
|
}
|
|
});
|
|
}
|
|
|
|
const response = await fetch('/api/post', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
content,
|
|
hashtopic,
|
|
identity
|
|
}),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
|
|
return await response.json();
|
|
} catch (error) {
|
|
console.error('Failed to create post:', error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
export interface Article {
|
|
id: number;
|
|
content: string;
|
|
upvotes: number;
|
|
downvotes: number;
|
|
created_at?: string;
|
|
comment_count?: number;
|
|
total_pages?: number;
|
|
}
|
|
|
|
export const fetchArticles = async (page: number, signal?: AbortSignal): Promise<Article[]> => {
|
|
try {
|
|
const response = await fetch(`/api/posts_info?page=${page}`, { signal });
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
const json = await response.json();
|
|
if (json.code === 1000 && Array.isArray(json.data)) {
|
|
return json.data as Article[];
|
|
}
|
|
throw new Error('Invalid response code or missing data');
|
|
} catch (error) {
|
|
console.error('Failed to fetch articles:', error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
export const voteArticle = async (id: number, type: 'up' | 'down'): Promise<void> => {
|
|
try {
|
|
const response = await fetch(`/api/${type}`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({ id, type: 'submission' }),
|
|
});
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
const json = await response.json();
|
|
if (json.code !== 1000) {
|
|
throw new Error('Vote failed');
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to vote:', error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
export interface Comment {
|
|
id: number;
|
|
nickname: string;
|
|
content: string;
|
|
parent_comment_id: number;
|
|
}
|
|
|
|
export interface CommentPage {
|
|
comments: Comment[];
|
|
total_pages: number;
|
|
}
|
|
|
|
export const getComments = async (id: string | number, page: number = 1): Promise<CommentPage> => {
|
|
try {
|
|
const response = await fetch(`/api/get/comment?id=${id}&page=${page}`);
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
const json = await response.json();
|
|
if (json.code === 1000 && json.data && Array.isArray(json.data.comments)) {
|
|
return {
|
|
comments: json.data.comments as Comment[],
|
|
total_pages: Number(json.data.total_pages) || 0,
|
|
};
|
|
}
|
|
throw new Error('Invalid response code or missing data');
|
|
} catch (error) {
|
|
console.error('Failed to fetch comments:', error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
export interface PostCommentRequest {
|
|
content: string;
|
|
submission_id: number;
|
|
parent_comment_id: number;
|
|
nickname: string;
|
|
}
|
|
|
|
export interface PostCommentResponse {
|
|
id: number;
|
|
}
|
|
|
|
export const postComment = async (commentData: PostCommentRequest): Promise<PostCommentResponse> => {
|
|
try {
|
|
const identity = await get_id_token();
|
|
const hashtopic: string[] = [];
|
|
const regex = /#\S+/g;
|
|
const matches = commentData.content.match(regex);
|
|
if (matches) {
|
|
matches.forEach(tag => {
|
|
const cleanTag = tag.substring(1);
|
|
if (!hashtopic.includes(cleanTag)) {
|
|
hashtopic.push(cleanTag);
|
|
}
|
|
});
|
|
}
|
|
const response = await fetch('/api/comment', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
...commentData,
|
|
hashtopic,
|
|
identity,
|
|
}),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
|
|
const json = await response.json();
|
|
if (json.code === 1001 && json.data?.id !== undefined) {
|
|
return { id: Number(json.data.id) };
|
|
}
|
|
if (json.code === 2005) {
|
|
throw new Error('评论包含违禁词');
|
|
}
|
|
throw new Error('Comment failed');
|
|
} catch (error) {
|
|
console.error('Failed to post comment:', error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
export interface ReportPostResponse {
|
|
id: number;
|
|
}
|
|
|
|
export const reportPost = async (reportData: { id: number; title: string; content: string }): Promise<ReportPostResponse> => {
|
|
try {
|
|
const identity = await get_id_token();
|
|
const response = await fetch('/api/report', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
...reportData,
|
|
identity,
|
|
}),
|
|
});
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
const json = await response.json();
|
|
if (json.code === 1001 && json.data?.id !== undefined) {
|
|
return { id: Number(json.data.id) };
|
|
}
|
|
throw new Error(json.data || 'Report failed');
|
|
} catch (error) {
|
|
console.error('Failed to report post:', error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
export const uploadImage = async (file: File): Promise<string> => {
|
|
try {
|
|
const identity_token = await get_id_token();
|
|
const formData = new FormData();
|
|
formData.append('file', file);
|
|
if (identity_token) {
|
|
formData.append('identity_token', identity_token);
|
|
}
|
|
const response = await fetch('/api/upload_pic', {
|
|
method: 'POST',
|
|
body: formData,
|
|
});
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
const json = await response.json();
|
|
if (json.code === 1001 && typeof json.data === 'string') {
|
|
return json.data;
|
|
}
|
|
if (json.code === 2006) {
|
|
throw new Error('UPLOAD_TOO_LARGE');
|
|
}
|
|
if (json.code === 2007) {
|
|
throw new Error('UNSUPPORTED_FORMAT');
|
|
}
|
|
throw new Error(json.data || 'Upload failed');
|
|
} catch (error) {
|
|
console.error('Failed to upload image:', error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
export const getFileName = async (path: string): Promise<string> => {
|
|
try {
|
|
const response = await fetch(`/api/file_name?path=${encodeURIComponent(path)}`);
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
const json = await response.json();
|
|
if (json.code === 1000 && typeof json.data === 'string') {
|
|
return json.data;
|
|
}
|
|
throw new Error('Invalid response code or missing data');
|
|
} catch (error) {
|
|
console.error('Failed to fetch file name:', error);
|
|
throw error;
|
|
}
|
|
};
|