Files
devops-platform-web/src/stores/session.ts
T
湛兮 366f1d667e feat: 接入真实平台登录态
- login/session: 对接后端登录、鉴权恢复和首次改密
- api/store: 恢复 BPMN XML 真实接口并携带 Bearer token
- layout/settings: 展示真实角色、审计 JSON 和通知平台配置文档
2026-06-12 00:38:31 +08:00

172 lines
3.8 KiB
TypeScript

/**
* 维护后端真实登录态,前端只保存 DevOps API token,不保存任何第三方密钥。
*/
import { computed, ref } from 'vue'
import { defineStore } from 'pinia'
import { devopsApi } from '../services/devopsApi'
import { setAuthToken } from '../services/http'
import type {
AuthenticatedUser,
ChangePasswordPayload,
LoginPayload,
} from '../types/devops'
const STORAGE_KEY = 'devops-platform-session'
type StoredSession = {
token: string
expiresAt: string
user: AuthenticatedUser
}
export const useSessionStore = defineStore('session', () => {
const restored = loadStoredSession()
const operator = ref(restored?.user.account ?? '')
const displayName = ref(restored?.user.displayName ?? '')
const role = ref<AuthenticatedUser['role'] | ''>(restored?.user.role ?? '')
const token = ref(restored?.token ?? '')
const expiresAt = ref(restored?.expiresAt ?? '')
const currentUser = ref<AuthenticatedUser | null>(restored?.user ?? null)
const bootstrapped = ref(false)
setAuthToken(token.value)
const isAuthenticated = computed(() => Boolean(token.value && currentUser.value))
const mustChangePassword = computed(() =>
Boolean(currentUser.value?.mustChangePassword),
)
const roleLabel = computed(() => {
if (role.value === 'super_admin') {
return '超级管理员'
}
if (role.value === 'member') {
return '普通成员'
}
return '未登录'
})
async function bootstrap(): Promise<boolean> {
if (bootstrapped.value) {
return isAuthenticated.value
}
bootstrapped.value = true
if (!token.value) {
return false
}
try {
const user = await devopsApi.getCurrentUser()
applyUser(user)
persist()
return true
} catch {
clearSession()
return false
}
}
async function login(payload: LoginPayload) {
const result = await devopsApi.login(payload)
token.value = result.token
expiresAt.value = result.expiresAt
setAuthToken(result.token)
applyUser(result.user)
persist()
bootstrapped.value = true
return result.user
}
async function changePassword(payload: ChangePasswordPayload) {
const user = await devopsApi.changePassword(payload)
applyUser(user)
persist()
return user
}
async function logout() {
if (token.value) {
try {
await devopsApi.logout()
} catch {
// 退出以清理本地登录态为准,后端当前无状态 token 无需强依赖 logout 成功。
}
}
clearSession()
}
function applyUser(user: AuthenticatedUser) {
currentUser.value = user
operator.value = user.account
displayName.value = user.displayName
role.value = user.role
}
function persist() {
if (!token.value || !currentUser.value) {
localStorage.removeItem(STORAGE_KEY)
return
}
const stored: StoredSession = {
token: token.value,
expiresAt: expiresAt.value,
user: currentUser.value,
}
localStorage.setItem(STORAGE_KEY, JSON.stringify(stored))
}
function clearSession() {
operator.value = ''
displayName.value = ''
role.value = ''
token.value = ''
expiresAt.value = ''
currentUser.value = null
setAuthToken()
localStorage.removeItem(STORAGE_KEY)
}
return {
operator,
displayName,
role,
roleLabel,
token,
expiresAt,
currentUser,
isAuthenticated,
mustChangePassword,
bootstrap,
login,
changePassword,
logout,
}
})
function loadStoredSession(): StoredSession | null {
try {
const rawValue = localStorage.getItem(STORAGE_KEY)
if (!rawValue) {
return null
}
const parsed = JSON.parse(rawValue) as StoredSession
if (!parsed.token || !parsed.user?.account) {
return null
}
return parsed
} catch {
localStorage.removeItem(STORAGE_KEY)
return null
}
}