Files
shop-server/docs/superpowers/specs/2026-05-23-ip-gate-access-control-design.md
T
2026-05-23 10:56:08 +05:00

94 lines
4.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# IP-gate: ограничение доступа на время разработки
## Задача
Сайт доступен на реальном домене (через VPS + NPM + Netbird), но находится в активной разработке/тестировании. Нужно ограничить доступ, не мешая разработке и полному тестированию функционала (включая OAuth и webhook-и).
## Решение
IP-whitelist на уровне Fastify (`onRequest` хук). Только запросы с разрешённых IP проходят. Внешние webhook-и и OAuth callback-и исключены из проверки.
## Конфигурация
### `.env`
```env
SITE_ACCESS_IPS=1.2.3.4,5.6.7.8
```
- Не задана или пуста — защита **отключена**
- IP через запятую, пробелы игнорируются (трим)
- `request.ip` возвращает реальный IP клиента благодаря `trustProxy: true`
## Архитектура
### Новый плагин: `server/src/plugins/ip-gate.js`
Регистрируется в `server/src/index.js` **перед** всеми маршрутами.
```js
fastify.register(async function ipGate(fastify, opts) {
fastify.addHook('onRequest', async (request, reply) => {
// защита выключена
// путь в исключениях
// ip в списке
// иначе 403
})
})
```
### Логика `onRequest`
1. `SITE_ACCESS_IPS` пуст → `return` (пропустить)
2. Путь запроса в списке исключений → `return`
3. `request.ip` есть в `SITE_ACCESS_IPS``return`
4. Иначе → `reply.code(403).type('text/html').send(htmlPage)`
### Исключения
Маршруты, которые должны работать всегда (их вызывают внешние сервисы, а не браузер тестировщика):
| Путь | Причина |
|---|---|
| `/api/auth/oauth/vk/callback` | VK OAuth callback |
| `/api/auth/oauth/yandex/callback` | Yandex OAuth callback |
| `/api/webhooks/yookassa` | YooKassa payment webhook |
| `/api/admin/notifications/telegram/webhook` | Telegram webhook |
Статика (загружается браузером тестировщика, поэтому тоже проверяется):
- `/uploads/*` и `/uploads-resized/*`**не** исключаем, блокируются вместе со всем остальным
### 403-страница
HTML-страница с информацией о магазине и статусе разработки:
- Название: «Любимый Креатив»
- Подзаголовок: «Изделия ручной работы: вещи с характером и вниманием к деталям»
- Сообщение: «Сайт находится в разработке и скоро будет доступен»
- Показывает IP посетителя (чтобы можно было сообщить для добавления в whitelist)
- Минимальная стилизация (чистый HTML + inline CSS, без внешних ресурсов)
## Точки регистрации
В `server/src/index.js`:
```js
// после trustProxy, перед маршрутами
await fastify.register(require('./plugins/ip-gate'))
```
## Тестирование
- **Юнит-тесты**: `server/src/plugins/__tests__/ip-gate.test.js`
- IP в списке → запрос проходит
- IP не в списке → 403
- Путь-исключение → проходит с любым IP
- `SITE_ACCESS_IPS` не задан → защита выключена
- Пробелы в списке IP → корректная работа
## Включение/выключение
- **Включить**: задать `SITE_ACCESS_IPS` в `.env`
- **Выключить**: удалить `SITE_ACCESS_IPS` или оставить пустым
- Перезапуск сервера не требуется если используется `node --watch`