From fdab9978bf41e611fdc04e1a658b0d6719b47ee6 Mon Sep 17 00:00:00 2001 From: nanxun Date: Thu, 13 Aug 2026 22:25:00 +0800 Subject: [PATCH] fix: refresh uploads and remember login --- frontend/src/api/client.ts | 4 +- frontend/src/stores/auth.ts | 34 +++-- frontend/src/views/LoginView.vue | 15 +- frontend/src/views/SettingsView.vue | 3 +- .../Persistence/PersistenceContracts.cs | 2 + .../Models/Auth/AuthModels.cs | 2 + .../Services/AuthService.cs | 8 +- .../Entities/RecordResult.cs | 9 ++ .../Entities/RecordUploadJob.cs | 89 ++++++++++++ .../Persistence/LiveRecorderDbContext.cs | 3 + ...60813214000_AddUploadReplacementCleanup.cs | 28 ++++ .../LiveRecorderDbContextModelSnapshot.cs | 15 ++ .../Persistence/Repositories/Repositories.cs | 5 + .../Services/OpenListClient.cs | 25 ++++ .../Services/OpenListUploadQueueService.cs | 131 +++++++++++++++++- tests/LiveRecorder.Tests/AuthServiceTests.cs | 38 +++++ .../LiveRecorder.Tests/OpenListUploadTests.cs | 32 +++++ 17 files changed, 424 insertions(+), 19 deletions(-) create mode 100644 src/LiveRecorder.Infrastructure/Persistence/Migrations/20260813214000_AddUploadReplacementCleanup.cs create mode 100644 tests/LiveRecorder.Tests/AuthServiceTests.cs diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index c8e890f..b35f8ee 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -154,7 +154,7 @@ function notifyBackendUnavailable(message: string) { } apiClient.interceptors.request.use((config) => { - const token = localStorage.getItem("live-recorder-token"); + const token = localStorage.getItem("live-recorder-token") ?? sessionStorage.getItem("live-recorder-token"); if (token) { config.headers.Authorization = `Bearer ${token}`; } @@ -172,6 +172,8 @@ apiClient.interceptors.response.use( markBackendAvailable(); localStorage.removeItem("live-recorder-token"); localStorage.removeItem("live-recorder-user"); + sessionStorage.removeItem("live-recorder-token"); + sessionStorage.removeItem("live-recorder-user"); if (window.location.pathname !== "/login") { window.location.href = "/login"; diff --git a/frontend/src/stores/auth.ts b/frontend/src/stores/auth.ts index a369347..f682282 100644 --- a/frontend/src/stores/auth.ts +++ b/frontend/src/stores/auth.ts @@ -6,38 +6,51 @@ import type { AuthenticatedUser, LoginResponse } from "@/types"; const USER_STORAGE_KEY = "live-recorder-user"; const TOKEN_STORAGE_KEY = "live-recorder-token"; +function readStoredValue(key: string) { + return localStorage.getItem(key) ?? sessionStorage.getItem(key); +} + +function clearStoredSession() { + [localStorage, sessionStorage].forEach((storage) => { + storage.removeItem(TOKEN_STORAGE_KEY); + storage.removeItem(USER_STORAGE_KEY); + }); +} + export const useAuthStore = defineStore("auth", () => { - const token = ref(localStorage.getItem(TOKEN_STORAGE_KEY) ?? ""); + const token = ref(readStoredValue(TOKEN_STORAGE_KEY) ?? ""); const user = ref( (() => { - const raw = localStorage.getItem(USER_STORAGE_KEY); + const raw = readStoredValue(USER_STORAGE_KEY); return raw ? (JSON.parse(raw) as AuthenticatedUser) : null; })() ); const isAuthenticated = computed(() => Boolean(token.value)); - function persistSession(session: LoginResponse) { + function persistSession(session: LoginResponse, rememberMe: boolean) { token.value = session.token; user.value = session.user; - localStorage.setItem(TOKEN_STORAGE_KEY, session.token); - localStorage.setItem(USER_STORAGE_KEY, JSON.stringify(session.user)); + clearStoredSession(); + const storage = rememberMe ? localStorage : sessionStorage; + storage.setItem(TOKEN_STORAGE_KEY, session.token); + storage.setItem(USER_STORAGE_KEY, JSON.stringify(session.user)); } function clearSession() { token.value = ""; user.value = null; - localStorage.removeItem(TOKEN_STORAGE_KEY); - localStorage.removeItem(USER_STORAGE_KEY); + clearStoredSession(); } - async function login(username: string, password: string) { + async function login(username: string, password: string, rememberMe: boolean) { const { data } = await apiClient.post("/auth/login", { username, - password + password, + rememberMe }); - persistSession(data); + persistSession(data, rememberMe); return data; } @@ -54,6 +67,7 @@ export const useAuthStore = defineStore("auth", () => { currentPassword, newPassword }); + clearSession(); } return { diff --git a/frontend/src/views/LoginView.vue b/frontend/src/views/LoginView.vue index 5c0d3f6..5fcb738 100644 --- a/frontend/src/views/LoginView.vue +++ b/frontend/src/views/LoginView.vue @@ -16,7 +16,8 @@ const { themeMode } = useUiPreferences(); const form = reactive({ username: "admin", - password: "" + password: "", + rememberMe: false }); const currentThemeIcon = computed(() => { @@ -35,7 +36,7 @@ async function handleLogin() { loading.value = true; try { - await authStore.login(form.username, form.password); + await authStore.login(form.username, form.password, form.rememberMe); ElMessage.success("登录成功"); await router.push({ name: "dashboard" }); } catch (error) { @@ -94,10 +95,12 @@ async function handleLogin() { :description="backendMessage" /> -