fix: CI workflow, password reset flow, health/limits in services, session refactor, dead mixin stub cleanup

This commit is contained in:
loki5512344 2026-10-09 19:21:17 +02:00
parent 45dd592c40
commit f4e15b45c9
Signed by: boba
GPG key ID: 253067914055423B
95 changed files with 899 additions and 185 deletions

View file

@ -1,6 +1,6 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { createBrowserRouter, RouterProvider } from 'react-router'
import { SessionProvider } from '@features/auth/session'
import { SessionProvider } from '@features/auth/session/session'
import { ActiveThemeProvider } from '@features/themes/useActiveTheme'
import { routes } from './routes'

View file

@ -2,7 +2,7 @@ import { useCallback, useRef, useState, type DragEvent } from 'react'
import { useTranslation } from '@shared/i18n/react'
import { useNavigate } from 'react-router'
import { Button } from '@shared/ui/Button'
import { useSession } from '@features/auth/session'
import { useSession } from '@features/auth/session/useSession'
import { AvatarUpload } from './avatar/AvatarUpload'
/** The mod's profile panel: big avatar with an accent ring, nick, email and join date. The avatar circle doubles as a file drop target. */

View file

@ -2,7 +2,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'
import { useCallback, useEffect, useId, useState, type ChangeEvent } from 'react'
import { useTranslation } from '@shared/i18n/react'
import { Notice } from '@shared/ui/Notice'
import { useSession } from '@features/auth/session'
import { useSession } from '@features/auth/session/useSession'
import {
addAvatarHistory,
checkAvatarFile,

View file

@ -1,6 +1,6 @@
import { useTranslation } from '@shared/i18n/react'
import { Navigate, Outlet, useLocation } from 'react-router'
import { useSession } from './session'
import { useSession } from './session/useSession'
export function RequireAuth() {
const { t } = useTranslation('auth')

View file

@ -5,7 +5,7 @@ import { describeError } from '@shared/api/errors'
import { Button } from '@shared/ui/Button'
import { Notice } from '@shared/ui/Notice'
import { TextField } from '@shared/ui/text-field/TextField'
import { useSession } from '../session'
import { useSession } from '../session/useSession'
export function LoginForm() {
const { t } = useTranslation('auth')

View file

@ -5,7 +5,7 @@ import { describeError } from '@shared/api/errors'
import { Button } from '@shared/ui/Button'
import { Notice } from '@shared/ui/Notice'
import { TextField } from '@shared/ui/text-field/TextField'
import { useSession } from '../session'
import { useSession } from '../session/useSession'
import { validateEmail, validateNick, validatePassword } from '../validation'
export function RegisterForm() {

View file

@ -1,20 +1,10 @@
import { useQuery, useQueryClient } from '@tanstack/react-query'
import { createContext, useCallback, useContext, useMemo, type ReactNode } from 'react'
import { useCallback, useMemo, type ReactNode } from 'react'
import { api } from '@shared/api/client'
import type { Me } from '@shared/types'
import { fetchMe, login, logout, register } from './api'
import { fetchMe, login, logout, register } from '../api'
import { SessionContext, type Session } from './sessionContext'
import { hasSessionHint, setSessionHint } from './sessionHint'
type Session = {
status: 'loading' | 'anonymous' | 'authenticated'
me: Me | null
signIn(email: string, password: string): Promise<void>
signUp(email: string, password: string, nick: string): Promise<void>
signOut(): Promise<void>
}
const SessionContext = createContext<Session | null>(null)
export function SessionProvider({ children }: { children: ReactNode }) {
const client = useQueryClient()
// Restores the session after a reload: the refresh cookie survives, the
@ -64,9 +54,3 @@ export function SessionProvider({ children }: { children: ReactNode }) {
return <SessionContext value={value}>{children}</SessionContext>
}
export function useSession(): Session {
const session = useContext(SessionContext)
if (!session) throw new Error('useSession outside SessionProvider')
return session
}

View file

@ -0,0 +1,12 @@
import { createContext } from 'react'
import type { Me } from '@shared/types'
export type Session = {
status: 'loading' | 'anonymous' | 'authenticated'
me: Me | null
signIn(email: string, password: string): Promise<void>
signUp(email: string, password: string, nick: string): Promise<void>
signOut(): Promise<void>
}
export const SessionContext = createContext<Session | null>(null)

View file

@ -0,0 +1,8 @@
import { useContext } from 'react'
import { SessionContext, type Session } from './sessionContext'
export function useSession(): Session {
const session = useContext(SessionContext)
if (!session) throw new Error('useSession outside SessionProvider')
return session
}

View file

@ -1,7 +1,7 @@
import { screen } from '@testing-library/react'
import { expect, test } from 'vitest'
import { RequireAuth } from '../RequireAuth'
import { useSession } from '../session'
import { useSession } from '../session/useSession'
import { renderApp } from '@test/render'
import { json, mockFetch } from '@test/fetch'

View file

@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from 'react'
import { useTranslation } from '@shared/i18n/react'
import { prefersReducedMotion } from '@shared/motion/reducedMotion'
import { ButtonLink } from '@shared/ui/Button'
import { useSession } from '../auth/session'
import { useSession } from '../auth/session/useSession'
import { useInView } from './effects/useInView'
import '@features/landing/styles/sections.css'

View file

@ -6,7 +6,7 @@ import { Hero } from '../sections/hero/Hero'
import { createMemoryRouter, RouterProvider } from 'react-router'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { initI18n } from '@app/i18n'
import { SessionProvider } from '@features/auth/session'
import { SessionProvider } from '@features/auth/session/session'
import { json, mockFetch } from '@test/fetch'
test('reduced motion shows the full command at once', async () => {

View file

@ -4,7 +4,7 @@ import { useTranslation } from '@shared/i18n/react'
import { Link, useLocation } from 'react-router'
import { Notice } from '@shared/ui/Notice'
import { ApiError } from '@shared/api/client'
import { useSession } from '@features/auth/session'
import { useSession } from '@features/auth/session/useSession'
import { listSlots } from '@features/configs/api'
import { copyToSlot } from '../api'

View file

@ -1,6 +1,6 @@
import { useTranslation } from '@shared/i18n/react'
import { Link } from 'react-router'
import { useSession } from '@features/auth/session'
import { useSession } from '@features/auth/session/useSession'
import type { Me } from '../../types'
/** The pixel-heart mark, standalone (no badge background) — same shape as public/favicon.svg. */

View file

@ -1,7 +1,7 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { render } from '@testing-library/react'
import { createMemoryRouter, RouterProvider, type RouteObject } from 'react-router'
import { SessionProvider } from '@features/auth/session'
import { SessionProvider } from '@features/auth/session/session'
export function renderApp(routes: RouteObject[], initialPath = '/') {
const client = new QueryClient({ defaultOptions: { queries: { retry: false }, mutations: { retry: false } } })