# Система регистрации пользователей PocketBase ## Архитектура ``` ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ sign-up.astro │ -> │ sign-up.ts API │ -> │ PocketBase │ │ (форма) │ │ (создание) │ │ (запись) │ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ v ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ verify.astro │ -> │ confirm.ts API │ -> │ PocketBase │ │ (страница) │ │ (верификация) │ │ (обновление) │ └─────────────────┘ └─────────────────┘ └─────────────────┘ ``` ## Файловая структура ``` frontend/src/ ├── pages/ │ ├── auth/ │ │ ├── sign-up.astro # Форма регистрации │ │ ├── sign-in.astro # Форма входа │ │ └── verify.astro # Страница подтверждения │ └── api/ │ └── auth/ │ ├── sign-up.ts # API регистрации │ ├── sign-in.ts # API входа │ ├── confirm.ts # API подтверждения │ └── logout.ts # API выхода └── lib/ └── email.ts # Отправка писем ``` --- ## 1. Регистрация (sign-up.ts) ### Создание пользователя ```typescript import PocketBase from 'pocketbase'; const pb = new PocketBase(POCKETBASE_URL); // Создаём пользователя const record = await pb.collection('users').create({ firstName, lastName, email, phone, password, passwordConfirm: password, emailVisibility: true, }); ``` **Важно:** - `email` должен быть уникальным - `password` минимум 8 символов - `passwordConfirm` должен совпадать с `password` ### Генерация токена подтверждения ```typescript // Создаём свой токен (не от PocketBase) const token = Buffer.from(`${record.id}:${email}:${Date.now()}`) .toString('base64') .replace(/=/g, ''); // Формируем ссылку const verifyLink = `${SITE_URL}/auth/verify?token=${token}&userId=${record.id}`; ``` **Формат токена:** `userId:email:timestamp` ### Отправка письма ```typescript import { sendEmail, generateVerifyEmailHtml } from '@/lib/email'; const html = generateVerifyEmailHtml(firstName, verifyLink); await sendEmail({ to: email, subject: 'Подтверждение регистрации', html }); ``` --- ## 2. Подтверждение email (confirm.ts) ### Валидация токена ```typescript const decodeToken = (token: string) => { const decoded = Buffer.from(token, 'base64').toString('utf8'); const [userId, email, timestamp] = decoded.split(':'); // Проверяем срок (24 часа) const tokenTime = parseInt(timestamp); const now = Date.now(); const maxAge = 24 * 60 * 60 * 1000; if (now - tokenTime > maxAge) { throw new Error('Срок действия ссылки истёк'); } return { userId, email }; }; ``` ### Обновление verified Для обновления системного поля `verified` нужен админ-доступ: ```typescript // Аутентификация как superuser const authResponse = await fetch(`${PB_URL}/api/collections/_superusers/auth-with-password`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ identity: PB_ADMIN_EMAIL, password: PB_ADMIN_PASSWORD, }), }); const { token } = await authResponse.json(); // Обновляем пользователя await fetch(`${PB_URL}/api/collections/users/records/${userId}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}`, }, body: JSON.stringify({ verified: true }), }); ``` **Важно:** - Endpoint: `_superusers` (с подчёркиванием!) - Не `admins` или `/api/admins/...` ### Переменные окружения (.env) ```env POCKETBASE_URL=http://127.0.0.1:8090 PB_ADMIN_EMAIL=admin@example.com PB_ADMIN_PASSWORD=secret_password ``` --- ## 3. Страница подтверждения (verify.astro) ### Client-side логика ```astro ``` --- ## 4. Вход пользователя (sign-in.ts) ### Аутентификация ```typescript const pb = new PocketBase(POCKETBASE_URL); const authData = await pb.collection('users').authWithPassword(email, password); // После успешного входа console.log(pb.authStore.isValid); // true console.log(pb.authStore.token); // JWT токен console.log(pb.authStore.record); // данные пользователя ``` ### Проверка верификации ```typescript // При входе проверяем verified if (!authData.record.verified) { return new Response(JSON.stringify({ error: 'Email не подтверждён' }), { status: 401 }); } ``` --- ## Типичные ошибки ### 1. 404 при authWithPassword для админа **Проблема:** Используется неправильный endpoint ```javascript // ❌ Неправильно /api/admins/auth-with-password // ✅ Правильно /api/collections/_superusers/auth-with-password ``` ### 2. Нельзя указать verified при создании **Проблема:** `verified` — системное поле ```javascript // ❌ Ошибка pb.collection('users').create({ email, password, verified: true // Нельзя! }); ``` **Решение:** Обновлять через отдельный запрос с админ-доступом ### 3. Токен верификации от PocketBase **Проблема:** PocketBase `confirmVerification` требует специальный токен **Решение:** Создать свой API эндпоинт для валидации ### 4. CORS ошибки **Реш��ние:** Настроить CORS в PocketBase Admin UI --- ## PocketBase SDK методы ### Users ```typescript // Создать пользователя pb.collection('users').create(data) // Аутентифицировать pb.collection('users').authWithPassword(email, password) // Запросить верификацию pb.collection('users').requestVerification(email) // Подтвердить верификацию pb.collection('users').confirmVerification(token) // Обновить данные pb.collection('users').update(id, data) // Получить текущего пользователя pb.collection('users').authRefresh() ``` ### Admins ```typescript // Аутентификация superuser pb.admins.authWithPassword(email, password) // Имперсонация pb.collection('users').impersonate(userId, duration) ``` --- ## Checklist при создании системы регистрации - [ ] 1. Создать API endpoint `/api/auth/sign-up` - [ ] 2. Создать форму `sign-up.astro` - [ ] 3. Настроить email отправку в `lib/email.ts` - [ ] 4. Создать API endpoint `/api/auth/confirm` - [ ] 5. Создать страницу `verify.astro` - [ ] 6. Добавить переменные в `.env` - [ ] 7. Протестировать регистрацию - [ ] 8. Протестировать подтверждение - [ ] 9. Протестировать вход --- ## Ссылки - PocketBase Auth: https://pocketbase.io/docs.authentication - PocketBase SDK: https://github.com/pocketbase/js-sdk - PocketBase API: https://pocketbase.io/docs/api-records