feat: initial MVP - FastAPI backend + Vue3 frontend + docker-compose
- backend: FastAPI + SQLAlchemy 2.0(async) + asyncpg + Alembic - 7 API routes: auth/me/articles/sources/bookmarks/subscriptions/admin - models: User/Source/Article/Bookmark/Subscription/ApiToken - services: RSS fetcher (feedparser) + Tencent TMT translator with quota + cache + local NLLB fallback - workers: APScheduler + asyncio pipeline (fetch -> dedupe -> insert -> translate) - seed scripts: create_user, seed_sources (5 RSS: Reuters/BBC/Al Jazeera/NHK/DW) - frontend: Vue 3 + Vite + Naive UI + Pinia + vue-router - pages: Login, Feed (24h), ArticleDetail, Sources, Bookmarks, AdminSources - deploy: docker-compose (postgres/redis/api/worker/frontend/caddy) - docs: README, DEPLOY, architecture, acceptance
This commit is contained in:
61
frontend/src/stores/auth.ts
Normal file
61
frontend/src/stores/auth.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { http } from '@/api/client'
|
||||
|
||||
interface User {
|
||||
id: number
|
||||
username: string
|
||||
role: 'owner' | 'member'
|
||||
email?: string | null
|
||||
}
|
||||
|
||||
const ACCESS_KEY = 'dn.access'
|
||||
const REFRESH_KEY = 'dn.refresh'
|
||||
const USER_KEY = 'dn.user'
|
||||
|
||||
export const useAuthStore = defineStore('auth', {
|
||||
state: () => ({
|
||||
accessToken: '' as string,
|
||||
refreshToken: '' as string,
|
||||
user: null as User | null,
|
||||
}),
|
||||
getters: {
|
||||
isLogged: (s) => !!s.accessToken,
|
||||
isOwner: (s) => s.user?.role === 'owner',
|
||||
},
|
||||
actions: {
|
||||
async login(username: string, password: string) {
|
||||
const { data } = await http.post('/auth/login', { username, password })
|
||||
this.accessToken = data.access_token
|
||||
this.refreshToken = data.refresh_token
|
||||
localStorage.setItem(ACCESS_KEY, this.accessToken)
|
||||
localStorage.setItem(REFRESH_KEY, this.refreshToken)
|
||||
await this.fetchMe()
|
||||
},
|
||||
async refresh() {
|
||||
const { data } = await http.post('/auth/refresh', { refresh_token: this.refreshToken })
|
||||
this.accessToken = data.access_token
|
||||
this.refreshToken = data.refresh_token
|
||||
localStorage.setItem(ACCESS_KEY, this.accessToken)
|
||||
localStorage.setItem(REFRESH_KEY, this.refreshToken)
|
||||
},
|
||||
async fetchMe() {
|
||||
const { data } = await http.get('/me')
|
||||
this.user = data
|
||||
localStorage.setItem(USER_KEY, JSON.stringify(data))
|
||||
},
|
||||
restore() {
|
||||
this.accessToken = localStorage.getItem(ACCESS_KEY) || ''
|
||||
this.refreshToken = localStorage.getItem(REFRESH_KEY) || ''
|
||||
const u = localStorage.getItem(USER_KEY)
|
||||
if (u) this.user = JSON.parse(u)
|
||||
},
|
||||
logout() {
|
||||
this.accessToken = ''
|
||||
this.refreshToken = ''
|
||||
this.user = null
|
||||
localStorage.removeItem(ACCESS_KEY)
|
||||
localStorage.removeItem(REFRESH_KEY)
|
||||
localStorage.removeItem(USER_KEY)
|
||||
},
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user