diff --git a/package-lock.json b/package-lock.json index 7d331b9..7b7dbef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@svgdotjs/svg.js": "^3.2.4", + "@tanstack/vue-table": "^8.21.3", "@types/lodash": "^4.17.16", "@vueuse/core": "^13.5.0", "async-mutex": "^0.5.0", @@ -1743,6 +1744,19 @@ "tailwindcss": "4.1.4" } }, + "node_modules/@tanstack/table-core": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.21.3.tgz", + "integrity": "sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@tanstack/virtual-core": { "version": "3.13.12", "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.12.tgz", @@ -1753,6 +1767,25 @@ "url": "https://github.com/sponsors/tannerlinsley" } }, + "node_modules/@tanstack/vue-table": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/@tanstack/vue-table/-/vue-table-8.21.3.tgz", + "integrity": "sha512-rusRyd77c5tDPloPskctMyPLFEQUeBzxdQ+2Eow4F7gDPlPOB1UnnhzfpdvqZ8ZyX2rRNGmqNnQWm87OI2OQPw==", + "license": "MIT", + "dependencies": { + "@tanstack/table-core": "8.21.3" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "vue": ">=3.2" + } + }, "node_modules/@tanstack/vue-virtual": { "version": "3.13.12", "resolved": "https://registry.npmjs.org/@tanstack/vue-virtual/-/vue-virtual-3.13.12.tgz", diff --git a/package.json b/package.json index 06d1c34..bcb73c6 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ }, "dependencies": { "@svgdotjs/svg.js": "^3.2.4", + "@tanstack/vue-table": "^8.21.3", "@types/lodash": "^4.17.16", "@vueuse/core": "^13.5.0", "async-mutex": "^0.5.0", diff --git a/server/Program.cs b/server/Program.cs index bb45f6c..3d519c5 100644 --- a/server/Program.cs +++ b/server/Program.cs @@ -62,6 +62,7 @@ try options.Authority = "http://localhost:5000"; options.RequireHttpsMetadata = false; }); + // Add JWT Token Authorization Policy builder.Services.AddAuthorization(options => { options.AddPolicy("Admin", policy => diff --git a/server/src/Controllers/DataController.cs b/server/src/Controllers/DataController.cs index 1a895c9..da1f4df 100644 --- a/server/src/Controllers/DataController.cs +++ b/server/src/Controllers/DataController.cs @@ -1,4 +1,5 @@ using System.IdentityModel.Tokens.Jwt; +using System.Net; using System.Security.Claims; using System.Text; using Microsoft.AspNetCore.Authorization; @@ -17,7 +18,10 @@ public class DataController : ControllerBase { private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); - public class GetUserInfoResponse + /// + /// [TODO:description] + /// + public class UserInfo { /// /// 用户的唯一标识符 @@ -118,7 +122,7 @@ public class DataController : ControllerBase [Authorize] [HttpGet("GetUserInfo")] [EnableCors("Users")] - [ProducesResponseType(typeof(GetUserInfoResponse), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(UserInfo), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] @@ -139,7 +143,7 @@ public class DataController : ControllerBase return BadRequest("用户不存在"); var user = ret.Value.Value; - return Ok(new GetUserInfoResponse + return Ok(new UserInfo { ID = user.ID, Name = user.Name, @@ -187,5 +191,85 @@ public class DataController : ControllerBase return StatusCode(StatusCodes.Status500InternalServerError, "注册失败,请稍后重试"); } } + + /// + /// 新增板子(管理员权限) + /// + [Authorize("Admin")] + [HttpPost("AddBoard")] + [EnableCors("Users")] + [ProducesResponseType(typeof(int), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public IActionResult AddBoard(string name, string ipAddr, int port) + { + if (string.IsNullOrWhiteSpace(name)) + return BadRequest("板子名称不能为空"); + if (string.IsNullOrWhiteSpace(ipAddr)) + return BadRequest("IP地址不能为空"); + if (port <= 0 || port > 65535) + return BadRequest("端口号不合法"); + try + { + using var db = new Database.AppDataConnection(); + var ret = db.AddBoard(name, ipAddr, port); + return Ok(ret); + } + catch (Exception ex) + { + logger.Error(ex, "新增板子时发生异常"); + return StatusCode(StatusCodes.Status500InternalServerError, "新增失败,请稍后重试"); + } + } + + /// + /// 删除板子(管理员权限) + /// + [Authorize("Admin")] + [HttpDelete("DeleteBoard")] + [EnableCors("Users")] + [ProducesResponseType(typeof(int), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public IActionResult DeleteBoard(Guid id) + { + if (id == Guid.Empty) + return BadRequest("板子Guid不能为空"); + + try + { + using var db = new Database.AppDataConnection(); + var ret = db.DeleteBoardByID(id); + return Ok(ret); + } + catch (Exception ex) + { + logger.Error(ex, "删除板子时发生异常"); + return StatusCode(StatusCodes.Status500InternalServerError, "删除失败,请稍后重试"); + } + } + + /// + /// 获取全部板子(管理员权限) + /// + [Authorize("Admin")] + [HttpGet("GetAllBoards")] + [EnableCors("Users")] + [ProducesResponseType(typeof(Database.Board[]), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public IActionResult GetAllBoards() + { + try + { + using var db = new Database.AppDataConnection(); + var boards = db.GetAllBoard(); + return Ok(boards); + } + catch (Exception ex) + { + logger.Error(ex, "获取全部板子时发生异常"); + return StatusCode(StatusCodes.Status500InternalServerError, "获取失败,请稍后重试"); + } + } } diff --git a/server/src/Controllers/RemoteUpdateController.cs b/server/src/Controllers/RemoteUpdateController.cs index a296a1a..5d51d6b 100644 --- a/server/src/Controllers/RemoteUpdateController.cs +++ b/server/src/Controllers/RemoteUpdateController.cs @@ -1,4 +1,5 @@ using DotNext; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Mvc; @@ -24,6 +25,7 @@ public class RemoteUpdateController : ControllerBase /// 比特流文件2 /// 比特流文件3 /// 上传结果 + [Authorize("Admin")] [HttpPost("UploadBitstream")] [EnableCors("Users")] [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)] @@ -129,6 +131,7 @@ public class RemoteUpdateController : ControllerBase /// 设备地址 /// 设备端口 /// 比特流位号 + [Authorize("Admin")] [HttpPost("DownloadBitstream")] [EnableCors("Users")] [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)] @@ -179,6 +182,7 @@ public class RemoteUpdateController : ControllerBase /// 设备端口 /// 比特流编号 /// 总共上传比特流的数量 + [Authorize("Admin")] [HttpPost("DownloadMultiBitstreams")] [EnableCors("Users")] [ProducesResponseType(typeof(int), StatusCodes.Status200OK)] @@ -239,6 +243,7 @@ public class RemoteUpdateController : ControllerBase /// 设备端口 /// 比特流编号 /// 操作结果 + [Authorize("Admin")] [HttpPost("HotResetBitstream")] [EnableCors("Users")] [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)] @@ -267,6 +272,7 @@ public class RemoteUpdateController : ControllerBase /// [TODO:parameter] /// [TODO:parameter] /// [TODO:return] + [Authorize("Admin")] [HttpPost("GetFirmwareVersion")] [EnableCors("Users")] [ProducesResponseType(typeof(UInt32), StatusCodes.Status200OK)] diff --git a/server/src/Database.cs b/server/src/Database.cs index b1f95da..7589ba4 100644 --- a/server/src/Database.cs +++ b/server/src/Database.cs @@ -98,6 +98,12 @@ public class Board [NotNull] public required BoardStatus Status { get; set; } + /// + /// [TODO:description] + /// + [NotNull] + public string FirmVersion { get; set; } = "1.0.0"; + /// /// [TODO:description] /// @@ -273,6 +279,35 @@ public class AppDataConnection : DataConnection return this.Insert(board); } + /// + /// [TODO:description] + /// + /// [TODO:parameter] + /// [TODO:return] + public int DeleteBoardByName(string name) + { + return this.Board.Where(board => board.BoardName == name).Delete(); + } + + /// + /// [TODO:description] + /// + /// [TODO:parameter] + /// [TODO:return] + public int DeleteBoardByID(Guid id) + { + return this.Board.Where(board => board.ID == id).Delete(); + } + + /// + /// [TODO:description] + /// + /// [TODO:return] + public Board[] GetAllBoard() + { + return this.Board.ToArray(); + } + /// /// [TODO:description] /// diff --git a/src/APIClient.ts b/src/APIClient.ts index 19ac4a5..55c32ed 100644 --- a/src/APIClient.ts +++ b/src/APIClient.ts @@ -482,11 +482,57 @@ export class DataClient { return Promise.resolve(null as any); } + /** + * 测试管理员用户认证,需携带有效 JWT + * @return 认证成功信息 + */ + testAdminAuth(): Promise { + let url_ = this.baseUrl + "/api/Data/TestAdminAuth"; + url_ = url_.replace(/[?&]$/, ""); + + let options_: RequestInit = { + method: "GET", + headers: { + "Accept": "application/json" + } + }; + + return this.http.fetch(url_, options_).then((_response: Response) => { + return this.processTestAdminAuth(_response); + }); + } + + protected processTestAdminAuth(response: Response): Promise { + const status = response.status; + let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); }; + if (status === 200) { + return response.text().then((_responseText) => { + let result200: any = null; + let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver); + result200 = resultData200 !== undefined ? resultData200 : null; + + return result200; + }); + } else if (status === 401) { + return response.text().then((_responseText) => { + let result401: any = null; + let resultData401 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver); + result401 = ProblemDetails.fromJS(resultData401); + return throwException("A server side error occurred.", status, _responseText, _headers, result401); + }); + } else if (status !== 200 && status !== 204) { + return response.text().then((_responseText) => { + return throwException("An unexpected server error occurred.", status, _responseText, _headers); + }); + } + return Promise.resolve(null as any); + } + /** * 获取当前用户信息 * @return 用户信息,包括ID、用户名、邮箱和板卡ID */ - getUserInfo(): Promise { + getUserInfo(): Promise { let url_ = this.baseUrl + "/api/Data/GetUserInfo"; url_ = url_.replace(/[?&]$/, ""); @@ -502,14 +548,14 @@ export class DataClient { }); } - protected processGetUserInfo(response: Response): Promise { + protected processGetUserInfo(response: Response): Promise { const status = response.status; let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); }; if (status === 200) { return response.text().then((_responseText) => { let result200: any = null; let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver); - result200 = GetUserInfoResponse.fromJS(resultData200); + result200 = UserInfo.fromJS(resultData200); return result200; }); } else if (status === 400) { @@ -535,7 +581,7 @@ export class DataClient { return throwException("An unexpected server error occurred.", status, _responseText, _headers); }); } - return Promise.resolve(null as any); + return Promise.resolve(null as any); } /** @@ -602,6 +648,172 @@ export class DataClient { } return Promise.resolve(null as any); } + + /** + * 新增板子(管理员权限) + * @param name (optional) + * @param ipAddr (optional) + * @param port (optional) + */ + addBoard(name: string | undefined, ipAddr: string | undefined, port: number | undefined): Promise { + let url_ = this.baseUrl + "/api/Data/AddBoard?"; + if (name === null) + throw new Error("The parameter 'name' cannot be null."); + else if (name !== undefined) + url_ += "name=" + encodeURIComponent("" + name) + "&"; + if (ipAddr === null) + throw new Error("The parameter 'ipAddr' cannot be null."); + else if (ipAddr !== undefined) + url_ += "ipAddr=" + encodeURIComponent("" + ipAddr) + "&"; + if (port === null) + throw new Error("The parameter 'port' cannot be null."); + else if (port !== undefined) + url_ += "port=" + encodeURIComponent("" + port) + "&"; + url_ = url_.replace(/[?&]$/, ""); + + let options_: RequestInit = { + method: "POST", + headers: { + "Accept": "application/json" + } + }; + + return this.http.fetch(url_, options_).then((_response: Response) => { + return this.processAddBoard(_response); + }); + } + + protected processAddBoard(response: Response): Promise { + const status = response.status; + let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); }; + if (status === 200) { + return response.text().then((_responseText) => { + let result200: any = null; + let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver); + result200 = resultData200 !== undefined ? resultData200 : null; + + return result200; + }); + } else if (status === 400) { + return response.text().then((_responseText) => { + let result400: any = null; + let resultData400 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver); + result400 = ProblemDetails.fromJS(resultData400); + return throwException("A server side error occurred.", status, _responseText, _headers, result400); + }); + } else if (status === 500) { + return response.text().then((_responseText) => { + return throwException("A server side error occurred.", status, _responseText, _headers); + }); + } else if (status !== 200 && status !== 204) { + return response.text().then((_responseText) => { + return throwException("An unexpected server error occurred.", status, _responseText, _headers); + }); + } + return Promise.resolve(null as any); + } + + /** + * 删除板子(管理员权限) + * @param id (optional) + */ + deleteBoard(id: string | undefined): Promise { + let url_ = this.baseUrl + "/api/Data/DeleteBoard?"; + if (id === null) + throw new Error("The parameter 'id' cannot be null."); + else if (id !== undefined) + url_ += "id=" + encodeURIComponent("" + id) + "&"; + url_ = url_.replace(/[?&]$/, ""); + + let options_: RequestInit = { + method: "DELETE", + headers: { + "Accept": "application/json" + } + }; + + return this.http.fetch(url_, options_).then((_response: Response) => { + return this.processDeleteBoard(_response); + }); + } + + protected processDeleteBoard(response: Response): Promise { + const status = response.status; + let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); }; + if (status === 200) { + return response.text().then((_responseText) => { + let result200: any = null; + let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver); + result200 = resultData200 !== undefined ? resultData200 : null; + + return result200; + }); + } else if (status === 400) { + return response.text().then((_responseText) => { + let result400: any = null; + let resultData400 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver); + result400 = ProblemDetails.fromJS(resultData400); + return throwException("A server side error occurred.", status, _responseText, _headers, result400); + }); + } else if (status === 500) { + return response.text().then((_responseText) => { + return throwException("A server side error occurred.", status, _responseText, _headers); + }); + } else if (status !== 200 && status !== 204) { + return response.text().then((_responseText) => { + return throwException("An unexpected server error occurred.", status, _responseText, _headers); + }); + } + return Promise.resolve(null as any); + } + + /** + * 获取全部板子(管理员权限) + */ + getAllBoards(): Promise { + let url_ = this.baseUrl + "/api/Data/GetAllBoards"; + url_ = url_.replace(/[?&]$/, ""); + + let options_: RequestInit = { + method: "GET", + headers: { + "Accept": "application/json" + } + }; + + return this.http.fetch(url_, options_).then((_response: Response) => { + return this.processGetAllBoards(_response); + }); + } + + protected processGetAllBoards(response: Response): Promise { + const status = response.status; + let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); }; + if (status === 200) { + return response.text().then((_responseText) => { + let result200: any = null; + let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver); + if (Array.isArray(resultData200)) { + result200 = [] as any; + for (let item of resultData200) + result200!.push(Board.fromJS(item)); + } + else { + result200 = null; + } + return result200; + }); + } else if (status === 500) { + return response.text().then((_responseText) => { + return throwException("A server side error occurred.", status, _responseText, _headers); + }); + } else if (status !== 200 && status !== 204) { + return response.text().then((_responseText) => { + return throwException("An unexpected server error occurred.", status, _responseText, _headers); + }); + } + return Promise.resolve(null as any); + } } export class DDSClient { @@ -2445,7 +2657,8 @@ export interface IProblemDetails { [key: string]: any; } -export class GetUserInfoResponse implements IGetUserInfoResponse { +/** [TODO:description] */ +export class UserInfo implements IUserInfo { /** 用户的唯一标识符 */ id!: string; /** 用户的名称 */ @@ -2455,7 +2668,7 @@ export class GetUserInfoResponse implements IGetUserInfoResponse { /** 用户关联的板卡ID */ boardID!: string; - constructor(data?: IGetUserInfoResponse) { + constructor(data?: IUserInfo) { if (data) { for (var property in data) { if (data.hasOwnProperty(property)) @@ -2473,9 +2686,9 @@ export class GetUserInfoResponse implements IGetUserInfoResponse { } } - static fromJS(data: any): GetUserInfoResponse { + static fromJS(data: any): UserInfo { data = typeof data === 'object' ? data : {}; - let result = new GetUserInfoResponse(); + let result = new UserInfo(); result.init(data); return result; } @@ -2490,7 +2703,8 @@ export class GetUserInfoResponse implements IGetUserInfoResponse { } } -export interface IGetUserInfoResponse { +/** [TODO:description] */ +export interface IUserInfo { /** 用户的唯一标识符 */ id: string; /** 用户的名称 */ @@ -2501,6 +2715,82 @@ export interface IGetUserInfoResponse { boardID: string; } +/** FPGA 板子类,表示板子信息 */ +export class Board implements IBoard { + /** FPGA 板子的唯一标识符 */ + id!: string; + /** FPGA 板子的名称 */ + boardName!: string; + /** [TODO:description] */ + ipAddr!: string; + /** [TODO:description] */ + port!: number; + /** [TODO:description] */ + status!: BoardStatus; + /** [TODO:description] */ + firmVersion!: string; + + constructor(data?: IBoard) { + if (data) { + for (var property in data) { + if (data.hasOwnProperty(property)) + (this)[property] = (data)[property]; + } + } + } + + init(_data?: any) { + if (_data) { + this.id = _data["id"]; + this.boardName = _data["boardName"]; + this.ipAddr = _data["ipAddr"]; + this.port = _data["port"]; + this.status = _data["status"]; + this.firmVersion = _data["firmVersion"]; + } + } + + static fromJS(data: any): Board { + data = typeof data === 'object' ? data : {}; + let result = new Board(); + result.init(data); + return result; + } + + toJSON(data?: any) { + data = typeof data === 'object' ? data : {}; + data["id"] = this.id; + data["boardName"] = this.boardName; + data["ipAddr"] = this.ipAddr; + data["port"] = this.port; + data["status"] = this.status; + data["firmVersion"] = this.firmVersion; + return data; + } +} + +/** FPGA 板子类,表示板子信息 */ +export interface IBoard { + /** FPGA 板子的唯一标识符 */ + id: string; + /** FPGA 板子的名称 */ + boardName: string; + /** [TODO:description] */ + ipAddr: string; + /** [TODO:description] */ + port: number; + /** [TODO:description] */ + status: BoardStatus; + /** [TODO:description] */ + firmVersion: string; +} + +/** [TODO:description] */ +export enum BoardStatus { + Busy = 0, + Available = 1, +} + export class SystemException extends Exception implements ISystemException { constructor(data?: ISystemException) { diff --git a/src/utils/AuthManager.ts b/src/utils/AuthManager.ts index 0663530..fd2a531 100644 --- a/src/utils/AuthManager.ts +++ b/src/utils/AuthManager.ts @@ -1,93 +1,122 @@ -import { DataClient } from '@/APIClient' +import { DataClient } from "@/APIClient"; export class AuthManager { // 存储token到localStorage public static setToken(token: string): void { - localStorage.setItem('authToken', token) + localStorage.setItem("authToken", token); } - + // 从localStorage获取token public static getToken(): string | null { - return localStorage.getItem('authToken') + return localStorage.getItem("authToken"); } - + // 清除token public static clearToken(): void { - localStorage.removeItem('authToken') + localStorage.removeItem("authToken"); } - + // 检查是否已认证 public static async isAuthenticated(): Promise { - return await AuthManager.verifyToken() + return await AuthManager.verifyToken(); } - + // 为HTTP请求添加Authorization header public static addAuthHeader(client: any): void { - const token = AuthManager.getToken() + const token = AuthManager.getToken(); if (token && client.http) { - const originalFetch = client.http.fetch + const originalFetch = client.http.fetch; client.http.fetch = (url: RequestInfo, init?: RequestInit) => { - if (!init) init = {} - if (!init.headers) init.headers = {} - + if (!init) init = {}; + if (!init.headers) init.headers = {}; + // 添加Authorization header - if (typeof init.headers === 'object' && init.headers !== null) { - (init.headers as any)['Authorization'] = `Bearer ${token}` + if (typeof init.headers === "object" && init.headers !== null) { + (init.headers as any)["Authorization"] = `Bearer ${token}`; } - - return originalFetch(url, init) - } + + return originalFetch(url, init); + }; } } - + // 创建已配置认证的API客户端 public static createAuthenticatedClient(): DataClient { - const client = new DataClient() - AuthManager.addAuthHeader(client) - return client + const client = new DataClient(); + AuthManager.addAuthHeader(client); + return client; } - + // 登录函数 - public static async login(username: string, password: string): Promise { + public static async login( + username: string, + password: string, + ): Promise { try { - const client = new DataClient() - const token = await client.login(username, password) - + const client = new DataClient(); + const token = await client.login(username, password); + if (token) { - AuthManager.setToken(token) - + AuthManager.setToken(token); + // 验证token - const authClient = AuthManager.createAuthenticatedClient() - await authClient.testAuth() - - return true + const authClient = AuthManager.createAuthenticatedClient(); + await authClient.testAuth(); + + return true; } - return false + return false; } catch (error) { - AuthManager.clearToken() - throw error + AuthManager.clearToken(); + throw error; } } - + // 登出函数 public static logout(): void { - AuthManager.clearToken() + AuthManager.clearToken(); } - + // 验证当前token是否有效 public static async verifyToken(): Promise { try { - const token = AuthManager.getToken() + const token = AuthManager.getToken(); if (!token) { - return false + return false; } - - const client = AuthManager.createAuthenticatedClient() - await client.testAuth() - return true + + const client = AuthManager.createAuthenticatedClient(); + await client.testAuth(); + return true; } catch (error) { - AuthManager.clearToken() - return false + AuthManager.clearToken(); + return false; } } -} \ No newline at end of file + + // 验证管理员权限 + public static async verifyAdminAuth(): Promise { + try { + const token = AuthManager.getToken(); + if (!token) { + return false; + } + + const client = AuthManager.createAuthenticatedClient(); + await client.testAdminAuth(); + return true; + } catch (error) { + // 如果是401错误,说明token有效但不是管理员,不需要清除token + // 如果是其他错误,可能token无效,清除token + if ( + error && + typeof error === "object" && + "status" in error && + error.status !== 401 + ) { + AuthManager.clearToken(); + } + return false; + } + } +} diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue index 2c61551..482f5a4 100644 --- a/src/views/HomeView.vue +++ b/src/views/HomeView.vue @@ -1,19 +1,27 @@