SnapOtter использует PostgreSQL 17 с [Drizzle ORM](https://orm.drizzle.team/) (pg-core / node-postgres) для хранения данных. Схема определена в `apps/api/src/db/schema.ts`.
Подключение настраивается через переменную окружения `DATABASE_URL` (по умолчанию `postgres://snapotter:snapotter@postgres:5432/snapotter`). В Docker Compose контейнер Postgres хранит свои данные в именованном томе `SnapOtter-pgdata`.
## Таблицы {#tables}
### users {#users}
Хранит учётные записи пользователей. Создаётся автоматически при первом запуске из `DEFAULT_USERNAME` и `DEFAULT_PASSWORD`.
Постоянная библиотека файлов. По умолчанию сохранённое изменение вставляется как независимая корневая строка («сохранить как новый»: `version` 1, `parentId` null, поэтому оригинал остаётся в списке) или как связанная с родителем версия, когда вы перезаписываете оригинал (`parentId` задан, `version` увеличивается, вытесняя его). Столбец `toolChain` записывает применённые инструменты.
Состояние интерфейса для каждого пользователя, ключом служит имя настройки. Хранит закреплённые инструменты главной страницы, которые записываются через `PUT /api/v1/preferences`.
| Столбец | Тип | Примечания |
|---|---|---|
| `userId` | text | FK к users, каскадное удаление. Первичный ключ вместе с `key` |
| `key` | text | Имя настройки. Первичный ключ вместе с `userId` |
| `value` | jsonb | Содержимое настройки |
| `updatedAt` | timestamp | Время последней записи |
Этот дамп базы данных не содержит сохраненных объектов библиотеки в `/data/files` или устойчивого состояния BullMQ в Redis. Создайте резервные копии и восстановите их, выполнив согласованную процедуру в разделе [Безопасность и усиление защиты](/ru/guide/security#backup-and-recovery).
Не копируйте действующий каталог данных PostgreSQL с помощью `tar`. Составляйте префиксы имен томов по проектам, поэтому разрешайте идентификаторы подключенных томов из `docker inspect` или вашей платформы хранения, а не принимайте буквальную метку `SnapOtter-pgdata`.
### Миграция с 1.x (SQLite) {#migrating-from-1-x-sqlite}
Обновление со SnapOtter 1.x описано в отдельном руководстве: см. [Обновление с 1.x до 2.0](./upgrading). Вкратце: переиспользуйте существующий том `/data`, и 2.0 автоматически обнаружит и импортирует `/data/snapotter.db` при первом запуске (или задайте `SQLITE_MIGRATE_PATH`, чтобы явно указать путь к нему). Сначала сделайте резервную копию всего тома `/data`, а не только `snapotter.db`: 1.x использует режим SQLite WAL, поэтому остановленный контейнер часто оставляет большую часть данных в `snapotter.db-wal` рядом с почти пустым `snapotter.db`.