Refactor frontend workspaces and centralize API contracts

Organize domain APIs and types under common/apis, shared components under named folders, and private workspace logic alongside each page.

Use Bearer sessions, explicit response envelopes, session-aware routing and streams, and configurable login captcha presentation for the deployed backend contract.

Preserve news, economic calendar, crawler, market, chat, Skills, settings, and demo trading workflows with responsive layouts and explicit configuration saves.

Validate with frontend lint, type checking, 22 test groups, production builds, and deployed desktop/mobile page checks.
This commit is contained in:
eezd committed 2026-10-26 11:39:52 +08:00
1 parent 77b33e5e77
commit bc480fb806
102 files changed
+4716 -1707

No files matched your search

+31
View File
@@ -0,0 +1,31 @@
import { listSourcesApi } from "@@/apis/crawler"
import { defineStore } from "pinia"
import { ref, watch } from "vue"
import { useUserStore } from "@/pinia/stores/user"
export const useSourcesStore = defineStore("sources", () => {
const names = ref<Record<string, string>>({})
const user = useUserStore()
let loading: Promise<void> | undefined
watch(() => user.sessionVersion, () => {
names.value = {}
loading = undefined
}, { flush: "sync" })
function load() {
if (Object.keys(names.value).length)
return Promise.resolve()
if (!loading) {
const version = user.sessionVersion
loading = listSourcesApi().then((info) => {
if (version === user.sessionVersion)
names.value = Object.fromEntries(info.sources.map(s => [s.code, s.name]))
}).finally(() => {
if (version === user.sessionVersion)
loading = undefined
})
}
return loading
}
return { names, load }
})
+53
View File
@@ -0,0 +1,53 @@
import type { Identity } from "@@/apis/auth/type"
import { getIdentityApi, revokeSessionApi } from "@@/apis/auth"
import { CanceledError } from "axios"
import { defineStore } from "pinia"
import { computed, ref } from "vue"
export const TOKEN_KEY = "alphapulse.auth.token"
export const useUserStore = defineStore("user", () => {
const token = ref(localStorage.getItem(TOKEN_KEY) ?? "")
const sessionVersion = ref(0)
const identity = ref<Identity>()
const username = computed(() => identity.value?.username)
let restoring: Promise<void> | undefined
function setToken(value: string, persist = true) {
sessionVersion.value++
token.value = value
identity.value = undefined
restoring = undefined
if (persist) {
if (value)
localStorage.setItem(TOKEN_KEY, value)
else
localStorage.removeItem(TOKEN_KEY)
}
}
async function fetchMe() {
if (!restoring) {
const version = sessionVersion.value
restoring = getIdentityApi().then((me) => {
if (version !== sessionVersion.value)
throw new CanceledError("会话已切换")
identity.value = me
}).finally(() => {
if (version === sessionVersion.value)
restoring = undefined
})
}
return restoring
}
async function logout() {
const version = sessionVersion.value
await revokeSessionApi()
if (version !== sessionVersion.value)
throw new CanceledError("会话已切换")
setToken("")
}
return { token, sessionVersion, identity, username, setToken, fetchMe, logout }
})