Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .cursor/33-style.mdc
Original file line number Diff line number Diff line change
@@ -1,3 +1,42 @@
---
description: Style rules for Hawk monorepo - always applied
alwaysApply: true
---

1. Write clean, readable code
2. Do not duplicate code blocks, reuse existing code if possible
3. Write simple yet straightforward comments to each logic block, function, class, propert, interface, constant, etc.
4. Always use block-style comments. Even for one liners.
6. Common types that are used across Garage / API / Workers / Catchers should be placed in [@hawk.so/types](../types) package.

## Classes

Do not create and export class instance in the same file where class is defined.
Create a separate file for the class instance and export it.

Do not do like that:

```typescript
export class MyClass {
constructor() {
// ...
}
}

export const myClassInstance = new MyClass();
```

Do like that:

```typescript
export class MyClass {
constructor() {
// ...
}
}
```

## TypeScript

- Do not cast types to `any` and `unknown`.
- Place private methods below public methods in classes.
12 changes: 12 additions & 0 deletions .cursor/45-api-rules.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
alwaysApply: true
globs: ["api/**"]
---

- Для отладочных логов в консоль использовать метод `sgr` для цветного вывода важных данных.

Пример:

```ts
this.log('warn', 'SSO not enabled for workspace:', sgr(workspaceId, Effect.ForegroundCyan));
```
16 changes: 0 additions & 16 deletions .cursor/55-garage.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,6 @@ garage/
└── config/ # Configuration
```

## Components

- Vue 3 components (`.vue` files)
- Always use `<script setup>` and TypeScript for new components.

## State Management (Vuex)

Vuex store modules in `src/store/modules/`:
Expand All @@ -49,17 +44,6 @@ Vuex store modules in `src/store/modules/`:

Actions types are defined in `actionTypes.js` files of modules.

## API calls

- Always use `api.call()` for API calls. Never use `api.callOld()`.
- API responses should be handled on Store level. Filter and handle errors there.


## Internationalization

- Always move all UI tokens to i18n.
- Always fill all translations in `messages/` files.

## Styles

- Global styles in `src/styles/`
Expand Down
18 changes: 18 additions & 0 deletions .cursor/56-garage-rules.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
alwaysApply: true
globs: ["garage/**"]
---

- Always use `UiButton` component for buttons.
- Always use `<script setup>` for components.
- Always use TypeScript for components.

## API calls

- Always use `api.call()` for API calls. Never use `api.callOld()`.
- API responses should be handled on Store (Vuex) level. Filter and handle errors there.

## Internationalization

- Always move all UI tokens to i18n.
- Always fill all translations in `messages/` files.
223 changes: 223 additions & 0 deletions docs/redis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
# Redis ключи, используемые в пакете @collector/

Данный документ описывает все ключи Redis, которые используются в пакете `collector` для хранения данных и управления состоянием системы.

## Обзор

Пакет `collector` использует Redis для следующих целей:
- Управление заблокированными проектами
- Управление черным списком IP-адресов
- Отслеживание количества запросов с IP-адресов
- Ограничение частоты запросов (rate limiting) для проектов

## Список ключей

| Значение по умолчанию | Тип Redis | Назначение | Переменная окружения |
|----------------------|-----------|------------|----------------------|
| `DisabledProjectsSet` | Set | ID заблокированных проектов | `REDIS_DISABLED_PROJECT_SET` |
| `BlacklistIPsSet` | Set | Черный список IP-адресов | `REDIS_BLACKLIST_IP_SET` |
| `AllIPsMap` | Hash | Все IP и накопительные счетчики запросов | `REDIS_ALL_IPS_MAP` |
| `CurrentPeriodMap` | Hash | IP и счетчики запросов за текущий период | `REDIS_CURRENT_PERIOD_MAP` |
| `rate_limits` (хардкод) | Hash | Rate limiting по проектам (поле: project_id, значение: `timestamp:count`) | — |

## Ключи Redis

### 1. `REDIS_DISABLED_PROJECT_SET` (blockedIDsSetName)

**Тип:** Redis Set
**Переменная окружения:** `REDIS_DISABLED_PROJECT_SET`
**Значение по умолчанию:** `DisabledProjectsSet`

**Описание:**
Содержит множество ID проектов, которые были заблокированы или отключены. Используется для проверки, может ли проект отправлять события.

**Операции:**
- **Чтение:** `SSCAN` - загрузка всех ID заблокированных проектов (метод `LoadBlockedIDs()`)
- **Период обновления:** Настраивается через `BLOCKED_PROJECTS_UPDATE_PERIOD` (по умолчанию 5 секунд)

**Использование:**
- Проверка блокировки проекта перед обработкой ошибок и релизов
- Метод `IsBlocked(projectId)` проверяет наличие проекта в этом множестве

**Пример значения:**
```
SADD DisabledProjectsSet "6762b5db032b200023854b2c"
SADD DisabledProjectsSet "6762b5db032b200023854b2d"
```

---

### 2. `REDIS_BLACKLIST_IP_SET` (blacklistSetName)

**Тип:** Redis Set
**Переменная окружения:** `REDIS_BLACKLIST_IP_SET`
**Значение по умолчанию:** `BlacklistIPsSet`

**Описание:**
Содержит множество IP-адресов, которые находятся в черном списке. IP-адреса добавляются в черный список, когда они превышают порог запросов (`BLACKLIST_THRESHOLD`).

**Операции:**
- **Чтение:** `SSCAN` - загрузка всех IP-адресов из черного списка (метод `LoadBlacklist()`)
- **Период обновления:** Настраивается через `BLACKLIST_UPDATE_PERIOD` (по умолчанию 15 секунд)

**Использование:**
- Проверка IP-адреса перед обработкой любого запроса
- Метод `CheckBlacklist(ip)` проверяет наличие IP в черном списке
- Если IP в черном списке, запрос отклоняется с кодом 429 (Too Many Requests)

**Пример значения:**
```
SADD BlacklistIPsSet "192.168.1.100"
SADD BlacklistIPsSet "10.0.0.50"
```

---

### 3. `REDIS_ALL_IPS_MAP` (allIPsMapName)

**Тип:** Redis Hash
**Переменная окружения:** `REDIS_ALL_IPS_MAP`
**Значение по умолчанию:** `AllIPsMap`

**Описание:**
Хранит накопительный счетчик запросов для всех IP-адресов с момента начала работы системы. Используется для долгосрочной статистики и мониторинга.

**Структура:**
- **Ключ (field):** IP-адрес (строка)
- **Значение:** Количество запросов (число)

**Операции:**
- **Запись:** `HINCRBY` - инкремент счетчика для IP-адреса (метод `IncrementIP()`)
- Счетчик увеличивается при каждом запросе

**Использование:**
- Накопительная статистика по IP-адресам
- Используется для анализа трафика и выявления подозрительной активности

**Пример значения:**
```
HSET AllIPsMap "192.168.1.100" 15000
HSET AllIPsMap "10.0.0.50" 8500
```

---

### 4. `REDIS_CURRENT_PERIOD_MAP` (currentPeriodMapName)

**Тип:** Redis Hash
**Переменная окружения:** `REDIS_CURRENT_PERIOD_MAP`
**Значение по умолчанию:** `CurrentPeriodMap`

**Описание:**
Хранит счетчики запросов для IP-адресов за текущий период. Этот ключ периодически очищается (удаляется) при обновлении черного списка, и данные из него используются для определения IP-адресов, которые превысили порог запросов.

**Структура:**
- **Ключ (field):** IP-адрес (строка)
- **Значение:** Количество запросов за текущий период (число)

**Операции:**
- **Запись:** `HINCRBY` - инкремент счетчика для IP-адреса (метод `IncrementIP()`)
- **Чтение:** `HKEYS` и `HVALS` - получение всех IP-адресов и их счетчиков (метод `LoadBlacklist()`)
- **Удаление:** `DEL` - полное удаление ключа после обработки (метод `updateBlacklist()`)

**Использование:**
- Отслеживание активности IP-адресов за текущий период
- При обновлении черного списка проверяется, какие IP превысили `BLACKLIST_THRESHOLD`
- После обработки ключ удаляется, и начинается новый период подсчета

**Пример значения:**
```
HSET CurrentPeriodMap "192.168.1.100" 5000
HSET CurrentPeriodMap "10.0.0.50" 12000
```

**Логика работы:**
1. При каждом запросе счетчик IP увеличивается в `CurrentPeriodMap`
2. Периодически (каждые 15 секунд по умолчанию) вызывается `UpdateBlacklist()`
3. Из `CurrentPeriodMap` извлекаются все IP и их счетчики
4. IP с счетчиком >= `BLACKLIST_THRESHOLD` добавляются в `BlacklistIPsSet`
5. Ключ `CurrentPeriodMap` полностью удаляется
6. Начинается новый период подсчета

---

### 5. `rate_limits`

**Тип:** Redis Hash
**Переменная окружения:** Не настраивается (хардкод)
**Значение:** `rate_limits`

**Описание:**
Хранит данные об ограничении частоты запросов (rate limiting) для каждого проекта. Используется для контроля количества событий, которые может отправить проект в определенный период времени.

**Структура:**
- **Ключ (field):** ID проекта (строка)
- **Значение:** Строка в формате `"timestamp:count"`, где:
- `timestamp` - Unix timestamp начала текущего окна времени
- `count` - количество событий в текущем окне

**Операции:**
- **Запись/Чтение:** Выполняется через Lua-скрипт атомарно (метод `UpdateRateLimit()`)
- Lua-скрипт выполняет:
- Проверку существования записи
- Проверку, не истекло ли окно времени
- Проверку, не превышен ли лимит
- Инкремент счетчика или создание новой записи

**Использование:**
- Проверка и обновление rate limit для каждого проекта перед обработкой события
- Используется в обработчиках ошибок (`errorshandler`) и релизов (`releasehandler`)
- Если лимит превышен, возвращается ошибка 402 с сообщением "Rate limit exceeded"

**Параметры rate limiting:**
- `EventsLimit` - максимальное количество событий в периоде (N)
- `EventsPeriod` - окно времени в секундах (T)

**Пример значения:**
```
HSET rate_limits "6762b5db032b200023854b2c" "1737483572:5"
HSET rate_limits "6762b5db032b200023854b2d" "1737483600:12"
```

**Логика работы:**
1. При получении события проверяется rate limit для проекта
2. Если записи нет - создается новая с текущим timestamp и count=1
3. Если запись есть:
- Проверяется, не истекло ли окно времени (текущее время - timestamp >= period)
- Если окно истекло - создается новая запись с текущим timestamp и count=1
- Если окно не истекло - проверяется, не превышен ли лимит (count + 1 > limit)
- Если лимит не превышен - счетчик увеличивается
- Если лимит превышен - возвращается false

**Приоритет лимитов:**
Rate limits настраиваются на трех уровнях (в порядке приоритета):
1. Уровень проекта (project level) - индивидуальные лимиты для проекта
2. Уровень workspace (workspace level) - лимиты для всех проектов в workspace
3. Уровень плана (plan level) - лимиты по умолчанию из тарифного плана

Лимиты хранятся в MongoDB в поле `rateLimitSettings` коллекций `plans`, `workspaces`, `projects`.

---

## Конфигурация

Все ключи Redis (кроме `rate_limits`) настраиваются через переменные окружения:

| Переменная | Описание | Значение по умолчанию |
|------------|----------|----------------------|
| `REDIS_URL` | Адрес Redis сервера | `localhost:6379` |
| `REDIS_PASSWORD` | Пароль для Redis | (пусто) |
| `REDIS_DISABLED_PROJECT_SET` | Имя Set для заблокированных проектов | `DisabledProjectsSet` |
| `REDIS_BLACKLIST_IP_SET` | Имя Set для черного списка IP | `BlacklistIPsSet` |
| `REDIS_ALL_IPS_MAP` | Имя Hash для всех IP и счетчиков | `AllIPsMap` |
| `REDIS_CURRENT_PERIOD_MAP` | Имя Hash для IP текущего периода | `CurrentPeriodMap` |
| `BLOCKED_PROJECTS_UPDATE_PERIOD` | Период обновления списка заблокированных проектов | `5s` |
| `BLACKLIST_UPDATE_PERIOD` | Период обновления черного списка | `15s` |
| `BLACKLIST_THRESHOLD` | Порог запросов для блокировки IP | `10000` |

## Примечания

- Все операции с Redis выполняются с использованием контекста для управления таймаутами
- При загрузке данных из Redis используется exponential backoff с максимальным временем ожидания 3 минуты
- Операции с rate limiting выполняются атомарно через Lua-скрипты для обеспечения консистентности данных
- Ключ `CurrentPeriodMap` периодически полностью удаляется и создается заново для нового периода подсчета
Loading