diff --git a/app/components/AppShell.vue b/app/components/AppShell.vue index 11098c2..b12d5c9 100644 --- a/app/components/AppShell.vue +++ b/app/components/AppShell.vue @@ -10,14 +10,14 @@ withDefaults( ) const route = useRoute() -const { loggedIn, user, refresh, clear, initialized } = useAuthSession() +const { loggedIn, user, clear, initialized, ensureClientMeSynced } = useAuthSession() const { fetchData } = useClientApi() const { allowRegister, siteName } = useGlobalConfig() const logoutLoading = ref(false) onMounted(() => { - refresh().catch(() => {}) + ensureClientMeSynced().catch(() => {}) }) const displayName = computed(() => { diff --git a/app/composables/useAuthSession.ts b/app/composables/useAuthSession.ts index 8d51de2..9eee9ef 100644 --- a/app/composables/useAuthSession.ts +++ b/app/composables/useAuthSession.ts @@ -13,6 +13,10 @@ type MeResult = { user: AuthUser; }; +type SessionResult = { + user: AuthUser | null; +}; + export type AuthSessionState = { initialized: boolean; pending: boolean; @@ -21,6 +25,8 @@ export type AuthSessionState = { }; export const AUTH_SESSION_STATE_KEY = "auth:session"; +/** 客户端是否已做过与 Cookie 的一次强制对齐(`ensureClientMeSynced`);登出等场景需重置 */ +export const AUTH_CLIENT_ME_SYNCED_KEY = "auth:client-me-synced"; export const DEFAULT_AUTH_SESSION_STATE: AuthSessionState = { initialized: false, pending: false, @@ -39,6 +45,7 @@ export function useAuthSession() { const state = useState(AUTH_SESSION_STATE_KEY, () => ({ ...DEFAULT_AUTH_SESSION_STATE, })); + const clientMeSynced = useState(AUTH_CLIENT_ME_SYNCED_KEY, () => false); const applyUser = (user: AuthUser | null) => { state.value.user = user; @@ -47,6 +54,7 @@ export function useAuthSession() { }; const clear = () => { + clientMeSynced.value = false; applyUser(null); }; @@ -75,6 +83,24 @@ export function useAuthSession() { } }; + /** + * 全站 SPA 生命周期内至多同步一次客户端会话(`GET /api/auth/session`)。 + * 无会话 Cookie 时服务端不查库;有 Cookie 时与 `/api/auth/me` 相比少一次额外查询。 + */ + const ensureClientMeSynced = async (): Promise => { + if (import.meta.server || clientMeSynced.value) { + return; + } + try { + const payload = await request>("/api/auth/session"); + const data = unwrapApiBody(payload); + applyUser(data.user); + clientMeSynced.value = true; + } catch { + /* 网络错误等:保留 SSR 状态,下次进入带壳页面可再试 */ + } + }; + return { initialized: computed(() => state.value.initialized), loggedIn: computed(() => state.value.loggedIn), @@ -82,5 +108,6 @@ export function useAuthSession() { pending: computed(() => state.value.pending), refresh, clear, + ensureClientMeSynced, }; } diff --git a/app/layouts/public.vue b/app/layouts/public.vue index f528e77..72e6584 100644 --- a/app/layouts/public.vue +++ b/app/layouts/public.vue @@ -7,7 +7,7 @@ import { import { unwrapApiBody, type ApiResponse } from '../utils/http/factory' const route = useRoute() -const { loggedIn, user, refresh, clear, initialized } = useAuthSession() +const { loggedIn, user, clear, initialized, ensureClientMeSynced } = useAuthSession() const { fetchData } = useClientApi() const { siteName, allowRegister } = useGlobalConfig() const logoutLoading = ref(false) @@ -125,7 +125,7 @@ watch(publicLayoutMode, (m) => { }) onMounted(() => { - refresh().catch(() => {}) + ensureClientMeSynced().catch(() => {}) }) diff --git a/app/pages/@[publicSlug]/posts/[postSlug].vue b/app/pages/@[publicSlug]/posts/[postSlug].vue index 514fb47..c8fbbac 100644 --- a/app/pages/@[publicSlug]/posts/[postSlug].vue +++ b/app/pages/@[publicSlug]/posts/[postSlug].vue @@ -12,7 +12,7 @@ definePageMeta({ const route = useRoute() const publicSlug = computed(() => route.params.publicSlug as string) const postSlug = computed(() => route.params.postSlug as string) -const { user, loggedIn, refresh: refreshAuth } = useAuthSession() +const { user, loggedIn } = useAuthSession() type Post = { id: number @@ -63,10 +63,6 @@ usePageTitle(() => { return [t, `@${ps}`] }) -onMounted(() => { - void refreshAuth(true) -}) - /** 当前登录用户是否为该公开主页所有者(可编辑此文) */ const canEditPost = computed(() => { const slug = user.value?.publicSlug diff --git a/app/pages/me/media/index.vue b/app/pages/me/media/index.vue index 3ed824e..14e399d 100644 --- a/app/pages/me/media/index.vue +++ b/app/pages/me/media/index.vue @@ -1,6 +1,4 @@