astro_avtourist/REGISTRATION_SYSTEM.md

313 lines
8.9 KiB
Markdown
Raw Permalink Normal View History

# Система регистрации пользователей 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
<script>
async function verifyEmail() {
const urlParams = new URLSearchParams(window.location.search);
const token = urlParams.get('token');
const userId = urlParams.get('userId');
const response = await fetch('/api/auth/confirm', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token, userId }),
});
const data = await response.json();
if (response.ok && data.success) {
// Показать успех
} else {
// Показать ошибку
}
}
if (token && userId) {
verifyEmail();
}
</script>
```
---
## 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 ошибки
**Реш<D0B5><D188>ние:** Настроить 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