Перейти к основному содержимому

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.js15.2React фреймворк
React19.0UI библиотека
NextAuth4.24Аутентификация
Tailwind CSS3.4Стилизация
React Hook Form7.54Формы
Zod3.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")
})
}

Packages

@frontend/ui

Переиспользуемые UI компоненты:

// packages/ui/src/Button.tsx
export function Button({ children, variant, ...props }) {
return (
<button
className={cn("px-4 py-2 rounded", variants[variant])}
{...props}
>
{children}
</button>
)
}

Установка в приложение:

pnpm --filter web add @frontend/ui

@frontend/types

Сгенерированные TypeScript типы из OpenAPI:

// Автоматически генерируется
export type Request = {
id: string
status: "PENDING" | "COMPLETED" | "PARTIAL_FAILURE"
tasks: Task[]
created_at: string
}

Разработка

Запуск dev сервера

# Через Docker
docker compose up web

# Локально
cd frontend
pnpm install -r
pnpm --filter web dev

Добавление зависимостей

# Глобальная зависимость
docker compose exec web pnpm add package-name -w

# Для конкретного приложения
docker compose exec web pnpm --filter web add package-name

Линтинг

# Biome (formatter + linter)
biome check .