feat: 重构前端api封装结构
This commit is contained in:
@@ -1,2 +0,0 @@
|
|||||||
export * as user from './user/index';
|
|
||||||
export * as web from './web/index';
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import { User } from "@/lib/types/user";
|
|
||||||
import fetcher from "../../fetcher";
|
|
||||||
|
|
||||||
interface createUserParams {
|
|
||||||
username: string | null;
|
|
||||||
nickname: string | null;
|
|
||||||
email: string | null;
|
|
||||||
phone: string | null;
|
|
||||||
password: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function create(data: createUserParams) {
|
|
||||||
return fetcher<User>("/api/admin/user", {
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify(data),
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import { User } from "@/lib/types/user";
|
|
||||||
import fetcher from "../../fetcher";
|
|
||||||
|
|
||||||
export function get(userId: string) {
|
|
||||||
return fetcher<User>(`/api/admin/user/${userId}`);
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
export * from './list';
|
|
||||||
export * from './get';
|
|
||||||
export * from './create';
|
|
||||||
export * from './update';
|
|
||||||
export * from './set-password';
|
|
||||||
export * from './remove';
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import { User } from "@/lib/types/user"
|
|
||||||
import fetcher from "../../fetcher"
|
|
||||||
|
|
||||||
export interface UserListParams {
|
|
||||||
page?: number
|
|
||||||
pageSize?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UserListResponse {
|
|
||||||
items: User[],
|
|
||||||
total: number
|
|
||||||
page: number
|
|
||||||
pageSize: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export function list(params?: UserListParams): Promise<UserListResponse> {
|
|
||||||
const searchParams = new URLSearchParams()
|
|
||||||
if (params?.page) searchParams.set('page', params.page.toString())
|
|
||||||
if (params?.pageSize) searchParams.set('pageSize', params.pageSize.toString())
|
|
||||||
|
|
||||||
return fetcher<UserListResponse>('/api/admin/user')
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
import fetcher from "../../fetcher";
|
|
||||||
|
|
||||||
export async function remove(userId: string, soft: boolean) {
|
|
||||||
return fetcher(`/api/admin/user/${userId}?soft=${soft}`, {
|
|
||||||
method: 'DELETE',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import fetcher from "../../fetcher";
|
|
||||||
|
|
||||||
export async function setPassword(userId: string, password: string) {
|
|
||||||
return fetcher(`/api/admin/user/${userId}/password`, {
|
|
||||||
method: 'POST',
|
|
||||||
body: JSON.stringify({
|
|
||||||
password,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import { User } from "@/lib/types/user";
|
|
||||||
import fetcher from "../../fetcher";
|
|
||||||
|
|
||||||
export type updateUser = {
|
|
||||||
username: string ;
|
|
||||||
nickname: string ;
|
|
||||||
email: string | null;
|
|
||||||
phone: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function update(userId: string, user: updateUser) {
|
|
||||||
return fetcher<User>(`/api/admin/user/${userId}`, {
|
|
||||||
body: JSON.stringify(user),
|
|
||||||
method: "PUT",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import fetcher from "@/lib/api/fetcher";
|
|
||||||
|
|
||||||
type CreateBlogParams = {
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
contentUrl: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function create(data: CreateBlogParams) {
|
|
||||||
return fetcher('/api/admin/web/blog', {
|
|
||||||
method: 'POST',
|
|
||||||
body: JSON.stringify(data)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import fetcher from "@/lib/api/fetcher";
|
|
||||||
import { Blog } from "@/lib/types/blog";
|
|
||||||
|
|
||||||
export async function get(id: string) {
|
|
||||||
return fetcher<Blog>(`/api/admin/web/blog/${id}`)
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
export * from './create';
|
|
||||||
export * from './remove';
|
|
||||||
export * from './list';
|
|
||||||
export * from './update';
|
|
||||||
export * from './get';
|
|
||||||
export * from './setPassword';
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import fetcher from "@/lib/api/fetcher";
|
|
||||||
import { Blog } from "@/lib/types/blog";
|
|
||||||
|
|
||||||
export async function list() {
|
|
||||||
return fetcher<Blog[]>('/api/admin/web/blog')
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
import fetcher from "@/lib/api/fetcher";
|
|
||||||
|
|
||||||
export async function remove(id: string) {
|
|
||||||
return fetcher(`/api/admin/web/blog/${id}`, {
|
|
||||||
method: 'DELETE',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import fetcher from "@/lib/api/fetcher";
|
|
||||||
|
|
||||||
export async function setPassword(id: string, password: string) {
|
|
||||||
return fetcher<boolean>(`/api/admin/web/blog/${id}/password`, {
|
|
||||||
method: 'POST',
|
|
||||||
body: JSON.stringify({
|
|
||||||
password,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import fetcher from "@/lib/api/fetcher";
|
|
||||||
import { BlogPermission } from "@/lib/types/Blog.Permission.enum";
|
|
||||||
|
|
||||||
type UpdateBlogParams = {
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
contentUrl: string;
|
|
||||||
permissions: BlogPermission[],
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function update(id: string, data: UpdateBlogParams) {
|
|
||||||
return fetcher(`/api/admin/web/blog/${id}`, {
|
|
||||||
method: 'PUT',
|
|
||||||
body: JSON.stringify(data)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
export * as blog from './blog/index';
|
|
||||||
export * as resource from './resource/index';
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import fetcher from "@/lib/api/fetcher";
|
|
||||||
|
|
||||||
type CreateResourceParams = {
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
imageUrl: string;
|
|
||||||
link: string;
|
|
||||||
tags: {
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
}[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function create(data: CreateResourceParams) {
|
|
||||||
return fetcher('/api/admin/web/resource', {
|
|
||||||
method: 'POST',
|
|
||||||
body: JSON.stringify(data)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import fetcher from "@/lib/api/fetcher";
|
|
||||||
import { Resource } from "@/lib/types/resource";
|
|
||||||
|
|
||||||
export async function get(id: string) {
|
|
||||||
return fetcher<Resource>(`/api/admin/web/resource/${id}`)
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
export * from './create';
|
|
||||||
export * from './remove';
|
|
||||||
export * from './list';
|
|
||||||
export * from './update';
|
|
||||||
export * from './get';
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import fetcher from "@/lib/api/fetcher";
|
|
||||||
import { Resource } from "@/lib/types/resource";
|
|
||||||
|
|
||||||
export async function list() {
|
|
||||||
return fetcher<Resource[]>('/api/admin/web/resource')
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
import fetcher from "@/lib/api/fetcher";
|
|
||||||
|
|
||||||
export async function remove(id: string) {
|
|
||||||
return fetcher(`/api/admin/web/resource/${id}`, {
|
|
||||||
method: 'DELETE',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import fetcher from "@/lib/api/fetcher";
|
|
||||||
|
|
||||||
type UpdateResourceParams = {
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
imageUrl: string;
|
|
||||||
link: string;
|
|
||||||
tags: {
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
}[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function update(id: string, data: UpdateResourceParams) {
|
|
||||||
return fetcher(`/api/admin/web/resource/${id}`, {
|
|
||||||
method: 'PUT',
|
|
||||||
body: JSON.stringify(data)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
export * from './login';
|
|
||||||
export * from './logout';
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
import fetcher, { ApiError } from "../fetcher"
|
|
||||||
|
|
||||||
interface LoginParams {
|
|
||||||
type: 'password' | 'phone' | 'email';
|
|
||||||
account?: string;
|
|
||||||
password?: string;
|
|
||||||
phone?: string;
|
|
||||||
email?: string;
|
|
||||||
code?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const login = async (data: LoginParams): Promise<{ token: string }> => {
|
|
||||||
if (data.type === 'password') {
|
|
||||||
if (!data.account || !data.password) {
|
|
||||||
throw new ApiError(400, '请输入账户和密码')
|
|
||||||
}
|
|
||||||
if (data.account.length < 1 || data.account.length > 254) {
|
|
||||||
throw new ApiError(400, '请输入正确的账户')
|
|
||||||
}
|
|
||||||
if (data.password.length < 6 || data.password.length > 32) {
|
|
||||||
throw new ApiError(400, '请输入正确的密码')
|
|
||||||
}
|
|
||||||
} else if (data.type === 'phone') {
|
|
||||||
if (!data.phone || !data.code) {
|
|
||||||
throw new ApiError(400, '请输入手机号和验证码')
|
|
||||||
}
|
|
||||||
if (data.phone.length !== 11) {
|
|
||||||
throw new ApiError(400, '请输入正确的手机号')
|
|
||||||
}
|
|
||||||
if (data.code.length != 6) {
|
|
||||||
throw new ApiError(400, '请输入正确的验证码')
|
|
||||||
}
|
|
||||||
} else if (data.type === 'email') {
|
|
||||||
if (!data.email || !data.code) {
|
|
||||||
throw new ApiError(400, '请输入邮箱和验证码')
|
|
||||||
}
|
|
||||||
if (data.email.length < 1 || data.email.length > 254) {
|
|
||||||
throw new ApiError(400, '请输入正确的邮箱')
|
|
||||||
}
|
|
||||||
if (data.code.length != 6) {
|
|
||||||
throw new ApiError(400, '请输入正确的验证码')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new ApiError(400, '登录方式异常')
|
|
||||||
}
|
|
||||||
|
|
||||||
return fetcher<{
|
|
||||||
token: string;
|
|
||||||
}>('/api/auth/login', {
|
|
||||||
method: 'POST',
|
|
||||||
body: JSON.stringify(data),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
import fetcher from "../fetcher";
|
|
||||||
|
|
||||||
export async function logout() {
|
|
||||||
return fetcher('/api/auth/logout', { method: 'POST' });
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import { BlogComment } from "@/lib/types/blogComment";
|
|
||||||
import fetcher from "../fetcher";
|
|
||||||
|
|
||||||
export async function createComment(blogId: string, content: string, parentId?: string) {
|
|
||||||
return fetcher<BlogComment>(`/api/blog/${blogId}/comment`, {
|
|
||||||
method: 'POST',
|
|
||||||
body: JSON.stringify({
|
|
||||||
content,
|
|
||||||
parentId: parentId || null,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import fetcher from "../fetcher";
|
|
||||||
|
|
||||||
export async function get(id: string, option: {
|
|
||||||
password?: string;
|
|
||||||
} = {}) {
|
|
||||||
const { password } = option;
|
|
||||||
return fetcher<{
|
|
||||||
id: string;
|
|
||||||
title: string;
|
|
||||||
createdAt: string;
|
|
||||||
content: string;
|
|
||||||
}>(`/api/blog/${id}` + (password ? `?p=${password}` : ''));
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import { BlogComment } from "@/lib/types/blogComment";
|
|
||||||
import fetcher from "../fetcher";
|
|
||||||
|
|
||||||
export async function getComments(blogId: string) {
|
|
||||||
return fetcher<BlogComment[]>(`/api/blog/${blogId}/comments`, { method: 'GET' });
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
export * from './list';
|
|
||||||
export * from './get';
|
|
||||||
export * from './getComments';
|
|
||||||
export * from './createComment';
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import { Blog } from "@/lib/types/blog";
|
|
||||||
import { apiFetch } from "../client";
|
|
||||||
|
|
||||||
export async function list() {
|
|
||||||
return apiFetch<Blog[]>('/api/blog');
|
|
||||||
}
|
|
||||||
@@ -1,34 +1,48 @@
|
|||||||
// import { headers } from 'next/headers'
|
import { APIResponse, HttpMethod, normalizeAPIError } from './common';
|
||||||
|
|
||||||
export async function apiFetch<T extends unknown>(
|
interface ClientFetchRequestOptions extends RequestInit {
|
||||||
url: string,
|
method?: HttpMethod;
|
||||||
options: RequestInit = {}
|
body?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function clientFetch<T = unknown>(
|
||||||
|
endpoint: string,
|
||||||
|
options: ClientFetchRequestOptions = {}
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
// const nextHeaders = await headers()
|
|
||||||
|
|
||||||
const defaultHeaders: HeadersInit = {
|
const defaultHeaders: HeadersInit = {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
// ...nextHeaders,
|
};
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch(new URL(url, process.env.API_BASE), {
|
const response = await fetch(endpoint, {
|
||||||
...options,
|
method: options.method || 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
...defaultHeaders,
|
...defaultHeaders,
|
||||||
...options.headers,
|
...options.headers,
|
||||||
},
|
},
|
||||||
cache: 'no-store',
|
body: options.body ?? JSON.stringify(options.body),
|
||||||
|
credentials: 'include',
|
||||||
|
...options,
|
||||||
});
|
});
|
||||||
if (res.status === 200) {
|
|
||||||
const data = await res.json();
|
if (!response.ok) {
|
||||||
if (data.statusCode === 200) {
|
const errorText = await response.text();
|
||||||
return data.data;
|
throw JSON.parse(errorText);
|
||||||
}
|
}
|
||||||
throw new Error(data.message ?? '未知错误');
|
|
||||||
|
const data: APIResponse<T> = await response.json();
|
||||||
|
|
||||||
|
if (!data.success) {
|
||||||
|
throw data;
|
||||||
}
|
}
|
||||||
throw new Error('请求失败');
|
|
||||||
|
return data.data as T;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error;
|
normalizeAPIError(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export * as AuthAPI from './endpoints/auth.client'
|
||||||
67
apps/frontend/lib/api/common.ts
Normal file
67
apps/frontend/lib/api/common.ts
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
||||||
|
|
||||||
|
export interface APIResponse<T = unknown> {
|
||||||
|
success: boolean;
|
||||||
|
code: number;
|
||||||
|
message: string;
|
||||||
|
data: T;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class APIError extends Error {
|
||||||
|
constructor(
|
||||||
|
message: string,
|
||||||
|
public status: number = 400,
|
||||||
|
public code: number = -1,
|
||||||
|
public data: unknown = null
|
||||||
|
) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeAPIError(error: unknown): never {
|
||||||
|
if (error instanceof APIError) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error instanceof Error) {
|
||||||
|
throw new APIError(
|
||||||
|
error.message || '未知错误',
|
||||||
|
400,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof error === 'object' && error !== null) {
|
||||||
|
const { message, status, code, data } = {
|
||||||
|
message: '未知错误',
|
||||||
|
status: 400,
|
||||||
|
code: -1,
|
||||||
|
data: null,
|
||||||
|
...error
|
||||||
|
};
|
||||||
|
|
||||||
|
throw new APIError(
|
||||||
|
message,
|
||||||
|
status,
|
||||||
|
code,
|
||||||
|
data
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new APIError((error instanceof Error ? `${error.message}` : '') || '未知错误', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function handleAPIError(error: unknown, handler: (e: APIError) => void): void {
|
||||||
|
if (error instanceof APIError) {
|
||||||
|
return handler(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
normalizeAPIError(error)
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof APIError) {
|
||||||
|
return handler(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
27
apps/frontend/lib/api/endpoints/auth.client.ts
Normal file
27
apps/frontend/lib/api/endpoints/auth.client.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { User } from "@/lib/types/user";
|
||||||
|
import { clientFetch } from "../client";
|
||||||
|
import { APIError } from "../common";
|
||||||
|
|
||||||
|
export async function loginByPassword(identifier: string, password: string) {
|
||||||
|
identifier = identifier.trim();
|
||||||
|
password = identifier.trim();
|
||||||
|
if (identifier.length === 0 || password.length === 0) {
|
||||||
|
throw new APIError('请输入账户和密码')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (identifier.length < 1 || identifier.length > 254) {
|
||||||
|
throw new APIError('账户长度只能为1~254位')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (password.length < 6 || password.length > 32) {
|
||||||
|
throw new APIError('密码长度只能为6~32位')
|
||||||
|
}
|
||||||
|
|
||||||
|
return clientFetch<User>('/api/auth/login/password', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
identifier,
|
||||||
|
password,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
5
apps/frontend/lib/api/endpoints/blog.server.ts
Normal file
5
apps/frontend/lib/api/endpoints/blog.server.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { serverFetch } from "../server";
|
||||||
|
|
||||||
|
export async function list() {
|
||||||
|
return serverFetch('/api/blog')
|
||||||
|
}
|
||||||
19
apps/frontend/lib/api/endpoints/resource.server.ts
Normal file
19
apps/frontend/lib/api/endpoints/resource.server.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { serverFetch } from "../server";
|
||||||
|
|
||||||
|
export type ResourceTagType = {
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Resource {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
imageUrl: string;
|
||||||
|
link: string;
|
||||||
|
tags: ResourceTagType[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function list() {
|
||||||
|
return serverFetch<Resource[]>('/api/resource')
|
||||||
|
}
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
export interface StanderResponse<T> {
|
|
||||||
statusCode: number;
|
|
||||||
message: string;
|
|
||||||
data?: T;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ApiError extends Error {
|
|
||||||
constructor(
|
|
||||||
public statusCode: number,
|
|
||||||
public message: string,
|
|
||||||
public data?: unknown,
|
|
||||||
) {
|
|
||||||
super(message);
|
|
||||||
this.name = 'ApiError';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fetcher = async<T>(url: string, options?: RequestInit): Promise<T> => {
|
|
||||||
const res = await fetch(url, {
|
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
// 自动带上 token
|
|
||||||
...(typeof window !== 'undefined' && localStorage.getItem('token')
|
|
||||||
? { Authorization: `Bearer ${localStorage.getItem('token')}` }
|
|
||||||
: {}),
|
|
||||||
},
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = await res.json();
|
|
||||||
if (result.statusCode !== 200) {
|
|
||||||
throw new ApiError(result.statusCode, result.message, result.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.data as T;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default fetcher
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
export * as authApi from './auth/index';
|
|
||||||
export * as verificationApi from './verification/index';
|
|
||||||
export * as AdminApi from './admin/index';
|
|
||||||
export * as ResourceApi from './resource/index';
|
|
||||||
export * as BlogApi from './blog/index';
|
|
||||||
export * as UserApi from './user/index';
|
|
||||||
export * as OssApi from './oss/index';
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import fetcher from "../fetcher";
|
|
||||||
|
|
||||||
export interface StsToken {
|
|
||||||
AccessKeyId: string;
|
|
||||||
AccessKeySecret: string;
|
|
||||||
Expiration: string;// ISO 8601 格式
|
|
||||||
SecurityToken: string;
|
|
||||||
userId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getStsToken() {
|
|
||||||
return fetcher<StsToken>('/api/oss/sts', { method: 'GET' });
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from './list';
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import { Resource } from "@/lib/types/resource";
|
|
||||||
import { apiFetch } from "../client";
|
|
||||||
|
|
||||||
export async function list() {
|
|
||||||
return apiFetch<Resource[]>('/api/resource');
|
|
||||||
}
|
|
||||||
59
apps/frontend/lib/api/server.ts
Normal file
59
apps/frontend/lib/api/server.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { cookies, headers } from 'next/headers';
|
||||||
|
import { APIResponse, HttpMethod, normalizeAPIError } from './common';
|
||||||
|
|
||||||
|
interface ServerFetchRequestOptions extends RequestInit {
|
||||||
|
method?: HttpMethod;
|
||||||
|
body?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function serverFetch<T = unknown>(
|
||||||
|
endpoint: string,
|
||||||
|
options: ServerFetchRequestOptions = {}
|
||||||
|
): Promise<T> {
|
||||||
|
const cookieStore = await cookies();
|
||||||
|
const cookieHeader = cookieStore.toString();
|
||||||
|
|
||||||
|
const reqHeaders = new Headers(await headers());
|
||||||
|
const forwardedHeaders: Record<string, string> = {};
|
||||||
|
['user-agent', 'x-forwarded-for', 'x-real-ip'].forEach((key) => {
|
||||||
|
const value = reqHeaders.get(key);
|
||||||
|
if (value) forwardedHeaders[key] = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
const defaultHeaders: HeadersInit = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
...(cookieHeader ? { Cookie: cookieHeader } : {}),
|
||||||
|
...forwardedHeaders,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(new URL(endpoint, process.env.API_BASE).href, {
|
||||||
|
method: options.method || 'GET',
|
||||||
|
headers: {
|
||||||
|
...defaultHeaders,
|
||||||
|
...options.headers,
|
||||||
|
},
|
||||||
|
body: options.body ?? JSON.stringify(options.body),
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorText = await response.text();
|
||||||
|
throw JSON.parse(errorText);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data: APIResponse<T> = await response.json();
|
||||||
|
if (!data.success) {
|
||||||
|
throw data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.data as T;
|
||||||
|
} catch (error) {
|
||||||
|
normalizeAPIError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export * as BlogAPI from './endpoints/blog.server';
|
||||||
|
export * as ResourceAPI from './endpoints/resource.server';
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
export * from './me';
|
|
||||||
export * from './updatePassword';
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
import { User } from "@/lib/types/user";
|
|
||||||
import fetcher from "../fetcher";
|
|
||||||
|
|
||||||
export async function me() {
|
|
||||||
return fetcher<User>('/api/user/me');
|
|
||||||
}
|
|
||||||
|
|
||||||
export const USER_ME_CACHE_KEY = 'user-me-cache';
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import fetcher from "../fetcher";
|
|
||||||
|
|
||||||
export async function updatePassword(password: string) {
|
|
||||||
return fetcher(`/api/user/password`, {
|
|
||||||
method: 'PUT',
|
|
||||||
body: JSON.stringify({
|
|
||||||
password: password,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from './send';
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import fetcher from "../fetcher";
|
|
||||||
|
|
||||||
interface SendVerificationCodeParam {
|
|
||||||
targetType: 'phone' | 'email';
|
|
||||||
type: 'login';
|
|
||||||
phone?: string;
|
|
||||||
email?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const send = async (data: SendVerificationCodeParam) => {
|
|
||||||
return fetcher<boolean>('/api/verification/send', {
|
|
||||||
method: 'POST',
|
|
||||||
body: JSON.stringify(data),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user