添加账号修改,后台管理

This commit is contained in:
yoyuzh
2026-03-19 17:52:58 +08:00
parent c39fde6b19
commit ff8d47f44f
60 changed files with 4264 additions and 58 deletions

View File

@@ -1,6 +1,7 @@
import React, { createContext, useContext, useEffect, useState } from 'react';
import { apiRequest } from '@/src/lib/api';
import { fetchAdminAccessStatus } from './admin-access';
import {
clearStoredSession,
createSession,
@@ -19,6 +20,7 @@ interface AuthContextValue {
ready: boolean;
session: AuthSession | null;
user: UserProfile | null;
isAdmin: boolean;
login: (payload: LoginPayload) => Promise<void>;
devLogin: (username?: string) => Promise<void>;
logout: () => void;
@@ -34,6 +36,7 @@ function buildSession(auth: AuthResponse): AuthSession {
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [session, setSession] = useState<AuthSession | null>(() => readStoredSession());
const [ready, setReady] = useState(false);
const [isAdmin, setIsAdmin] = useState(false);
useEffect(() => {
const syncSession = () => {
@@ -93,6 +96,36 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
};
}, []);
useEffect(() => {
let active = true;
async function syncAdminAccess() {
if (!session?.token) {
if (active) {
setIsAdmin(false);
}
return;
}
try {
const allowed = await fetchAdminAccessStatus();
if (active) {
setIsAdmin(allowed);
}
} catch {
if (active) {
setIsAdmin(false);
}
}
}
syncAdminAccess();
return () => {
active = false;
};
}, [session?.token]);
async function refreshProfile() {
const currentSession = readStoredSession();
if (!currentSession) {
@@ -146,6 +179,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
ready,
session,
user: session?.user || null,
isAdmin,
login,
devLogin,
logout,

View File

@@ -0,0 +1,28 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { ApiError } from '@/src/lib/api';
import { fetchAdminAccessStatus } from './admin-access';
test('fetchAdminAccessStatus returns true when the admin summary request succeeds', async () => {
const request = async () => ({
totalUsers: 1,
totalFiles: 2,
usersWithSchoolCache: 3,
});
await assert.doesNotReject(async () => {
const allowed = await fetchAdminAccessStatus(request);
assert.equal(allowed, true);
});
});
test('fetchAdminAccessStatus returns false when the server rejects the user with 403', async () => {
const request = async () => {
throw new ApiError('没有后台权限', 403);
};
const allowed = await fetchAdminAccessStatus(request);
assert.equal(allowed, false);
});

View File

@@ -0,0 +1,19 @@
import { ApiError, apiRequest } from '@/src/lib/api';
import type { AdminSummary } from '@/src/lib/types';
type AdminSummaryRequest = () => Promise<AdminSummary>;
export async function fetchAdminAccessStatus(
request: AdminSummaryRequest = () => apiRequest<AdminSummary>('/admin/summary'),
) {
try {
await request();
return true;
} catch (error) {
if (error instanceof ApiError && error.status === 403) {
return false;
}
throw error;
}
}