# Система регистрации пользователей 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 ошибки

**Реш��ние:** Настроить 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