> For the complete documentation index, see [llms.txt](https://docs.ipcheck.ing/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ipcheck.ing/developer/ru/development/coding-conventions.md).

# Соглашениям по коду

Это правила этой кодовой базы. Это не стилистические предпочтения для обсуждения в каждом pull request — именно по ним проверяют изменения, и они помогают сохранить читаемость проекта с двумя рантаймами и четырьмя языками.

## Язык

{% hint style="warning" %}
**Только JavaScript. Без TypeScript.**
{% endhint %}

Новые файлы — `.js` или `.vue`. Без `lang="ts"` на `<script setup>` блоке, без `.ts` модулей, без поэтапной миграции на TypeScript. В PR, который добавит TypeScript, попросят его убрать.

Комментарии в коде, сообщения коммитов и документация репозитория пишутся на **английском**. Локальные пакеты, разумеется, используют свой язык.

## Функции

Новые и переписанные функции используют **`const` стрелочный синтаксис**:

{% code title="принятый стиль" %}

```js
const isValidMAC = (address) => {
    const normalized = address.replace(/[:-]/g, '');
    return normalized.length === 12 && /^[0-9A-Fa-f]+$/.test(normalized);
};

const loadSecurityChecklist = async () => { /* … */ };
```

{% endcode %}

Два нюанса:

* **Методы объектов сохраняют сокращённый синтаксис.** `{ status(code) { … } }` остается как есть.
* **Стрелочные const не поднимаются.** Объявляйте их до кода, который их вызывает.

Это относится к коду, который вы пишете или переписываете. Не **не** массово преобразовывайте существующие `функция` объявления — дифф с кучей несвязанного стилистического шума сложнее проверять, чем функцию, которую он скрывает.

## Комментарии

Три правила, по важности:

1. **Каждый новый файл начинается с комментария в шапке, в котором указано его назначение.** Для API-обработчика это означает его маршрут и то, что он делает. Благодаря этому по остальной кодовой базе можно ориентироваться, не открывая каждый файл, — документация на уровне каталога намеренно заканчивается на «читайте комментарии в шапке».
2. **Крупные шаблоны и большие функции содержат блочные комментарии для каждого значимого участка.** 400-строчный `.vue` шаблон должен подсказывать, где заканчивается область ввода и начинается область результата.
3. **Комментарии описывают код таким, какой он есть сейчас.** Без пересказа журнала изменений: никаких «раньше мы делали X», никаких «это исправляет баг, когда…». За прошлое отвечает история Git. Комментарий, который объясняет *почему* было сделано неочевидное решение, полезен; комментарий, который пересказывает правку, его породившую, — это шум.

Комментарий должен быть короче кода, который он объясняет.

## Соглашения для фронтенда

Полная архитектура в [Frontend](/developer/ru/architecture/frontend.md). Правила, которые нужны при написании кода:

* **Composition API, `<script setup>`, везде.** Никакого Options API.
* **Псевдоним пути `@` → `frontend/`.** Импортируйте как `@/utils/valid-ip.js`, никогда с кучей `../`.
* **сначала shadcn-vue.** Проверьте `frontend/components/ui/` наличие уже готового примитива, затем каталог shadcn-vue в поисках того, что можно скопировать. Самописный Tailwind — это крайняя мера.
* **Только семантические дизайн-токены.** `bg-info`, `bg-action`, `text-muted-foreground`, и подобные — токены сами себя оформляют через тему. Никогда не пишите `dark:` двойные парные утилиты.
* **`console.*` на фронтенде это нормально.** Запрещено только на бэкенде (см. ниже).

### Куда помещать хелпер

Этот вопрос возникает почти в каждом изменении, поэтому ответ на него фиксирован:

<table><thead><tr><th width="200">Каталог</th><th>Что туда относится</th></tr></thead><tbody><tr><td><code>frontend/composables/</code></td><td>Логика, которой нужны реактивность или жизненный цикл Vue. Именуется <code>use-xxx.js</code>, экспортируя <code>useXxx()</code>.</td></tr><tr><td><code>frontend/utils/</code></td><td>Независимые от фреймворка хелперы и I/O. Никогда <code>use-</code> с префиксом.</td></tr><tr><td><code>frontend/lib/</code></td><td>только слой поддержки shadcn — сейчас лишь <code>cn()</code>. Не добавляйте туда ничего.</td></tr><tr><td><code>frontend/data/</code></td><td>Статическая конфигурация и реестры: инструменты, разделы, достижения, журнал изменений.</td></tr></tbody></table>

Одно уточнение: **чистая функция, которая живет рядом с composable, экспортируется из файла этого composable**, а не выносится в отдельный модуль в `utils/`. `ipFieldTone()` выходящая из `composables/use-status-tone.js` — это шаблон, который следует копировать.

## Общий код находится в `common/`

Все, что нужно обеим частям, помещается в `common/` — единый источник истины — а фронтенд обращается к нему через **тонкий мост повторного экспорта** в `utils/`поэтому фронтенд-импорты сохраняют привычный `@/utils/...` вид:

{% code title="frontend/utils/valid-ip.js" %}

```js
// Единый источник истины — common/valid-ip.js (общий с бэкендом).
// Этот файл существует как тонкий мост повторного экспорта, чтобы фронтенд-код мог по-прежнему писать
// `import { isValidIP } from '@/utils/valid-ip.js'` без заботы о том, где
// находится реализация.
export { isValidIP, isIPv6, isValidDomain } from '../../common/valid-ip.js';
```

{% endcode %}

`frontend/utils/fetch-with-timeout.js` следует той же схеме. Когда вы добавляете мост, добавляйте спецификацию, которая импортирует **оба** пути и проверяет, что они совпадают — `tests/valid-ip.test.js` делает именно это, и именно поэтому мост не обзаводится тихо второй реализацией.

## Соглашения для бэкенда

Полная картина в [Backend](/developer/ru/architecture/backend.md). Правила, которые реально важны:

### Форма обработчика

Один файл на маршрут в `api/`, с единственным экспортом по умолчанию:

```js
export default async (req, res) => {
    // читать req.query / req.body, вызывать upstream, отправлять один ответ
};
```

Форма ошибки краткая и единообразная: `400` при неверном вводе, `res.status(500).json({ error: error.message })` при сбое upstream. Фронтенд не отображает их дословно.

### Никогда не `fetch()`

Каждый исходящий HTTP-запрос из `api/` проходит через `fetchUpstream` с `common/fetch-with-timeout.js`. Он применяет таймаут в 8 секунд и значение по умолчанию `User-Agent`. Зависший upstream-провайдер должен отваливаться по таймауту, а не держать соединение открытым.

### Гарды, а не встроенные проверки

Контроль доступа и проверка параметров живут в middleware (`common/guards.js`), подключаемом в `backend-server.js`. Обработчики никогда их не дублируют:

* `requireReferer` — глобально в `/api/*`
* `requireValidIP()` / `requireValidDomain()` / `requireValidPrefix()` / `requireValidASN()` / `requireValidProviderId()` / `requireValidReportId()` — для каждого маршрута

Новый формат параметра означает новый гард в `common/guards.js`, а не явную проверку в начале обработчика.

### Логирование

{% hint style="danger" %}
**`console.*` запрещено в файлах бэкенда.** Всегда используйте общий pino-логгер из `common/logger.js`.
{% endhint %}

Pino ориентирован на контекст — сначала объект, потом короткое сообщение:

{% code title="принятый стиль" %}

```js
import logger from '../common/logger.js';

logger.error({ err: e, mac: macAddress }, 'mac-checker handler failed');
logger.warn({ err: e, query }, 'whois: RDAP IP lookup failed, trying WHOIS');
```

{% endcode %}

Еще два правила:

* **Обработчики никогда не пишут строки «received request».** Логирование на каждый запрос — `pino-http`— задача `/api` только когда `LOG_HTTP=true`.
* **Строки только для запуска начинаются с эмодзи** — 🚀 listening, 📦 ready, 📥 downloading, 🛡️ security, 🐢 throttling, 🗓️ schedule, ⚠️ recoverable, ❌ failure. Логи на каждый запрос остаются обычными.

Конфигурация — `LOG_LEVEL` (по умолчанию `info`), `LOG_FORMAT=json` для лог-шейперов, а `LOG_HTTP=true`. Нет никакого `NODE_ENV` где-либо в этом проекте — см. [Логирование](/developer/ru/configuration/logging.md).

### Кэширование на границе

Каждый `/api/*` ответ по умолчанию имеет `Cache-Control: no-store`. Медленно меняющиеся публичные маршруты включают это через `cacheable(maxAgeSeconds)` middleware в `backend-server.js`. Пишите TTL как выражения умножения (`24 * 60 * 60`), а не как сырые секунды. Сами обработчики никогда не трогают `Cache-Control`, а индивидуальные или аутентифицированные эндпоинты никогда не оборачиваются.

## Что идет вместе с вашим изменением

Две вещи обязательны, и обе проверяются:

* **покрытие i18n.** Все, что выводит текст, попадает в **все четыре локали** (`en` / `zh` / `fr` / `ru`) в рамках того же изменения, включая `frontend/data/changelog.json` запись. См. [i18n](/developer/ru/development/i18n.md).
* **Тесты.** Любая невизуальная логика, которую можно прогнать без сетевого вызова, поставляется со спецификацией в `tests/`, в рамках того же изменения. Обновляйте затронутые спецификации при изменении поведения — не откладывайте. См. [Тестирование](/developer/ru/development/testing.md).

Затем запустите `pnpm check`. Перед передачей изменения оно должно быть зелёным.

## Далее

* [Добавление нового инструмента](/developer/ru/development/adding-a-new-tool.md) — всё вышеописанное, применённое от начала до конца.
* [Как внести вклад](/developer/ru/contributing/how-to-contribute.md) — ветки, коммиты и ожидания от PR.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.ipcheck.ing/developer/ru/development/coding-conventions.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
