Skip to content

Commit 4417975

Browse files
committed
[web] feat: sessions
1 parent e8f0b72 commit 4417975

20 files changed

Lines changed: 1086 additions & 946 deletions

File tree

Cargo.lock

Lines changed: 229 additions & 432 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/web/Cargo.toml

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,14 @@ edition = "2024"
55

66
[dependencies]
77
actix-web = "4"
8-
env_logger = "0.11.8"
8+
env_logger = "0.11.9"
99
include_dir = "0.7"
10-
log = "0.4.28"
10+
log = "0.4.29"
1111
mime_guess = "2.0.5"
12-
serde_json = "1.0.145"
13-
serde = { "version" = "1.0.145", features = ["derive"] }
14-
rand = "0.9.2"
12+
serde_json = "1.0.149"
13+
serde = { "version" = "1.0.149", features = ["derive"] }
14+
rand = "0.10.0"
1515
sha2 = "0.10.9"
1616
hex = "0.4.3"
17-
jsonwebtoken = { version = "10.2.0", features = ["rust_crypto"] }
18-
regex = "1.10.5"
17+
regex = "1.12.3"
1918
lazy_static = "1.5.0"

apps/web/src-frontend/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ export function setShouldRegister(value: boolean) {
122122
}
123123

124124
router.beforeEach((to, _from, next) => {
125-
if (useAccount().accessToken) {
125+
if (useAccount().token) {
126126
if (to.path.startsWith("/auth")) next("/");
127127
} else {
128128
if (!to.path.startsWith("/auth") && !to.path.startsWith("/welcome"))
@@ -139,13 +139,13 @@ export function setShouldRegister(value: boolean) {
139139
});
140140

141141
if (
142-
!useAccount().accessToken &&
142+
!useAccount().token &&
143143
!router.currentRoute.value.path.startsWith("/auth") &&
144144
!router.currentRoute.value.path.startsWith("/welcome")
145145
)
146146
await router.push("/auth");
147147

148-
loadingStep.value = t("web.loading.fetch-user");
148+
loadingStep.value = t("web.loading.user-fetch");
149149

150150
await useAccount().updateSelfInfo();
151151
setInterval(

apps/web/src-frontend/utils/network.ts

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,8 @@ export async function requestWithToken<T>(
8787
data?: any,
8888
headers?: object,
8989
requestConfig?: AxiosRequestConfig,
90-
autoRefreshToken: boolean = true,
9190
): Promise<T> {
92-
const token = useAccount().accessToken;
91+
const token = useAccount().token;
9392
if (!token) {
9493
const err = new ApiError(path, method, "invalid-token");
9594
await errHandler?.(err);
@@ -99,24 +98,21 @@ export async function requestWithToken<T>(
9998
path,
10099
method,
101100
async (e) => {
102-
if (token) {
103-
if (e.err == "invalid-token" && autoRefreshToken) {
104-
await useAccount().refreshToken();
105-
return await requestWithToken(
106-
path,
107-
method,
108-
errHandler,
109-
data,
110-
headers,
111-
requestConfig,
112-
false,
113-
);
114-
}
101+
await errHandler?.(e);
115102

103+
if (token) {
116104
if (e.err == "permission-denied") await useAccount().updateSelfInfo();
105+
if (e.err == "invalid-token") {
106+
await useAccount().logout();
107+
new MCSLNotif({
108+
data: {
109+
title: useLocale().getI18n().t("ui.notification.title.warning"),
110+
message: useLocale().getI18n().t("web.auth.login.expired"),
111+
color: "warning",
112+
},
113+
}).open();
114+
}
117115
}
118-
119-
await errHandler?.(e);
120116
},
121117
data,
122118
{
@@ -134,10 +130,8 @@ export class ApiError extends Error {
134130
public readonly err: string,
135131
public readonly cause?: any,
136132
) {
137-
super(
138-
useLocale()
139-
.getI18n()
140-
.t("web.api.error." + err),
141-
);
133+
const key = "web.api.error." + err;
134+
const localized = useLocale().getI18n().t(key);
135+
super(key != localized ? localized : err);
142136
}
143137
}

apps/web/src-frontend/utils/store.ts

Lines changed: 8 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,26 @@
11
import { defineStore } from "pinia";
22
import { computed, ref } from "vue";
3-
import {
4-
type Serializer,
5-
useLocalStorage,
6-
useSessionStorage,
7-
} from "@vueuse/core";
8-
import { notifyErr, requestApi, requestWithToken } from "./network.ts";
9-
import { MCSLNotif } from "@repo/ui/src/utils/notifications.ts";
10-
import { useLocale } from "@repo/ui/src/utils/stores.ts";
3+
import { useLocalStorage, useSessionStorage } from "@vueuse/core";
4+
import { notifyErr, requestWithToken } from "./network.ts";
115
import router from "@repo/shared/src/router.ts";
126

13-
export type TokenPair = { access_token: string; refresh_token: string };
14-
157
export type UserInfo = {
16-
id: number;
178
name: string;
189
permissions: string[];
1910
};
2011

2112
export const useAccount = defineStore("account", () => {
22-
const serializer: Serializer<TokenPair | null> = {
23-
write: (token: TokenPair | null) =>
24-
token == null ? "" : JSON.stringify(token),
25-
read: (token: string) =>
26-
token == "" ? null : (JSON.parse(token) as TokenPair),
27-
};
28-
29-
const tokenPermanent = useLocalStorage<TokenPair | null>(
30-
"mcsl-web-token",
31-
null,
32-
{ serializer },
33-
);
34-
const tokenTemporary = useSessionStorage<TokenPair | null>(
13+
const tokenPermanent = useLocalStorage<string | null>("mcsl-web-token", null);
14+
const tokenTemporary = useSessionStorage<string | null>(
3515
"mcsl-web-token",
3616
null,
37-
{ serializer },
3817
);
3918

4019
const token = computed(() => {
4120
return tokenPermanent.value ?? tokenTemporary.value;
4221
});
4322

44-
const accessToken = computed(() => {
45-
return token.value?.access_token;
46-
});
47-
48-
async function setToken(token: TokenPair | null, isPermanent?: boolean) {
23+
async function setToken(token: string | null, isPermanent?: boolean) {
4924
if (isPermanent === true || isPermanent == undefined) {
5025
tokenPermanent.value = token;
5126
}
@@ -61,59 +36,28 @@ export const useAccount = defineStore("account", () => {
6136
await router.push("/auth");
6237
}
6338

64-
async function refreshToken() {
65-
if (!token.value) return;
66-
try {
67-
const res = await requestApi<TokenPair>(
68-
"/account/refresh",
69-
"POST",
70-
async (e) => {
71-
if (e.err == "invalid-token") {
72-
await logout();
73-
const t = useLocale().getI18n().t;
74-
new MCSLNotif({
75-
data: {
76-
title: t("ui.notification.title.warning"),
77-
message: t("web.auth.login.expired"),
78-
color: "warning",
79-
},
80-
}).open();
81-
} else notifyErr(e, "web.auth.login.refresh-error");
82-
},
83-
token.value,
84-
);
85-
await setToken(res, true);
86-
} catch {
87-
/* ignore */
88-
}
89-
}
90-
9139
const selfInfo = ref<UserInfo | null>(null);
9240

9341
async function updateSelfInfo() {
94-
if (!accessToken.value) {
42+
if (!token.value) {
9543
selfInfo.value = null;
9644
return;
9745
}
9846
try {
9947
selfInfo.value = await requestWithToken<UserInfo>(
10048
"/user/self",
10149
"GET",
102-
async (err) => {
103-
if (err.err == "invalid-token") await useAccount().refreshToken();
104-
else notifyErr(err, "web.user-center.user-fetch-error");
105-
},
50+
(err) => notifyErr(err, "web.user-center.user-fetch-error"),
10651
);
10752
} catch {
10853
/* ignore */
10954
}
11055
}
11156

11257
return {
113-
accessToken,
11458
setToken,
59+
token,
11560
logout,
116-
refreshToken,
11761
selfInfo,
11862
updateSelfInfo,
11963
};

apps/web/src-frontend/views/Login.vue

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,7 @@ async function submit() {
6161
"/account/login",
6262
"POST",
6363
(e) => notifyErr(e, "web.auth.login.error"),
64-
{
65-
username: form.data.value.username,
66-
password: form.data.value.password,
67-
},
64+
form.data.value,
6865
);
6966
7067
await useAccount().setToken(tokenPair, form.data.value.remember);

0 commit comments

Comments
 (0)