mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-30 03:36:37 +08:00
b0fe40142a
* Rebrand MMTL to MeBox across codebase and assets Rename the project display name, Go module path, environment variable prefix (MEBOX_*), Docker image references, and UI branding from MMTL/mmtl to MeBox/mebox. Replace logo assets with the new MeBox icon and keep legacy SQLite migration support for existing mmtl.db deployments. Co-authored-by: truewhile <truewhile@users.noreply.github.com> * Fix logo icons: use cube-only crop without truncated text Previous icon generation cropped too much of the source image, including partial MeBox wordmark text that was cut off in square icon containers. Regenerate logo-64/192/512, favicon, and SVG from cube-only region. Co-authored-by: truewhile <truewhile@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: truewhile <truewhile@users.noreply.github.com>
91 lines
2.5 KiB
TypeScript
91 lines
2.5 KiB
TypeScript
import { create } from 'zustand'
|
|
import { persist } from 'zustand/middleware'
|
|
|
|
import type { User } from '../types'
|
|
import { refreshToken } from '../api/refresh'
|
|
|
|
// Single source of truth for the authenticated user + JWT.
|
|
// Persisted to localStorage so a page reload does not drop the session.
|
|
interface AuthState {
|
|
token: string | null
|
|
refreshToken: string | null
|
|
user: User | null
|
|
tier: string
|
|
setSession: (token: string, refreshToken: string, user: User) => void
|
|
setUser: (user: User) => void
|
|
setToken: (token: string) => void
|
|
setRefreshToken: (refreshToken: string) => void
|
|
logout: () => void
|
|
tokenRefresh: () => Promise<boolean>
|
|
}
|
|
|
|
export const useAuthStore = create<AuthState>()(
|
|
persist(
|
|
(set, get) => ({
|
|
token: null,
|
|
refreshToken: null,
|
|
user: null,
|
|
tier: 'free',
|
|
setSession: (token, refreshToken, user) => set({
|
|
token,
|
|
refreshToken,
|
|
user,
|
|
tier: user.tier || 'free'
|
|
}),
|
|
setUser: (user) => set({ user, tier: user.tier || 'free' }),
|
|
setToken: (token) => set({ token }),
|
|
setRefreshToken: (refreshToken) => set({ refreshToken }),
|
|
logout: () => set({ token: null, refreshToken: null, user: null, tier: 'free' }),
|
|
tokenRefresh: async () => {
|
|
const rt = get().refreshToken
|
|
if (!rt) {
|
|
return false
|
|
}
|
|
try {
|
|
const resp = await refreshToken(rt)
|
|
set({
|
|
token: resp.token,
|
|
refreshToken: resp.refresh_token
|
|
})
|
|
return true
|
|
} catch {
|
|
// Refresh failed, need to logout
|
|
set({ token: null, refreshToken: null, user: null, tier: 'free' })
|
|
return false
|
|
}
|
|
},
|
|
}),
|
|
{
|
|
name: 'mebox-auth',
|
|
partialize: (state) => ({
|
|
token: state.token,
|
|
refreshToken: state.refreshToken,
|
|
user: state.user,
|
|
tier: state.tier
|
|
}),
|
|
},
|
|
),
|
|
)
|
|
|
|
// Helper function to check if user is authenticated
|
|
export function isAuthenticated(): boolean {
|
|
return useAuthStore.getState().token !== null
|
|
}
|
|
|
|
// Helper function to check if user is admin
|
|
export function isAdmin(): boolean {
|
|
const user = useAuthStore.getState().user
|
|
return user?.role === 'admin'
|
|
}
|
|
|
|
// Helper function to check if user is plus
|
|
export function isPlus(): boolean {
|
|
const state = useAuthStore.getState()
|
|
return state.tier === 'plus' || state.user?.role === 'admin'
|
|
}
|
|
|
|
// Helper function to check if user is super user (admin or plus)
|
|
export function isSuperUser(): boolean {
|
|
return isAdmin() || isPlus()
|
|
}
|