feat: admin frontend - React + Vite, auth pages, user management, roles, permissions, webhooks, devices, logs

This commit is contained in:
2026-04-02 11:20:20 +08:00
parent dcc1f186f8
commit 4718980ab5
235 changed files with 35682 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
export {
getRefreshToken,
setRefreshToken,
clearRefreshToken,
hasRefreshToken,
hasSessionPresenceCookie,
} from './token-storage'

View File

@@ -0,0 +1,68 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import {
clearRefreshToken,
getRefreshToken,
hasRefreshToken,
hasSessionPresenceCookie,
setRefreshToken,
} from './token-storage'
const originalDocument = globalThis.document
describe('token-storage', () => {
afterEach(() => {
clearRefreshToken()
vi.restoreAllMocks()
Object.defineProperty(globalThis, 'document', {
configurable: true,
value: originalDocument,
})
})
it('stores refresh tokens in memory and normalizes empty values to null', () => {
setRefreshToken(' refresh-token ')
expect(getRefreshToken()).toBe('refresh-token')
expect(hasRefreshToken()).toBe(true)
setRefreshToken(' ')
expect(getRefreshToken()).toBeNull()
expect(hasRefreshToken()).toBe(false)
setRefreshToken(undefined)
expect(getRefreshToken()).toBeNull()
})
it('clears the in-memory refresh token explicitly', () => {
setRefreshToken('token-to-clear')
expect(hasRefreshToken()).toBe(true)
clearRefreshToken()
expect(getRefreshToken()).toBeNull()
expect(hasRefreshToken()).toBe(false)
})
it('detects the session presence cookie when it is present among other cookies', () => {
vi.spyOn(document, 'cookie', 'get').mockReturnValue('foo=bar; ums_session_present=1; theme=dark')
expect(hasSessionPresenceCookie()).toBe(true)
})
it('returns false when the session presence cookie is absent', () => {
vi.spyOn(document, 'cookie', 'get').mockReturnValue('foo=bar; theme=dark')
expect(hasSessionPresenceCookie()).toBe(false)
})
it('returns false when document is unavailable', () => {
Object.defineProperty(globalThis, 'document', {
configurable: true,
value: undefined,
})
expect(hasSessionPresenceCookie()).toBe(false)
})
})

View File

@@ -0,0 +1,38 @@
/**
* In-memory refresh token storage.
*
* The authoritative session continuity mechanism is now the backend-managed
* HttpOnly refresh cookie. This module only keeps a process-local copy so the
* current tab can still send an explicit logout payload when available.
*/
let refreshToken: string | null = null
const SESSION_PRESENCE_COOKIE_NAME = 'ums_session_present'
export function getRefreshToken(): string | null {
return refreshToken
}
export function setRefreshToken(token: string | null | undefined): void {
const value = (token || '').trim()
refreshToken = value || null
}
export function clearRefreshToken(): void {
refreshToken = null
}
export function hasRefreshToken(): boolean {
return refreshToken !== null
}
export function hasSessionPresenceCookie(): boolean {
if (typeof document === 'undefined') {
return false
}
return document.cookie
.split(';')
.map((cookie) => cookie.trim())
.some((cookie) => cookie.startsWith(`${SESSION_PRESENCE_COOKIE_NAME}=`))
}