> 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/architecture/project-structure.md).

# Структура проекта

MyIP — это единый репозиторий, который включает в себя **два процесса Node** и содержит **три слоя кода**. Больше ничего. Как только вы удержите эту картину в голове, у каждого файла в дереве будет очевидное место.

## Два процесса

| Процесс            | Файл                 | Порт по умолчанию         | Задача                                                                                                   |
| ------------------ | -------------------- | ------------------------- | -------------------------------------------------------------------------------------------------------- |
| Статический сервер | `frontend-server.js` | `18966` (`FRONTEND_PORT`) | Раздаёт собранное SPA из `dist/`, проксирует `/api` запросы на бэкенд, обрабатывает fallback истории SPA |
| API-сервер         | `backend-server.js`  | `11966` (`BACKEND_PORT`)  | Приложение Express 5 — всё `/api/*` маршруты, guards, ограничение частоты, офлайн-датасеты               |

`pnpm start` запускает оба через `concurrently`. В production они обычно управляются через pm2 (`ecosystem.config.cjs` определяет `myip-frontend` и `myip-backend`) или Docker-образом, который запускает `npm start` в одном контейнере и открывает только `18966`.

```mermaid
flowchart LR
    B["Браузер"]
    F["frontend-server.js :18966<br/>статика dist/ + fallback SPA"]
    A["backend-server.js :11966<br/>API Express 5"]
    U["Внешние провайдеры<br/>ipinfo.io, ip-api.com, RIPEstat, OONI"]
    D["Локальные наборы данных<br/>MaxMind mmdb, CAIDA as2org / as-rel"]

    B -->|"GET / , /tools/whois , /assets/*"| F
    B -->|"GET /api/*"| F
    F -->|"http-proxy-middleware"| A
    A -->|"fetchUpstream, таймаут 8 с"| U
    A --> D
```

{% hint style="info" %}
Из Интернета должен быть доступен только порт фронтенда. Бэкенд слушает на `11966` для прокси; держите его на том же хосте или в частной сети. См. [Обратный прокси и домены](/developer/ru/getting-started/reverse-proxy-and-domains.md).
{% endhint %}

## Три слоя кода

| Каталог     | Где выполняется    | Содержимое                                                                                                        |
| ----------- | ------------------ | ----------------------------------------------------------------------------------------------------------------- |
| `frontend/` | Браузер            | Vue 3 SPA — компоненты, роутер, хранилище Pinia, локали, реестр инструментов                                      |
| `api/`      | Node               | По одному модулю обработчика Express на маршрут, и ничего больше                                                  |
| `common/`   | Node **и** браузер | Код, общий для обеих частей: валидаторы, обёртка для fetch, guards, логгер, сервисы MaxMind / CAIDA, схема отчёта |

`common/` — единственный слой, пересекающий границу. Модули, которые нужны и браузеру (`valid-ip.js`, `fetch-with-timeout.js`, `report-schema.js`) остаются без `fs` и `process` доступа, и переэкспортируются через тонкие мосты в `frontend/utils/` так что код приложения по-прежнему импортирует `@/utils/...`. Вещи, относящиеся только к Node и нарушающие это правило, живут в отдельном файле — например, User-Agent для внешних запросов собирается в `common/upstream-ua.js` (который читает `package.json` с диска) и внедряется в `common/fetch-with-timeout.js` при запуске.

## Дерево с комментариями

```
.
├── backend-server.js       Приложение Express: таблица маршрутов, порядок middleware, cacheable()
├── frontend-server.js      Статический сервер + прокси /api + fallback истории SPA
├── sentry-instrument.js    Инициализация Sentry для бэкенда, загружается через `node --import`
├── ecosystem.config.cjs    определения процессов pm2 (содержит флаг --import)
├── index.html              Точка входа Vite / оболочка SPA
├── vite.config.js          Конфиг сборки: алиасы, ручные чанки, dev-прокси
├── Dockerfile              Сборка в два этапа (см. ниже)
│
├── frontend/               Vue 3 SPA  → см. Frontend
│   ├── App.vue             Тонкая оболочка: глобальные провайдеры + <router-view>
│   ├── main.js             Bootstrap + динамическая инициализация по env
│   ├── store.js            Основное хранилище Pinia
│   ├── router/             Таблица маршрутов
│   ├── data/               Статические реестры: инструменты, разделы, достижения, IP-базы
│   ├── components/         Главная / StandaloneTool / разделы / advanced-tools / ui
│   ├── composables/        Логика `useXxx`, учитывающая Vue
│   ├── utils/              Хелперы, не зависящие от фреймворка (шина событий, getips/, …)
│   └── locales/            en / zh / fr / ru + подпакеты по требованию
│
├── api/                    По одному обработчику на маршрут  → см. Backend
│
├── common/                 Общий код
│   ├── guards.js           middleware для проверки параметров
│   ├── fetch-with-timeout.js  fetchWithTimeout (5 с) / fetchUpstream (8 с)
│   ├── logger.js           singleton pino
│   ├── maxmind-service.js  Локальные считыватели GeoLite2 + поиск
│   ├── maxmind-updater.js  Плановая загрузка GeoLite2
│   ├── caida-updater.js    Плановая загрузка as2org / as-rel
│   ├── as-org-db.js        Поиск CAIDA AS → организация
│   ├── as-rel-db.js        Связи CAIDA AS (граф p2c)
│   ├── service-status-*.js Список провайдеров, опросчик, преобразование ответа
│   ├── maxmind-db/         GeoLite2-City.mmdb · GeoLite2-ASN.mmdb
│   ├── as-org-db/          as-org2info.txt
│   └── as-rel-db/          as-rel2.txt
│
├── tests/                  Спеки для Node test runner (`node --test`)
└── dist/                   Результат сборки (генерируется, не коммитится)
```

