feat: 嵌入 StarLight 皮肤站并同步登录状态

This commit is contained in:
Disy committed 2026-09-13 21:34:09 +08:00
1 parent 1593ac7a7c
commit 3471cd3da2
13 files changed
+449 -311

No files matched your search

@@ -0,0 +1,51 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import {
openSkinSiteLogin,
receiveSkinSiteSession,
resetSkinSiteSession,
SKIN_SITE_ORIGIN,
skinSiteFrameUrl,
skinSiteStatus,
skinSiteUser,
} from './skin-site-session.ts'
test('skin site session accepts only the embedded origin and window, and redirects after verification', () => {
const frame = {} as Window
const message = (status: string, user?: unknown) =>
({
origin: SKIN_SITE_ORIGIN,
source: frame,
data: { type: 'starlight-skin-session', status, user },
}) as MessageEvent
resetSkinSiteSession()
openSkinSiteLogin()
assert.equal(skinSiteFrameUrl.value, `${SKIN_SITE_ORIGIN}/login`)
const signedIn = message('signed-in', {
uuid: 'test-user',
username: '测试用户',
jwt: 'must-not-copy',
})
assert.equal(
receiveSkinSiteSession({ ...signedIn, origin: 'https://evil.example' } as MessageEvent, frame),
false,
)
assert.equal(receiveSkinSiteSession(signedIn, {} as Window), false)
assert.equal(receiveSkinSiteSession(message('signed-in', {}), frame), false)
assert.equal(skinSiteUser.value, null)
assert.equal(receiveSkinSiteSession(signedIn, frame), true)
assert.deepEqual(skinSiteUser.value, { uuid: 'test-user', username: '测试用户' })
assert.equal(skinSiteStatus.value, 'signed-in')
assert.equal(skinSiteFrameUrl.value, `${SKIN_SITE_ORIGIN}/profile`)
assert.equal(receiveSkinSiteSession(message('checking'), frame), true)
assert.equal(skinSiteUser.value, null)
receiveSkinSiteSession(message('signed-in', { uuid: 'second', username: '另一个账号' }), frame)
assert.deepEqual(skinSiteUser.value, { uuid: 'second', username: '另一个账号' })
receiveSkinSiteSession(message('signed-out'), frame)
assert.equal(skinSiteUser.value, null)
assert.equal(skinSiteStatus.value, 'signed-out')
receiveSkinSiteSession(message('error'), frame)
assert.equal(skinSiteStatus.value, 'error')
resetSkinSiteSession()
})
@@ -0,0 +1,48 @@
import { readonly, ref } from 'vue'
export const SKIN_SITE_ORIGIN = 'https://skin.starlight.cool'
export type SkinSiteUser = { uuid: string; username: string }
export type SkinSiteStatus = 'checking' | 'signed-out' | 'signed-in' | 'error'
const user = ref<SkinSiteUser | null>(null)
const status = ref<SkinSiteStatus>('signed-out')
const frameUrl = ref(`${SKIN_SITE_ORIGIN}/`)
export const skinSiteUser = readonly(user)
export const skinSiteStatus = readonly(status)
export const skinSiteFrameUrl = readonly(frameUrl)
export function openSkinSiteLogin() {
frameUrl.value = `${SKIN_SITE_ORIGIN}/login`
}
export function resetSkinSiteSession() {
user.value = null
status.value = 'signed-out'
}
export function receiveSkinSiteSession(event: MessageEvent, frame: Window | null) {
if (!frame || event.source !== frame || event.origin !== SKIN_SITE_ORIGIN) return false
const data = event.data
if (!data || data.type !== 'starlight-skin-session') return false
if (!['checking', 'signed-out', 'signed-in', 'error'].includes(data.status)) return false
if (data.status === 'signed-in') {
if (
!data.user ||
typeof data.user.uuid !== 'string' ||
typeof data.user.username !== 'string' ||
!data.user.uuid ||
!data.user.username ||
data.user.username.length > 256
)
return false
user.value = { uuid: data.user.uuid, username: data.user.username }
if ([`${SKIN_SITE_ORIGIN}/`, `${SKIN_SITE_ORIGIN}/login`].includes(frameUrl.value)) {
frameUrl.value = `${SKIN_SITE_ORIGIN}/profile`
}
} else {
user.value = null
}
status.value = data.status
return true
}