Skip to content

Commit ee6da31

Browse files
committed
[Add] sopport cc98 login
1 parent 940a4c0 commit ee6da31

5 files changed

Lines changed: 103 additions & 2 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
# 2025/12/06 v1.0.7
2+
3+
- 新增了 cc98.org 的支持
4+
5+
# 2025/12/01 v1.0.6
6+
7+
- Add type:module to ensure module import works fine with older node.
8+
9+
10+
111
# 2025/11/25 v1.0.4
212

313
- 新增了 open.zju.edu.cn (浙大先生开放平台) 的支持

‎README.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ This is mainly used for server-side applications.
4040
| 学在浙大(zju_web) | courses.zju.edu.cn | `COURSES` | 2025/11/14 | |
4141
| 校园卡二维码页面 | yqfkgl.zju.edu.cn | `YQFKGL` | 2025/11/26 | 用于`https://yqfkgl.zju.edu.cn/_web/_customizes/ykt/index3.jsp`,不排除其它path下会有其它登录流程 |
4242
| 浙大先生开放平台 | open.zju.edu.cn | `OPEN` | 2025/11/26 | 其实就是HiAgent |
43+
| CC98 | cc98.org | `CC98` | 2025/12/6 | 传参为账号密码 |
4344

4445
鉴于部分服务可能会变更登录流程,如果发现登录流程炸了,可以[提交一个issue](https://github.com/5dbwat4/login-ZJU/issues)或者[发布一个PR](https://github.com/5dbwat4/login-ZJU/pulls)。
4546

‎index.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ import { ZJUAM } from "./src/zjuam.js";
55
import { FORM } from "./src/form.js";
66
import { CLASSROOM } from "./src/classroom.js";
77
import { YQFKGL } from "./src/yqfkgl.js";
8+
import { CC98 } from "./src/CC98.js";
89

910
import pkg from "./package.json" with { type: "json" };
1011
console.log("[login ZJU] You (or your application) are using login-ZJU version: " + pkg.version);
1112
console.log("[login ZJU] If you find any issues, please report them at");
1213
console.log(" https://github.com/5dbwat4/login-ZJU");
1314

1415

15-
export {COURSES,ZDBK,ZJUAM,FORM,CLASSROOM,YQFKGL}
16+
export {COURSES,ZDBK,ZJUAM,FORM,CLASSROOM,YQFKGL,CC98}

‎package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "login-zju",
3-
"version": "1.0.6",
3+
"version": "1.0.7",
44
"description": "Server-side library helping your application login to ZJU services",
55
"main": "dist/login-ZJU.js",
66
"scripts": {

‎src/CC98.ts‎

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
const body = {
2+
client_id: "9a1fd200-8687-44b1-4c20-08d50a96e5cd",
3+
client_secret: "8b53f727-08e2-4509-8857-e34bf92b27f2",
4+
}// can be found in main.js t.prototype.handleLogin
5+
6+
class CC98 {
7+
#accessToken = "";
8+
#refreshToken = "";
9+
#tokenType = "";
10+
#expiresIn = 0;
11+
#username: string;
12+
#password: string;
13+
constructor(username: string, password: string) {
14+
this.#username = username;
15+
this.#password = password;
16+
}
17+
async login() {
18+
console.log("[CC98] login begins")
19+
const response = await fetch("https://openid.cc98.org/connect/token", {
20+
method: "POST",
21+
headers: {
22+
"Content-Type": "application/x-www-form-urlencoded",
23+
},
24+
body: new URLSearchParams({...body, username: this.#username, password: this.#password, grant_type: "password"}),
25+
});
26+
// const text = await response.text();
27+
// console.log(response.status,text);
28+
// throw 0;
29+
const data = await response.json();
30+
if (response.ok) {
31+
this.#accessToken = data.access_token;
32+
this.#refreshToken = data.refresh_token;
33+
this.#tokenType = data.token_type;
34+
this.#expiresIn = data.expires_in;
35+
}
36+
else {
37+
console.log("[CC98] login failed", data.error, data.error_description);
38+
throw new Error(`Login failed: ${data.error} - ${data.error_description}`);
39+
}
40+
console.log("[CC98] login successful");
41+
return true
42+
}
43+
44+
async #refresh() {
45+
console.log("[CC98] token refresh begins");
46+
const response = await fetch("https://openid.cc98.org/connect/token", {
47+
method: "POST",
48+
headers: {
49+
"Content-Type": "application/x-www-form-urlencoded",
50+
},
51+
body: new URLSearchParams({
52+
...body,
53+
refresh_token: this.#refreshToken,
54+
grant_type: "refresh_token",
55+
}),
56+
});
57+
const data = await response.json();
58+
if (response.ok) {
59+
this.#accessToken = data.access_token;
60+
this.#refreshToken = data.refresh_token;
61+
this.#tokenType = data.token_type;
62+
this.#expiresIn = data.expires_in;
63+
}
64+
else {
65+
console.log("[CC98] token refresh failed", data.error, data.error_description);
66+
throw new Error(`Login failed: ${data.error} - ${data.error_description}`);
67+
}
68+
console.log("[CC98] token refresh successful");
69+
return true
70+
}
71+
72+
async fetch(url: string, options: RequestInit = {}) {
73+
console.log("[CC98] fetch:", url);
74+
if (!this.#accessToken) {
75+
await this.login();
76+
}
77+
if (this.#expiresIn <= 60) { // Refresh token if it expires in less than 60 seconds
78+
await this.#refresh();
79+
}
80+
options.headers = {
81+
...options.headers,
82+
Authorization: `${this.#tokenType} ${this.#accessToken}`,
83+
};
84+
85+
return fetch(url, options);
86+
}
87+
}
88+
89+
export { CC98 };

0 commit comments

Comments
 (0)