Files
MeBox/web/src/stores/auth.ts
T
truewhile b0fe40142a Rebrand MMTL to MeBox (name, logo, Docker image) (#17)
* 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>
2026-09-02 16:26:28 +08:00

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()
}