Repository: BingyanStudio/hust-sdk
Pull request #3: fix: 修复登录校验逻辑
Author: login: wangyinyuan
Created: 2025-05-30T16:48:43Z | Merged: 2025-05-31T11:44:50Z

This pull request introduces several improvements and refactors to the authentication and client handling logic in the `auth`, `clients`, and `utils` modules. Key changes include the removal of redundant methods, the addition of more granular login status checks, and enhancements to error handling. These updates aim to streamline the codebase and improve maintainability.

### Authentication Enhancements

* Removed redundant methods (`testHUBS`, `testPETYXY`, `testONE`) from `CASAuth` as they were no longer needed.
* Added new methods (`checkPETYXY`, `checkHUBS`, `checkONE`) in `CASAuth` to provide granular login status checks for specific services. These methods use the `isNeedAuth` utility to determine login status based on HTTP responses.

### Client Handling Improvements

* Updated the `HUST` class to dynamically determine the list of clients (`Object.values(Client)`) and added a private method `checkClient` to check the login status of specific clients. [[1]](diffhunk://#diff-dcdc3e0b3362edb8fec2a51d3fa51f8fb8af8f70247e06d9887fa934834c9122L21-R23) [[2]](diffhunk://#diff-dcdc3e0b3362edb8fec2a51d3fa51f8fb8af8f70247e06d9887fa934834c9122L159-R201)
* Modified the `isLoggedIn` method in `HUST` to check the login status of all clients in parallel using `Promise.all`.

### Error Handling Enhancements

* Improved error handling in `handleRequest` by ensuring only `AxiosError` instances are processed automatically, while other error types are logged and re-thrown. [[1]](diffhunk://#diff-dcdc3e0b3362edb8fec2a51d3fa51f8fb8af8f70247e06d9887fa934834c9122R220-R223) [[2]](diffhunk://#diff-dcdc3e0b3362edb8fec2a51d3fa51f8fb8af8f70247e06d9887fa934834c9122R233-R241)
* Added a new utility function `isNeedAuth` in `utils/request.ts` to determine if a response indicates an unauthenticated state.

### Miscellaneous Changes

* Adjusted default values for `loginCheckIntervalTime` and `clients` in the `HUST` class for better configurability.
* Refactored the `login` method in `HUST` to allow skipping login status checks when explicitly specified. [[1]](diffhunk://#diff-dcdc3e0b3362edb8fec2a51d3fa51f8fb8af8f70247e06d9887fa934834c9122R65-R69) [[2]](diffhunk://#diff-dcdc3e0b3362edb8fec2a51d3fa51f8fb8af8f70247e06d9887fa934834c9122R99-R116)


Diff:
diff --git a/auth/cas.ts b/auth/cas.ts
index 8c14e30..eacb807 100644
--- a/auth/cas.ts
+++ b/auth/cas.ts
@@ -1,7 +1,12 @@
 import CookieManager from '@/auth/cookie-manager';
-import type { LoginInfo, LoginTickets, PhoneCodeCallback, RSAResponse } from '@/types/auth';
+import type {
+  LoginInfo,
+  LoginTickets,
+  PhoneCodeCallback,
+  RSAResponse,
+} from '@/types/auth';
 import { recognizeGifCaptcha } from '@/utils/dynamic-code';
-import { followRedirect } from '@/utils/request';
+import { followRedirect, isNeedAuth } from '@/utils/request';
 import { rsaEncrypt } from '@/utils/rsa';
 import { useCookie } from '@/utils/use-cookie';
 import type { AxiosInstance, AxiosResponse } from 'axios';
@@ -28,7 +33,7 @@ export default class CASAuth {
   }
 
   /**
-   * 检查登录状态
+   * 检查 hustpass 登录状态
    * @returns {boolean} 已登录为 true，否则为 false
    */
   async checkLoginStatus() {
@@ -216,90 +221,8 @@ export default class CASAuth {
     return false;
   }
 
-  async testHUBS() {
-    const apiUrl = 'https://hubs.hust.edu.cn/schedule/getCurrentXq';
-
-    try {
-      const response = await this.axios.get(apiUrl);
-      console.log('testAuth success with status', response.status);
-      console.log('testAuth response', response.data);
-    } catch (e) {
-      console.log('testAuth error', e);
-    }
-  }
-
-  async testPETYXY() {
-    const url = 'http://petyxy.hust.edu.cn/pft/app/resultList?periodId=20242';
-
-    try {
-      let response = await this.axios.get(url);
-
-      console.log('testPE success with status', response.status);
-      console.log('testPE response', response.data);
-      return response.status === 200;
-    } catch (e) {
-      return false;
-    }
-  }
-
-  async testONE() {
-    const url = 'https://one.hust.edu.cn/dcp/pim/pim.action';
-
-    try {
-      const response = await this.axios.post(
-        url,
-        {
-          map: {
-            method: 'getAllPimList',
-            params: {
-              javaClass: 'java.util.ArrayList',
-              list: ['', '', '', '', '', ''],
-            },
-            pm: {
-              javaClass: 'com.neusoft.education.edp.client.PageManager',
-              pageSize: 10,
-              pageNo: '1',
-              totalCount: -1,
-              order: null,
-              filters: {
-                javaClass: 'com.neusoft.education.edp.client.QueryFilter',
-                parameters: {
-                  javaClass: 'java.util.HashMap',
-                  map: {},
-                },
-              },
-              pageSumcols: null,
-              pageSums: null,
-              sumcols: null,
-              isNewSum: null,
-              sums: null,
-              resPojoName: '',
-            },
-          },
-          javaClass: 'java.util.HashMap',
-        },
-        {
-          headers: {
-            Clienttype: 'json',
-            Render: 'json',
-          },
-        },
-      );
-      console.log('testONE success with status', response.status);
-      console.log('testONE response', response.data);
-      return response.status === 200;
-    } catch (e) {
-      return false;
-    }
-  }
-
   async loginPETYXY() {
     const url = 'https://petyxy.hust.edu.cn/pft/app/resultList';
-    const isLogin = await this.checkLoginStatus();
-
-    if (!isLogin) {
-      return false;
-    }
 
     try {
       let response = await this.axios.get(url);
@@ -319,11 +242,6 @@ export default class CASAuth {
 
   async loginHUBS() {
     const url = 'https://hubs.hust.edu.cn';
-    const isLogin = await this.checkLoginStatus();
-
-    if (!isLogin) {
-      return false;
-    }
 
     try {
       let response = await this.axios.get(url);
@@ -346,11 +264,6 @@ export default class CASAuth {
 
   async loginONE() {
     const url = 'https://one.hust.edu.cn/dcp/';
-    const isLogin = await this.checkLoginStatus();
-
-    if (!isLogin) {
-      return false;
-    }
 
     try {
       let response = await this.axios.get(url);
@@ -370,6 +283,40 @@ export default class CASAuth {
       return false;
     }
   }
+
+  // 检查对应网站是否登录
+  async checkPETYXY() {
+    const url = 'https://petyxy.hust.edu.cn';
+
+    try {
+      const response = await this.axios.get(url);
+      return !isNeedAuth(response);
+    } catch (e) {
+      return false;
+    }
+  }
+
+  async checkHUBS() {
+    const url = 'https://hubs.hust.edu.cn';
+
+    try {
+      const response = await this.axios.get(url);
+      return !isNeedAuth(response);
+    } catch (e) {
+      return false;
+    }
+  }
+
+  async checkONE() {
+    const url = 'https://one.hust.edu.cn/dcp/';
+
+    try {
+      const response = await this.axios.get(url);
+      return !isNeedAuth(response);
+    } catch (e) {
+      return false;
+    }
+  }
 }
 
 // const cookieManager = new CookieManager();
diff --git a/clients/news-client.ts b/clients/news-client.ts
index 4d09f4c..9198005 100644
--- a/clients/news-client.ts
+++ b/clients/news-client.ts
@@ -12,7 +12,6 @@ export default class NewsClient {
     this.axios = axios.create({
       timeout: 30000,
       maxRedirects: 0,
-      validateStatus: (status) => status < 400,
       headers: {
         'User-Agent':
           'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36',
@@ -25,51 +24,47 @@ export default class NewsClient {
     const requestFunc = async () => {
       const url = 'https://one.hust.edu.cn/dcp/pim/pim.action';
 
-      try {
-        const response = await this.axios.post(
-          url,
-          {
-            map: {
-              method: 'getAllPimList',
-              params: {
-                javaClass: 'java.util.ArrayList',
-                list: ['', '', '', '', '', ''],
-              },
-              pm: {
-                javaClass: 'com.neusoft.education.edp.client.PageManager',
-                pageSize: 10,
-                pageNo: '1',
-                totalCount: -1,
-                order: null,
-                filters: {
-                  javaClass: 'com.neusoft.education.edp.client.QueryFilter',
-                  parameters: {
-                    javaClass: 'java.util.HashMap',
-                    map: {},
-                  },
+      const response = await this.axios.post(
+        url,
+        {
+          map: {
+            method: 'getAllPimList',
+            params: {
+              javaClass: 'java.util.ArrayList',
+              list: ['', '', '', '', '', ''],
+            },
+            pm: {
+              javaClass: 'com.neusoft.education.edp.client.PageManager',
+              pageSize: 10,
+              pageNo: '1',
+              totalCount: -1,
+              order: null,
+              filters: {
+                javaClass: 'com.neusoft.education.edp.client.QueryFilter',
+                parameters: {
+                  javaClass: 'java.util.HashMap',
+                  map: {},
                 },
-                pageSumcols: null,
-                pageSums: null,
-                sumcols: null,
-                isNewSum: null,
-                sums: null,
-                resPojoName: '',
               },
+              pageSumcols: null,
+              pageSums: null,
+              sumcols: null,
+              isNewSum: null,
+              sums: null,
+              resPojoName: '',
             },
-            javaClass: 'java.util.HashMap',
           },
-          {
-            headers: {
-              Clienttype: 'json',
-              Render: 'json',
-            },
+          javaClass: 'java.util.HashMap',
+        },
+        {
+          headers: {
+            Clienttype: 'json',
+            Render: 'json',
           },
-        );
-        return response.data;
-      } catch (e) {
-        console.error('testONE error', e);
-        return null;
-      }
+        },
+      );
+
+      return response.data;
     };
     return await this.hust.handleRequest(requestFunc);
   }
diff --git a/index.ts b/index.ts
index 7f6a9d8..c6921a9 100644
--- a/index.ts
+++ b/index.ts
@@ -5,6 +5,7 @@ import NewsClient from '@/clients/news-client';
 import { Client, type HUSTConfig } from '@/types/hust';
 import { isAuthError } from './utils/request';
 import { getPhoneCodeFromConsole } from './utils/console-input';
+import { AxiosError } from 'axios';
 
 export default class HUST {
   private readonly cookieManager: CookieManager;
@@ -18,8 +19,8 @@ export default class HUST {
 
   // 配置项
   private maxLoginRetries: number = 10;
-  private loginCheckIntervalTime: number = 60000;
-  private clients: Client[] = [];
+  private loginCheckIntervalTime: number = 1000 * 60 * 10; // 默认 10 分钟
+  private clients: Client[] = Object.values(Client); // 默认登录所有客户端
 
   // clients 客户端
   public readonly news: NewsClient;
@@ -61,8 +62,11 @@ export default class HUST {
 
   /**
    * 登录
+   * @param info 登录信息
+   * @param check 是否检查登录状态，默认为 true \
+   * 如果上下文中已经确认未登录，则可以设置为 false 来跳过检查
    */
-  async login(info: LoginInfo): Promise<boolean> {
+  async login(info: LoginInfo, check = true): Promise<boolean> {
     if (!info) {
       throw new Error('Login info is required');
     }
@@ -72,27 +76,14 @@ export default class HUST {
     let loginRetryCount = 0;
     let loginSuccess = false;
 
-    try {
-      const isLoggedIn = await this.isLoggedIn();
-
-      if (isLoggedIn) {
-        return true;
-      }
-
-      if (this.isLoggingIn) {
-        return false;
-      }
-      this.isLoggingIn = true;
-
+    // 直接封装执行登录的操作，为了可以跳过检查
+    const doLogin = async () => {
       while (loginRetryCount < maxRetries && !loginSuccess) {
         try {
           loginSuccess = await this.auth.login(info, this.phoneCodeCallback);
 
           if (loginSuccess) {
             let loginClients = this.clients;
-            if (loginClients.length === 0) {
-              loginClients = Object.values(Client);
-            }
             await Promise.all(
               loginClients.map((client) => this.loginClient(client)),
             );
@@ -105,6 +96,24 @@ export default class HUST {
         }
       }
       return false;
+    };
+
+    try {
+      // 如果正在登录中，直接返回 false
+      if (this.isLoggingIn) {
+        return false;
+      }
+      this.isLoggingIn = true;
+
+      if (check) {
+        const isLoggedIn = await this.isLoggedIn();
+
+        if (isLoggedIn) {
+          return true;
+        }
+      }
+
+      return await doLogin();
     } finally {
       this.isLoggingIn = false;
       if (!this.loginCheckTimer) {
@@ -156,10 +165,40 @@ export default class HUST {
   }
 
   /**
-   * 检查登录状态
+   * 检查特定客户端的登录状态 \
+   * 如果一个 client 涉及多个域名，则需要使用 Promise.all 来并行检查所有域名的登录状态
+   *
+   * @private
+   * @async
+   * @param {Client} client 客户端
+   * @returns {Promise<boolean>} 如果已登录返回 true，否则返回 false
+   */
+  private async checkClient(client: Client): Promise<boolean> {
+    switch (client) {
+      case Client.news:
+        return await this.auth.checkONE();
+      default:
+        throw new Error(`Client ${client} not supported`);
+    }
+  }
+
+  /**
+   * 检查所有服务的登录状态
    */
   async isLoggedIn(): Promise<boolean> {
-    return await this.auth.checkLoginStatus();
+    const isAuthLogin = await this.auth.checkLoginStatus();
+
+    if (!isAuthLogin) {
+      return false;
+    }
+
+    // 检查所有客户端的登录状态
+    const clientCheckPromises = this.clients.map((client) =>
+      this.checkClient(client),
+    );
+    const clientResults = await Promise.all(clientCheckPromises);
+
+    return clientResults.every((result) => result);
   }
 
   async handleRequest<T>(requestFn: () => Promise<T>): Promise<T> {
@@ -178,15 +217,26 @@ export default class HUST {
     try {
       return await requestFn();
     } catch (error) {
-      if (isAuthError(error)) {
-        const reLoginSuccess = await this.login(this.loginInfo!);
-        if (reLoginSuccess) {
-          return await requestFn();
+      // 只处理 AxiosError，其他错误应该在 requestFn 内部处理
+      if (error instanceof AxiosError) {
+        if (isAuthError(error)) {
+          const reLoginSuccess = await this.login(this.loginInfo!, false);
+          if (reLoginSuccess) {
+            return await requestFn();
+          } else {
+            console.error('Re-login failed');
+            throw error;
+          }
         } else {
-          console.error('Re-login failed');
           throw error;
         }
       } else {
+        console.error(
+          '[HUST SDK] Non-AxiosError caught in handleRequest. ' +
+            'Please handle this error type in your requestFn. ' +
+            'handleRequest only processes AxiosError automatically.',
+          error,
+        );
         throw error;
       }
     }
@@ -251,7 +301,7 @@ const now = new Date();
 // const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
 
 console.log('Initializing HUST SDK...');
-// await sleep(5000);
+// await sleep(1000 * 2);
 const res = await hust.news.getNewsList();
 console.log('News List:', res);
 
diff --git a/utils/request.ts b/utils/request.ts
index 3895ac8..97c91c4 100644
--- a/utils/request.ts
+++ b/utils/request.ts
@@ -1,4 +1,9 @@
-import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
+import type {
+  AxiosError,
+  AxiosInstance,
+  AxiosRequestConfig,
+  AxiosResponse,
+} from 'axios';
 import type { RedirectOptions } from '@/types/utils';
 
 export async function followRedirect(
@@ -49,12 +54,24 @@ export async function followRedirect(
   return finalResponse;
 }
 
-export function isAuthError(error: any): boolean {
+export function isAuthError(error: AxiosError): boolean {
   if (error && error.response) {
     return (
-      error.response === 302 &&
+      error.response.status === 302 &&
       error.response.headers.location.includes('login')
     );
   }
   return false;
 }
+
+/**
+ * 根据响应判断是否未登录 \
+ * 假定未登录的响应状态码为 302，且 Location 头包含 'login' 字符串
+ *
+ * @export
+ * @param {AxiosResponse} response
+ * @returns {boolean}
+ */
+export function isNeedAuth(response: AxiosResponse): boolean {
+  return response.status === 302 && response.headers.location.includes('login');
+}


Reviews:
user: login: copilot-pull-request-reviewer[bot]
body: ## Pull Request Overview

This PR refines authentication logic by adding granular login checks, streamlines client handling in the HUST class, and enhances error handling in requests.

- Added `isNeedAuth` utility and updated `isAuthError` in `utils/request.ts`.
- Refactored `HUST` to dynamically build its client list, introduced `checkClient` and parallel login checks, and added a `check` flag to `login`.
- Removed legacy test methods from `CASAuth`, introduced `checkPETYXY`, `checkHUBS`, `checkONE`, and improved error handling in `handleRequest`.

### Reviewed Changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

| File                  | Description                                                      |
| --------------------- | ---------------------------------------------------------------- |
| utils/request.ts      | Added `isNeedAuth` and tightened up `isAuthError` signature      |
| index.ts              | Overhauled `login`, `isLoggedIn`, added `checkClient`, error handling and demo code |
| clients/news-client.ts| Removed internal try/catch to delegate error handling to `handleRequest` |
| auth/cas.ts           | Dropped redundant `test*` methods, added `check*` methods for services |


<details>
<summary>Comments suppressed due to low confidence (2)</summary>

**index.ts:81**
* The variable `maxRetries` is not defined in this scope. You likely intended to use `this.maxLoginRetries` or capture it before defining `doLogin`.
```
while (loginRetryCount < maxRetries && !loginSuccess) {
```
**auth/cas.ts:288**
* New methods `checkPETYXY`, `checkHUBS`, and `checkONE` lack dedicated tests. Consider adding unit tests to validate their behavior against expected response scenarios.
```
async checkPETYXY() {
```
</details>


state: COMMENTED
author_association: NONE
submitted_at: 2025-05-30T16:51:01Z
user: login: GoForceX
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-05-30T17:14:57Z
user: login: wangyinyuan
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-30T17:17:42Z
user: login: wangyinyuan
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-30T17:46:29Z
user: login: GoForceX
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-31T09:20:51Z

Review comments:
diff_hunk: @@ -156,10 +165,40 @@ export default class HUST {
   }
 
   /**
-   * 检查登录状态
+   * 检查特定客户端的登录状态 \
+   * 如果一个 client 涉及多个域名，则需要使用 Promise.all 来并行检查所有域名的登录状态
+   *
+   * @private
+   * @async
+   * @param {Client} client 客户端
+   * @returns {Promise<boolean>} 如果已登录返回 true，否则返回 false
+   */
+  private async checkClient(client: Client): Promise<boolean> {
+    switch (client) {
+      case Client.news:
+        return await this.auth.checkONE();
+      default:
path: index.ts
user: login: Copilot
body: `checkClient` only handles `Client.news` but `this.clients` defaults to all values. Calls for unsupported clients will throw at runtime—consider supporting all clients or filtering the list.
created_at: 2025-05-30T16:51:00Z
author_association: NONE
diff_hunk: @@ -248,10 +298,16 @@ const hust = new HUST({
 
 const now = new Date();
 
-// const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
+const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
path: index.ts
user: login: Copilot
body: [nitpick] This example/demo code (including `sleep`, cookie setup, and top-level awaits) may not belong in the library entry point. Consider moving it to a separate example script or test harness.
created_at: 2025-05-30T16:51:00Z
author_association: NONE
diff_hunk: @@ -49,12 +54,24 @@ export async function followRedirect(
   return finalResponse;
 }
 
-export function isAuthError(error: any): boolean {
+export function isAuthError(error: AxiosError): boolean {
   if (error && error.response) {
     return (
-      error.response === 302 &&
+      error.response.status === 302 &&
       error.response.headers.location.includes('login')
     );
   }
   return false;
path: utils/request.ts
user: login: Copilot
body: [nitpick] The logic in `isAuthError` duplicates the check in `isNeedAuth`. Consider calling `isNeedAuth(error.response)` inside `isAuthError` to reduce duplication.
created_at: 2025-05-30T16:51:00Z
author_association: NONE
diff_hunk: @@ -49,12 +54,24 @@ export async function followRedirect(
   return finalResponse;
 }
 
-export function isAuthError(error: any): boolean {
+export function isAuthError(error: AxiosError): boolean {
   if (error && error.response) {
     return (
-      error.response === 302 &&
+      error.response.status === 302 &&
       error.response.headers.location.includes('login')
     );
   }
   return false;
 }
+
+/**
+ * 根据响应判断是否未登录 \
path: utils/request.ts
user: login: Copilot
body: [nitpick] The backslash at the end of this JSDoc line may be unintended. Removing it will keep the comment clearer.
created_at: 2025-05-30T16:51:01Z
author_association: NONE
diff_hunk: @@ -248,10 +298,16 @@ const hust = new HUST({
 
 const now = new Date();
 
-// const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
+const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
 
 console.log('Initializing HUST SDK...');
-// await sleep(5000);
+await sleep(1000 * 2);
+// 修改 cookie
+const cookieManager = hust.getCookieManager();
+await cookieManager.setCookies('https://one.hust.edu.cn/dcp/', [
+  'dcp_session_id=pXgh7rQPQeL8M4C2!255489232; Path=/; HttpOnly; SameSite=Lax',
+]);
+console.log('Cookie set successfully.');
path: index.ts
user: login: GoForceX
body: 忘删了？
created_at: 2025-05-30T16:58:11Z
author_association: MEMBER
diff_hunk: @@ -370,6 +283,25 @@ export default class CASAuth {
       return false;
     }
   }
+
+  // 检查对应网站是否登录
+  async checkPETYXY() {
+    const url = 'https://petyxy.hust.edu.cn';
+    const response = await this.axios.get(url);
+    return !isNeedAuth(response);
+  }
+
+  async checkHUBS() {
+    const url = 'https://hubs.hust.edu.cn';
+    const response = await this.axios.get(url);
+    return !isNeedAuth(response);
+  }
+
+  async checkONE() {
+    const url = 'https://one.hust.edu.cn/dcp/';
+    const response = await this.axios.get(url);
+    return !isNeedAuth(response);
+  }
path: auth/cas.ts
user: login: GoForceX
body: 是否需要做一下 Error Handling？现在看起来如果在某一次 Check 时断网了 / 爆了的话，Error 并不会被 catch 到，然后就把整个服务爆了。感觉不是很 fail-safe。
created_at: 2025-05-30T17:14:45Z
author_association: MEMBER
diff_hunk: @@ -248,10 +298,16 @@ const hust = new HUST({
 
 const now = new Date();
 
-// const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
+const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
 
 console.log('Initializing HUST SDK...');
-// await sleep(5000);
+await sleep(1000 * 2);
+// 修改 cookie
+const cookieManager = hust.getCookieManager();
+await cookieManager.setCookies('https://one.hust.edu.cn/dcp/', [
+  'dcp_session_id=pXgh7rQPQeL8M4C2!255489232; Path=/; HttpOnly; SameSite=Lax',
+]);
+console.log('Cookie set successfully.');
path: index.ts
user: login: wangyinyuan
body: 草，真忘删了
created_at: 2025-05-30T17:17:42Z
author_association: MEMBER
diff_hunk: @@ -370,6 +283,25 @@ export default class CASAuth {
       return false;
     }
   }
+
+  // 检查对应网站是否登录
+  async checkPETYXY() {
+    const url = 'https://petyxy.hust.edu.cn';
+    const response = await this.axios.get(url);
+    return !isNeedAuth(response);
+  }
+
+  async checkHUBS() {
+    const url = 'https://hubs.hust.edu.cn';
+    const response = await this.axios.get(url);
+    return !isNeedAuth(response);
+  }
+
+  async checkONE() {
+    const url = 'https://one.hust.edu.cn/dcp/';
+    const response = await this.axios.get(url);
+    return !isNeedAuth(response);
+  }
path: auth/cas.ts
user: login: wangyinyuan
body: > 是否需要做一下 Error Handling？现在看起来如果在某一次 Check 时断网了 / 爆了的话，Error 并不会被 catch 到，然后就把整个服务爆了。感觉不是很 fail-safe。

断网就 all boom boom 了(x)

可以加上吧，看了一下链路如果不加的话 check 爆了就会直接放弃登录，那后面就都爆了，返回 false 起码还能走进登录的逻辑
created_at: 2025-05-30T17:46:29Z
author_association: MEMBER

PR comments:
user: login: GoForceX
created_at: 2025-05-30T16:49:34Z
body: 哈哈，来辣！
author_association: MEMBER
user: login: GoForceX
created_at: 2025-05-30T17:15:47Z
body: 嘻嘻，先睡了
author_association: MEMBER
user: login: GoForceX
created_at: 2025-05-31T09:21:12Z
body: LGTM
author_association: MEMBER