## Как проходит запрос

**Загрузка страницы.** Браузер запрашивает `frontend-server.js` URL.

1. `/api/*` сначала перехватывается middleware прокси и пересылается на `http://localhost:11966/api`.
2. В противном случае `express.static` пытается отдать реальный файл из `dist/`, применяя `Cache-Control` заголовок для каждого класса ресурсов — `dist/assets/**` и `dist/fonts/**` получают год плюс `immutable` (Vite присваивает им content-hash), изображения верхнего уровня — 7 дней, `index.html` и `manifest.webmanifest` 0 в браузерах, но 24 часа на edge, всё остальное — час.
3. Если ни один файл не подходит, fallback истории SPA возвращает `index.html` чтобы vue-router мог разрешить клиентский маршрут вроде `/tools/whois`. Fallback намеренно узкий: `GET` только, `Accept: text/html` только, и никогда для пути, последний сегмент которого содержит точку — отсутствующий `/assets/x.js` должен вернуть 404, а не HTML-тело.

**API-запрос.** Внутри бэкенда запрос проходит цепочку middleware в `backend-server.js` — необязательные `pino-http`, ограничитель частоты, замедлитель, JSON-парсер тела,  `no-store` по умолчанию, глобальный guard referer, затем guards параметров для каждого маршрута и обработчик. Обработчик делает не более одного внешнего запроса через `fetchUpstream`. Подробности в [Backend](/developer/ru/architecture/backend.md).

{% hint style="warning" %}
`backend-server.js` также монтирует `express.static('./dist')`. Это удобно для конфигураций, где бэкенд открыт напрямую; обычный путь по-прежнему браузер → frontend-сервер → прокси.
{% endhint %}

## Пайплайн сборки

`pnpm build` запускает Vite, который выдаёт `dist/`:

* `@` разрешается в `frontend/`.
* `manualChunks` разбивает тяжёлые зависимости на собственные чанки (`vendor` для vue / vue-router / vue-i18n, а также `chart`, `speedtest`, `svgmap`, `browser-detect`) и выносит хелперы для источника IP и авторизации в `utils-getips` / `utils-auth`.
* Шрифты попадают в `dist/fonts/`, всё остальное — с content-hash в `dist/assets/`.
* Source map'ы генерируются только когда `SENTRY_AUTH_TOKEN` установлен, как `скрытые` карты, и удаляются из `dist/` после загрузки — см. [Мониторинг ошибок](/developer/ru/configuration/error-monitoring.md).

Docker собирается в два этапа. Этап сборки устанавливает с помощью `pnpm install --frozen-lockfile` и запускает `pnpm run build`; production-этап копирует только `node_modules`, `package.json`, `dist/`, два файла серверов, `sentry-instrument.js`, `api/` и `common/`. Никакого toolchain, никакой установки во время запуска. См. [Развертывание с Docker](/developer/ru/getting-started/deploy-with-docker.md).

## Режим разработки

`pnpm dev` запускает Vite и бэкенд вместе. Vite раздаёт SPA на `FRONTEND_PORT` себе (без `dist/`, без `frontend-server.js`) и проксирует `/api` на `BACKEND_PORT` — так что структура URL идентична production. Бэкенд запускается под `nodemon` с `--import ./sentry-instrument.js`, что ничего не делает без backend DSN. Подробности настройки в [Среда разработки](/developer/ru/development/dev-environment.md).

## Где вносить изменения

| Вы хотите…               | Перейдите на                                                                                                   |
| ------------------------ | -------------------------------------------------------------------------------------------------------------- |
| Добавить инструмент в UI | `frontend/data/tools.js` — см. [Добавление нового инструмента](/developer/ru/development/adding-a-new-tool.md) |
| Добавить API-маршрут     | Новый файл в `api/`, подключённый в `backend-server.js`                                                        |
| Добавить общий валидатор | `common/`, плюс мост в `frontend/utils/` если он нужен браузеру                                                |
| Изменить текст           | `frontend/locales/` — см. [i18n](/developer/ru/development/i18n.md)                                            |
| Добавить тест            | `tests/` — см. [Тестирование](/developer/ru/development/testing.md)                                            |


---

# 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/architecture/project-structure.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.
