chore: initial infrastructure setup with Syncthing, Git and documentation
Set up three-tier synchronization: Syncthing (real-time), GitHub (version control), rsync (disaster recovery). Includes complete documentation for future Claude sessions. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
commit
a987d45fbc
592 changed files with 79469 additions and 0 deletions
404
.claude/project-structure.md
Normal file
404
.claude/project-structure.md
Normal file
|
|
@ -0,0 +1,404 @@
|
|||
# Структура проекта OVHserver
|
||||
|
||||
## Общая информация
|
||||
|
||||
**Сервер:** ubuntu@51.89.231.46
|
||||
**Локальная копия:** /Volumes/SSD/Aimpress_Cloud_Prod/
|
||||
**GitHub:** git@github.com:SamoilenkoVadym/OVHserver.git
|
||||
|
||||
---
|
||||
|
||||
## Основная структура
|
||||
|
||||
```
|
||||
/Volumes/SSD/Aimpress_Cloud_Prod/
|
||||
├── opt/ # Основные проекты и сервисы
|
||||
├── data/ # Данные приложений
|
||||
├── home/ # Пользовательские файлы ubuntu
|
||||
├── .git/ # Git репозиторий
|
||||
├── .gitignore # Исключения для Git
|
||||
├── .claude/ # Документация для Claude
|
||||
│ ├── workflow.md # Рабочий процесс
|
||||
│ └── project-structure.md # Этот файл
|
||||
└── README.md # Общее описание
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Детальная структура /opt
|
||||
|
||||
### 00-infrastructure/ — Инфраструктурные сервисы
|
||||
Базовые компоненты инфраструктуры, необходимые для работы других сервисов.
|
||||
|
||||
```
|
||||
00-infrastructure/
|
||||
├── postgres/ # PostgreSQL база данных
|
||||
│ ├── docker-compose.yml
|
||||
│ └── data/ # Данные БД
|
||||
├── redis/ # Redis кэш
|
||||
├── rabbitmq/ # RabbitMQ message broker
|
||||
├── traefik/ # Traefik reverse proxy
|
||||
│ ├── docker-compose.yml
|
||||
│ ├── traefik.yml # Основной конфиг
|
||||
│ └── acme.json # SSL сертификаты
|
||||
├── vault/ # HashiCorp Vault (секреты)
|
||||
│ ├── docker-compose.yml
|
||||
│ └── data/ # Хранилище секретов
|
||||
├── blackbox/ # Blackbox Exporter (мониторинг)
|
||||
└── loki/ # Loki (агрегация логов)
|
||||
```
|
||||
|
||||
**Назначение:**
|
||||
- PostgreSQL: Общая база данных для всех сервисов
|
||||
- Redis: Кэширование и очереди задач
|
||||
- RabbitMQ: Message broker для асинхронной обработки
|
||||
- Traefik: Reverse proxy с автоматическим SSL
|
||||
- Vault: Управление секретами и credentials
|
||||
- Loki: Сбор и хранение логов
|
||||
- Blackbox: Мониторинг доступности эндпоинтов
|
||||
|
||||
---
|
||||
|
||||
### 01-security/ — Безопасность и аутентификация
|
||||
Сервисы для управления доступом и безопасностью.
|
||||
|
||||
```
|
||||
01-security/
|
||||
├── authentik/ # Authentik SSO (Single Sign-On)
|
||||
│ ├── docker-compose.yml
|
||||
│ ├── custom-templates/
|
||||
│ └── media/ # Статические файлы
|
||||
└── vaultwarden/ # Vaultwarden (менеджер паролей)
|
||||
├── docker-compose.yml
|
||||
└── data/ # Хранилище паролей
|
||||
```
|
||||
|
||||
**Назначение:**
|
||||
- Authentik: Единая система аутентификации (SSO) для всех сервисов
|
||||
- Vaultwarden: Корпоративный менеджер паролей (Bitwarden compatible)
|
||||
|
||||
---
|
||||
|
||||
### 02-core/ — Ядро системы
|
||||
Основные рабочие сервисы, критичные для бизнеса.
|
||||
|
||||
```
|
||||
02-core/
|
||||
├── n8n-shared/ # n8n (workflow automation) - общий
|
||||
│ ├── docker-compose.yml
|
||||
│ └── .n8n/ # Workflows и credentials
|
||||
├── n8n-vip/ # n8n VIP instance (отдельный)
|
||||
├── evolution-api/ # Evolution API (WhatsApp Business API)
|
||||
│ ├── docker-compose.yml
|
||||
│ ├── evolution-instances/ # Инстансы WhatsApp
|
||||
│ └── evolution-store/ # Хранилище данных
|
||||
└── supabase/ # Supabase (Backend-as-a-Service)
|
||||
├── docker-compose.yml
|
||||
└── volumes/ # Данные Supabase
|
||||
```
|
||||
|
||||
**Назначение:**
|
||||
- n8n: Автоматизация бизнес-процессов (workflow automation)
|
||||
- n8n-shared: Для общих workflow
|
||||
- n8n-vip: Для VIP клиентов или критичных задач
|
||||
- Evolution API: WhatsApp Business API для мессенджер-интеграций
|
||||
- Supabase: Backend с БД, Auth, Storage для приложений
|
||||
|
||||
---
|
||||
|
||||
### 03-business/ — Бизнес-приложения
|
||||
Приложения для работы бизнеса и команды.
|
||||
|
||||
```
|
||||
03-business/
|
||||
├── odoo/ # Odoo ERP
|
||||
│ ├── docker-compose.yml
|
||||
│ ├── addons/ # Кастомные модули
|
||||
│ ├── config/ # Конфигурация
|
||||
│ └── filestore/ # Файлы и attachments
|
||||
├── outline/ # Outline (корпоративная wiki)
|
||||
│ ├── docker-compose.yml
|
||||
│ └── data/ # Документы
|
||||
├── documenso/ # Documenso (электронная подпись)
|
||||
│ ├── docker-compose.yml
|
||||
│ └── uploads/ # Загруженные документы
|
||||
└── wikijs/ # Wiki.js (документация)
|
||||
├── docker-compose.yml
|
||||
└── data/ # Wiki контент
|
||||
```
|
||||
|
||||
**Назначение:**
|
||||
- Odoo: ERP система для управления бизнесом
|
||||
- CRM, Sales, Inventory, Accounting, HR
|
||||
- Кастомные модули в /addons
|
||||
- Outline: Корпоративная база знаний (wiki)
|
||||
- Documenso: Система электронного документооборота и подписи
|
||||
- Wiki.js: Техническая документация
|
||||
|
||||
---
|
||||
|
||||
### 04-tools/ — Утилиты и мониторинг
|
||||
Инструменты для управления и мониторинга инфраструктуры.
|
||||
|
||||
```
|
||||
04-tools/
|
||||
├── portainer/ # Portainer (Docker UI)
|
||||
│ └── docker-compose.yml
|
||||
├── grafana/ # Grafana (визуализация метрик)
|
||||
│ ├── docker-compose.yml
|
||||
│ └── dashboards/ # Дашборды
|
||||
├── monitoring/ # Prometheus + Node Exporter
|
||||
│ ├── prometheus/
|
||||
│ ├── alertmanager/
|
||||
│ └── node-exporter/
|
||||
├── uptime-kuma/ # Uptime Kuma (мониторинг доступности)
|
||||
│ └── docker-compose.yml
|
||||
├── watchtower/ # Watchtower (авто-обновление Docker)
|
||||
└── sftp/ # SFTP сервер
|
||||
```
|
||||
|
||||
**Назначение:**
|
||||
- Portainer: Web UI для управления Docker контейнерами
|
||||
- Grafana: Дашборды для визуализации метрик
|
||||
- Monitoring: Сбор метрик (CPU, RAM, disk, etc.)
|
||||
- Uptime Kuma: Мониторинг доступности сервисов
|
||||
- Watchtower: Автоматическое обновление Docker образов
|
||||
- SFTP: Файловый сервер для обмена файлами
|
||||
|
||||
---
|
||||
|
||||
### 05-backups/ — Бэкапы и скрипты
|
||||
Резервные копии, логи и административные скрипты.
|
||||
|
||||
⚠️ **Исключено из Git** (большие файлы, бинарные данные)
|
||||
|
||||
```
|
||||
05-backups/
|
||||
├── scripts/ # Bash скрипты для обслуживания
|
||||
│ ├── backup-*.sh
|
||||
│ ├── restore-*.sh
|
||||
│ └── maintenance-*.sh
|
||||
├── reports/ # Отчеты о бэкапах
|
||||
├── logs/ # Логи бэкапов
|
||||
├── data/ # Временные данные
|
||||
├── config-versions/ # Версии конфигураций
|
||||
├── credentials/ # Экспортированные credentials
|
||||
├── restic/ # Restic бэкапы
|
||||
├── docs/ # Документация по скриптам
|
||||
├── SCRIPTS-REGISTRY.md # Реестр всех скриптов
|
||||
└── README-SCRIPTS.md # Документация скриптов
|
||||
```
|
||||
|
||||
**Назначение:**
|
||||
- Автоматические бэкапы всех сервисов
|
||||
- Скрипты для обслуживания и восстановления
|
||||
- Логи всех операций
|
||||
- Версионирование конфигураций
|
||||
|
||||
---
|
||||
|
||||
### 06-webflow/ — Webflow интеграция
|
||||
Landing pages и интеграция с Webflow.
|
||||
|
||||
```
|
||||
06-webflow/
|
||||
└── landing/ # Landing page
|
||||
├── index.html
|
||||
├── css/
|
||||
└── js/
|
||||
```
|
||||
|
||||
**Назначение:**
|
||||
- Публичные landing pages
|
||||
- Интеграция с Webflow CMS
|
||||
|
||||
---
|
||||
|
||||
### Другие файлы в /opt
|
||||
|
||||
```
|
||||
/opt/
|
||||
├── infrastructure-docs/ # Документация инфраструктуры
|
||||
│ ├── setup-guides/
|
||||
│ ├── troubleshooting/
|
||||
│ └── architecture/
|
||||
├── postiz-config/ # Конфигурация Postiz
|
||||
├── containerd/ # Containerd данные (системные)
|
||||
└── fix_odoo_pass.py # Утилита для сброса пароля Odoo
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Структура /data
|
||||
|
||||
```
|
||||
/data/
|
||||
├── docker/ # Docker данные (volumes, overlay2)
|
||||
├── app-data/ # Данные приложений
|
||||
└── temp/ # Временные файлы
|
||||
```
|
||||
|
||||
**Назначение:** Хранилище данных Docker контейнеров и приложений.
|
||||
|
||||
---
|
||||
|
||||
## Структура /home/ubuntu
|
||||
|
||||
```
|
||||
/home/ubuntu/
|
||||
├── .ssh/ # SSH ключи
|
||||
├── .bashrc # Bash конфигурация
|
||||
├── scripts/ # Персональные скрипты
|
||||
└── .local/ # Локальные данные пользователя
|
||||
```
|
||||
|
||||
**Назначение:** Домашняя директория пользователя ubuntu.
|
||||
|
||||
---
|
||||
|
||||
## Технологический стек
|
||||
|
||||
### Оркестрация
|
||||
- **Docker** + **Docker Compose** - контейнеризация всех сервисов
|
||||
|
||||
### Reverse Proxy & SSL
|
||||
- **Traefik** - автоматический SSL (Let's Encrypt), маршрутизация
|
||||
|
||||
### Базы данных
|
||||
- **PostgreSQL** - основная RDBMS
|
||||
- **Redis** - кэш и очереди
|
||||
- **RabbitMQ** - message broker
|
||||
|
||||
### Безопасность
|
||||
- **HashiCorp Vault** - управление секретами
|
||||
- **Authentik** - SSO и Identity Provider
|
||||
- **Vaultwarden** - менеджер паролей
|
||||
|
||||
### Мониторинг
|
||||
- **Prometheus** - сбор метрик
|
||||
- **Grafana** - визуализация
|
||||
- **Loki** - агрегация логов
|
||||
- **Uptime Kuma** - проверка доступности
|
||||
- **Blackbox Exporter** - HTTP/TCP мониторинг
|
||||
|
||||
### Бизнес-приложения
|
||||
- **Odoo** - ERP
|
||||
- **n8n** - workflow automation
|
||||
- **Evolution API** - WhatsApp Business API
|
||||
- **Supabase** - BaaS
|
||||
- **Outline** - wiki
|
||||
- **Documenso** - e-signature
|
||||
|
||||
### DevOps
|
||||
- **Portainer** - Docker UI
|
||||
- **Watchtower** - авто-обновления
|
||||
- **Restic** - инкрементальные бэкапы
|
||||
|
||||
---
|
||||
|
||||
## Сетевая архитектура
|
||||
|
||||
```
|
||||
Internet
|
||||
↓
|
||||
Traefik (51.89.231.46:80/443)
|
||||
↓
|
||||
├→ authentik.domain.com → Authentik
|
||||
├→ n8n.domain.com → n8n
|
||||
├→ odoo.domain.com → Odoo
|
||||
├→ outline.domain.com → Outline
|
||||
├→ vault.domain.com → Vault
|
||||
├→ portainer.domain.com → Portainer
|
||||
├→ grafana.domain.com → Grafana
|
||||
└→ etc.
|
||||
```
|
||||
|
||||
Все сервисы доступны через Traefik с автоматическим SSL.
|
||||
|
||||
---
|
||||
|
||||
## Важные замечания
|
||||
|
||||
### Права доступа
|
||||
- Большинство сервисов запущены от пользователя `ubuntu`
|
||||
- Некоторые системные файлы (vault/data, containerd) принадлежат root
|
||||
- Syncthing может не иметь доступа к root-файлам (это нормально)
|
||||
|
||||
### Бэкапы
|
||||
- Автоматические бэкапы в `/opt/05-backups/`
|
||||
- Restic для инкрементальных бэкапов
|
||||
- Бэкапы НЕ коммитятся в Git (большие файлы)
|
||||
|
||||
### Секреты
|
||||
- Хранятся в HashiCorp Vault
|
||||
- .env файлы исключены из Git
|
||||
- Credentials в `/opt/05-backups/credentials/` (не в Git)
|
||||
|
||||
### Docker
|
||||
- Все сервисы в Docker контейнерах
|
||||
- docker-compose.yml для каждого сервиса
|
||||
- Volumes в `/data/` и локальных директориях
|
||||
|
||||
---
|
||||
|
||||
## Зависимости между сервисами
|
||||
|
||||
```
|
||||
Traefik (обязательно для всех)
|
||||
↓
|
||||
PostgreSQL (используется: Odoo, Outline, Authentik, n8n, Supabase)
|
||||
↓
|
||||
Redis (используется: Odoo, n8n, Outline)
|
||||
↓
|
||||
Vault (хранит секреты для всех сервисов)
|
||||
↓
|
||||
Authentik (SSO для: Outline, Grafana, Portainer, и др.)
|
||||
```
|
||||
|
||||
**Порядок запуска:**
|
||||
1. Traefik
|
||||
2. PostgreSQL, Redis, RabbitMQ
|
||||
3. Vault
|
||||
4. Authentik
|
||||
5. Остальные сервисы
|
||||
|
||||
---
|
||||
|
||||
## Полезные пути
|
||||
|
||||
### Конфигурации
|
||||
- `/opt/00-infrastructure/traefik/traefik.yml` - основной конфиг Traefik
|
||||
- `/opt/*/docker-compose.yml` - конфигурации всех сервисов
|
||||
|
||||
### Логи
|
||||
- `/opt/05-backups/logs/` - логи бэкапов
|
||||
- `docker logs <container>` - логи контейнеров
|
||||
|
||||
### Данные
|
||||
- `/data/` - Docker volumes
|
||||
- `/opt/*/data/` - данные конкретных сервисов
|
||||
|
||||
### Скрипты
|
||||
- `/opt/05-backups/scripts/` - административные скрипты
|
||||
- `/opt/05-backups/SCRIPTS-REGISTRY.md` - список всех скриптов
|
||||
|
||||
---
|
||||
|
||||
## Как найти нужный сервис
|
||||
|
||||
1. **Инфраструктура?** → `00-infrastructure/`
|
||||
2. **Аутентификация?** → `01-security/`
|
||||
3. **Автоматизация?** → `02-core/n8n-*`
|
||||
4. **CRM/ERP?** → `03-business/odoo/`
|
||||
5. **Документация?** → `03-business/outline/` или `03-business/wikijs/`
|
||||
6. **Мониторинг?** → `04-tools/grafana/` или `04-tools/monitoring/`
|
||||
7. **Управление Docker?** → `04-tools/portainer/`
|
||||
8. **Бэкапы?** → `05-backups/`
|
||||
|
||||
---
|
||||
|
||||
## Обновления структуры
|
||||
|
||||
Этот документ должен обновляться при добавлении новых сервисов или изменении структуры проекта.
|
||||
|
||||
**Последнее обновление:** 2025-11-05
|
||||
237
.claude/workflow.md
Normal file
237
.claude/workflow.md
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
# Claude Code Workflow для OVHserver
|
||||
|
||||
## Общая архитектура проекта
|
||||
|
||||
Этот проект использует **трёхуровневую систему синхронизации и бэкапа**:
|
||||
|
||||
### 1. Syncthing (Синхронизация в реальном времени)
|
||||
- **Назначение:** Мгновенная двусторонняя синхронизация между Mac и сервером
|
||||
- **Режим:** Send & Receive (bidirectional)
|
||||
- **Синхронизируемые папки:**
|
||||
- `/opt` (сервер) ↔ `/Volumes/SSD/Aimpress_Cloud_Prod/opt` (Mac)
|
||||
- `/data` (сервер) ↔ `/Volumes/SSD/Aimpress_Cloud_Prod/data` (Mac)
|
||||
- `/home/ubuntu` (сервер) ↔ `/Volumes/SSD/Aimpress_Cloud_Prod/home` (Mac)
|
||||
- **Автозапуск:** Да, через systemd (сервер) и brew services (Mac)
|
||||
|
||||
### 2. GitHub (Версионный контроль)
|
||||
- **Репозиторий:** git@github.com:SamoilenkoVadym/OVHserver.git
|
||||
- **Что хранится:** Весь код из `/opt`, конфигурации, документация
|
||||
- **Исключения:** См. `.gitignore` - логи, секреты, бэкапы, зависимости
|
||||
|
||||
### 3. rsync (Полный системный бэкап)
|
||||
- **Назначение:** Полная копия системы для восстановления
|
||||
- **Запуск:** По требованию через скрипт
|
||||
- **Место хранения:** `/Volumes/SSD/Aimpress_Cloud_Prod/system-backup/`
|
||||
|
||||
---
|
||||
|
||||
## Как я (Claude) работаю с проектом
|
||||
|
||||
### ⚠️ ВАЖНО: Пользователь работает ТОЛЬКО через меня
|
||||
- Все изменения в проект вносятся через меня
|
||||
- Я работаю с **локальными файлами** на Mac
|
||||
- После моих изменений Syncthing автоматически синхронизирует их на сервер (10-30 сек)
|
||||
|
||||
### Процесс работы:
|
||||
|
||||
```
|
||||
1. Пользователь: "Claude, добавь функцию X"
|
||||
↓
|
||||
2. Я читаю локальные файлы из /Volumes/SSD/Aimpress_Cloud_Prod/opt
|
||||
↓
|
||||
3. Я вношу изменения в файлы
|
||||
↓
|
||||
4. Syncthing: Автоматически синхронизирует на сервер (10-30 сек)
|
||||
↓
|
||||
5. Я делаю git commit с осмысленным сообщением:
|
||||
"feat: add authentication module with JWT"
|
||||
↓
|
||||
6. Git hook: Автоматически выполняет git push в GitHub
|
||||
```
|
||||
|
||||
### Когда делать git commit:
|
||||
|
||||
✅ **ВСЕГДА делаю commit после каждого значимого изменения:**
|
||||
- Добавлена новая функция → `feat: <description>`
|
||||
- Исправлен баг → `fix: <description>`
|
||||
- Рефакторинг кода → `refactor: <description>`
|
||||
- Обновлена документация → `docs: <description>`
|
||||
- Изменена конфигурация → `chore: update <config name>`
|
||||
- Улучшена производительность → `perf: <description>`
|
||||
|
||||
✅ **Формат сообщений коммита:**
|
||||
```
|
||||
<type>: <short description>
|
||||
|
||||
<detailed explanation if needed>
|
||||
|
||||
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
||||
|
||||
Co-Authored-By: Claude <noreply@anthropic.com>
|
||||
```
|
||||
|
||||
❌ **НЕ коммичу:**
|
||||
- Временные файлы (они в .gitignore)
|
||||
- Логи (они в .gitignore)
|
||||
- Секреты типа .env, .key (они в .gitignore + pre-commit hook блокирует)
|
||||
- Незавершенные изменения (только работающий код)
|
||||
|
||||
---
|
||||
|
||||
## Git Hooks
|
||||
|
||||
### pre-commit
|
||||
- **Проверяет:** Наличие секретов в коммите (.env, .key, .pem файлы)
|
||||
- **Действие:** Блокирует коммит если найдены секреты
|
||||
|
||||
### post-commit
|
||||
- **Действие:** Автоматически пушит каждый коммит в GitHub
|
||||
- **Ветка:** main или master
|
||||
|
||||
---
|
||||
|
||||
## Структура проекта
|
||||
|
||||
```
|
||||
/Volumes/SSD/Aimpress_Cloud_Prod/
|
||||
├── opt/ # Основные проекты и приложения
|
||||
│ ├── 00-infrastructure/ # Инфраструктура (vault, nginx, etc.)
|
||||
│ ├── 01-security/ # Безопасность
|
||||
│ ├── 02-core/ # Ядро системы
|
||||
│ ├── 03-business/ # Бизнес-логика
|
||||
│ ├── 04-tools/ # Утилиты и инструменты
|
||||
│ ├── 05-backups/ # Бэкапы (исключены из Git)
|
||||
│ └── 06-webflow/ # Webflow интеграция
|
||||
├── data/ # Данные приложений
|
||||
├── home/ # Пользовательские файлы ubuntu
|
||||
├── .git/ # Git репозиторий
|
||||
├── .gitignore # Исключения для Git
|
||||
├── .claude/ # Документация для Claude
|
||||
│ ├── workflow.md # Этот файл
|
||||
│ └── project-structure.md # Детальная структура проекта
|
||||
└── README.md # Общая информация
|
||||
|
||||
На сервере:
|
||||
/opt/ # Синхронизировано с Mac
|
||||
/data/ # Синхронизировано с Mac
|
||||
/home/ubuntu/ # Синхронизировано с Mac
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Проверка состояния систем
|
||||
|
||||
### Syncthing:
|
||||
```bash
|
||||
# На сервере:
|
||||
ssh ubuntu@51.89.231.46 "systemctl --user status syncthing"
|
||||
|
||||
# На Mac:
|
||||
brew services list | grep syncthing
|
||||
```
|
||||
|
||||
### Git:
|
||||
```bash
|
||||
# Проверить статус:
|
||||
git status
|
||||
|
||||
# Проверить последние коммиты:
|
||||
git log --oneline -10
|
||||
|
||||
# Проверить remote:
|
||||
git remote -v
|
||||
```
|
||||
|
||||
### Размер синхронизированных данных:
|
||||
```bash
|
||||
du -sh /Volumes/SSD/Aimpress_Cloud_Prod/{opt,data,home}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Типичные задачи
|
||||
|
||||
### Добавить новую функцию:
|
||||
1. Читаю существующий код
|
||||
2. Вношу изменения
|
||||
3. Жду синхронизации (10-30 сек)
|
||||
4. Делаю коммит: `git commit -m "feat: add new feature"`
|
||||
5. Push происходит автоматически через hook
|
||||
|
||||
### Исправить баг:
|
||||
1. Нахожу проблемный код
|
||||
2. Исправляю
|
||||
3. Жду синхронизации
|
||||
4. Коммит: `git commit -m "fix: resolve issue with X"`
|
||||
|
||||
### Обновить конфигурацию:
|
||||
1. Изменяю конфиг файл
|
||||
2. Жду синхронизации
|
||||
3. Коммит: `git commit -m "chore: update nginx config"`
|
||||
|
||||
### Создать документацию:
|
||||
1. Создаю .md файл
|
||||
2. Коммит: `git commit -m "docs: add API documentation"`
|
||||
|
||||
---
|
||||
|
||||
## Важные замечания
|
||||
|
||||
⚠️ **Права доступа:**
|
||||
- Некоторые файлы на сервере принадлежат root (например vault/data)
|
||||
- Syncthing может не иметь прав на их чтение
|
||||
- Это нормально, игнорируем эти файлы через .stignore
|
||||
|
||||
⚠️ **Большие файлы:**
|
||||
- Бэкапы из `/opt/05-backups/` исключены из Git
|
||||
- Логи не синхронизируются (в .stignore)
|
||||
- SQL дампы и архивы не коммитятся
|
||||
|
||||
⚠️ **Секреты:**
|
||||
- .env файлы в .gitignore
|
||||
- pre-commit hook блокирует коммит секретов
|
||||
- Если нужно - использую vault или отдельное хранилище
|
||||
|
||||
---
|
||||
|
||||
## Полезные команды
|
||||
|
||||
```bash
|
||||
# Проверить синхронизацию Syncthing
|
||||
syncthing cli show system
|
||||
|
||||
# Посмотреть что синхронизируется
|
||||
du -sh /Volumes/SSD/Aimpress_Cloud_Prod/*
|
||||
|
||||
# Проверить Git статус
|
||||
git status
|
||||
|
||||
# Посмотреть последние изменения
|
||||
git log --oneline -20
|
||||
|
||||
# Создать коммит вручную (если нужно)
|
||||
git add .
|
||||
git commit -m "type: description"
|
||||
# Push произойдет автоматически
|
||||
|
||||
# Проверить что на сервере
|
||||
ssh ubuntu@51.89.231.46 "ls -la /opt"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## В следующих сессиях
|
||||
|
||||
Когда я (Claude) начинаю новую сессию:
|
||||
1. Читаю этот файл (.claude/workflow.md)
|
||||
2. Понимаю что проект работает через Syncthing + Git
|
||||
3. Работаю с локальными файлами
|
||||
4. Делаю коммиты после каждого изменения
|
||||
5. Не спрашиваю пользователя о workflow - он задокументирован здесь
|
||||
|
||||
Этот workflow обеспечивает:
|
||||
✅ Мгновенную синхронизацию изменений
|
||||
✅ Полную историю версий в Git
|
||||
✅ Автоматический бэкап в GitHub
|
||||
✅ Безопасность (pre-commit hooks)
|
||||
✅ Возможность восстановления системы (через rsync)
|
||||
103
.gitignore
vendored
Normal file
103
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
# Dependencies
|
||||
node_modules/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.pyd
|
||||
.Python
|
||||
*.so
|
||||
*.egg
|
||||
*.egg-info/
|
||||
dist/
|
||||
build/
|
||||
.venv/
|
||||
venv/
|
||||
env/
|
||||
ENV/
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.project
|
||||
.classpath
|
||||
.settings/
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
logs/
|
||||
*.out
|
||||
|
||||
# Temporary files
|
||||
*.tmp
|
||||
*.temp
|
||||
.cache/
|
||||
.pytest_cache/
|
||||
.coverage
|
||||
htmlcov/
|
||||
|
||||
# System files
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
desktop.ini
|
||||
|
||||
# Secrets and credentials
|
||||
.env
|
||||
.env.*
|
||||
*.key
|
||||
*.pem
|
||||
*.p12
|
||||
*.pfx
|
||||
credentials.json
|
||||
secrets.yaml
|
||||
config/secrets/
|
||||
|
||||
# Database dumps and large backups
|
||||
*.sql
|
||||
*.sql.gz
|
||||
*.dump
|
||||
*.tar.gz
|
||||
*.zip
|
||||
*.bak
|
||||
opt/05-backups/
|
||||
**/backups/*.tar.gz
|
||||
**/backups/*.sql.gz
|
||||
|
||||
# Docker volumes and data
|
||||
**/docker-data/
|
||||
**/volumes/
|
||||
|
||||
# Syncthing internal files
|
||||
.stfolder
|
||||
.stversions
|
||||
.stignore
|
||||
|
||||
# Nested git repositories
|
||||
opt/02-core/supabase/supabase/
|
||||
|
||||
# Compiled files
|
||||
*.class
|
||||
*.dll
|
||||
*.exe
|
||||
*.o
|
||||
*.so
|
||||
|
||||
# Package files
|
||||
*.jar
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
*.rar
|
||||
|
||||
# OS generated files
|
||||
.fuse_hidden*
|
||||
.directory
|
||||
.Trash-*
|
||||
.nfs*
|
||||
255
README.md
Normal file
255
README.md
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
# OVHserver Infrastructure
|
||||
|
||||
Production infrastructure для AI-Impress с полной автоматизацией, мониторингом и системой бэкапов.
|
||||
|
||||
**Сервер:** ubuntu@51.89.231.46
|
||||
**GitHub:** [SamoilenkoVadym/OVHserver](https://github.com/SamoilenkoVadym/OVHserver)
|
||||
|
||||
---
|
||||
|
||||
## Трёхуровневая система синхронизации
|
||||
|
||||
### 🔄 1. Syncthing (Реального времени)
|
||||
Мгновенная двусторонняя синхронизация между Mac и сервером.
|
||||
|
||||
- **Режим:** Send & Receive (bidirectional)
|
||||
- **Папки:** `/opt`, `/data`, `/home/ubuntu`
|
||||
- **Автозапуск:** systemd (сервер) + brew services (Mac)
|
||||
- **Скорость:** 10-30 секунд
|
||||
|
||||
### 📦 2. GitHub (Версионный контроль)
|
||||
Полная история изменений кода и конфигураций.
|
||||
|
||||
- **Репозиторий:** git@github.com:SamoilenkoVadym/OVHserver.git
|
||||
- **Автопуш:** Через post-commit hook
|
||||
- **Защита:** pre-commit hook блокирует коммит секретов
|
||||
|
||||
### 💾 3. rsync (Системный бэкап)
|
||||
Полная копия сервера для восстановления.
|
||||
|
||||
- **Назначение:** Disaster recovery
|
||||
- **Запуск:** По требованию
|
||||
- **Хранение:** `/Volumes/SSD/Aimpress_Cloud_Prod/system-backup/`
|
||||
|
||||
---
|
||||
|
||||
## Структура проекта
|
||||
|
||||
```
|
||||
.
|
||||
├── opt/ # Основные проекты
|
||||
│ ├── 00-infrastructure/ # Traefik, PostgreSQL, Redis, Vault, RabbitMQ
|
||||
│ ├── 01-security/ # Authentik (SSO), Vaultwarden
|
||||
│ ├── 02-core/ # n8n, Evolution API, Supabase
|
||||
│ ├── 03-business/ # Odoo, Outline, Documenso, Wiki.js
|
||||
│ ├── 04-tools/ # Portainer, Grafana, Monitoring, Uptime Kuma
|
||||
│ ├── 05-backups/ # Бэкапы и скрипты (не в Git)
|
||||
│ └── 06-webflow/ # Landing pages
|
||||
├── data/ # Данные приложений
|
||||
├── home/ # /home/ubuntu
|
||||
└── .claude/ # Документация для Claude Code
|
||||
├── workflow.md # Рабочий процесс
|
||||
└── project-structure.md # Детальная структура
|
||||
```
|
||||
|
||||
Подробная документация в [.claude/project-structure.md](.claude/project-structure.md)
|
||||
|
||||
---
|
||||
|
||||
## Технологический стек
|
||||
|
||||
### Инфраструктура
|
||||
- **Docker** + **Docker Compose** - контейнеризация
|
||||
- **Traefik** - reverse proxy с автоматическим SSL
|
||||
- **PostgreSQL** - основная БД
|
||||
- **Redis** - кэш и очереди
|
||||
- **RabbitMQ** - message broker
|
||||
|
||||
### Безопасность
|
||||
- **HashiCorp Vault** - управление секретами
|
||||
- **Authentik** - SSO (Single Sign-On)
|
||||
- **Vaultwarden** - менеджер паролей
|
||||
|
||||
### Мониторинг
|
||||
- **Prometheus** + **Grafana** - метрики и дашборды
|
||||
- **Loki** - агрегация логов
|
||||
- **Uptime Kuma** - проверка доступности
|
||||
|
||||
### Бизнес-приложения
|
||||
- **Odoo** - ERP система
|
||||
- **n8n** - workflow automation
|
||||
- **Evolution API** - WhatsApp Business API
|
||||
- **Supabase** - Backend-as-a-Service
|
||||
- **Outline** - корпоративная wiki
|
||||
- **Documenso** - электронная подпись
|
||||
|
||||
---
|
||||
|
||||
## Быстрый старт
|
||||
|
||||
### Проверка синхронизации
|
||||
```bash
|
||||
# Статус Syncthing на сервере
|
||||
ssh ubuntu@51.89.231.46 "systemctl --user status syncthing"
|
||||
|
||||
# Статус на Mac
|
||||
brew services list | grep syncthing
|
||||
|
||||
# Размер синхронизированных данных
|
||||
du -sh /Volumes/SSD/Aimpress_Cloud_Prod/{opt,data,home}
|
||||
```
|
||||
|
||||
### Git операции
|
||||
```bash
|
||||
# Проверить статус
|
||||
git status
|
||||
|
||||
# Посмотреть последние коммиты
|
||||
git log --oneline -20
|
||||
|
||||
# Сделать коммит (push автоматический через hook)
|
||||
git add .
|
||||
git commit -m "feat: add new feature"
|
||||
```
|
||||
|
||||
### Доступ к сервисам
|
||||
Все сервисы доступны через Traefik с автоматическим SSL:
|
||||
- **Portainer:** portainer.domain.com
|
||||
- **Grafana:** grafana.domain.com
|
||||
- **Authentik:** authentik.domain.com
|
||||
- **Odoo:** odoo.domain.com
|
||||
- **n8n:** n8n.domain.com
|
||||
|
||||
---
|
||||
|
||||
## Работа с проектом через Claude Code
|
||||
|
||||
### Рабочий процесс
|
||||
1. Пользователь даёт задачу Claude
|
||||
2. Claude работает с **локальными файлами** на Mac
|
||||
3. Syncthing автоматически синхронизирует изменения на сервер (10-30 сек)
|
||||
4. Claude делает git commit с понятным сообщением
|
||||
5. Git hook автоматически пушит в GitHub
|
||||
|
||||
### Формат коммитов
|
||||
```
|
||||
<type>: <short description>
|
||||
|
||||
<detailed explanation if needed>
|
||||
|
||||
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
||||
|
||||
Co-Authored-By: Claude <noreply@anthropic.com>
|
||||
```
|
||||
|
||||
**Типы коммитов:**
|
||||
- `feat:` - новая функция
|
||||
- `fix:` - исправление бага
|
||||
- `refactor:` - рефакторинг кода
|
||||
- `docs:` - документация
|
||||
- `chore:` - обслуживание (конфиги, зависимости)
|
||||
- `perf:` - улучшение производительности
|
||||
|
||||
Полная документация в [.claude/workflow.md](.claude/workflow.md)
|
||||
|
||||
---
|
||||
|
||||
## Безопасность
|
||||
|
||||
### Git Hooks
|
||||
|
||||
**pre-commit:**
|
||||
- Блокирует коммит файлов `.env`, `.key`, `.pem`
|
||||
- Предупреждает о потенциальных секретах в коде
|
||||
- Можно пропустить: `git commit --no-verify`
|
||||
|
||||
**post-commit:**
|
||||
- Автоматически пушит каждый коммит в GitHub
|
||||
- Работает с ветками main/master
|
||||
|
||||
### .gitignore
|
||||
Исключены из Git:
|
||||
- Зависимости: `node_modules`, `__pycache__`, `venv`
|
||||
- Логи: `*.log`, `logs/`
|
||||
- Секреты: `.env`, `*.key`, `*.pem`
|
||||
- Бэкапы: `05-backups/`, `*.sql.gz`, `*.tar.gz`
|
||||
- Временные: `.cache`, `*.tmp`, `.DS_Store`
|
||||
|
||||
---
|
||||
|
||||
## Мониторинг и поддержка
|
||||
|
||||
### Проверка здоровья сервисов
|
||||
```bash
|
||||
# Docker контейнеры
|
||||
ssh ubuntu@51.89.231.46 "docker ps"
|
||||
|
||||
# Логи контейнера
|
||||
ssh ubuntu@51.89.231.46 "docker logs <container_name>"
|
||||
|
||||
# Disk usage
|
||||
ssh ubuntu@51.89.231.46 "df -h"
|
||||
```
|
||||
|
||||
### Бэкапы
|
||||
- **Автоматические:** В `/opt/05-backups/`
|
||||
- **Restic:** Инкрементальные бэкапы
|
||||
- **Скрипты:** В `/opt/05-backups/scripts/`
|
||||
- **Документация:** `/opt/05-backups/SCRIPTS-REGISTRY.md`
|
||||
|
||||
### Uptime Kuma
|
||||
Мониторинг доступности всех сервисов 24/7 через веб-интерфейс.
|
||||
|
||||
---
|
||||
|
||||
## Восстановление системы
|
||||
|
||||
### Из rsync бэкапа
|
||||
```bash
|
||||
# Полное восстановление (осторожно!)
|
||||
rsync -avz --progress /Volumes/SSD/Aimpress_Cloud_Prod/system-backup/ ubuntu@51.89.231.46:/
|
||||
```
|
||||
|
||||
### Из Git
|
||||
```bash
|
||||
# Откат к предыдущему коммиту
|
||||
git log --oneline
|
||||
git checkout <commit_hash> -- <file_path>
|
||||
git commit -m "chore: rollback to <commit>"
|
||||
```
|
||||
|
||||
### Из Restic
|
||||
```bash
|
||||
# Посмотреть snapshots
|
||||
restic snapshots
|
||||
|
||||
# Восстановить конкретный snapshot
|
||||
restic restore <snapshot_id> --target /restore/path
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Документация
|
||||
|
||||
- **[.claude/workflow.md](.claude/workflow.md)** - Рабочий процесс Claude Code
|
||||
- **[.claude/project-structure.md](.claude/project-structure.md)** - Детальная структура проекта
|
||||
- **[opt/05-backups/SCRIPTS-REGISTRY.md](opt/05-backups/SCRIPTS-REGISTRY.md)** - Реестр скриптов
|
||||
- **[opt/05-backups/README-SCRIPTS.md](opt/05-backups/README-SCRIPTS.md)** - Документация скриптов
|
||||
|
||||
---
|
||||
|
||||
## Контакты и поддержка
|
||||
|
||||
- **GitHub Issues:** [SamoilenkoVadym/OVHserver/issues](https://github.com/SamoilenkoVadym/OVHserver/issues)
|
||||
- **Сервер:** ubuntu@51.89.231.46
|
||||
|
||||
---
|
||||
|
||||
## Лицензия
|
||||
|
||||
Private repository - все права защищены.
|
||||
|
||||
---
|
||||
|
||||
**Последнее обновление:** 2025-11-05
|
||||
**Версия инфраструктуры:** 1.0.0
|
||||
268
home/$OUT_FILE
Normal file
268
home/$OUT_FILE
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
|
||||
## 🖥️ System Overview
|
||||
|
||||
Static hostname: ai-impress-prod
|
||||
Icon name: computer-vm
|
||||
Chassis: vm 🖴
|
||||
Machine ID: 2c3c6d97e54246e9bcfdeb513b8947f3
|
||||
Boot ID: e0fb6e0813b54674ac1ca99a87864c74
|
||||
Virtualization: kvm
|
||||
Operating System: Ubuntu 24.04.3 LTS
|
||||
Kernel: Linux 6.8.0-86-generic
|
||||
Architecture: x86-64
|
||||
Hardware Vendor: OpenStack Foundation
|
||||
Hardware Model: OpenStack Nova
|
||||
Firmware Version: 1.16.3-debian-1.16.3-2~bpo12+1
|
||||
Firmware Date: Tue 2014-04-01
|
||||
Firmware Age: 11y 6month 4w 1d
|
||||
|
||||
**Uptime:** $(uptime -p)
|
||||
|
||||
Filesystem Type Size Used Avail Use% Mounted on
|
||||
/dev/sda1 ext4 387G 44G 344G 12% /
|
||||
/dev/sda16 ext4 881M 117M 703M 15% /boot
|
||||
/dev/sda15 vfat 105M 6.2M 99M 6% /boot/efi
|
||||
/dev/sdc ext4 393G 3.6G 370G 1% /mnt/backups
|
||||
/dev/sdb ext4 295G 256M 279G 1% /mnt/psql-data
|
||||
|
||||
## 🐳 Docker Summary
|
||||
|
||||
Server: 28.5.1, Containers: 46, Running: 46, Images: 37, Driver: overlay2
|
||||
|
||||
authentik-postgres postgres:16-alpine Up 12 minutes (healthy)
|
||||
authentik-proxy ghcr.io/goauthentik/proxy:2025.10 Up 12 minutes (healthy)
|
||||
authentik-redis redis:alpine Up 12 minutes (healthy)
|
||||
authentik-server ghcr.io/goauthentik/server:2025.10 Up 12 minutes (healthy)
|
||||
authentik-worker ghcr.io/goauthentik/server:2025.10 Up 12 minutes (healthy)
|
||||
evolution-api atendai/evolution-api:latest Up 2 days
|
||||
inspiring_williams ghcr.io/czlonkowski/n8n-mcp:latest Up 4 hours (unhealthy)
|
||||
landing-page nginx:alpine Up 4 days
|
||||
mautic-db mariadb:11 Up 2 days
|
||||
mautic mautic/mautic:latest Up 2 days
|
||||
mautic-odoo-sync-v2 python:3.11-slim Up 2 days (healthy)
|
||||
mautic-sync-webhook python:3.11-slim Up 2 days
|
||||
modest_archimedes ghcr.io/czlonkowski/n8n-mcp:latest Up 3 hours (unhealthy)
|
||||
n8n-shared n8nio/n8n:latest Up 2 days
|
||||
n8n-worker-1 n8nio/n8n:latest Up 2 days
|
||||
n8n-worker-2 n8nio/n8n:latest Up 2 days
|
||||
n8n-worker-3 n8nio/n8n:latest Up 2 days
|
||||
n8n-worker-4 n8nio/n8n:latest Up 2 days
|
||||
NAMES IMAGE STATUS
|
||||
odoo odoo:17 Up 45 hours
|
||||
outline outlinewiki/outline:latest Up 16 hours (healthy)
|
||||
pgadmin dpage/pgadmin4:9.9 Up 2 days
|
||||
portainer portainer/portainer-ce:latest Up 2 days
|
||||
postgres-main postgres:16-alpine Up 2 days (healthy)
|
||||
postiz ghcr.io/gitroomhq/postiz-app:latest Up 3 hours
|
||||
postiz-postgres postgres:16-alpine Up 3 hours (healthy)
|
||||
postiz-redis redis:7-alpine Up 3 hours (healthy)
|
||||
rabbitmq rabbitmq:3.13-management-alpine Up 2 days (healthy)
|
||||
realtime-dev.supabase-realtime supabase/realtime:v2.51.11 Up 2 days (healthy)
|
||||
redis-main redis:7-alpine Up 4 days (healthy)
|
||||
sftp-server atmoz/sftp:latest Up 4 days
|
||||
supabase-analytics supabase/logflare:1.22.6 Up 2 days (healthy)
|
||||
supabase-auth supabase/gotrue:v2.180.0 Up 2 days (healthy)
|
||||
supabase-db supabase/postgres:15.8.1.085 Up 2 days (healthy)
|
||||
supabase-edge-functions supabase/edge-runtime:v1.69.6 Up 2 days
|
||||
supabase-imgproxy darthsim/imgproxy:v3.8.0 Up 2 days (healthy)
|
||||
supabase-kong kong:2.8.1 Up 2 days (healthy)
|
||||
supabase-meta supabase/postgres-meta:v0.91.6 Up 2 days (healthy)
|
||||
supabase-pooler supabase/supavisor:2.7.0 Up 2 days (healthy)
|
||||
supabase-rest postgrest/postgrest:v13.0.7 Up 2 days
|
||||
supabase-storage supabase/storage-api:v1.28.0 Up 2 days (healthy)
|
||||
supabase-studio supabase/studio:2025.10.01-sha-8460121 Up 2 days (healthy)
|
||||
supabase-vector timberio/vector:0.28.1-alpine Up 2 days (healthy)
|
||||
traefik traefik:v3.0 Up 2 days
|
||||
uptime-kuma louislam/uptime-kuma:latest Up 2 days (healthy)
|
||||
vault hashicorp/vault:1.15 Up 4 days (healthy)
|
||||
vaultwarden vaultwarden/server:1.30.5-alpine Up 2 days (healthy)
|
||||
|
||||
## 🌐 Docker Networks
|
||||
|
||||
NETWORK ID NAME DRIVER SCOPE
|
||||
5e188b0a38a2 authentik_authentik-internal bridge local
|
||||
f02abd181cda bridge bridge local
|
||||
cdaceadfcefb database-internal bridge local
|
||||
4b9a877223ee host host local
|
||||
a0dd7c187961 mautic-internal bridge local
|
||||
f2be33f79fe4 mautic_mautic-internal bridge local
|
||||
7c3083a44e08 monitoring bridge local
|
||||
01cd476e5307 n8n-shared bridge local
|
||||
d21678395b05 none null local
|
||||
39a0028fdb88 odoo-internal bridge local
|
||||
e581070d94e4 postiz-config_postiz-internal bridge local
|
||||
9e103eeb0591 postiz_postiz-internal bridge local
|
||||
c797531af13d sftp_default bridge local
|
||||
36cf0e468fc7 supabase_default bridge local
|
||||
b28f70541993 traefik-public bridge local
|
||||
4bc92c6c74a8 vault-internal bridge local
|
||||
|
||||
## 📁 /opt Structure
|
||||
|
||||
/opt
|
||||
/opt/00-infrastructure
|
||||
/opt/00-infrastructure/postgres
|
||||
/opt/00-infrastructure/postgres/backups
|
||||
/opt/00-infrastructure/postgres/init-scripts
|
||||
/opt/00-infrastructure/postgres/pgadmin-config
|
||||
/opt/00-infrastructure/postgres/scripts
|
||||
/opt/00-infrastructure/rabbitmq
|
||||
/opt/00-infrastructure/redis
|
||||
/opt/00-infrastructure/traefik
|
||||
/opt/00-infrastructure/traefik/acme
|
||||
/opt/00-infrastructure/traefik/config
|
||||
/opt/00-infrastructure/traefik/logs
|
||||
/opt/00-infrastructure/vault
|
||||
/opt/00-infrastructure/vault/config
|
||||
/opt/00-infrastructure/vault/data
|
||||
/opt/00-infrastructure/vault/logs
|
||||
/opt/00-infrastructure/vault/policies
|
||||
/opt/01-security
|
||||
/opt/01-security/authentik
|
||||
/opt/01-security/authentik/blueprints
|
||||
/opt/01-security/authentik/certs
|
||||
/opt/01-security/authentik/custom-templates
|
||||
/opt/01-security/authentik/media
|
||||
/opt/01-security/vaultwarden
|
||||
/opt/02-core
|
||||
/opt/02-core/evolution-api
|
||||
/opt/02-core/n8n-shared
|
||||
/opt/02-core/n8n-shared/custom-nodes
|
||||
/opt/02-core/n8n-vip
|
||||
/opt/02-core/supabase
|
||||
/opt/02-core/supabase/supabase
|
||||
/opt/03-business
|
||||
/opt/03-business/mautic
|
||||
/opt/03-business/mautic/sync
|
||||
/opt/03-business/mautic/sync_v2
|
||||
/opt/03-business/odoo
|
||||
/opt/03-business/outline
|
||||
/opt/04-tools
|
||||
/opt/04-tools/portainer
|
||||
/opt/04-tools/uptime-kuma
|
||||
/opt/05-backups
|
||||
/opt/05-backups/credentials
|
||||
/opt/05-backups/data
|
||||
/opt/05-backups/docs
|
||||
/opt/05-backups/docs/full
|
||||
/opt/05-backups/logs
|
||||
/opt/05-backups/migration-20251026-171124
|
||||
/opt/05-backups/restic
|
||||
/opt/05-backups/scripts
|
||||
/opt/infrastructure-docs
|
||||
/opt/postiz-config
|
||||
|
||||
## 💾 Databases
|
||||
|
||||
### PostgreSQL
|
||||
- postgres-main:
|
||||
postgres
|
||||
authentik
|
||||
supabase
|
||||
n8n_shared
|
||||
odoo
|
||||
outline
|
||||
vaultwarden
|
||||
evolution
|
||||
aimpress_admin
|
||||
authelia
|
||||
- supabase-db:
|
||||
postgres
|
||||
_supabase
|
||||
- authentik-postgres:
|
||||
postgres
|
||||
authentik
|
||||
- postiz-postgres:
|
||||
postgres
|
||||
postiz
|
||||
|
||||
### MariaDB (Mautic)
|
||||
Database
|
||||
information_schema
|
||||
mautic
|
||||
|
||||
### Redis
|
||||
- authentik-redis
|
||||
- postiz-redis
|
||||
redis_version:7.4.6
|
||||
connected_clients:12
|
||||
used_memory_human:2.44M
|
||||
- redis-main
|
||||
|
||||
## 🔐 Authentik
|
||||
|
||||
- Config: /opt/01-security/authentik/.env
|
||||
AUTHENTIK_EMAIL__HOST=aiimpress-com0e.mail.protection.outlook.com
|
||||
AUTHENTIK_EMAIL__FROM=noreply@ai-impress.com
|
||||
- Blueprints:
|
||||
outline-app.yaml
|
||||
postiz-app.yaml
|
||||
- Proxy token:
|
||||
present
|
||||
|
||||
## 🏦 Vault
|
||||
|
||||
Key Value
|
||||
--- -----
|
||||
Seal Type shamir
|
||||
Initialized true
|
||||
Sealed false
|
||||
Total Shares 5
|
||||
Threshold 3
|
||||
Version 1.15.6
|
||||
Build Date 2024-02-28T17:07:34Z
|
||||
Storage Type file
|
||||
Cluster Name vault-cluster-bdcdf7bc
|
||||
Cluster ID a8ec6e5c-2298-eb9d-b0c5-636a68d96a1d
|
||||
HA Enabled false
|
||||
|
||||
- Config:
|
||||
vault.hcl
|
||||
- Token:
|
||||
present
|
||||
- Policies:
|
||||
aimpress-admin.hcl
|
||||
|
||||
## 🕒 Cron Jobs
|
||||
|
||||
No root cron
|
||||
|
||||
## 📦 Backup Scripts
|
||||
|
||||
authentik-services-manager.sh
|
||||
auto-update.sh
|
||||
backup-app.sh
|
||||
backup-authentik-improved.sh
|
||||
backup-authentik.sh
|
||||
backup-full.sh
|
||||
backup-full.sh.backup-20251024-231153
|
||||
backup-full.sh.backup-20251026-145737
|
||||
backup-full.sh.bak
|
||||
check-updates.sh
|
||||
check-updates.sh.backup
|
||||
check-updates.sh.backup-20251020-115409
|
||||
create-client-database.sh
|
||||
create-n8n-vip-instance.sh
|
||||
generate-env-from-vault.sh
|
||||
generate-env-from-vault.sh.backup-20251023-225538
|
||||
get-client-credentials.sh
|
||||
health-check-improved.sh
|
||||
health-check.sh
|
||||
health-check.sh.backup-20251020-160110
|
||||
health-check.sh.backup-20251024-223943
|
||||
health-check.sh.backup-20251025-223638
|
||||
health-check.sh.backup-20251025-233447
|
||||
health-check.sh.backup-20251026-040001
|
||||
health-check.sh.backup-20251026-181026
|
||||
health-check.sh.backup-20251027-122447
|
||||
health-check.sh.backup-20251029-135119
|
||||
infrastructure-scanner.sh
|
||||
migrate-authelia-to-authentik.sh
|
||||
README.md
|
||||
scripts-help.sh
|
||||
update-app.sh
|
||||
update-app.sh.backup-20251020-115409
|
||||
update-manager.sh
|
||||
update-manager.sh.backup-20251020-115409
|
||||
upload-full-docs.sh
|
||||
vault-helper.sh
|
||||
2000
home/.bash_history
Normal file
2000
home/.bash_history
Normal file
File diff suppressed because it is too large
Load diff
7
home/.bash_logout
Normal file
7
home/.bash_logout
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# ~/.bash_logout: executed by bash(1) when login shell exits.
|
||||
|
||||
# when leaving the console clear the screen to increase privacy
|
||||
|
||||
if [ "$SHLVL" = 1 ]; then
|
||||
[ -x /usr/bin/clear_console ] && /usr/bin/clear_console -q
|
||||
fi
|
||||
164
home/.bashrc
Normal file
164
home/.bashrc
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
# ~/.bashrc: executed by bash(1) for non-login shells.
|
||||
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
|
||||
# for examples
|
||||
|
||||
# If not running interactively, don't do anything
|
||||
case $- in
|
||||
*i*) ;;
|
||||
*) return;;
|
||||
esac
|
||||
|
||||
# don't put duplicate lines or lines starting with space in the history.
|
||||
# See bash(1) for more options
|
||||
HISTCONTROL=ignoreboth
|
||||
|
||||
# append to the history file, don't overwrite it
|
||||
shopt -s histappend
|
||||
|
||||
# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
|
||||
HISTSIZE=1000
|
||||
HISTFILESIZE=2000
|
||||
|
||||
# check the window size after each command and, if necessary,
|
||||
# update the values of LINES and COLUMNS.
|
||||
shopt -s checkwinsize
|
||||
|
||||
# If set, the pattern "**" used in a pathname expansion context will
|
||||
# match all files and zero or more directories and subdirectories.
|
||||
#shopt -s globstar
|
||||
|
||||
# make less more friendly for non-text input files, see lesspipe(1)
|
||||
[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"
|
||||
|
||||
# set variable identifying the chroot you work in (used in the prompt below)
|
||||
if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then
|
||||
debian_chroot=$(cat /etc/debian_chroot)
|
||||
fi
|
||||
|
||||
# set a fancy prompt (non-color, unless we know we "want" color)
|
||||
case "$TERM" in
|
||||
xterm-color|*-256color) color_prompt=yes;;
|
||||
esac
|
||||
|
||||
# uncomment for a colored prompt, if the terminal has the capability; turned
|
||||
# off by default to not distract the user: the focus in a terminal window
|
||||
# should be on the output of commands, not on the prompt
|
||||
#force_color_prompt=yes
|
||||
|
||||
if [ -n "$force_color_prompt" ]; then
|
||||
if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
|
||||
# We have color support; assume it's compliant with Ecma-48
|
||||
# (ISO/IEC-6429). (Lack of such support is extremely rare, and such
|
||||
# a case would tend to support setf rather than setaf.)
|
||||
color_prompt=yes
|
||||
else
|
||||
color_prompt=
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$color_prompt" = yes ]; then
|
||||
PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
|
||||
else
|
||||
PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
|
||||
fi
|
||||
unset color_prompt force_color_prompt
|
||||
|
||||
# If this is an xterm set the title to user@host:dir
|
||||
case "$TERM" in
|
||||
xterm*|rxvt*)
|
||||
PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1"
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
|
||||
# enable color support of ls and also add handy aliases
|
||||
if [ -x /usr/bin/dircolors ]; then
|
||||
test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
|
||||
alias ls='ls --color=auto'
|
||||
#alias dir='dir --color=auto'
|
||||
#alias vdir='vdir --color=auto'
|
||||
|
||||
alias grep='grep --color=auto'
|
||||
alias fgrep='fgrep --color=auto'
|
||||
alias egrep='egrep --color=auto'
|
||||
fi
|
||||
|
||||
# colored GCC warnings and errors
|
||||
#export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'
|
||||
|
||||
# some more ls aliases
|
||||
alias ll='ls -alF'
|
||||
alias la='ls -A'
|
||||
alias l='ls -CF'
|
||||
|
||||
# Add an "alert" alias for long running commands. Use like so:
|
||||
# sleep 10; alert
|
||||
alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'
|
||||
|
||||
# Alias definitions.
|
||||
# You may want to put all your additions into a separate file like
|
||||
# ~/.bash_aliases, instead of adding them here directly.
|
||||
# See /usr/share/doc/bash-doc/examples in the bash-doc package.
|
||||
|
||||
if [ -f ~/.bash_aliases ]; then
|
||||
. ~/.bash_aliases
|
||||
fi
|
||||
|
||||
# enable programmable completion features (you don't need to enable
|
||||
# this, if it's already enabled in /etc/bash.bashrc and /etc/profile
|
||||
# sources /etc/bash.bashrc).
|
||||
if ! shopt -oq posix; then
|
||||
if [ -f /usr/share/bash-completion/bash_completion ]; then
|
||||
. /usr/share/bash-completion/bash_completion
|
||||
elif [ -f /etc/bash_completion ]; then
|
||||
. /etc/bash_completion
|
||||
fi
|
||||
fi
|
||||
export LANG=en_US.UTF-8
|
||||
export VAULT_ADDR="http://127.0.0.1:8200"
|
||||
export VAULT_TOKEN="hvs.CAESIKhabWn_M1AmzRqaW-WBZx7MGYP1cHsmdrcQmIMcmXR6Gh4KHGh2cy55bVk1WmpFZ3Y0TE82YVQzTWlmWWc5Z2Q"
|
||||
export VAULT_ADDR="http://127.0.0.1:8200"
|
||||
export VAULT_TOKEN="hvs.CAESIKhabWn_M1AmzRqaW-WBZx7MGYP1cHsmdrcQmIMcmXR6Gh4KHGh2cy55bVk1WmpFZ3Y0TE82YVQzTWlmWWc5Z2Q"
|
||||
alias vault-secret='/opt/05-backups/scripts/vault-helper.sh'
|
||||
|
||||
# ============================================
|
||||
# AImpress Aliases
|
||||
# ============================================
|
||||
|
||||
# Vault
|
||||
alias vault-secret='/opt/05-backups/scripts/vault-helper.sh'
|
||||
alias vault-unseal='/opt/00-infrastructure/vault/unseal.sh'
|
||||
|
||||
# Client Management
|
||||
alias create-client='/opt/05-backups/scripts/create-client-database.sh'
|
||||
alias create-n8n='/opt/05-backups/scripts/create-n8n-vip-instance.sh'
|
||||
alias get-client='/opt/05-backups/scripts/get-client-credentials.sh'
|
||||
|
||||
# System
|
||||
alias health-check='/opt/05-backups/scripts/health-check.sh'
|
||||
alias backup-now='export VAULT_TOKEN=$(cat /opt/00-infrastructure/vault/.vault-token) && /opt/05-backups/scripts/backup-full.sh'
|
||||
alias generate-env='/opt/05-backups/scripts/generate-env-from-vault.sh'
|
||||
|
||||
# Docker shortcuts
|
||||
alias dps='docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"'
|
||||
alias dlogs='docker compose logs -f'
|
||||
alias dstats='docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}"'
|
||||
|
||||
# Logs
|
||||
alias logs-traefik='docker logs -f traefik'
|
||||
alias logs-vault='docker logs -f vault'
|
||||
alias logs-postgres='docker logs -f postgres-main'
|
||||
alias logs-n8n='docker logs -f n8n-shared'
|
||||
|
||||
|
||||
# ════════════════════════════════════════════════════════════════
|
||||
# AImpress Infrastructure Tools Aliases
|
||||
# ════════════════════════════════════════════════════════════════
|
||||
|
||||
alias scripts='/opt/05-backups/scripts/scripts-help.sh'
|
||||
alias health='/opt/05-backups/scripts/health-check.sh'
|
||||
alias scan='/opt/05-backups/scripts/infrastructure-scanner.sh'
|
||||
alias update-all='/opt/05-backups/scripts/auto-update.sh'
|
||||
alias backup='/opt/05-backups/scripts/backup-full.sh'
|
||||
|
||||
7
home/.config/rclone/rclone.conf
Normal file
7
home/.config/rclone/rclone.conf
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
[r2]
|
||||
type = s3
|
||||
provider = Cloudflare
|
||||
access_key_id = a92cb6c958a5392fd1ba5e4d1cb857af
|
||||
secret_access_key = 0a4a0863e3afe2088b2990cca9152166b036343975ff3b3e5ad39fa923323aed
|
||||
endpoint = https://6aff840a680098927b58beb93b59dd03.r2.cloudflarestorage.com
|
||||
acl = private
|
||||
1
home/.config/systemd/user/default.target.wants/syncthing.service
Symbolic link
1
home/.config/systemd/user/default.target.wants/syncthing.service
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
/usr/lib/systemd/user/syncthing.service
|
||||
5
home/.docker/.token_seed
Normal file
5
home/.docker/.token_seed
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"registry-1.docker.io": {
|
||||
"Seed": "nUB+D7wwNzJAYuxW98LG7g=="
|
||||
}
|
||||
}
|
||||
0
home/.docker/.token_seed.lock
Normal file
0
home/.docker/.token_seed.lock
Normal file
1
home/.docker/buildx/.buildNodeID
Normal file
1
home/.docker/buildx/.buildNodeID
Normal file
|
|
@ -0,0 +1 @@
|
|||
35a9aa8bc7025bac
|
||||
0
home/.docker/buildx/.lock
Normal file
0
home/.docker/buildx/.lock
Normal file
1
home/.docker/buildx/activity/default
Normal file
1
home/.docker/buildx/activity/default
Normal file
|
|
@ -0,0 +1 @@
|
|||
2025-11-03T14:52:20Z
|
||||
1
home/.docker/buildx/current
Normal file
1
home/.docker/buildx/current
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"Key":"unix:///var/run/docker.sock","Name":"","Global":false}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Targets":["default"],"Refs":["coxvioxaj8vn5e7s8hqzbaycd"]}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Targets":["default"],"Refs":["xh9axp3ym4qcf852ndxxjqh9w"]}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Targets":["default"],"Refs":["ug6s8rodxw9ykroetepcbjupy"]}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Targets":["default"],"Refs":["i4dyv81q364vcs58lvd98z656"]}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Targets":["default"],"Refs":["sfyrmppbk0udq6ufxesh218rc"]}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Targets":["default"],"Refs":["ik8cdrh2rs30uu9a72pgfbedh"]}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Targets":["default"],"Refs":["515b7gh11hhcbv361w7dbtm65"]}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Targets":["default"],"Refs":["vhxqpunvttin7o4usxjo75i5l"]}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Targets":["default"],"Refs":["81y7sj7ratv81ybnbr0ai4gre"]}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Targets":["default"],"Refs":["dj3if82z7hjqil787j05p9jkc"]}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Targets":["default"],"Refs":["5xp225v1k3a620x0fye8mcfwq"]}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Targets":["default"],"Refs":["vm5uw7sg76nl7zb4k2klk3rh9"]}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Targets":["default"],"Refs":["favyuxo9e6kn826ezfz3d1q52"]}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Targets":["default"],"Refs":["l62xyapi5dg0mtgfvslfkbsgw"]}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Targets":["default"],"Refs":["bcvzf1etshxh3rdq4d41oowwz"]}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Targets":["default"],"Refs":["wi4ktf3n548ihwrfx187mke5z"]}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Targets":["default"],"Refs":["7ykc3ahz4iz1v32fbkqxjrf1x"]}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Targets":["default"],"Refs":["id3wnjohsddxxzr8thxhv55tf"]}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Targets":["default"],"Refs":["iv7s2neqb2jt7jcr5szaxvti5"]}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Targets":["default"],"Refs":["40kmufup0z67r0v9zsf7o7gfj"]}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Targets":["default"],"Refs":["tofs8bynq7za75ytn0cb1d53o"]}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Targets":["default"],"Refs":["oev1eyfznq7dg4f5drzxa586a"]}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Targets":["default"],"Refs":["5zegqe73g8ptmt0cra4no368u"]}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Targets":["default"],"Refs":["r40pa45kyv9oi6va2lldcx4i5"]}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Targets":["default"],"Refs":["zsu0k3o94hvmavzbfczpixynt"]}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Target":"server","LocalPath":"/mnt/psql-data/n8n-mcp/outline-mcp","DockerfilePath":"/mnt/psql-data/n8n-mcp/outline-mcp/Dockerfile","GroupRef":"w2nf8x2relyavrmj7lrv76eqh"}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Target":"copilotkit-runtime","LocalPath":"/opt/03-business/copilotkit-runtime-service","DockerfilePath":"/opt/03-business/copilotkit-runtime-service/Dockerfile","GroupRef":"cpjevk8wn71rvx99p1euk0lqm"}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Target":"authelia-manager","LocalPath":"/home/ubuntu/authelia-user-manager","DockerfilePath":"/home/ubuntu/authelia-user-manager/Dockerfile","GroupRef":"ixh7pp32pu7kg9r8hx3ytun4n"}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Target":"authelia-manager","LocalPath":"/home/ubuntu/authelia-user-manager","DockerfilePath":"/home/ubuntu/authelia-user-manager/Dockerfile","GroupRef":"xc49wmh8rud8vu34fkf1cn535"}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Target":"mautic-ai-widget","LocalPath":"/opt/03-business/mautic-ai-widget","DockerfilePath":"/opt/03-business/mautic-ai-widget/Dockerfile","GroupRef":"s9xg5aq0eqyjdrrvlnqoa5uc0"}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Target":"n8n-mcp-api","LocalPath":"/mnt/psql-data/n8n-mcp/kingler-server","DockerfilePath":"/mnt/psql-data/n8n-mcp/kingler-server/Dockerfile","GroupRef":"exr9lr5ee663ndyy94kq9cq5g"}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Target":"authelia-manager","LocalPath":"/home/ubuntu/authelia-user-manager","DockerfilePath":"/home/ubuntu/authelia-user-manager/Dockerfile","GroupRef":"ndxdhvirihbemq1bpofv6wog5"}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Target":"mautic-ai-widget","LocalPath":"/opt/03-business/mautic-ai-widget","DockerfilePath":"/opt/03-business/mautic-ai-widget/Dockerfile","GroupRef":"00ia54h1lqk6h9rjfac8316un"}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Target":"n8n-mcp-api","LocalPath":"/mnt/psql-data/n8n-mcp/kingler-server","DockerfilePath":"/mnt/psql-data/n8n-mcp/kingler-server/Dockerfile","GroupRef":"ff5asjlwpnnq81p2yofifgdri"}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Target":"mautic-ai-backend","LocalPath":"/opt/03-business/mautic-ai-backend","DockerfilePath":"/opt/03-business/mautic-ai-backend/Dockerfile","GroupRef":"jc0kd7x0574c7m7uuo2qnpzko"}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Target":"web","LocalPath":"/opt/03-business/mautic","DockerfilePath":"/opt/03-business/mautic/Dockerfile","GroupRef":"bj0782i1o9upeefa9gzzw3zm8"}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Target":"mautic-ai-widget","LocalPath":"/opt/03-business/mautic-ai-widget","DockerfilePath":"/opt/03-business/mautic-ai-widget/Dockerfile","GroupRef":"sut5234cf718r4a56undefm69"}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Target":"mautic-ai-backend","LocalPath":"/opt/03-business/mautic-ai-backend","DockerfilePath":"/opt/03-business/mautic-ai-backend/Dockerfile","GroupRef":"c0xphwi7ajbpkmf2xza8rkqb0"}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Target":"authelia-manager","LocalPath":"/home/ubuntu/authelia-user-manager","DockerfilePath":"/home/ubuntu/authelia-user-manager/Dockerfile","GroupRef":"vvrivck3v35gyrl3qxg0dsucp"}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Target":"web","LocalPath":"/opt/03-business/mautic","DockerfilePath":"/opt/03-business/mautic/Dockerfile","GroupRef":"jmwbygb44wgoncgdwhs79znqr"}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Target":"web","LocalPath":"/opt/03-business/mautic","DockerfilePath":"/opt/03-business/mautic/Dockerfile","GroupRef":"x23hsxj6p8lmly9be8jvrjn2o"}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Target":"authelia-manager","LocalPath":"/home/ubuntu/authelia-user-manager","DockerfilePath":"/home/ubuntu/authelia-user-manager/Dockerfile","GroupRef":"xx79s0dohu8wjb609hydgrz3o"}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Target":"authelia-manager","LocalPath":"/home/ubuntu/authelia-user-manager","DockerfilePath":"/home/ubuntu/authelia-user-manager/Dockerfile","GroupRef":"bp02x9xpyuktgx5itlmsvvlpm"}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Target":"authelia-manager","LocalPath":"/home/ubuntu/authelia-user-manager","DockerfilePath":"/home/ubuntu/authelia-user-manager/Dockerfile","GroupRef":"w5ko1fi47iblxe5ymwph8cctx"}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Target":"default","LocalPath":"/opt/03-business/mautic-ai-widget","DockerfilePath":""}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Target":"authelia-manager","LocalPath":"/home/ubuntu/authelia-user-manager","DockerfilePath":"/home/ubuntu/authelia-user-manager/Dockerfile","GroupRef":"3vdznzboq7zfi6hp0sjevglbn"}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Target":"documenso","LocalPath":"/opt/03-business/documenso","DockerfilePath":"/opt/03-business/documenso/Dockerfile.custom","GroupRef":"e5l54xhahkmg5fhy0uajmb4n2"}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Target":"authelia-manager","LocalPath":"/home/ubuntu/authelia-user-manager","DockerfilePath":"/home/ubuntu/authelia-user-manager/Dockerfile","GroupRef":"j1k19adj7iy9rrmr1j8srvd2w"}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Target":"web","LocalPath":"/opt/03-business/mautic","DockerfilePath":"/opt/03-business/mautic/Dockerfile","GroupRef":"qv1sro1gt0rerxsnwkm95nn4k"}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Target":"n8n-mcp-api","LocalPath":"/mnt/psql-data/n8n-mcp/kingler-server","DockerfilePath":"/mnt/psql-data/n8n-mcp/kingler-server/Dockerfile","GroupRef":"39nztb9c2xc3exglqyc5z316p"}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Target":"n8n-mcp-api","LocalPath":"/mnt/psql-data/n8n-mcp/kingler-server","DockerfilePath":"/mnt/psql-data/n8n-mcp/kingler-server/Dockerfile","GroupRef":"yrur461lr1zgjzx79rlhok1hi"}
|
||||
1
home/.docker/buildx/refs/version
Normal file
1
home/.docker/buildx/refs/version
Normal file
|
|
@ -0,0 +1 @@
|
|||
2
|
||||
3
home/.lesshst
Normal file
3
home/.lesshst
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
.less-history-file:
|
||||
.search
|
||||
"q
|
||||
316
home/.local/state/syncthing/config.xml
Normal file
316
home/.local/state/syncthing/config.xml
Normal file
|
|
@ -0,0 +1,316 @@
|
|||
<configuration version="37">
|
||||
<folder id="data" label="data" path="/data" type="sendreceive" rescanIntervalS="3600" fsWatcherEnabled="true" fsWatcherDelayS="10" fsWatcherTimeoutS="0" ignorePerms="false" autoNormalize="true">
|
||||
<filesystemType>basic</filesystemType>
|
||||
<device id="FVT5C5F-R6BTJTF-6Y4NOWA-BS2EU3P-WM5MPNK-NQGJHWH-4MMZ4VK-PB4U6AT" introducedBy="">
|
||||
<encryptionPassword></encryptionPassword>
|
||||
</device>
|
||||
<device id="7WWT3Q3-IL2RSA2-XHO6JKN-LCL2XEL-NWFZCDJ-6XVWIZ7-NYJ6JKJ-C53YCAC" introducedBy="">
|
||||
<encryptionPassword></encryptionPassword>
|
||||
</device>
|
||||
<minDiskFree unit="">0</minDiskFree>
|
||||
<versioning>
|
||||
<cleanupIntervalS>3600</cleanupIntervalS>
|
||||
<fsPath></fsPath>
|
||||
<fsType>basic</fsType>
|
||||
</versioning>
|
||||
<copiers>0</copiers>
|
||||
<pullerMaxPendingKiB>0</pullerMaxPendingKiB>
|
||||
<hashers>0</hashers>
|
||||
<order>random</order>
|
||||
<ignoreDelete>false</ignoreDelete>
|
||||
<scanProgressIntervalS>0</scanProgressIntervalS>
|
||||
<pullerPauseS>0</pullerPauseS>
|
||||
<maxConflicts>10</maxConflicts>
|
||||
<disableSparseFiles>false</disableSparseFiles>
|
||||
<disableTempIndexes>false</disableTempIndexes>
|
||||
<paused>false</paused>
|
||||
<weakHashThresholdPct>25</weakHashThresholdPct>
|
||||
<markerName>.stfolder</markerName>
|
||||
<copyOwnershipFromParent>false</copyOwnershipFromParent>
|
||||
<modTimeWindowS>0</modTimeWindowS>
|
||||
<maxConcurrentWrites>2</maxConcurrentWrites>
|
||||
<disableFsync>false</disableFsync>
|
||||
<blockPullOrder>standard</blockPullOrder>
|
||||
<copyRangeMethod>standard</copyRangeMethod>
|
||||
<caseSensitiveFS>false</caseSensitiveFS>
|
||||
<junctionsAsDirs>false</junctionsAsDirs>
|
||||
<syncOwnership>false</syncOwnership>
|
||||
<sendOwnership>false</sendOwnership>
|
||||
<syncXattrs>false</syncXattrs>
|
||||
<sendXattrs>false</sendXattrs>
|
||||
<xattrFilter>
|
||||
<maxSingleEntrySize>1024</maxSingleEntrySize>
|
||||
<maxTotalSize>4096</maxTotalSize>
|
||||
</xattrFilter>
|
||||
</folder>
|
||||
<folder id="default" label="Default Folder" path="/home/ubuntu/Sync" type="sendreceive" rescanIntervalS="3600" fsWatcherEnabled="true" fsWatcherDelayS="10" fsWatcherTimeoutS="0" ignorePerms="false" autoNormalize="true">
|
||||
<filesystemType>basic</filesystemType>
|
||||
<device id="7WWT3Q3-IL2RSA2-XHO6JKN-LCL2XEL-NWFZCDJ-6XVWIZ7-NYJ6JKJ-C53YCAC" introducedBy="">
|
||||
<encryptionPassword></encryptionPassword>
|
||||
</device>
|
||||
<minDiskFree unit="%">1</minDiskFree>
|
||||
<versioning>
|
||||
<cleanupIntervalS>3600</cleanupIntervalS>
|
||||
<fsPath></fsPath>
|
||||
<fsType>basic</fsType>
|
||||
</versioning>
|
||||
<copiers>0</copiers>
|
||||
<pullerMaxPendingKiB>0</pullerMaxPendingKiB>
|
||||
<hashers>0</hashers>
|
||||
<order>random</order>
|
||||
<ignoreDelete>false</ignoreDelete>
|
||||
<scanProgressIntervalS>0</scanProgressIntervalS>
|
||||
<pullerPauseS>0</pullerPauseS>
|
||||
<maxConflicts>10</maxConflicts>
|
||||
<disableSparseFiles>false</disableSparseFiles>
|
||||
<disableTempIndexes>false</disableTempIndexes>
|
||||
<paused>false</paused>
|
||||
<weakHashThresholdPct>25</weakHashThresholdPct>
|
||||
<markerName>.stfolder</markerName>
|
||||
<copyOwnershipFromParent>false</copyOwnershipFromParent>
|
||||
<modTimeWindowS>0</modTimeWindowS>
|
||||
<maxConcurrentWrites>2</maxConcurrentWrites>
|
||||
<disableFsync>false</disableFsync>
|
||||
<blockPullOrder>standard</blockPullOrder>
|
||||
<copyRangeMethod>standard</copyRangeMethod>
|
||||
<caseSensitiveFS>false</caseSensitiveFS>
|
||||
<junctionsAsDirs>false</junctionsAsDirs>
|
||||
<syncOwnership>false</syncOwnership>
|
||||
<sendOwnership>false</sendOwnership>
|
||||
<syncXattrs>false</syncXattrs>
|
||||
<sendXattrs>false</sendXattrs>
|
||||
<xattrFilter>
|
||||
<maxSingleEntrySize>1024</maxSingleEntrySize>
|
||||
<maxTotalSize>4096</maxTotalSize>
|
||||
</xattrFilter>
|
||||
</folder>
|
||||
<folder id="home" label="home" path="/home/ubuntu" type="sendreceive" rescanIntervalS="3600" fsWatcherEnabled="true" fsWatcherDelayS="10" fsWatcherTimeoutS="0" ignorePerms="false" autoNormalize="true">
|
||||
<filesystemType>basic</filesystemType>
|
||||
<device id="FVT5C5F-R6BTJTF-6Y4NOWA-BS2EU3P-WM5MPNK-NQGJHWH-4MMZ4VK-PB4U6AT" introducedBy="">
|
||||
<encryptionPassword></encryptionPassword>
|
||||
</device>
|
||||
<device id="7WWT3Q3-IL2RSA2-XHO6JKN-LCL2XEL-NWFZCDJ-6XVWIZ7-NYJ6JKJ-C53YCAC" introducedBy="">
|
||||
<encryptionPassword></encryptionPassword>
|
||||
</device>
|
||||
<minDiskFree unit="">0</minDiskFree>
|
||||
<versioning>
|
||||
<cleanupIntervalS>3600</cleanupIntervalS>
|
||||
<fsPath></fsPath>
|
||||
<fsType>basic</fsType>
|
||||
</versioning>
|
||||
<copiers>0</copiers>
|
||||
<pullerMaxPendingKiB>0</pullerMaxPendingKiB>
|
||||
<hashers>0</hashers>
|
||||
<order>random</order>
|
||||
<ignoreDelete>false</ignoreDelete>
|
||||
<scanProgressIntervalS>0</scanProgressIntervalS>
|
||||
<pullerPauseS>0</pullerPauseS>
|
||||
<maxConflicts>10</maxConflicts>
|
||||
<disableSparseFiles>false</disableSparseFiles>
|
||||
<disableTempIndexes>false</disableTempIndexes>
|
||||
<paused>false</paused>
|
||||
<weakHashThresholdPct>25</weakHashThresholdPct>
|
||||
<markerName>.stfolder</markerName>
|
||||
<copyOwnershipFromParent>false</copyOwnershipFromParent>
|
||||
<modTimeWindowS>0</modTimeWindowS>
|
||||
<maxConcurrentWrites>2</maxConcurrentWrites>
|
||||
<disableFsync>false</disableFsync>
|
||||
<blockPullOrder>standard</blockPullOrder>
|
||||
<copyRangeMethod>standard</copyRangeMethod>
|
||||
<caseSensitiveFS>false</caseSensitiveFS>
|
||||
<junctionsAsDirs>false</junctionsAsDirs>
|
||||
<syncOwnership>false</syncOwnership>
|
||||
<sendOwnership>false</sendOwnership>
|
||||
<syncXattrs>false</syncXattrs>
|
||||
<sendXattrs>false</sendXattrs>
|
||||
<xattrFilter>
|
||||
<maxSingleEntrySize>1024</maxSingleEntrySize>
|
||||
<maxTotalSize>4096</maxTotalSize>
|
||||
</xattrFilter>
|
||||
</folder>
|
||||
<folder id="opt" label="opt" path="/opt" type="sendreceive" rescanIntervalS="3600" fsWatcherEnabled="true" fsWatcherDelayS="10" fsWatcherTimeoutS="0" ignorePerms="false" autoNormalize="true">
|
||||
<filesystemType>basic</filesystemType>
|
||||
<device id="FVT5C5F-R6BTJTF-6Y4NOWA-BS2EU3P-WM5MPNK-NQGJHWH-4MMZ4VK-PB4U6AT" introducedBy="">
|
||||
<encryptionPassword></encryptionPassword>
|
||||
</device>
|
||||
<device id="7WWT3Q3-IL2RSA2-XHO6JKN-LCL2XEL-NWFZCDJ-6XVWIZ7-NYJ6JKJ-C53YCAC" introducedBy="">
|
||||
<encryptionPassword></encryptionPassword>
|
||||
</device>
|
||||
<minDiskFree unit="">0</minDiskFree>
|
||||
<versioning>
|
||||
<cleanupIntervalS>3600</cleanupIntervalS>
|
||||
<fsPath></fsPath>
|
||||
<fsType>basic</fsType>
|
||||
</versioning>
|
||||
<copiers>0</copiers>
|
||||
<pullerMaxPendingKiB>0</pullerMaxPendingKiB>
|
||||
<hashers>0</hashers>
|
||||
<order>random</order>
|
||||
<ignoreDelete>false</ignoreDelete>
|
||||
<scanProgressIntervalS>0</scanProgressIntervalS>
|
||||
<pullerPauseS>0</pullerPauseS>
|
||||
<maxConflicts>10</maxConflicts>
|
||||
<disableSparseFiles>false</disableSparseFiles>
|
||||
<disableTempIndexes>false</disableTempIndexes>
|
||||
<paused>false</paused>
|
||||
<weakHashThresholdPct>25</weakHashThresholdPct>
|
||||
<markerName>.stfolder</markerName>
|
||||
<copyOwnershipFromParent>false</copyOwnershipFromParent>
|
||||
<modTimeWindowS>0</modTimeWindowS>
|
||||
<maxConcurrentWrites>2</maxConcurrentWrites>
|
||||
<disableFsync>false</disableFsync>
|
||||
<blockPullOrder>standard</blockPullOrder>
|
||||
<copyRangeMethod>standard</copyRangeMethod>
|
||||
<caseSensitiveFS>false</caseSensitiveFS>
|
||||
<junctionsAsDirs>false</junctionsAsDirs>
|
||||
<syncOwnership>false</syncOwnership>
|
||||
<sendOwnership>false</sendOwnership>
|
||||
<syncXattrs>false</syncXattrs>
|
||||
<sendXattrs>false</sendXattrs>
|
||||
<xattrFilter>
|
||||
<maxSingleEntrySize>1024</maxSingleEntrySize>
|
||||
<maxTotalSize>4096</maxTotalSize>
|
||||
</xattrFilter>
|
||||
</folder>
|
||||
<device id="FVT5C5F-R6BTJTF-6Y4NOWA-BS2EU3P-WM5MPNK-NQGJHWH-4MMZ4VK-PB4U6AT" name="Mac" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
|
||||
<address>dynamic</address>
|
||||
<paused>false</paused>
|
||||
<autoAcceptFolders>false</autoAcceptFolders>
|
||||
<maxSendKbps>0</maxSendKbps>
|
||||
<maxRecvKbps>0</maxRecvKbps>
|
||||
<maxRequestKiB>0</maxRequestKiB>
|
||||
<untrusted>false</untrusted>
|
||||
<remoteGUIPort>0</remoteGUIPort>
|
||||
<numConnections>0</numConnections>
|
||||
</device>
|
||||
<device id="7WWT3Q3-IL2RSA2-XHO6JKN-LCL2XEL-NWFZCDJ-6XVWIZ7-NYJ6JKJ-C53YCAC" name="ai-impress-prod" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
|
||||
<address>dynamic</address>
|
||||
<paused>false</paused>
|
||||
<autoAcceptFolders>false</autoAcceptFolders>
|
||||
<maxSendKbps>0</maxSendKbps>
|
||||
<maxRecvKbps>0</maxRecvKbps>
|
||||
<maxRequestKiB>0</maxRequestKiB>
|
||||
<untrusted>false</untrusted>
|
||||
<remoteGUIPort>0</remoteGUIPort>
|
||||
<numConnections>0</numConnections>
|
||||
</device>
|
||||
<gui enabled="true" tls="false" debugging="false" sendBasicAuthPrompt="false">
|
||||
<address>127.0.0.1:8384</address>
|
||||
<metricsWithoutAuth>false</metricsWithoutAuth>
|
||||
<apikey>zPRt2MVENXuPrYx6QiqX9CorrdPd2CvJ</apikey>
|
||||
<theme>default</theme>
|
||||
</gui>
|
||||
<ldap></ldap>
|
||||
<options>
|
||||
<listenAddress>default</listenAddress>
|
||||
<globalAnnounceServer>default</globalAnnounceServer>
|
||||
<globalAnnounceEnabled>true</globalAnnounceEnabled>
|
||||
<localAnnounceEnabled>true</localAnnounceEnabled>
|
||||
<localAnnouncePort>21027</localAnnouncePort>
|
||||
<localAnnounceMCAddr>[ff12::8384]:21027</localAnnounceMCAddr>
|
||||
<maxSendKbps>0</maxSendKbps>
|
||||
<maxRecvKbps>0</maxRecvKbps>
|
||||
<reconnectionIntervalS>60</reconnectionIntervalS>
|
||||
<relaysEnabled>true</relaysEnabled>
|
||||
<relayReconnectIntervalM>10</relayReconnectIntervalM>
|
||||
<startBrowser>true</startBrowser>
|
||||
<natEnabled>true</natEnabled>
|
||||
<natLeaseMinutes>60</natLeaseMinutes>
|
||||
<natRenewalMinutes>30</natRenewalMinutes>
|
||||
<natTimeoutSeconds>10</natTimeoutSeconds>
|
||||
<urAccepted>0</urAccepted>
|
||||
<urSeen>0</urSeen>
|
||||
<urUniqueID></urUniqueID>
|
||||
<urURL>https://data.syncthing.net/newdata</urURL>
|
||||
<urPostInsecurely>false</urPostInsecurely>
|
||||
<urInitialDelayS>1800</urInitialDelayS>
|
||||
<autoUpgradeIntervalH>12</autoUpgradeIntervalH>
|
||||
<upgradeToPreReleases>false</upgradeToPreReleases>
|
||||
<keepTemporariesH>24</keepTemporariesH>
|
||||
<cacheIgnoredFiles>false</cacheIgnoredFiles>
|
||||
<progressUpdateIntervalS>5</progressUpdateIntervalS>
|
||||
<limitBandwidthInLan>false</limitBandwidthInLan>
|
||||
<minHomeDiskFree unit="%">1</minHomeDiskFree>
|
||||
<releasesURL>https://upgrades.syncthing.net/meta.json</releasesURL>
|
||||
<overwriteRemoteDeviceNamesOnConnect>false</overwriteRemoteDeviceNamesOnConnect>
|
||||
<tempIndexMinBlocks>10</tempIndexMinBlocks>
|
||||
<unackedNotificationID>authenticationUserAndPassword</unackedNotificationID>
|
||||
<trafficClass>0</trafficClass>
|
||||
<setLowPriority>true</setLowPriority>
|
||||
<maxFolderConcurrency>0</maxFolderConcurrency>
|
||||
<crashReportingURL>https://crash.syncthing.net/newcrash</crashReportingURL>
|
||||
<crashReportingEnabled>true</crashReportingEnabled>
|
||||
<stunKeepaliveStartS>180</stunKeepaliveStartS>
|
||||
<stunKeepaliveMinS>20</stunKeepaliveMinS>
|
||||
<stunServer>default</stunServer>
|
||||
<databaseTuning>auto</databaseTuning>
|
||||
<maxConcurrentIncomingRequestKiB>0</maxConcurrentIncomingRequestKiB>
|
||||
<announceLANAddresses>true</announceLANAddresses>
|
||||
<sendFullIndexOnUpgrade>false</sendFullIndexOnUpgrade>
|
||||
<auditEnabled>false</auditEnabled>
|
||||
<auditFile></auditFile>
|
||||
<connectionLimitEnough>0</connectionLimitEnough>
|
||||
<connectionLimitMax>0</connectionLimitMax>
|
||||
<connectionPriorityTcpLan>10</connectionPriorityTcpLan>
|
||||
<connectionPriorityQuicLan>20</connectionPriorityQuicLan>
|
||||
<connectionPriorityTcpWan>30</connectionPriorityTcpWan>
|
||||
<connectionPriorityQuicWan>40</connectionPriorityQuicWan>
|
||||
<connectionPriorityRelay>50</connectionPriorityRelay>
|
||||
<connectionPriorityUpgradeThreshold>0</connectionPriorityUpgradeThreshold>
|
||||
</options>
|
||||
<defaults>
|
||||
<folder id="" label="" path="" type="sendreceive" rescanIntervalS="3600" fsWatcherEnabled="true" fsWatcherDelayS="10" fsWatcherTimeoutS="0" ignorePerms="false" autoNormalize="true">
|
||||
<filesystemType>basic</filesystemType>
|
||||
<device id="7WWT3Q3-IL2RSA2-XHO6JKN-LCL2XEL-NWFZCDJ-6XVWIZ7-NYJ6JKJ-C53YCAC" introducedBy="">
|
||||
<encryptionPassword></encryptionPassword>
|
||||
</device>
|
||||
<minDiskFree unit="%">1</minDiskFree>
|
||||
<versioning>
|
||||
<cleanupIntervalS>3600</cleanupIntervalS>
|
||||
<fsPath></fsPath>
|
||||
<fsType>basic</fsType>
|
||||
</versioning>
|
||||
<copiers>0</copiers>
|
||||
<pullerMaxPendingKiB>0</pullerMaxPendingKiB>
|
||||
<hashers>0</hashers>
|
||||
<order>random</order>
|
||||
<ignoreDelete>false</ignoreDelete>
|
||||
<scanProgressIntervalS>0</scanProgressIntervalS>
|
||||
<pullerPauseS>0</pullerPauseS>
|
||||
<maxConflicts>10</maxConflicts>
|
||||
<disableSparseFiles>false</disableSparseFiles>
|
||||
<disableTempIndexes>false</disableTempIndexes>
|
||||
<paused>false</paused>
|
||||
<weakHashThresholdPct>25</weakHashThresholdPct>
|
||||
<markerName>.stfolder</markerName>
|
||||
<copyOwnershipFromParent>false</copyOwnershipFromParent>
|
||||
<modTimeWindowS>0</modTimeWindowS>
|
||||
<maxConcurrentWrites>2</maxConcurrentWrites>
|
||||
<disableFsync>false</disableFsync>
|
||||
<blockPullOrder>standard</blockPullOrder>
|
||||
<copyRangeMethod>standard</copyRangeMethod>
|
||||
<caseSensitiveFS>false</caseSensitiveFS>
|
||||
<junctionsAsDirs>false</junctionsAsDirs>
|
||||
<syncOwnership>false</syncOwnership>
|
||||
<sendOwnership>false</sendOwnership>
|
||||
<syncXattrs>false</syncXattrs>
|
||||
<sendXattrs>false</sendXattrs>
|
||||
<xattrFilter>
|
||||
<maxSingleEntrySize>1024</maxSingleEntrySize>
|
||||
<maxTotalSize>4096</maxTotalSize>
|
||||
</xattrFilter>
|
||||
</folder>
|
||||
<device id="" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
|
||||
<address>dynamic</address>
|
||||
<paused>false</paused>
|
||||
<autoAcceptFolders>false</autoAcceptFolders>
|
||||
<maxSendKbps>0</maxSendKbps>
|
||||
<maxRecvKbps>0</maxRecvKbps>
|
||||
<maxRequestKiB>0</maxRequestKiB>
|
||||
<untrusted>false</untrusted>
|
||||
<remoteGUIPort>0</remoteGUIPort>
|
||||
<numConnections>0</numConnections>
|
||||
</device>
|
||||
<ignores></ignores>
|
||||
</defaults>
|
||||
</configuration>
|
||||
173
home/.local/state/syncthing/config.xml.v0
Normal file
173
home/.local/state/syncthing/config.xml.v0
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
<configuration version="37">
|
||||
<folder id="default" label="Default Folder" path="/home/ubuntu/Sync" type="sendreceive" rescanIntervalS="3600" fsWatcherEnabled="true" fsWatcherDelayS="10" fsWatcherTimeoutS="0" ignorePerms="false" autoNormalize="true">
|
||||
<filesystemType>basic</filesystemType>
|
||||
<device id="7WWT3Q3-IL2RSA2-XHO6JKN-LCL2XEL-NWFZCDJ-6XVWIZ7-NYJ6JKJ-C53YCAC" introducedBy="">
|
||||
<encryptionPassword></encryptionPassword>
|
||||
</device>
|
||||
<minDiskFree unit="%">1</minDiskFree>
|
||||
<versioning>
|
||||
<cleanupIntervalS>3600</cleanupIntervalS>
|
||||
<fsPath></fsPath>
|
||||
<fsType>basic</fsType>
|
||||
</versioning>
|
||||
<copiers>0</copiers>
|
||||
<pullerMaxPendingKiB>0</pullerMaxPendingKiB>
|
||||
<hashers>0</hashers>
|
||||
<order>random</order>
|
||||
<ignoreDelete>false</ignoreDelete>
|
||||
<scanProgressIntervalS>0</scanProgressIntervalS>
|
||||
<pullerPauseS>0</pullerPauseS>
|
||||
<maxConflicts>10</maxConflicts>
|
||||
<disableSparseFiles>false</disableSparseFiles>
|
||||
<disableTempIndexes>false</disableTempIndexes>
|
||||
<paused>false</paused>
|
||||
<weakHashThresholdPct>25</weakHashThresholdPct>
|
||||
<markerName>.stfolder</markerName>
|
||||
<copyOwnershipFromParent>false</copyOwnershipFromParent>
|
||||
<modTimeWindowS>0</modTimeWindowS>
|
||||
<maxConcurrentWrites>2</maxConcurrentWrites>
|
||||
<disableFsync>false</disableFsync>
|
||||
<blockPullOrder>standard</blockPullOrder>
|
||||
<copyRangeMethod>standard</copyRangeMethod>
|
||||
<caseSensitiveFS>false</caseSensitiveFS>
|
||||
<junctionsAsDirs>false</junctionsAsDirs>
|
||||
<syncOwnership>false</syncOwnership>
|
||||
<sendOwnership>false</sendOwnership>
|
||||
<syncXattrs>false</syncXattrs>
|
||||
<sendXattrs>false</sendXattrs>
|
||||
<xattrFilter>
|
||||
<maxSingleEntrySize>1024</maxSingleEntrySize>
|
||||
<maxTotalSize>4096</maxTotalSize>
|
||||
</xattrFilter>
|
||||
</folder>
|
||||
<device id="7WWT3Q3-IL2RSA2-XHO6JKN-LCL2XEL-NWFZCDJ-6XVWIZ7-NYJ6JKJ-C53YCAC" name="ai-impress-prod" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
|
||||
<address>dynamic</address>
|
||||
<paused>false</paused>
|
||||
<autoAcceptFolders>false</autoAcceptFolders>
|
||||
<maxSendKbps>0</maxSendKbps>
|
||||
<maxRecvKbps>0</maxRecvKbps>
|
||||
<maxRequestKiB>0</maxRequestKiB>
|
||||
<untrusted>false</untrusted>
|
||||
<remoteGUIPort>0</remoteGUIPort>
|
||||
<numConnections>0</numConnections>
|
||||
</device>
|
||||
<gui enabled="true" tls="false" debugging="false" sendBasicAuthPrompt="false">
|
||||
<address>127.0.0.1:8384</address>
|
||||
<metricsWithoutAuth>false</metricsWithoutAuth>
|
||||
<apikey>zPRt2MVENXuPrYx6QiqX9CorrdPd2CvJ</apikey>
|
||||
<theme>default</theme>
|
||||
</gui>
|
||||
<ldap></ldap>
|
||||
<options>
|
||||
<listenAddress>default</listenAddress>
|
||||
<globalAnnounceServer>default</globalAnnounceServer>
|
||||
<globalAnnounceEnabled>true</globalAnnounceEnabled>
|
||||
<localAnnounceEnabled>true</localAnnounceEnabled>
|
||||
<localAnnouncePort>21027</localAnnouncePort>
|
||||
<localAnnounceMCAddr>[ff12::8384]:21027</localAnnounceMCAddr>
|
||||
<maxSendKbps>0</maxSendKbps>
|
||||
<maxRecvKbps>0</maxRecvKbps>
|
||||
<reconnectionIntervalS>60</reconnectionIntervalS>
|
||||
<relaysEnabled>true</relaysEnabled>
|
||||
<relayReconnectIntervalM>10</relayReconnectIntervalM>
|
||||
<startBrowser>true</startBrowser>
|
||||
<natEnabled>true</natEnabled>
|
||||
<natLeaseMinutes>60</natLeaseMinutes>
|
||||
<natRenewalMinutes>30</natRenewalMinutes>
|
||||
<natTimeoutSeconds>10</natTimeoutSeconds>
|
||||
<urAccepted>0</urAccepted>
|
||||
<urSeen>0</urSeen>
|
||||
<urUniqueID></urUniqueID>
|
||||
<urURL>https://data.syncthing.net/newdata</urURL>
|
||||
<urPostInsecurely>false</urPostInsecurely>
|
||||
<urInitialDelayS>1800</urInitialDelayS>
|
||||
<autoUpgradeIntervalH>12</autoUpgradeIntervalH>
|
||||
<upgradeToPreReleases>false</upgradeToPreReleases>
|
||||
<keepTemporariesH>24</keepTemporariesH>
|
||||
<cacheIgnoredFiles>false</cacheIgnoredFiles>
|
||||
<progressUpdateIntervalS>5</progressUpdateIntervalS>
|
||||
<limitBandwidthInLan>false</limitBandwidthInLan>
|
||||
<minHomeDiskFree unit="%">1</minHomeDiskFree>
|
||||
<releasesURL>https://upgrades.syncthing.net/meta.json</releasesURL>
|
||||
<overwriteRemoteDeviceNamesOnConnect>false</overwriteRemoteDeviceNamesOnConnect>
|
||||
<tempIndexMinBlocks>10</tempIndexMinBlocks>
|
||||
<unackedNotificationID>authenticationUserAndPassword</unackedNotificationID>
|
||||
<trafficClass>0</trafficClass>
|
||||
<setLowPriority>true</setLowPriority>
|
||||
<maxFolderConcurrency>0</maxFolderConcurrency>
|
||||
<crashReportingURL>https://crash.syncthing.net/newcrash</crashReportingURL>
|
||||
<crashReportingEnabled>true</crashReportingEnabled>
|
||||
<stunKeepaliveStartS>180</stunKeepaliveStartS>
|
||||
<stunKeepaliveMinS>20</stunKeepaliveMinS>
|
||||
<stunServer>default</stunServer>
|
||||
<databaseTuning>auto</databaseTuning>
|
||||
<maxConcurrentIncomingRequestKiB>0</maxConcurrentIncomingRequestKiB>
|
||||
<announceLANAddresses>true</announceLANAddresses>
|
||||
<sendFullIndexOnUpgrade>false</sendFullIndexOnUpgrade>
|
||||
<auditEnabled>false</auditEnabled>
|
||||
<auditFile></auditFile>
|
||||
<connectionLimitEnough>0</connectionLimitEnough>
|
||||
<connectionLimitMax>0</connectionLimitMax>
|
||||
<connectionPriorityTcpLan>10</connectionPriorityTcpLan>
|
||||
<connectionPriorityQuicLan>20</connectionPriorityQuicLan>
|
||||
<connectionPriorityTcpWan>30</connectionPriorityTcpWan>
|
||||
<connectionPriorityQuicWan>40</connectionPriorityQuicWan>
|
||||
<connectionPriorityRelay>50</connectionPriorityRelay>
|
||||
<connectionPriorityUpgradeThreshold>0</connectionPriorityUpgradeThreshold>
|
||||
</options>
|
||||
<defaults>
|
||||
<folder id="" label="" path="" type="sendreceive" rescanIntervalS="3600" fsWatcherEnabled="true" fsWatcherDelayS="10" fsWatcherTimeoutS="0" ignorePerms="false" autoNormalize="true">
|
||||
<filesystemType>basic</filesystemType>
|
||||
<device id="7WWT3Q3-IL2RSA2-XHO6JKN-LCL2XEL-NWFZCDJ-6XVWIZ7-NYJ6JKJ-C53YCAC" introducedBy="">
|
||||
<encryptionPassword></encryptionPassword>
|
||||
</device>
|
||||
<minDiskFree unit="%">1</minDiskFree>
|
||||
<versioning>
|
||||
<cleanupIntervalS>3600</cleanupIntervalS>
|
||||
<fsPath></fsPath>
|
||||
<fsType>basic</fsType>
|
||||
</versioning>
|
||||
<copiers>0</copiers>
|
||||
<pullerMaxPendingKiB>0</pullerMaxPendingKiB>
|
||||
<hashers>0</hashers>
|
||||
<order>random</order>
|
||||
<ignoreDelete>false</ignoreDelete>
|
||||
<scanProgressIntervalS>0</scanProgressIntervalS>
|
||||
<pullerPauseS>0</pullerPauseS>
|
||||
<maxConflicts>10</maxConflicts>
|
||||
<disableSparseFiles>false</disableSparseFiles>
|
||||
<disableTempIndexes>false</disableTempIndexes>
|
||||
<paused>false</paused>
|
||||
<weakHashThresholdPct>25</weakHashThresholdPct>
|
||||
<markerName>.stfolder</markerName>
|
||||
<copyOwnershipFromParent>false</copyOwnershipFromParent>
|
||||
<modTimeWindowS>0</modTimeWindowS>
|
||||
<maxConcurrentWrites>2</maxConcurrentWrites>
|
||||
<disableFsync>false</disableFsync>
|
||||
<blockPullOrder>standard</blockPullOrder>
|
||||
<copyRangeMethod>standard</copyRangeMethod>
|
||||
<caseSensitiveFS>false</caseSensitiveFS>
|
||||
<junctionsAsDirs>false</junctionsAsDirs>
|
||||
<syncOwnership>false</syncOwnership>
|
||||
<sendOwnership>false</sendOwnership>
|
||||
<syncXattrs>false</syncXattrs>
|
||||
<sendXattrs>false</sendXattrs>
|
||||
<xattrFilter>
|
||||
<maxSingleEntrySize>1024</maxSingleEntrySize>
|
||||
<maxTotalSize>4096</maxTotalSize>
|
||||
</xattrFilter>
|
||||
</folder>
|
||||
<device id="" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
|
||||
<address>dynamic</address>
|
||||
<paused>false</paused>
|
||||
<autoAcceptFolders>false</autoAcceptFolders>
|
||||
<maxSendKbps>0</maxSendKbps>
|
||||
<maxRecvKbps>0</maxRecvKbps>
|
||||
<maxRequestKiB>0</maxRequestKiB>
|
||||
<untrusted>false</untrusted>
|
||||
<remoteGUIPort>0</remoteGUIPort>
|
||||
<numConnections>0</numConnections>
|
||||
</device>
|
||||
<ignores></ignores>
|
||||
</defaults>
|
||||
</configuration>
|
||||
BIN
home/.local/state/syncthing/index-v0.14.0.db/000007.ldb
Normal file
BIN
home/.local/state/syncthing/index-v0.14.0.db/000007.ldb
Normal file
Binary file not shown.
BIN
home/.local/state/syncthing/index-v0.14.0.db/000010.ldb
Normal file
BIN
home/.local/state/syncthing/index-v0.14.0.db/000010.ldb
Normal file
Binary file not shown.
BIN
home/.local/state/syncthing/index-v0.14.0.db/000011.ldb
Normal file
BIN
home/.local/state/syncthing/index-v0.14.0.db/000011.ldb
Normal file
Binary file not shown.
BIN
home/.local/state/syncthing/index-v0.14.0.db/000012.ldb
Normal file
BIN
home/.local/state/syncthing/index-v0.14.0.db/000012.ldb
Normal file
Binary file not shown.
BIN
home/.local/state/syncthing/index-v0.14.0.db/000013.ldb
Normal file
BIN
home/.local/state/syncthing/index-v0.14.0.db/000013.ldb
Normal file
Binary file not shown.
1
home/.local/state/syncthing/index-v0.14.0.db/CURRENT
Normal file
1
home/.local/state/syncthing/index-v0.14.0.db/CURRENT
Normal file
|
|
@ -0,0 +1 @@
|
|||
MANIFEST-000009
|
||||
0
home/.local/state/syncthing/index-v0.14.0.db/LOCK
Normal file
0
home/.local/state/syncthing/index-v0.14.0.db/LOCK
Normal file
43
home/.local/state/syncthing/index-v0.14.0.db/LOG
Normal file
43
home/.local/state/syncthing/index-v0.14.0.db/LOG
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
=============== Nov 5, 2025 (UTC) ===============
|
||||
16:21:02.095451 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
|
||||
16:21:02.097634 db@open opening
|
||||
16:21:02.098162 version@stat F·[] S·0B[] Sc·[]
|
||||
16:21:02.098963 db@janitor F·2 G·0
|
||||
16:21:02.098986 db@open done T·1.332791ms
|
||||
16:21:02.565455 memdb@flush N·18 S·582B
|
||||
16:21:02.566837 memdb@flush created L0@3 N·18 S·301B "\ndb..ion,v18":"\ndb..ion,v1"
|
||||
16:21:02.566888 version@stat F·[1] S·301B[301B] Sc·[0.25]
|
||||
16:21:02.567407 memdb@flush committed F·1 T·1.832323ms
|
||||
16:21:02.567453 journal@remove removed @1
|
||||
16:21:02.567486 table@compaction range L-1 "":""
|
||||
16:21:02.567496 table@compaction L0·1 -> L1·0 S·301B Q·18
|
||||
16:21:02.568675 table@build created L1@4 N·3 S·173B "\ndb..ion,v18":"\ndb..ion,v16"
|
||||
16:21:02.568715 version@stat F·[0 1] S·173B[0B 173B] Sc·[0.00 0.00]
|
||||
16:21:02.569234 table@compaction committed F~ S-128B Ke·0 D·15 T·1.721146ms
|
||||
16:21:02.569401 table@remove removed @3
|
||||
16:30:19.132255 memdb@flush N·97086 S·15MiB
|
||||
16:30:19.227732 memdb@flush created L0@6 N·97086 S·6MiB "\x00\x00\x00..ure,v396":"\x0e\x00\x00..4e8,v90139"
|
||||
16:30:19.227791 version@stat F·[1 1] S·6MiB[6MiB 173B] Sc·[0.25 0.00]
|
||||
16:30:19.228349 memdb@flush committed F·1 T·96.040008ms
|
||||
16:30:19.230900 journal@remove removed @2
|
||||
16:31:01.663311 db@close closing
|
||||
16:31:01.664259 db@close done T·949.337µs
|
||||
=============== Nov 5, 2025 (UTC) ===============
|
||||
16:31:01.729761 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
|
||||
16:31:01.729976 version@stat F·[1 1] S·6MiB[6MiB 173B] Sc·[0.25 0.00]
|
||||
16:31:01.730003 db@open opening
|
||||
16:31:01.730046 journal@recovery F·1
|
||||
16:31:01.730247 journal@recovery recovering @5
|
||||
16:31:01.746293 memdb@flush created L0@7 N·7157 S·582KiB "\x00\x00\x00..log,v101359":"\x0e\x00\x00..ldb,v98820"
|
||||
16:31:01.746723 version@stat F·[2 1] S·6MiB[6MiB 173B] Sc·[0.50 0.00]
|
||||
16:31:01.750307 db@janitor F·5 G·0
|
||||
16:31:01.750334 db@open done T·20.324227ms
|
||||
16:31:02.225492 table@compaction L0·2 -> L1·1 S·6MiB Q·104262
|
||||
16:31:02.261800 table@build created L1@10 N·17452 S·2MiB "\x00\x00\x00..ure,v396":"\x00\x00\x00..tsx,v103408"
|
||||
16:31:02.303994 table@build created L1@11 N·38055 S·2MiB "\x00\x00\x00..age,v96100":"\x02\x00\x00..png,v52688"
|
||||
16:31:02.338967 table@build created L1@12 N·31731 S·2MiB "\x02\x00\x00...ts,v81746":"\x0e\x00\x00..ico,v82015"
|
||||
16:31:02.349374 table@build created L1@13 N·8854 S·691KiB "\x0e\x00\x00...js,v80031":"\x0e\x00\x00..4e8,v90139"
|
||||
16:31:02.349433 version@stat F·[0 4] S·6MiB[0B 6MiB] Sc·[0.00 0.07]
|
||||
16:31:02.350017 table@compaction committed F+1 S-189KiB Ke·0 D·8154 T·124.48305ms
|
||||
16:31:02.351922 table@remove removed @6
|
||||
16:31:02.352011 table@remove removed @4
|
||||
BIN
home/.local/state/syncthing/index-v0.14.0.db/MANIFEST-000009
Normal file
BIN
home/.local/state/syncthing/index-v0.14.0.db/MANIFEST-000009
Normal file
Binary file not shown.
0
home/.local/state/syncthing/syncthing.lock
Normal file
0
home/.local/state/syncthing/syncthing.lock
Normal file
27
home/.profile
Normal file
27
home/.profile
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# ~/.profile: executed by the command interpreter for login shells.
|
||||
# This file is not read by bash(1), if ~/.bash_profile or ~/.bash_login
|
||||
# exists.
|
||||
# see /usr/share/doc/bash/examples/startup-files for examples.
|
||||
# the files are located in the bash-doc package.
|
||||
|
||||
# the default umask is set in /etc/profile; for setting the umask
|
||||
# for ssh logins, install and configure the libpam-umask package.
|
||||
#umask 022
|
||||
|
||||
# if running bash
|
||||
if [ -n "$BASH_VERSION" ]; then
|
||||
# include .bashrc if it exists
|
||||
if [ -f "$HOME/.bashrc" ]; then
|
||||
. "$HOME/.bashrc"
|
||||
fi
|
||||
fi
|
||||
|
||||
# set PATH so it includes user's private bin if it exists
|
||||
if [ -d "$HOME/bin" ] ; then
|
||||
PATH="$HOME/bin:$PATH"
|
||||
fi
|
||||
|
||||
# set PATH so it includes user's private bin if it exists
|
||||
if [ -d "$HOME/.local/bin" ] ; then
|
||||
PATH="$HOME/.local/bin:$PATH"
|
||||
fi
|
||||
1
home/.ssh/authorized_keys
Normal file
1
home/.ssh/authorized_keys
Normal file
|
|
@ -0,0 +1 @@
|
|||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDdbM/5/qdfeoSCewWWl9Tf2o1ZlBTsDL4ZXc0Jn15tM v.samoilenko@ai-impress.com
|
||||
1
home/.ssh/known_hosts
Normal file
1
home/.ssh/known_hosts
Normal file
|
|
@ -0,0 +1 @@
|
|||
|1|D6vS3jNOr8eht2FeXycOs0z3pWI=|7nZwF6dhYU13ez7/I+yBMLh52qY= ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDdrqXcRKI5FrDkNhEXPosajA7tY9hTVsm5YVandGqr3
|
||||
0
home/.sudo_as_admin_successful
Normal file
0
home/.sudo_as_admin_successful
Normal file
4
home/.wget-hsts
Normal file
4
home/.wget-hsts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# HSTS 1.0 Known Hosts database for GNU Wget.
|
||||
# Edit at your own risk.
|
||||
# <hostname> <port> <incl. subdomains> <created> <max-age>
|
||||
apt.releases.hashicorp.com 0 1 1760633236 31536000
|
||||
14
home/authelia-user-manager/Dockerfile
Normal file
14
home/authelia-user-manager/Dockerfile
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt-get update && apt-get install -y docker.io && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN pip install flask pyyaml argon2-cffi gunicorn
|
||||
|
||||
COPY app.py .
|
||||
COPY templates/ templates/
|
||||
|
||||
EXPOSE 5000
|
||||
|
||||
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:5000", "app:app"]
|
||||
105
home/authelia-user-manager/README.md
Normal file
105
home/authelia-user-manager/README.md
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
# Authelia User Manager
|
||||
|
||||
Веб-интерфейс для управления пользователями Authelia с автоматическим предоставлением доступа к сервисам.
|
||||
|
||||
## Возможности
|
||||
|
||||
- Добавление пользователей через веб-интерфейс
|
||||
- Автоматическая генерация безопасных паролей
|
||||
- Управление группами доступа
|
||||
- Автоматическое предоставление доступа к сервисам на основе групп
|
||||
- Удаление пользователей
|
||||
|
||||
## Группы и доступ
|
||||
|
||||
### admins
|
||||
Полный доступ ко всем сервисам:
|
||||
- traefik.ai-impress.com
|
||||
- pgadmin.ai-impress.com
|
||||
- rabbitmq.ai-impress.com
|
||||
- n8n.ai-impress.com
|
||||
- portainer.ai-impress.com
|
||||
- status.ai-impress.com
|
||||
- supabase.ai-impress.com
|
||||
- wpp.ai-impress.com
|
||||
- odoo.ai-impress.com
|
||||
|
||||
### developers
|
||||
Доступ к tech-сервисам:
|
||||
- n8n.ai-impress.com
|
||||
- portainer.ai-impress.com
|
||||
- traefik.ai-impress.com
|
||||
- status.ai-impress.com
|
||||
- odoo.ai-impress.com
|
||||
|
||||
### analysts
|
||||
Доступ к аналитике и данным:
|
||||
- pgadmin.ai-impress.com
|
||||
- status.ai-impress.com
|
||||
- odoo.ai-impress.com
|
||||
|
||||
### users
|
||||
Базовый доступ:
|
||||
- wiki.ai-impress.com
|
||||
- status.ai-impress.com
|
||||
|
||||
## Установка
|
||||
|
||||
1. Обновите ACL в Authelia:
|
||||
```bash
|
||||
# Скопируйте содержимое update_acl.yml в /path/to/authelia/configuration.yml
|
||||
# Замените секцию access_control
|
||||
```
|
||||
|
||||
2. Запустите User Manager:
|
||||
```bash
|
||||
cd authelia-user-manager
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
3. Откройте веб-интерфейс:
|
||||
```
|
||||
http://your-server:5000
|
||||
```
|
||||
|
||||
## Использование
|
||||
|
||||
### Добавление пользователя
|
||||
|
||||
1. Откройте веб-интерфейс
|
||||
2. Заполните форму:
|
||||
- Имя пользователя (username)
|
||||
- Email
|
||||
- Отображаемое имя
|
||||
- Выберите группы доступа
|
||||
3. Нажмите "Создать пользователя"
|
||||
4. Сохраните сгенерированный пароль!
|
||||
|
||||
### Удаление пользователя
|
||||
|
||||
1. Найдите пользователя в списке
|
||||
2. Нажмите кнопку "Удалить"
|
||||
3. Подтвердите действие
|
||||
|
||||
## Безопасность
|
||||
|
||||
- Пароли генерируются автоматически (16 символов)
|
||||
- Используется Argon2id для хеширования
|
||||
- Пароли показываются только один раз при создании
|
||||
- Рекомендуется защитить веб-интерфейс через Authelia
|
||||
|
||||
## Интеграция с Traefik
|
||||
|
||||
Добавьте в docker-compose.yml Authelia Manager:
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.authelia-manager.rule=Host(`users.ai-impress.com`)"
|
||||
- "traefik.http.routers.authelia-manager.entrypoints=websecure"
|
||||
- "traefik.http.routers.authelia-manager.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.routers.authelia-manager.middlewares=authelia@docker"
|
||||
- "traefik.http.services.authelia-manager.loadbalancer.server.port=5000"
|
||||
```
|
||||
|
||||
Это защитит интерфейс управления через Authelia.
|
||||
257
home/authelia-user-manager/app.py
Normal file
257
home/authelia-user-manager/app.py
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
#!/usr/bin/env python3
|
||||
from flask import Flask, render_template, request, jsonify
|
||||
import yaml
|
||||
import secrets
|
||||
import string
|
||||
import subprocess
|
||||
from argon2 import PasswordHasher
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
SERVICE_GROUPS = {
|
||||
'admins': [
|
||||
'traefik.ai-impress.com',
|
||||
'pgadmin.ai-impress.com',
|
||||
'rabbitmq.ai-impress.com',
|
||||
'n8n.ai-impress.com',
|
||||
'portainer.ai-impress.com',
|
||||
'status.ai-impress.com',
|
||||
'supabase.ai-impress.com',
|
||||
'wpp.ai-impress.com',
|
||||
'social.ai-impress.com',
|
||||
'odoo.ai-impress.com',
|
||||
'vault.ai-impress.com',
|
||||
'chat.ai-impress.com',
|
||||
'crm.ai-impress.com',
|
||||
'wiki.ai-impress.com',
|
||||
'portal.ai-impress.com',
|
||||
'llm.ai-impress.com'
|
||||
],
|
||||
'developers': [
|
||||
'n8n.ai-impress.com',
|
||||
'portainer.ai-impress.com',
|
||||
'traefik.ai-impress.com',
|
||||
'status.ai-impress.com',
|
||||
'odoo.ai-impress.com',
|
||||
'supabase.ai-impress.com',
|
||||
'vault.ai-impress.com',
|
||||
'llm.ai-impress.com'
|
||||
],
|
||||
'analysts': [
|
||||
'pgadmin.ai-impress.com',
|
||||
'status.ai-impress.com',
|
||||
'odoo.ai-impress.com',
|
||||
'crm.ai-impress.com'
|
||||
],
|
||||
'users': [
|
||||
'wiki.ai-impress.com',
|
||||
'status.ai-impress.com',
|
||||
'chat.ai-impress.com',
|
||||
'crm.ai-impress.com',
|
||||
'portal.ai-impress.com'
|
||||
]
|
||||
}
|
||||
|
||||
USERS_FILE = '/config/users_database.yml'
|
||||
|
||||
ph = PasswordHasher(
|
||||
time_cost=3,
|
||||
memory_cost=65536,
|
||||
parallelism=4,
|
||||
hash_len=32,
|
||||
salt_len=16
|
||||
)
|
||||
|
||||
def generate_password(length=16):
|
||||
alphabet = string.ascii_letters + string.digits + "!@#$%^&*"
|
||||
return ''.join(secrets.choice(alphabet) for _ in range(length))
|
||||
|
||||
def hash_password(password):
|
||||
return ph.hash(password)
|
||||
|
||||
def restart_authelia():
|
||||
try:
|
||||
subprocess.run(['docker', 'restart', 'authelia'], check=True, capture_output=True)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Failed to restart authelia: {e}")
|
||||
return False
|
||||
|
||||
def load_users():
|
||||
with open(USERS_FILE, 'r') as f:
|
||||
return yaml.safe_load(f) or {'users': {}}
|
||||
|
||||
def save_users(data):
|
||||
with open(USERS_FILE, 'w') as f:
|
||||
yaml.dump(data, f, default_flow_style=False, sort_keys=False)
|
||||
|
||||
def get_all_services():
|
||||
"""Extract all unique services from SERVICE_GROUPS"""
|
||||
all_services = set()
|
||||
for services in SERVICE_GROUPS.values():
|
||||
all_services.update(services)
|
||||
return sorted(all_services)
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return render_template('index.html')
|
||||
|
||||
@app.route('/api/groups')
|
||||
def get_groups():
|
||||
return jsonify({
|
||||
'groups': list(SERVICE_GROUPS.keys()),
|
||||
'services': SERVICE_GROUPS
|
||||
})
|
||||
|
||||
@app.route('/api/services')
|
||||
def get_services():
|
||||
"""Return all available services"""
|
||||
return jsonify({
|
||||
'services': get_all_services()
|
||||
})
|
||||
|
||||
@app.route('/api/users')
|
||||
def get_users():
|
||||
data = load_users()
|
||||
users = []
|
||||
for username, info in data['users'].items():
|
||||
# Get services from groups
|
||||
group_services = set()
|
||||
for group in info.get('groups', []):
|
||||
group_services.update(SERVICE_GROUPS.get(group, []))
|
||||
|
||||
# Get individual services
|
||||
individual_services = info.get('services', [])
|
||||
|
||||
users.append({
|
||||
'username': username,
|
||||
'email': info.get('email', ''),
|
||||
'displayname': info.get('displayname', ''),
|
||||
'groups': info.get('groups', []),
|
||||
'group_services': sorted(group_services),
|
||||
'individual_services': individual_services,
|
||||
'all_services': sorted(set(list(group_services) + individual_services))
|
||||
})
|
||||
return jsonify(users)
|
||||
|
||||
@app.route('/api/users', methods=['POST'])
|
||||
def add_user():
|
||||
try:
|
||||
data = request.json
|
||||
email = data.get('email')
|
||||
displayname = data.get('displayname')
|
||||
groups = data.get('groups', [])
|
||||
services = data.get('services', []) # Individual services
|
||||
|
||||
# Use email as username for consistency with Authelia login
|
||||
username = email
|
||||
|
||||
users_data = load_users()
|
||||
|
||||
if username in users_data['users']:
|
||||
return jsonify({'error': 'User already exists'}), 400
|
||||
|
||||
password = generate_password()
|
||||
password_hash = hash_password(password)
|
||||
|
||||
users_data['users'][username] = {
|
||||
'displayname': displayname,
|
||||
'password': password_hash,
|
||||
'email': email,
|
||||
'groups': groups,
|
||||
'services': services
|
||||
}
|
||||
|
||||
save_users(users_data)
|
||||
restart_authelia()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'username': username,
|
||||
'password': password
|
||||
})
|
||||
except Exception as e:
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
@app.route('/api/users/<username>', methods=['DELETE'])
|
||||
def delete_user(username):
|
||||
try:
|
||||
users_data = load_users()
|
||||
|
||||
if username not in users_data['users']:
|
||||
return jsonify({'error': 'User not found'}), 404
|
||||
|
||||
del users_data['users'][username]
|
||||
save_users(users_data)
|
||||
restart_authelia()
|
||||
|
||||
return jsonify({'success': True})
|
||||
except Exception as e:
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
@app.route('/api/users/<username>/groups', methods=['PUT'])
|
||||
def update_groups(username):
|
||||
try:
|
||||
data = request.json
|
||||
groups = data.get('groups', [])
|
||||
|
||||
users_data = load_users()
|
||||
|
||||
if username not in users_data['users']:
|
||||
return jsonify({'error': 'User not found'}), 404
|
||||
|
||||
users_data['users'][username]['groups'] = groups
|
||||
save_users(users_data)
|
||||
restart_authelia()
|
||||
|
||||
return jsonify({'success': True})
|
||||
except Exception as e:
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
@app.route('/api/users/<username>/services', methods=['PUT'])
|
||||
def update_services(username):
|
||||
"""Update individual services for a user"""
|
||||
try:
|
||||
data = request.json
|
||||
services = data.get('services', [])
|
||||
|
||||
users_data = load_users()
|
||||
|
||||
if username not in users_data['users']:
|
||||
return jsonify({'error': 'User not found'}), 404
|
||||
|
||||
users_data['users'][username]['services'] = services
|
||||
save_users(users_data)
|
||||
restart_authelia()
|
||||
|
||||
return jsonify({'success': True})
|
||||
except Exception as e:
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
@app.route('/api/users/<username>/reset-password', methods=['POST'])
|
||||
def reset_password(username):
|
||||
try:
|
||||
users_data = load_users()
|
||||
|
||||
if username not in users_data['users']:
|
||||
return jsonify({'error': 'User not found'}), 404
|
||||
|
||||
# Generate new password and hash it
|
||||
new_password = generate_password()
|
||||
password_hash = hash_password(new_password)
|
||||
|
||||
# Update the password in the database
|
||||
users_data['users'][username]['password'] = password_hash
|
||||
save_users(users_data)
|
||||
restart_authelia()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'username': username,
|
||||
'password': new_password
|
||||
})
|
||||
except Exception as e:
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0', port=5000, debug=False)
|
||||
197
home/authelia-user-manager/app.py.backup
Normal file
197
home/authelia-user-manager/app.py.backup
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
#!/usr/bin/env python3
|
||||
from flask import Flask, render_template, request, jsonify
|
||||
import yaml
|
||||
import secrets
|
||||
import string
|
||||
import subprocess
|
||||
from argon2 import PasswordHasher
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
SERVICE_GROUPS = {
|
||||
'admins': [
|
||||
'traefik.ai-impress.com',
|
||||
'pgadmin.ai-impress.com',
|
||||
'rabbitmq.ai-impress.com',
|
||||
'n8n.ai-impress.com',
|
||||
'portainer.ai-impress.com',
|
||||
'status.ai-impress.com',
|
||||
'supabase.ai-impress.com',
|
||||
'wpp.ai-impress.com',
|
||||
'odoo.ai-impress.com'
|
||||
],
|
||||
'developers': [
|
||||
'n8n.ai-impress.com',
|
||||
'portainer.ai-impress.com',
|
||||
'traefik.ai-impress.com',
|
||||
'status.ai-impress.com',
|
||||
'odoo.ai-impress.com'
|
||||
],
|
||||
'analysts': [
|
||||
'pgadmin.ai-impress.com',
|
||||
'status.ai-impress.com',
|
||||
'odoo.ai-impress.com'
|
||||
],
|
||||
'users': [
|
||||
'wiki.ai-impress.com',
|
||||
'status.ai-impress.com'
|
||||
]
|
||||
}
|
||||
|
||||
USERS_FILE = '/config/users_database.yml'
|
||||
|
||||
ph = PasswordHasher(
|
||||
time_cost=3,
|
||||
memory_cost=65536,
|
||||
parallelism=4,
|
||||
hash_len=32,
|
||||
salt_len=16
|
||||
)
|
||||
|
||||
def generate_password(length=16):
|
||||
alphabet = string.ascii_letters + string.digits + "!@#$%^&*"
|
||||
return ''.join(secrets.choice(alphabet) for _ in range(length))
|
||||
|
||||
def hash_password(password):
|
||||
return ph.hash(password)
|
||||
|
||||
def restart_authelia():
|
||||
try:
|
||||
subprocess.run(['docker', 'restart', 'authelia'], check=True, capture_output=True)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Failed to restart authelia: {e}")
|
||||
return False
|
||||
|
||||
def load_users():
|
||||
with open(USERS_FILE, 'r') as f:
|
||||
return yaml.safe_load(f) or {'users': {}}
|
||||
|
||||
def save_users(data):
|
||||
with open(USERS_FILE, 'w') as f:
|
||||
yaml.dump(data, f, default_flow_style=False, sort_keys=False)
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return render_template('index.html')
|
||||
|
||||
@app.route('/api/groups')
|
||||
def get_groups():
|
||||
return jsonify({
|
||||
'groups': list(SERVICE_GROUPS.keys()),
|
||||
'services': SERVICE_GROUPS
|
||||
})
|
||||
|
||||
@app.route('/api/users')
|
||||
def get_users():
|
||||
data = load_users()
|
||||
users = []
|
||||
for username, info in data['users'].items():
|
||||
services = set()
|
||||
for group in info.get('groups', []):
|
||||
services.update(SERVICE_GROUPS.get(group, []))
|
||||
users.append({
|
||||
'username': username,
|
||||
'email': info.get('email', ''),
|
||||
'displayname': info.get('displayname', ''),
|
||||
'groups': info.get('groups', []),
|
||||
'services': sorted(services)
|
||||
})
|
||||
return jsonify(users)
|
||||
|
||||
@app.route('/api/users', methods=['POST'])
|
||||
def add_user():
|
||||
try:
|
||||
data = request.json
|
||||
username = data.get('username')
|
||||
email = data.get('email')
|
||||
displayname = data.get('displayname')
|
||||
groups = data.get('groups', [])
|
||||
|
||||
users_data = load_users()
|
||||
|
||||
if username in users_data['users']:
|
||||
return jsonify({'error': 'User already exists'}), 400
|
||||
|
||||
password = generate_password()
|
||||
password_hash = hash_password(password)
|
||||
|
||||
users_data['users'][username] = {
|
||||
'displayname': displayname,
|
||||
'password': password_hash,
|
||||
'email': email,
|
||||
'groups': groups
|
||||
}
|
||||
|
||||
save_users(users_data)
|
||||
restart_authelia()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'username': username,
|
||||
'password': password
|
||||
})
|
||||
except Exception as e:
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
@app.route('/api/users/<username>', methods=['DELETE'])
|
||||
def delete_user(username):
|
||||
try:
|
||||
users_data = load_users()
|
||||
|
||||
if username not in users_data['users']:
|
||||
return jsonify({'error': 'User not found'}), 404
|
||||
|
||||
del users_data['users'][username]
|
||||
save_users(users_data)
|
||||
restart_authelia()
|
||||
|
||||
return jsonify({'success': True})
|
||||
except Exception as e:
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
@app.route('/api/users/<username>/groups', methods=['PUT'])
|
||||
def update_groups(username):
|
||||
try:
|
||||
data = request.json
|
||||
groups = data.get('groups', [])
|
||||
|
||||
users_data = load_users()
|
||||
|
||||
if username not in users_data['users']:
|
||||
return jsonify({'error': 'User not found'}), 404
|
||||
|
||||
users_data['users'][username]['groups'] = groups
|
||||
save_users(users_data)
|
||||
restart_authelia()
|
||||
|
||||
return jsonify({'success': True})
|
||||
except Exception as e:
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0', port=5000, debug=False)
|
||||
@app.route('/api/users/<username>/reset-password', methods=['POST'])
|
||||
def reset_password(username):
|
||||
try:
|
||||
users_data = load_users()
|
||||
|
||||
if username not in users_data['users']:
|
||||
return jsonify({'error': 'User not found'}), 404
|
||||
|
||||
# Generate new password and hash it
|
||||
new_password = generate_password()
|
||||
password_hash = hash_password(new_password)
|
||||
|
||||
# Update the password in the database
|
||||
users_data['users'][username]['password'] = password_hash
|
||||
save_users(users_data)
|
||||
restart_authelia()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'username': username,
|
||||
'password': new_password
|
||||
})
|
||||
except Exception as e:
|
||||
return jsonify({'error': str(e)}), 500
|
||||
241
home/authelia-user-manager/app.py.backup2
Normal file
241
home/authelia-user-manager/app.py.backup2
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
#!/usr/bin/env python3
|
||||
from flask import Flask, render_template, request, jsonify
|
||||
import yaml
|
||||
import secrets
|
||||
import string
|
||||
import subprocess
|
||||
from argon2 import PasswordHasher
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
SERVICE_GROUPS = {
|
||||
'admins': [
|
||||
'traefik.ai-impress.com',
|
||||
'pgadmin.ai-impress.com',
|
||||
'rabbitmq.ai-impress.com',
|
||||
'n8n.ai-impress.com',
|
||||
'portainer.ai-impress.com',
|
||||
'status.ai-impress.com',
|
||||
'supabase.ai-impress.com',
|
||||
'wpp.ai-impress.com',
|
||||
'odoo.ai-impress.com'
|
||||
],
|
||||
'developers': [
|
||||
'n8n.ai-impress.com',
|
||||
'portainer.ai-impress.com',
|
||||
'traefik.ai-impress.com',
|
||||
'status.ai-impress.com',
|
||||
'odoo.ai-impress.com'
|
||||
],
|
||||
'analysts': [
|
||||
'pgadmin.ai-impress.com',
|
||||
'status.ai-impress.com',
|
||||
'odoo.ai-impress.com'
|
||||
],
|
||||
'users': [
|
||||
'wiki.ai-impress.com',
|
||||
'status.ai-impress.com'
|
||||
]
|
||||
}
|
||||
|
||||
USERS_FILE = '/config/users_database.yml'
|
||||
|
||||
ph = PasswordHasher(
|
||||
time_cost=3,
|
||||
memory_cost=65536,
|
||||
parallelism=4,
|
||||
hash_len=32,
|
||||
salt_len=16
|
||||
)
|
||||
|
||||
def generate_password(length=16):
|
||||
alphabet = string.ascii_letters + string.digits + "!@#$%^&*"
|
||||
return ''.join(secrets.choice(alphabet) for _ in range(length))
|
||||
|
||||
def hash_password(password):
|
||||
return ph.hash(password)
|
||||
|
||||
def restart_authelia():
|
||||
try:
|
||||
subprocess.run(['docker', 'restart', 'authelia'], check=True, capture_output=True)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Failed to restart authelia: {e}")
|
||||
return False
|
||||
|
||||
def load_users():
|
||||
with open(USERS_FILE, 'r') as f:
|
||||
return yaml.safe_load(f) or {'users': {}}
|
||||
|
||||
def save_users(data):
|
||||
with open(USERS_FILE, 'w') as f:
|
||||
yaml.dump(data, f, default_flow_style=False, sort_keys=False)
|
||||
|
||||
def get_all_services():
|
||||
"""Extract all unique services from SERVICE_GROUPS"""
|
||||
all_services = set()
|
||||
for services in SERVICE_GROUPS.values():
|
||||
all_services.update(services)
|
||||
return sorted(all_services)
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return render_template('index.html')
|
||||
|
||||
@app.route('/api/groups')
|
||||
def get_groups():
|
||||
return jsonify({
|
||||
'groups': list(SERVICE_GROUPS.keys()),
|
||||
'services': SERVICE_GROUPS
|
||||
})
|
||||
|
||||
@app.route('/api/services')
|
||||
def get_services():
|
||||
"""Return all available services"""
|
||||
return jsonify({
|
||||
'services': get_all_services()
|
||||
})
|
||||
|
||||
@app.route('/api/users')
|
||||
def get_users():
|
||||
data = load_users()
|
||||
users = []
|
||||
for username, info in data['users'].items():
|
||||
# Get services from groups
|
||||
group_services = set()
|
||||
for group in info.get('groups', []):
|
||||
group_services.update(SERVICE_GROUPS.get(group, []))
|
||||
|
||||
# Get individual services
|
||||
individual_services = info.get('services', [])
|
||||
|
||||
users.append({
|
||||
'username': username,
|
||||
'email': info.get('email', ''),
|
||||
'displayname': info.get('displayname', ''),
|
||||
'groups': info.get('groups', []),
|
||||
'group_services': sorted(group_services),
|
||||
'individual_services': individual_services,
|
||||
'all_services': sorted(set(list(group_services) + individual_services))
|
||||
})
|
||||
return jsonify(users)
|
||||
|
||||
@app.route('/api/users', methods=['POST'])
|
||||
def add_user():
|
||||
try:
|
||||
data = request.json
|
||||
username = data.get('username')
|
||||
email = data.get('email')
|
||||
displayname = data.get('displayname')
|
||||
groups = data.get('groups', [])
|
||||
services = data.get('services', []) # Individual services
|
||||
|
||||
users_data = load_users()
|
||||
|
||||
if username in users_data['users']:
|
||||
return jsonify({'error': 'User already exists'}), 400
|
||||
|
||||
password = generate_password()
|
||||
password_hash = hash_password(password)
|
||||
|
||||
users_data['users'][username] = {
|
||||
'displayname': displayname,
|
||||
'password': password_hash,
|
||||
'email': email,
|
||||
'groups': groups,
|
||||
'services': services
|
||||
}
|
||||
|
||||
save_users(users_data)
|
||||
restart_authelia()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'username': username,
|
||||
'password': password
|
||||
})
|
||||
except Exception as e:
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
@app.route('/api/users/<username>', methods=['DELETE'])
|
||||
def delete_user(username):
|
||||
try:
|
||||
users_data = load_users()
|
||||
|
||||
if username not in users_data['users']:
|
||||
return jsonify({'error': 'User not found'}), 404
|
||||
|
||||
del users_data['users'][username]
|
||||
save_users(users_data)
|
||||
restart_authelia()
|
||||
|
||||
return jsonify({'success': True})
|
||||
except Exception as e:
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
@app.route('/api/users/<username>/groups', methods=['PUT'])
|
||||
def update_groups(username):
|
||||
try:
|
||||
data = request.json
|
||||
groups = data.get('groups', [])
|
||||
|
||||
users_data = load_users()
|
||||
|
||||
if username not in users_data['users']:
|
||||
return jsonify({'error': 'User not found'}), 404
|
||||
|
||||
users_data['users'][username]['groups'] = groups
|
||||
save_users(users_data)
|
||||
restart_authelia()
|
||||
|
||||
return jsonify({'success': True})
|
||||
except Exception as e:
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
@app.route('/api/users/<username>/services', methods=['PUT'])
|
||||
def update_services(username):
|
||||
"""Update individual services for a user"""
|
||||
try:
|
||||
data = request.json
|
||||
services = data.get('services', [])
|
||||
|
||||
users_data = load_users()
|
||||
|
||||
if username not in users_data['users']:
|
||||
return jsonify({'error': 'User not found'}), 404
|
||||
|
||||
users_data['users'][username]['services'] = services
|
||||
save_users(users_data)
|
||||
restart_authelia()
|
||||
|
||||
return jsonify({'success': True})
|
||||
except Exception as e:
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
@app.route('/api/users/<username>/reset-password', methods=['POST'])
|
||||
def reset_password(username):
|
||||
try:
|
||||
users_data = load_users()
|
||||
|
||||
if username not in users_data['users']:
|
||||
return jsonify({'error': 'User not found'}), 404
|
||||
|
||||
# Generate new password and hash it
|
||||
new_password = generate_password()
|
||||
password_hash = hash_password(new_password)
|
||||
|
||||
# Update the password in the database
|
||||
users_data['users'][username]['password'] = password_hash
|
||||
save_users(users_data)
|
||||
restart_authelia()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'username': username,
|
||||
'password': new_password
|
||||
})
|
||||
except Exception as e:
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0', port=5000, debug=False)
|
||||
24
home/authelia-user-manager/docker-compose.yml
Normal file
24
home/authelia-user-manager/docker-compose.yml
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
version: '3.8'
|
||||
|
||||
services:
|
||||
authelia-manager:
|
||||
build: .
|
||||
container_name: authelia-manager
|
||||
volumes:
|
||||
- /opt/01-security/authelia/config:/config
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
networks:
|
||||
- traefik-public
|
||||
restart: unless-stopped
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.authelia-manager.rule=Host(`users.ai-impress.com`)"
|
||||
- "traefik.http.routers.authelia-manager.entrypoints=websecure"
|
||||
- "traefik.http.routers.authelia-manager.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.routers.authelia-manager.middlewares=authelia@file"
|
||||
- "traefik.http.services.authelia-manager.loadbalancer.server.port=5000"
|
||||
- "traefik.docker.network=traefik-public"
|
||||
|
||||
networks:
|
||||
traefik-public:
|
||||
external: true
|
||||
1910
home/authelia-user-manager/templates/index.html
Normal file
1910
home/authelia-user-manager/templates/index.html
Normal file
File diff suppressed because it is too large
Load diff
595
home/authelia-user-manager/templates/index.html.backup
Normal file
595
home/authelia-user-manager/templates/index.html.backup
Normal file
|
|
@ -0,0 +1,595 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Authelia User Manager</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.header {
|
||||
background: white;
|
||||
padding: 30px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #333;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.add-user-section {
|
||||
background: white;
|
||||
padding: 30px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type="email"] {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 2px solid #e0e0e0;
|
||||
border-radius: 5px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
input:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
.checkbox-group {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.checkbox-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 12px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 5px;
|
||||
border: 2px solid #e0e0e0;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.checkbox-item:hover {
|
||||
border-color: #667eea;
|
||||
background: #f0f4ff;
|
||||
}
|
||||
|
||||
.checkbox-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.checkbox-header input {
|
||||
margin-right: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.checkbox-header label {
|
||||
margin: 0;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.group-services {
|
||||
margin-left: 24px;
|
||||
font-size: 11px;
|
||||
color: #666;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.service-tag {
|
||||
display: inline-block;
|
||||
background: #e8eaf6;
|
||||
color: #5568d3;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
margin: 2px 2px 2px 0;
|
||||
font-size: 10px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.btn {
|
||||
background: #667eea;
|
||||
color: white;
|
||||
padding: 12px 30px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
transition: background 0.3s;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background: #5568d3;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: #e74c3c;
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
background: #c0392b;
|
||||
}
|
||||
|
||||
.btn-edit {
|
||||
background: #3498db;
|
||||
padding: 8px 16px;
|
||||
font-size: 14px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.btn-edit:hover {
|
||||
background: #2980b9;
|
||||
}
|
||||
|
||||
.btn-warning {
|
||||
background: #f39c12;
|
||||
padding: 8px 16px;
|
||||
font-size: 14px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.btn-warning:hover {
|
||||
background: #e67e22;
|
||||
}
|
||||
|
||||
.users-list {
|
||||
background: white;
|
||||
padding: 30px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.user-card {
|
||||
border: 2px solid #e0e0e0;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 15px;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.user-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.user-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.user-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.user-info h3 {
|
||||
color: #333;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.user-email {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.user-groups {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.group-badge {
|
||||
display: inline-block;
|
||||
padding: 5px 12px;
|
||||
background: #667eea;
|
||||
color: white;
|
||||
border-radius: 20px;
|
||||
font-size: 12px;
|
||||
margin-right: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.services-list {
|
||||
margin-top: 10px;
|
||||
padding: 10px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.service-item {
|
||||
color: #666;
|
||||
font-size: 13px;
|
||||
padding: 3px 0;
|
||||
}
|
||||
|
||||
.modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0,0,0,0.5);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.modal.active {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background: white;
|
||||
padding: 30px;
|
||||
border-radius: 10px;
|
||||
max-width: 600px;
|
||||
width: 90%;
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.modal-close {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 24px;
|
||||
cursor: pointer;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.modal-close:hover {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.password-display {
|
||||
background: #f8f9fa;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
font-family: monospace;
|
||||
word-break: break-all;
|
||||
margin: 10px 0;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1>Authelia User Manager</h1>
|
||||
<p class="subtitle">Управление пользователями и доступом к сервисам</p>
|
||||
</div>
|
||||
|
||||
<div class="add-user-section">
|
||||
<h2 style="margin-bottom: 20px;">Добавить пользователя</h2>
|
||||
<form id="addUserForm">
|
||||
<div class="form-group">
|
||||
<label for="username">Имя пользователя:</label>
|
||||
<input type="text" id="username" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="email">Email:</label>
|
||||
<input type="email" id="email" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="displayname">Отображаемое имя:</label>
|
||||
<input type="text" id="displayname" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Группы доступа:</label>
|
||||
<div class="checkbox-group" id="groupsCheckbox"></div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn">Создать пользователя</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="users-list">
|
||||
<h2 style="margin-bottom: 20px;">Список пользователей</h2>
|
||||
<div id="usersList"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="passwordModal" class="modal">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h2>Пользователь создан!</h2>
|
||||
<button class="modal-close" onclick="closePasswordModal()">×</button>
|
||||
</div>
|
||||
<p>Сохраните пароль в надёжном месте:</p>
|
||||
<div class="password-display" id="generatedPassword"></div>
|
||||
<button class="btn" onclick="closePasswordModal()">Закрыть</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="resetPasswordModal" class="modal">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h2>Пароль сброшен!</h2>
|
||||
<button class="modal-close" onclick="closeResetPasswordModal()">×</button>
|
||||
</div>
|
||||
<p>Новый пароль для пользователя: <strong id="resetUsername"></strong></p>
|
||||
<div class="password-display" id="resetPassword"></div>
|
||||
<p style="color: #e74c3c; margin-top: 10px;">Сохраните пароль в надёжном месте! Он больше не будет показан.</p>
|
||||
<button class="btn" onclick="closeResetPasswordModal()">Закрыть</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="editGroupsModal" class="modal">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h2>Изменить группы</h2>
|
||||
<button class="modal-close" onclick="closeEditGroupsModal()">×</button>
|
||||
</div>
|
||||
<p>Пользователь: <strong id="editUsername"></strong></p>
|
||||
<div class="form-group" style="margin-top: 20px;">
|
||||
<label>Группы доступа:</label>
|
||||
<div class="checkbox-group" id="editGroupsCheckbox"></div>
|
||||
</div>
|
||||
<button class="btn" onclick="saveUserGroups()">Сохранить</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let groups = [];
|
||||
let serviceGroups = {};
|
||||
let currentEditUser = null;
|
||||
|
||||
async function loadGroups() {
|
||||
const response = await fetch('/api/groups');
|
||||
const data = await response.json();
|
||||
groups = data.groups;
|
||||
serviceGroups = data.services;
|
||||
|
||||
const container = document.getElementById('groupsCheckbox');
|
||||
const editContainer = document.getElementById('editGroupsCheckbox');
|
||||
|
||||
[container, editContainer].forEach(cont => {
|
||||
cont.innerHTML = '';
|
||||
groups.forEach(group => {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'checkbox-item';
|
||||
const prefix = cont.id === 'groupsCheckbox' ? 'group_' : 'edit_group_';
|
||||
|
||||
// Get services for this group
|
||||
const services = serviceGroups[group] || [];
|
||||
const servicesHtml = services.length > 0
|
||||
? services.map(s => `<span class="service-tag">${s}</span>`).join('')
|
||||
: '<span class="service-tag">No services</span>';
|
||||
|
||||
div.innerHTML = `
|
||||
<div class="checkbox-header">
|
||||
<input type="checkbox" id="${prefix}${group}" value="${group}">
|
||||
<label for="${prefix}${group}">${group}</label>
|
||||
</div>
|
||||
<div class="group-services">
|
||||
${servicesHtml}
|
||||
</div>
|
||||
`;
|
||||
cont.appendChild(div);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function loadUsers() {
|
||||
const response = await fetch('/api/users');
|
||||
const users = await response.json();
|
||||
|
||||
const container = document.getElementById('usersList');
|
||||
container.innerHTML = '';
|
||||
|
||||
users.forEach(user => {
|
||||
const card = document.createElement('div');
|
||||
card.className = 'user-card';
|
||||
card.innerHTML = `
|
||||
<div class="user-header">
|
||||
<div class="user-info">
|
||||
<h3>${user.displayname}</h3>
|
||||
<p class="user-email">${user.email}</p>
|
||||
</div>
|
||||
<div class="user-actions">
|
||||
<button class="btn btn-edit" onclick="editUserGroups('${user.username}')">Изменить группы</button>
|
||||
<button class="btn btn-warning" onclick="resetUserPassword('${user.username}')">Сбросить пароль</button>
|
||||
<button class="btn btn-danger" onclick="deleteUser('${user.username}')">Удалить</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="user-groups">
|
||||
${user.groups.map(g => `<span class="group-badge">${g}</span>`).join('')}
|
||||
</div>
|
||||
<div class="services-list">
|
||||
<strong>Доступ к сервисам:</strong>
|
||||
${user.services.map(s => `<div class="service-item">• ${s}</div>`).join('')}
|
||||
</div>
|
||||
`;
|
||||
container.appendChild(card);
|
||||
});
|
||||
}
|
||||
|
||||
document.getElementById('addUserForm').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const username = document.getElementById('username').value;
|
||||
const email = document.getElementById('email').value;
|
||||
const displayname = document.getElementById('displayname').value;
|
||||
|
||||
const selectedGroups = [];
|
||||
groups.forEach(group => {
|
||||
if (document.getElementById(`group_${group}`).checked) {
|
||||
selectedGroups.push(group);
|
||||
}
|
||||
});
|
||||
|
||||
if (selectedGroups.length === 0) {
|
||||
alert('Выберите хотя бы одну группу!');
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await fetch('/api/users', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
username,
|
||||
email,
|
||||
displayname,
|
||||
groups: selectedGroups
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
document.getElementById('generatedPassword').textContent = result.password;
|
||||
document.getElementById('passwordModal').classList.add('active');
|
||||
document.getElementById('addUserForm').reset();
|
||||
groups.forEach(group => {
|
||||
document.getElementById(`group_${group}`).checked = false;
|
||||
});
|
||||
loadUsers();
|
||||
} else {
|
||||
alert('Ошибка: ' + result.error);
|
||||
}
|
||||
});
|
||||
|
||||
async function editUserGroups(username) {
|
||||
currentEditUser = username;
|
||||
const response = await fetch('/api/users');
|
||||
const users = await response.json();
|
||||
const user = users.find(u => u.username === username);
|
||||
|
||||
document.getElementById('editUsername').textContent = username;
|
||||
|
||||
groups.forEach(group => {
|
||||
const checkbox = document.getElementById(`edit_group_${group}`);
|
||||
checkbox.checked = user.groups.includes(group);
|
||||
});
|
||||
|
||||
document.getElementById('editGroupsModal').classList.add('active');
|
||||
}
|
||||
|
||||
async function saveUserGroups() {
|
||||
const selectedGroups = [];
|
||||
groups.forEach(group => {
|
||||
if (document.getElementById(`edit_group_${group}`).checked) {
|
||||
selectedGroups.push(group);
|
||||
}
|
||||
});
|
||||
|
||||
if (selectedGroups.length === 0) {
|
||||
alert('Выберите хотя бы одну группу!');
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await fetch(`/api/users/${currentEditUser}/groups`, {
|
||||
method: 'PUT',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({ groups: selectedGroups })
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
closeEditGroupsModal();
|
||||
loadUsers();
|
||||
} else {
|
||||
alert('Ошибка: ' + result.error);
|
||||
}
|
||||
}
|
||||
|
||||
async function resetUserPassword(username) {
|
||||
if (!confirm(`Сбросить пароль для пользователя ${username}?`)) return;
|
||||
|
||||
const response = await fetch(`/api/users/${username}/reset-password`, {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'}
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
document.getElementById('resetUsername').textContent = username;
|
||||
document.getElementById('resetPassword').textContent = result.password;
|
||||
document.getElementById('resetPasswordModal').classList.add('active');
|
||||
loadUsers();
|
||||
} else {
|
||||
alert('Ошибка: ' + result.error);
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteUser(username) {
|
||||
if (!confirm(`Удалить пользователя ${username}?`)) return;
|
||||
|
||||
await fetch(`/api/users/${username}`, {method: 'DELETE'});
|
||||
loadUsers();
|
||||
}
|
||||
|
||||
function closePasswordModal() {
|
||||
document.getElementById('passwordModal').classList.remove('active');
|
||||
}
|
||||
|
||||
function closeResetPasswordModal() {
|
||||
document.getElementById('resetPasswordModal').classList.remove('active');
|
||||
}
|
||||
|
||||
function closeEditGroupsModal() {
|
||||
document.getElementById('editGroupsModal').classList.remove('active');
|
||||
currentEditUser = null;
|
||||
}
|
||||
|
||||
loadGroups();
|
||||
loadUsers();
|
||||
setInterval(loadUsers, 5000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
55
home/authelia-user-manager/update_acl.yml
Normal file
55
home/authelia-user-manager/update_acl.yml
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# Обновленная конфигурация ACL с группами и odoo
|
||||
# Добавьте это в configuration.yml вашего Authelia
|
||||
|
||||
access_control:
|
||||
default_policy: deny
|
||||
|
||||
rules:
|
||||
# Admins - полный доступ
|
||||
- domain:
|
||||
- traefik.ai-impress.com
|
||||
- pgadmin.ai-impress.com
|
||||
- rabbitmq.ai-impress.com
|
||||
- n8n.ai-impress.com
|
||||
- portainer.ai-impress.com
|
||||
- status.ai-impress.com
|
||||
- supabase.ai-impress.com
|
||||
- wpp.ai-impress.com
|
||||
- odoo.ai-impress.com
|
||||
policy: two_factor
|
||||
subject:
|
||||
- "group:admins"
|
||||
|
||||
# Developers - tech сервисы
|
||||
- domain:
|
||||
- n8n.ai-impress.com
|
||||
- portainer.ai-impress.com
|
||||
- traefik.ai-impress.com
|
||||
- status.ai-impress.com
|
||||
- odoo.ai-impress.com
|
||||
policy: two_factor
|
||||
subject:
|
||||
- "group:developers"
|
||||
|
||||
# Analysts - аналитика и данные
|
||||
- domain:
|
||||
- pgadmin.ai-impress.com
|
||||
- status.ai-impress.com
|
||||
- odoo.ai-impress.com
|
||||
policy: two_factor
|
||||
subject:
|
||||
- "group:analysts"
|
||||
|
||||
# Users - базовый доступ
|
||||
- domain:
|
||||
- wiki.ai-impress.com
|
||||
- status.ai-impress.com
|
||||
policy: one_factor
|
||||
subject:
|
||||
- "group:users"
|
||||
|
||||
# Odoo database manager - публичный доступ (как было)
|
||||
- domain: odoo.ai-impress.com
|
||||
resources:
|
||||
- "^/web/database.*$"
|
||||
policy: bypass
|
||||
720
home/get-docker.sh
Normal file
720
home/get-docker.sh
Normal file
|
|
@ -0,0 +1,720 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
# Docker Engine for Linux installation script.
|
||||
#
|
||||
# This script is intended as a convenient way to configure docker's package
|
||||
# repositories and to install Docker Engine, This script is not recommended
|
||||
# for production environments. Before running this script, make yourself familiar
|
||||
# with potential risks and limitations, and refer to the installation manual
|
||||
# at https://docs.docker.com/engine/install/ for alternative installation methods.
|
||||
#
|
||||
# The script:
|
||||
#
|
||||
# - Requires `root` or `sudo` privileges to run.
|
||||
# - Attempts to detect your Linux distribution and version and configure your
|
||||
# package management system for you.
|
||||
# - Doesn't allow you to customize most installation parameters.
|
||||
# - Installs dependencies and recommendations without asking for confirmation.
|
||||
# - Installs the latest stable release (by default) of Docker CLI, Docker Engine,
|
||||
# Docker Buildx, Docker Compose, containerd, and runc. When using this script
|
||||
# to provision a machine, this may result in unexpected major version upgrades
|
||||
# of these packages. Always test upgrades in a test environment before
|
||||
# deploying to your production systems.
|
||||
# - Isn't designed to upgrade an existing Docker installation. When using the
|
||||
# script to update an existing installation, dependencies may not be updated
|
||||
# to the expected version, resulting in outdated versions.
|
||||
#
|
||||
# Source code is available at https://github.com/docker/docker-install/
|
||||
#
|
||||
# Usage
|
||||
# ==============================================================================
|
||||
#
|
||||
# To install the latest stable versions of Docker CLI, Docker Engine, and their
|
||||
# dependencies:
|
||||
#
|
||||
# 1. download the script
|
||||
#
|
||||
# $ curl -fsSL https://get.docker.com -o install-docker.sh
|
||||
#
|
||||
# 2. verify the script's content
|
||||
#
|
||||
# $ cat install-docker.sh
|
||||
#
|
||||
# 3. run the script with --dry-run to verify the steps it executes
|
||||
#
|
||||
# $ sh install-docker.sh --dry-run
|
||||
#
|
||||
# 4. run the script either as root, or using sudo to perform the installation.
|
||||
#
|
||||
# $ sudo sh install-docker.sh
|
||||
#
|
||||
# Command-line options
|
||||
# ==============================================================================
|
||||
#
|
||||
# --version <VERSION>
|
||||
# Use the --version option to install a specific version, for example:
|
||||
#
|
||||
# $ sudo sh install-docker.sh --version 23.0
|
||||
#
|
||||
# --channel <stable|test>
|
||||
#
|
||||
# Use the --channel option to install from an alternative installation channel.
|
||||
# The following example installs the latest versions from the "test" channel,
|
||||
# which includes pre-releases (alpha, beta, rc):
|
||||
#
|
||||
# $ sudo sh install-docker.sh --channel test
|
||||
#
|
||||
# Alternatively, use the script at https://test.docker.com, which uses the test
|
||||
# channel as default.
|
||||
#
|
||||
# --mirror <Aliyun|AzureChinaCloud>
|
||||
#
|
||||
# Use the --mirror option to install from a mirror supported by this script.
|
||||
# Available mirrors are "Aliyun" (https://mirrors.aliyun.com/docker-ce), and
|
||||
# "AzureChinaCloud" (https://mirror.azure.cn/docker-ce), for example:
|
||||
#
|
||||
# $ sudo sh install-docker.sh --mirror AzureChinaCloud
|
||||
#
|
||||
# --setup-repo
|
||||
#
|
||||
# Use the --setup-repo option to configure Docker's package repositories without
|
||||
# installing Docker packages. This is useful when you want to add the repository
|
||||
# but install packages separately:
|
||||
#
|
||||
# $ sudo sh install-docker.sh --setup-repo
|
||||
#
|
||||
# ==============================================================================
|
||||
|
||||
|
||||
# Git commit from https://github.com/docker/docker-install when
|
||||
# the script was uploaded (Should only be modified by upload job):
|
||||
SCRIPT_COMMIT_SHA="86415efcfe5f8d966625843da41a0f798238cce5"
|
||||
|
||||
# strip "v" prefix if present
|
||||
VERSION="${VERSION#v}"
|
||||
|
||||
# The channel to install from:
|
||||
# * stable
|
||||
# * test
|
||||
DEFAULT_CHANNEL_VALUE="stable"
|
||||
if [ -z "$CHANNEL" ]; then
|
||||
CHANNEL=$DEFAULT_CHANNEL_VALUE
|
||||
fi
|
||||
|
||||
DEFAULT_DOWNLOAD_URL="https://download.docker.com"
|
||||
if [ -z "$DOWNLOAD_URL" ]; then
|
||||
DOWNLOAD_URL=$DEFAULT_DOWNLOAD_URL
|
||||
fi
|
||||
|
||||
DEFAULT_REPO_FILE="docker-ce.repo"
|
||||
if [ -z "$REPO_FILE" ]; then
|
||||
REPO_FILE="$DEFAULT_REPO_FILE"
|
||||
# Automatically default to a staging repo fora
|
||||
# a staging download url (download-stage.docker.com)
|
||||
case "$DOWNLOAD_URL" in
|
||||
*-stage*) REPO_FILE="docker-ce-staging.repo";;
|
||||
esac
|
||||
fi
|
||||
|
||||
mirror=''
|
||||
DRY_RUN=${DRY_RUN:-}
|
||||
REPO_ONLY=${REPO_ONLY:-0}
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--channel)
|
||||
CHANNEL="$2"
|
||||
shift
|
||||
;;
|
||||
--dry-run)
|
||||
DRY_RUN=1
|
||||
;;
|
||||
--mirror)
|
||||
mirror="$2"
|
||||
shift
|
||||
;;
|
||||
--version)
|
||||
VERSION="${2#v}"
|
||||
shift
|
||||
;;
|
||||
--setup-repo)
|
||||
REPO_ONLY=1
|
||||
shift
|
||||
;;
|
||||
--*)
|
||||
echo "Illegal option $1"
|
||||
;;
|
||||
esac
|
||||
shift $(( $# > 0 ? 1 : 0 ))
|
||||
done
|
||||
|
||||
case "$mirror" in
|
||||
Aliyun)
|
||||
DOWNLOAD_URL="https://mirrors.aliyun.com/docker-ce"
|
||||
;;
|
||||
AzureChinaCloud)
|
||||
DOWNLOAD_URL="https://mirror.azure.cn/docker-ce"
|
||||
;;
|
||||
"")
|
||||
;;
|
||||
*)
|
||||
>&2 echo "unknown mirror '$mirror': use either 'Aliyun', or 'AzureChinaCloud'."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$CHANNEL" in
|
||||
stable|test)
|
||||
;;
|
||||
*)
|
||||
>&2 echo "unknown CHANNEL '$CHANNEL': use either stable or test."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
command_exists() {
|
||||
command -v "$@" > /dev/null 2>&1
|
||||
}
|
||||
|
||||
# version_gte checks if the version specified in $VERSION is at least the given
|
||||
# SemVer (Maj.Minor[.Patch]), or CalVer (YY.MM) version.It returns 0 (success)
|
||||
# if $VERSION is either unset (=latest) or newer or equal than the specified
|
||||
# version, or returns 1 (fail) otherwise.
|
||||
#
|
||||
# examples:
|
||||
#
|
||||
# VERSION=23.0
|
||||
# version_gte 23.0 // 0 (success)
|
||||
# version_gte 20.10 // 0 (success)
|
||||
# version_gte 19.03 // 0 (success)
|
||||
# version_gte 26.1 // 1 (fail)
|
||||
version_gte() {
|
||||
if [ -z "$VERSION" ]; then
|
||||
return 0
|
||||
fi
|
||||
version_compare "$VERSION" "$1"
|
||||
}
|
||||
|
||||
# version_compare compares two version strings (either SemVer (Major.Minor.Path),
|
||||
# or CalVer (YY.MM) version strings. It returns 0 (success) if version A is newer
|
||||
# or equal than version B, or 1 (fail) otherwise. Patch releases and pre-release
|
||||
# (-alpha/-beta) are not taken into account
|
||||
#
|
||||
# examples:
|
||||
#
|
||||
# version_compare 23.0.0 20.10 // 0 (success)
|
||||
# version_compare 23.0 20.10 // 0 (success)
|
||||
# version_compare 20.10 19.03 // 0 (success)
|
||||
# version_compare 20.10 20.10 // 0 (success)
|
||||
# version_compare 19.03 20.10 // 1 (fail)
|
||||
version_compare() (
|
||||
set +x
|
||||
|
||||
yy_a="$(echo "$1" | cut -d'.' -f1)"
|
||||
yy_b="$(echo "$2" | cut -d'.' -f1)"
|
||||
if [ "$yy_a" -lt "$yy_b" ]; then
|
||||
return 1
|
||||
fi
|
||||
if [ "$yy_a" -gt "$yy_b" ]; then
|
||||
return 0
|
||||
fi
|
||||
mm_a="$(echo "$1" | cut -d'.' -f2)"
|
||||
mm_b="$(echo "$2" | cut -d'.' -f2)"
|
||||
|
||||
# trim leading zeros to accommodate CalVer
|
||||
mm_a="${mm_a#0}"
|
||||
mm_b="${mm_b#0}"
|
||||
|
||||
if [ "${mm_a:-0}" -lt "${mm_b:-0}" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
)
|
||||
|
||||
is_dry_run() {
|
||||
if [ -z "$DRY_RUN" ]; then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
is_wsl() {
|
||||
case "$(uname -r)" in
|
||||
*microsoft* ) true ;; # WSL 2
|
||||
*Microsoft* ) true ;; # WSL 1
|
||||
* ) false;;
|
||||
esac
|
||||
}
|
||||
|
||||
is_darwin() {
|
||||
case "$(uname -s)" in
|
||||
*darwin* ) true ;;
|
||||
*Darwin* ) true ;;
|
||||
* ) false;;
|
||||
esac
|
||||
}
|
||||
|
||||
deprecation_notice() {
|
||||
distro=$1
|
||||
distro_version=$2
|
||||
echo
|
||||
printf "\033[91;1mDEPRECATION WARNING\033[0m\n"
|
||||
printf " This Linux distribution (\033[1m%s %s\033[0m) reached end-of-life and is no longer supported by this script.\n" "$distro" "$distro_version"
|
||||
echo " No updates or security fixes will be released for this distribution, and users are recommended"
|
||||
echo " to upgrade to a currently maintained version of $distro."
|
||||
echo
|
||||
printf "Press \033[1mCtrl+C\033[0m now to abort this script, or wait for the installation to continue."
|
||||
echo
|
||||
sleep 10
|
||||
}
|
||||
|
||||
get_distribution() {
|
||||
lsb_dist=""
|
||||
# Every system that we officially support has /etc/os-release
|
||||
if [ -r /etc/os-release ]; then
|
||||
lsb_dist="$(. /etc/os-release && echo "$ID")"
|
||||
fi
|
||||
# Returning an empty string here should be alright since the
|
||||
# case statements don't act unless you provide an actual value
|
||||
echo "$lsb_dist"
|
||||
}
|
||||
|
||||
echo_docker_as_nonroot() {
|
||||
if is_dry_run; then
|
||||
return
|
||||
fi
|
||||
if command_exists docker && [ -e /var/run/docker.sock ]; then
|
||||
(
|
||||
set -x
|
||||
$sh_c 'docker version'
|
||||
) || true
|
||||
fi
|
||||
|
||||
# intentionally mixed spaces and tabs here -- tabs are stripped by "<<-EOF", spaces are kept in the output
|
||||
echo
|
||||
echo "================================================================================"
|
||||
echo
|
||||
if version_gte "20.10"; then
|
||||
echo "To run Docker as a non-privileged user, consider setting up the"
|
||||
echo "Docker daemon in rootless mode for your user:"
|
||||
echo
|
||||
echo " dockerd-rootless-setuptool.sh install"
|
||||
echo
|
||||
echo "Visit https://docs.docker.com/go/rootless/ to learn about rootless mode."
|
||||
echo
|
||||
fi
|
||||
echo
|
||||
echo "To run the Docker daemon as a fully privileged service, but granting non-root"
|
||||
echo "users access, refer to https://docs.docker.com/go/daemon-access/"
|
||||
echo
|
||||
echo "WARNING: Access to the remote API on a privileged Docker daemon is equivalent"
|
||||
echo " to root access on the host. Refer to the 'Docker daemon attack surface'"
|
||||
echo " documentation for details: https://docs.docker.com/go/attack-surface/"
|
||||
echo
|
||||
echo "================================================================================"
|
||||
echo
|
||||
}
|
||||
|
||||
# Check if this is a forked Linux distro
|
||||
check_forked() {
|
||||
|
||||
# Check for lsb_release command existence, it usually exists in forked distros
|
||||
if command_exists lsb_release; then
|
||||
# Check if the `-u` option is supported
|
||||
set +e
|
||||
lsb_release -a -u > /dev/null 2>&1
|
||||
lsb_release_exit_code=$?
|
||||
set -e
|
||||
|
||||
# Check if the command has exited successfully, it means we're in a forked distro
|
||||
if [ "$lsb_release_exit_code" = "0" ]; then
|
||||
# Print info about current distro
|
||||
cat <<-EOF
|
||||
You're using '$lsb_dist' version '$dist_version'.
|
||||
EOF
|
||||
|
||||
# Get the upstream release info
|
||||
lsb_dist=$(lsb_release -a -u 2>&1 | tr '[:upper:]' '[:lower:]' | grep -E 'id' | cut -d ':' -f 2 | tr -d '[:space:]')
|
||||
dist_version=$(lsb_release -a -u 2>&1 | tr '[:upper:]' '[:lower:]' | grep -E 'codename' | cut -d ':' -f 2 | tr -d '[:space:]')
|
||||
|
||||
# Print info about upstream distro
|
||||
cat <<-EOF
|
||||
Upstream release is '$lsb_dist' version '$dist_version'.
|
||||
EOF
|
||||
else
|
||||
if [ -r /etc/debian_version ] && [ "$lsb_dist" != "ubuntu" ] && [ "$lsb_dist" != "raspbian" ]; then
|
||||
if [ "$lsb_dist" = "osmc" ]; then
|
||||
# OSMC runs Raspbian
|
||||
lsb_dist=raspbian
|
||||
else
|
||||
# We're Debian and don't even know it!
|
||||
lsb_dist=debian
|
||||
fi
|
||||
dist_version="$(sed 's/\/.*//' /etc/debian_version | sed 's/\..*//')"
|
||||
case "$dist_version" in
|
||||
13)
|
||||
dist_version="trixie"
|
||||
;;
|
||||
12)
|
||||
dist_version="bookworm"
|
||||
;;
|
||||
11)
|
||||
dist_version="bullseye"
|
||||
;;
|
||||
10)
|
||||
dist_version="buster"
|
||||
;;
|
||||
9)
|
||||
dist_version="stretch"
|
||||
;;
|
||||
8)
|
||||
dist_version="jessie"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
do_install() {
|
||||
echo "# Executing docker install script, commit: $SCRIPT_COMMIT_SHA"
|
||||
|
||||
if command_exists docker; then
|
||||
cat >&2 <<-'EOF'
|
||||
Warning: the "docker" command appears to already exist on this system.
|
||||
|
||||
If you already have Docker installed, this script can cause trouble, which is
|
||||
why we're displaying this warning and provide the opportunity to cancel the
|
||||
installation.
|
||||
|
||||
If you installed the current Docker package using this script and are using it
|
||||
again to update Docker, you can ignore this message, but be aware that the
|
||||
script resets any custom changes in the deb and rpm repo configuration
|
||||
files to match the parameters passed to the script.
|
||||
|
||||
You may press Ctrl+C now to abort this script.
|
||||
EOF
|
||||
( set -x; sleep 20 )
|
||||
fi
|
||||
|
||||
user="$(id -un 2>/dev/null || true)"
|
||||
|
||||
sh_c='sh -c'
|
||||
if [ "$user" != 'root' ]; then
|
||||
if command_exists sudo; then
|
||||
sh_c='sudo -E sh -c'
|
||||
elif command_exists su; then
|
||||
sh_c='su -c'
|
||||
else
|
||||
cat >&2 <<-'EOF'
|
||||
Error: this installer needs the ability to run commands as root.
|
||||
We are unable to find either "sudo" or "su" available to make this happen.
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if is_dry_run; then
|
||||
sh_c="echo"
|
||||
fi
|
||||
|
||||
# perform some very rudimentary platform detection
|
||||
lsb_dist=$( get_distribution )
|
||||
lsb_dist="$(echo "$lsb_dist" | tr '[:upper:]' '[:lower:]')"
|
||||
|
||||
if is_wsl; then
|
||||
echo
|
||||
echo "WSL DETECTED: We recommend using Docker Desktop for Windows."
|
||||
echo "Please get Docker Desktop from https://www.docker.com/products/docker-desktop/"
|
||||
echo
|
||||
cat >&2 <<-'EOF'
|
||||
|
||||
You may press Ctrl+C now to abort this script.
|
||||
EOF
|
||||
( set -x; sleep 20 )
|
||||
fi
|
||||
|
||||
case "$lsb_dist" in
|
||||
|
||||
ubuntu)
|
||||
if command_exists lsb_release; then
|
||||
dist_version="$(lsb_release --codename | cut -f2)"
|
||||
fi
|
||||
if [ -z "$dist_version" ] && [ -r /etc/lsb-release ]; then
|
||||
dist_version="$(. /etc/lsb-release && echo "$DISTRIB_CODENAME")"
|
||||
fi
|
||||
;;
|
||||
|
||||
debian|raspbian)
|
||||
dist_version="$(sed 's/\/.*//' /etc/debian_version | sed 's/\..*//')"
|
||||
case "$dist_version" in
|
||||
13)
|
||||
dist_version="trixie"
|
||||
;;
|
||||
12)
|
||||
dist_version="bookworm"
|
||||
;;
|
||||
11)
|
||||
dist_version="bullseye"
|
||||
;;
|
||||
10)
|
||||
dist_version="buster"
|
||||
;;
|
||||
9)
|
||||
dist_version="stretch"
|
||||
;;
|
||||
8)
|
||||
dist_version="jessie"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
|
||||
centos|rhel)
|
||||
if [ -z "$dist_version" ] && [ -r /etc/os-release ]; then
|
||||
dist_version="$(. /etc/os-release && echo "$VERSION_ID")"
|
||||
fi
|
||||
;;
|
||||
|
||||
*)
|
||||
if command_exists lsb_release; then
|
||||
dist_version="$(lsb_release --release | cut -f2)"
|
||||
fi
|
||||
if [ -z "$dist_version" ] && [ -r /etc/os-release ]; then
|
||||
dist_version="$(. /etc/os-release && echo "$VERSION_ID")"
|
||||
fi
|
||||
;;
|
||||
|
||||
esac
|
||||
|
||||
# Check if this is a forked Linux distro
|
||||
check_forked
|
||||
|
||||
# Print deprecation warnings for distro versions that recently reached EOL,
|
||||
# but may still be commonly used (especially LTS versions).
|
||||
case "$lsb_dist.$dist_version" in
|
||||
centos.8|centos.7|rhel.7)
|
||||
deprecation_notice "$lsb_dist" "$dist_version"
|
||||
;;
|
||||
debian.buster|debian.stretch|debian.jessie)
|
||||
deprecation_notice "$lsb_dist" "$dist_version"
|
||||
;;
|
||||
raspbian.buster|raspbian.stretch|raspbian.jessie)
|
||||
deprecation_notice "$lsb_dist" "$dist_version"
|
||||
;;
|
||||
ubuntu.focal|ubuntu.bionic|ubuntu.xenial|ubuntu.trusty)
|
||||
deprecation_notice "$lsb_dist" "$dist_version"
|
||||
;;
|
||||
ubuntu.oracular|ubuntu.mantic|ubuntu.lunar|ubuntu.kinetic|ubuntu.impish|ubuntu.hirsute|ubuntu.groovy|ubuntu.eoan|ubuntu.disco|ubuntu.cosmic)
|
||||
deprecation_notice "$lsb_dist" "$dist_version"
|
||||
;;
|
||||
fedora.*)
|
||||
if [ "$dist_version" -lt 41 ]; then
|
||||
deprecation_notice "$lsb_dist" "$dist_version"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# Run setup for each distro accordingly
|
||||
case "$lsb_dist" in
|
||||
ubuntu|debian|raspbian)
|
||||
pre_reqs="ca-certificates curl"
|
||||
apt_repo="deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] $DOWNLOAD_URL/linux/$lsb_dist $dist_version $CHANNEL"
|
||||
(
|
||||
if ! is_dry_run; then
|
||||
set -x
|
||||
fi
|
||||
$sh_c 'apt-get -qq update >/dev/null'
|
||||
$sh_c "DEBIAN_FRONTEND=noninteractive apt-get -y -qq install $pre_reqs >/dev/null"
|
||||
$sh_c 'install -m 0755 -d /etc/apt/keyrings'
|
||||
$sh_c "curl -fsSL \"$DOWNLOAD_URL/linux/$lsb_dist/gpg\" -o /etc/apt/keyrings/docker.asc"
|
||||
$sh_c "chmod a+r /etc/apt/keyrings/docker.asc"
|
||||
$sh_c "echo \"$apt_repo\" > /etc/apt/sources.list.d/docker.list"
|
||||
$sh_c 'apt-get -qq update >/dev/null'
|
||||
)
|
||||
|
||||
if [ "$REPO_ONLY" = "1" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
pkg_version=""
|
||||
if [ -n "$VERSION" ]; then
|
||||
if is_dry_run; then
|
||||
echo "# WARNING: VERSION pinning is not supported in DRY_RUN"
|
||||
else
|
||||
# Will work for incomplete versions IE (17.12), but may not actually grab the "latest" if in the test channel
|
||||
pkg_pattern="$(echo "$VERSION" | sed 's/-ce-/~ce~.*/g' | sed 's/-/.*/g')"
|
||||
search_command="apt-cache madison docker-ce | grep '$pkg_pattern' | head -1 | awk '{\$1=\$1};1' | cut -d' ' -f 3"
|
||||
pkg_version="$($sh_c "$search_command")"
|
||||
echo "INFO: Searching repository for VERSION '$VERSION'"
|
||||
echo "INFO: $search_command"
|
||||
if [ -z "$pkg_version" ]; then
|
||||
echo
|
||||
echo "ERROR: '$VERSION' not found amongst apt-cache madison results"
|
||||
echo
|
||||
exit 1
|
||||
fi
|
||||
if version_gte "18.09"; then
|
||||
search_command="apt-cache madison docker-ce-cli | grep '$pkg_pattern' | head -1 | awk '{\$1=\$1};1' | cut -d' ' -f 3"
|
||||
echo "INFO: $search_command"
|
||||
cli_pkg_version="=$($sh_c "$search_command")"
|
||||
fi
|
||||
pkg_version="=$pkg_version"
|
||||
fi
|
||||
fi
|
||||
(
|
||||
pkgs="docker-ce${pkg_version%=}"
|
||||
if version_gte "18.09"; then
|
||||
# older versions didn't ship the cli and containerd as separate packages
|
||||
pkgs="$pkgs docker-ce-cli${cli_pkg_version%=} containerd.io"
|
||||
fi
|
||||
if version_gte "20.10"; then
|
||||
pkgs="$pkgs docker-compose-plugin docker-ce-rootless-extras$pkg_version"
|
||||
fi
|
||||
if version_gte "23.0"; then
|
||||
pkgs="$pkgs docker-buildx-plugin"
|
||||
fi
|
||||
if version_gte "28.2"; then
|
||||
pkgs="$pkgs docker-model-plugin"
|
||||
fi
|
||||
if ! is_dry_run; then
|
||||
set -x
|
||||
fi
|
||||
$sh_c "DEBIAN_FRONTEND=noninteractive apt-get -y -qq install $pkgs >/dev/null"
|
||||
)
|
||||
echo_docker_as_nonroot
|
||||
exit 0
|
||||
;;
|
||||
centos|fedora|rhel)
|
||||
if [ "$(uname -m)" = "s390x" ]; then
|
||||
echo "Effective v27.5, please consult RHEL distro statement for s390x support."
|
||||
exit 1
|
||||
fi
|
||||
repo_file_url="$DOWNLOAD_URL/linux/$lsb_dist/$REPO_FILE"
|
||||
(
|
||||
if ! is_dry_run; then
|
||||
set -x
|
||||
fi
|
||||
if command_exists dnf5; then
|
||||
$sh_c "dnf -y -q --setopt=install_weak_deps=False install dnf-plugins-core"
|
||||
$sh_c "dnf5 config-manager addrepo --overwrite --save-filename=docker-ce.repo --from-repofile='$repo_file_url'"
|
||||
|
||||
if [ "$CHANNEL" != "stable" ]; then
|
||||
$sh_c "dnf5 config-manager setopt \"docker-ce-*.enabled=0\""
|
||||
$sh_c "dnf5 config-manager setopt \"docker-ce-$CHANNEL.enabled=1\""
|
||||
fi
|
||||
$sh_c "dnf makecache"
|
||||
elif command_exists dnf; then
|
||||
$sh_c "dnf -y -q --setopt=install_weak_deps=False install dnf-plugins-core"
|
||||
$sh_c "rm -f /etc/yum.repos.d/docker-ce.repo /etc/yum.repos.d/docker-ce-staging.repo"
|
||||
$sh_c "dnf config-manager --add-repo $repo_file_url"
|
||||
|
||||
if [ "$CHANNEL" != "stable" ]; then
|
||||
$sh_c "dnf config-manager --set-disabled \"docker-ce-*\""
|
||||
$sh_c "dnf config-manager --set-enabled \"docker-ce-$CHANNEL\""
|
||||
fi
|
||||
$sh_c "dnf makecache"
|
||||
else
|
||||
$sh_c "yum -y -q install yum-utils"
|
||||
$sh_c "rm -f /etc/yum.repos.d/docker-ce.repo /etc/yum.repos.d/docker-ce-staging.repo"
|
||||
$sh_c "yum-config-manager --add-repo $repo_file_url"
|
||||
|
||||
if [ "$CHANNEL" != "stable" ]; then
|
||||
$sh_c "yum-config-manager --disable \"docker-ce-*\""
|
||||
$sh_c "yum-config-manager --enable \"docker-ce-$CHANNEL\""
|
||||
fi
|
||||
$sh_c "yum makecache"
|
||||
fi
|
||||
)
|
||||
|
||||
if [ "$REPO_ONLY" = "1" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
pkg_version=""
|
||||
if command_exists dnf; then
|
||||
pkg_manager="dnf"
|
||||
pkg_manager_flags="-y -q --best"
|
||||
else
|
||||
pkg_manager="yum"
|
||||
pkg_manager_flags="-y -q"
|
||||
fi
|
||||
if [ -n "$VERSION" ]; then
|
||||
if is_dry_run; then
|
||||
echo "# WARNING: VERSION pinning is not supported in DRY_RUN"
|
||||
else
|
||||
if [ "$lsb_dist" = "fedora" ]; then
|
||||
pkg_suffix="fc$dist_version"
|
||||
else
|
||||
pkg_suffix="el"
|
||||
fi
|
||||
pkg_pattern="$(echo "$VERSION" | sed 's/-ce-/\\\\.ce.*/g' | sed 's/-/.*/g').*$pkg_suffix"
|
||||
search_command="$pkg_manager list --showduplicates docker-ce | grep '$pkg_pattern' | tail -1 | awk '{print \$2}'"
|
||||
pkg_version="$($sh_c "$search_command")"
|
||||
echo "INFO: Searching repository for VERSION '$VERSION'"
|
||||
echo "INFO: $search_command"
|
||||
if [ -z "$pkg_version" ]; then
|
||||
echo
|
||||
echo "ERROR: '$VERSION' not found amongst $pkg_manager list results"
|
||||
echo
|
||||
exit 1
|
||||
fi
|
||||
if version_gte "18.09"; then
|
||||
# older versions don't support a cli package
|
||||
search_command="$pkg_manager list --showduplicates docker-ce-cli | grep '$pkg_pattern' | tail -1 | awk '{print \$2}'"
|
||||
cli_pkg_version="$($sh_c "$search_command" | cut -d':' -f 2)"
|
||||
fi
|
||||
# Cut out the epoch and prefix with a '-'
|
||||
pkg_version="-$(echo "$pkg_version" | cut -d':' -f 2)"
|
||||
fi
|
||||
fi
|
||||
(
|
||||
pkgs="docker-ce$pkg_version"
|
||||
if version_gte "18.09"; then
|
||||
# older versions didn't ship the cli and containerd as separate packages
|
||||
if [ -n "$cli_pkg_version" ]; then
|
||||
pkgs="$pkgs docker-ce-cli-$cli_pkg_version containerd.io"
|
||||
else
|
||||
pkgs="$pkgs docker-ce-cli containerd.io"
|
||||
fi
|
||||
fi
|
||||
if version_gte "20.10"; then
|
||||
pkgs="$pkgs docker-compose-plugin docker-ce-rootless-extras$pkg_version"
|
||||
fi
|
||||
if version_gte "23.0"; then
|
||||
pkgs="$pkgs docker-buildx-plugin docker-model-plugin"
|
||||
fi
|
||||
if ! is_dry_run; then
|
||||
set -x
|
||||
fi
|
||||
$sh_c "$pkg_manager $pkg_manager_flags install $pkgs"
|
||||
)
|
||||
echo_docker_as_nonroot
|
||||
exit 0
|
||||
;;
|
||||
sles)
|
||||
echo "Effective v27.5, please consult SLES distro statement for s390x support."
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
if [ -z "$lsb_dist" ]; then
|
||||
if is_darwin; then
|
||||
echo
|
||||
echo "ERROR: Unsupported operating system 'macOS'"
|
||||
echo "Please get Docker Desktop from https://www.docker.com/products/docker-desktop"
|
||||
echo
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
echo
|
||||
echo "ERROR: Unsupported distribution '$lsb_dist'"
|
||||
echo
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
exit 1
|
||||
}
|
||||
|
||||
# wrapped up in a function so that we have some protection against only getting
|
||||
# half the file during "curl | sh"
|
||||
do_install
|
||||
62
opt/00-infrastructure/blackbox/blackbox.yml
Normal file
62
opt/00-infrastructure/blackbox/blackbox.yml
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
modules:
|
||||
http_2xx:
|
||||
prober: http
|
||||
timeout: 10s
|
||||
http:
|
||||
valid_status_codes: [200, 301, 302]
|
||||
method: GET
|
||||
preferred_ip_protocol: "ip4"
|
||||
|
||||
http_post_2xx:
|
||||
prober: http
|
||||
timeout: 10s
|
||||
http:
|
||||
method: POST
|
||||
preferred_ip_protocol: "ip4"
|
||||
|
||||
tcp_connect:
|
||||
prober: tcp
|
||||
timeout: 10s
|
||||
|
||||
pop3s_banner:
|
||||
prober: tcp
|
||||
tcp:
|
||||
query_response:
|
||||
- expect: "^+OK"
|
||||
tls: true
|
||||
tls_config:
|
||||
insecure_skip_verify: false
|
||||
|
||||
ssh_banner:
|
||||
prober: tcp
|
||||
timeout: 10s
|
||||
tcp:
|
||||
query_response:
|
||||
- expect: "^SSH-"
|
||||
|
||||
irc_banner:
|
||||
prober: tcp
|
||||
timeout: 10s
|
||||
tcp:
|
||||
query_response:
|
||||
- send: "NICK prober"
|
||||
- send: "USER prober prober prober :prober"
|
||||
- expect: "PING :([^ ]+)"
|
||||
- send: "PONG :${1}"
|
||||
- expect: "^:[^ ]+ 001"
|
||||
|
||||
icmp:
|
||||
prober: icmp
|
||||
timeout: 10s
|
||||
icmp:
|
||||
preferred_ip_protocol: "ip4"
|
||||
|
||||
# SSL certificate check
|
||||
ssl_check:
|
||||
prober: http
|
||||
timeout: 10s
|
||||
http:
|
||||
method: GET
|
||||
preferred_ip_protocol: "ip4"
|
||||
tls_config:
|
||||
insecure_skip_verify: false
|
||||
19
opt/00-infrastructure/blackbox/docker-compose.yml
Normal file
19
opt/00-infrastructure/blackbox/docker-compose.yml
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
version: '3.8'
|
||||
|
||||
services:
|
||||
blackbox-exporter:
|
||||
image: prom/blackbox-exporter:latest
|
||||
container_name: blackbox-exporter
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "9115:9115"
|
||||
volumes:
|
||||
- ./blackbox.yml:/etc/blackbox_exporter/config.yml
|
||||
command:
|
||||
- '--config.file=/etc/blackbox_exporter/config.yml'
|
||||
networks:
|
||||
- monitoring_monitoring
|
||||
|
||||
networks:
|
||||
monitoring_monitoring:
|
||||
external: true
|
||||
76
opt/00-infrastructure/blackbox/optimized-prometheus.yml
Normal file
76
opt/00-infrastructure/blackbox/optimized-prometheus.yml
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
global:
|
||||
scrape_interval: 30s
|
||||
evaluation_interval: 30s
|
||||
scrape_timeout: 10s
|
||||
|
||||
# Оптимизация retention политики
|
||||
storage:
|
||||
tsdb:
|
||||
retention: 15d
|
||||
retention_size: 10GB
|
||||
out_of_order_time_window: 2h
|
||||
|
||||
alerting:
|
||||
alertmanagers:
|
||||
- static_configs:
|
||||
- targets:
|
||||
- alertmanager:9093
|
||||
|
||||
scrape_configs:
|
||||
- job_name: 'prometheus'
|
||||
static_configs:
|
||||
- targets: ['localhost:9090']
|
||||
scrape_interval: 30s
|
||||
|
||||
- job_name: 'node-exporter'
|
||||
static_configs:
|
||||
- targets: ['node-exporter:9100']
|
||||
scrape_interval: 30s
|
||||
|
||||
- job_name: 'cadvisor'
|
||||
static_configs:
|
||||
- targets: ['cadvisor:8080']
|
||||
scrape_interval: 30s
|
||||
metric_relabel_configs:
|
||||
- source_labels: [__name__]
|
||||
regex: '(container_tasks_state|container_memory_failures_total)'
|
||||
action: drop
|
||||
|
||||
- job_name: 'alertmanager'
|
||||
static_configs:
|
||||
- targets: ['alertmanager:9093']
|
||||
scrape_interval: 30s
|
||||
|
||||
- job_name: 'blackbox-ssl'
|
||||
metrics_path: /probe
|
||||
params:
|
||||
module: [http_2xx]
|
||||
static_configs:
|
||||
- targets:
|
||||
- https://ai-impress.com
|
||||
- https://auth.ai-impress.com
|
||||
- https://marketing.ai-impress.com
|
||||
- https://n8n.ai-impress.com
|
||||
- https://odoo.ai-impress.com
|
||||
- https://pgadmin.ai-impress.com
|
||||
- https://portainer.ai-impress.com
|
||||
- https://rabbitmq.ai-impress.com
|
||||
- https://social.ai-impress.com
|
||||
- https://status.ai-impress.com
|
||||
- https://supabase.ai-impress.com
|
||||
- https://traefik.ai-impress.com
|
||||
- https://uploads.ai-impress.com
|
||||
- https://vault-admin.ai-impress.com
|
||||
- https://vault.ai-impress.com
|
||||
- https://webhook.ai-impress.com
|
||||
- https://wiki.ai-impress.com
|
||||
- https://wpp.ai-impress.com
|
||||
- https://www.ai-impress.com
|
||||
scrape_interval: 5m
|
||||
relabel_configs:
|
||||
- source_labels: [__address__]
|
||||
target_label: __param_target
|
||||
- source_labels: [__param_target]
|
||||
target_label: instance
|
||||
- target_label: __address__
|
||||
replacement: blackbox-exporter:9115
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue