Frontend архитектура
Frontend построен на Next.js 15 с React 19.
Структура проекта
frontend/
├── apps/
│ └── web/ # Основное приложение
│ ├── app/ # Next.js App Router
│ ├── components/ # React компоненты
│ ├── lib/ # Утилиты и конфигурация
│ │ ├── auth.ts # NextAuth конфигурация
│ │ └── api.ts # API клиент
│ └── actions/ # Server Actions
└── packages/
├── ui/ # Переиспользуемые UI компоненты
└── types/ # Сгенерированные TypeScript типы
Технологии
| Технология | Версия | Назначение |
|---|---|---|
| Next.js | 15.2 | React фреймворк |
| React | 19.0 | UI библиотека |
| NextAuth | 4.24 | Аутентификация |
| Tailwind CSS | 3.4 | Стилизация |
| React Hook Form | 7.54 | Формы |
| Zod | 3.24 | Валидация |
Аутентификация
NextAuth конфигурация
// lib/auth.ts
export const authOptions: NextAuthOptions = {
providers: [
CredentialsProvider({
credentials: {
email: { label: "Email", type: "email" },
password: { label: "Password", type: "password" }
},
async authorize(credentials) {
// POST /api/token/ для получения JWT
// Вернуть user с accessToken
}
})
],
callbacks: {
async jwt({ token, user }) {
// Добавить accessToken в JWT
},
async session({ session, token }) {
// Добавить accessToken в session
}
}
}
Защита маршрутов
// app/dashboard/page.tsx
import { getServerSession } from "next-auth"
import { redirect } from "next/navigation"
import { authOptions } from "@/lib/auth"
export default async function DashboardPage() {
const session = await getServerSession(authOptions)
if (!session) {
redirect("/login")
}
return <Dashboard />
}
API клиент
Генерация типов
Типы генерируются из OpenAPI схемы backend:
# Обновить типы
docker compose exec web pnpm openapi:generate
Использование
// lib/api.ts
import { ApiClient } from "@frontend/types"
export function getApiClient(accessToken?: string) {
return new ApiClient({
BASE: process.env.API_URL,
HEADERS: accessToken ? {
Authorization: `Bearer ${accessToken}`
} : {}
})
}
Server Actions
// actions/documents.ts
"use server"
import { getServerSession } from "next-auth"
import { getApiClient } from "@/lib/api"
export async function uploadDocument(formData: FormData) {
const session = await getServerSession(authOptions)
const client = getApiClient(session?.accessToken)
return client.documents.requestCreate({
files_list: formData.getAll("files")
})
}