Implement rate limiting and enhance authentication flow
CI / Backend (build + test) (push) Successful in 1m17s
CI / Frontend (lint + typecheck + build) (push) Successful in 35s

- Added rate limiting configuration for authentication endpoints, allowing customizable request limits via environment variables.
- Updated authentication flow to utilize HttpRequest for cookie management, ensuring secure handling of refresh tokens.
- Introduced a new endpoint to retrieve user subscription details.
- Enhanced the handling of Telegram bot token validation to prevent errors with empty tokens.
- Updated the application to serialize enums as strings for better documentation and compatibility with TypeScript.
- Improved test coverage for new features and adjustments in command handlers.
This commit is contained in:
Leonid Pershin
2026-07-02 12:40:23 +03:00
parent ed07221ca5
commit 8067be3c35
106 changed files with 8823 additions and 172 deletions
+51
View File
@@ -0,0 +1,51 @@
import { createContext, useContext, useEffect, useState, type ReactNode } from 'react'
export type Theme = 'light' | 'dark' | 'system'
type ThemeContextValue = {
theme: Theme
setTheme: (theme: Theme) => void
}
const STORAGE_KEY = 'pnv-theme'
const ThemeContext = createContext<ThemeContextValue | undefined>(undefined)
function resolve(theme: Theme): 'light' | 'dark' {
if (theme === 'system') {
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
}
return theme
}
function applyTheme(theme: Theme) {
const root = document.documentElement
root.classList.toggle('dark', resolve(theme) === 'dark')
}
export function ThemeProvider({ children }: { children: ReactNode }) {
const [theme, setThemeState] = useState<Theme>(
() => (localStorage.getItem(STORAGE_KEY) as Theme | null) ?? 'system',
)
useEffect(() => {
applyTheme(theme)
if (theme !== 'system') return
const media = window.matchMedia('(prefers-color-scheme: dark)')
const onChange = () => applyTheme('system')
media.addEventListener('change', onChange)
return () => media.removeEventListener('change', onChange)
}, [theme])
const setTheme = (next: Theme) => {
localStorage.setItem(STORAGE_KEY, next)
setThemeState(next)
}
return <ThemeContext value={{ theme, setTheme }}>{children}</ThemeContext>
}
export function useTheme(): ThemeContextValue {
const ctx = useContext(ThemeContext)
if (!ctx) throw new Error('useTheme must be used within ThemeProvider')
return ctx
}