Добавлена документация и скрипты для работы с БД
- Удалены лишние .gitignore из подпапок (теперь один общий в корне) - Добавлен import_db.sh для импорта базы данных - Создана документация: docs/ARCHITECTURE.md для разработчиков - Создана документация: docs/USER_GUIDE.md для пользователей - Обновлен .gitignore для исключения временных файлов
This commit is contained in:
parent
6cb5a9b99f
commit
e8ced9bca7
7 changed files with 418 additions and 140 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -55,6 +55,12 @@ npm-debug.log
|
||||||
# Файлы кеша
|
# Файлы кеша
|
||||||
.cache/
|
.cache/
|
||||||
|
|
||||||
|
# Документация и временные файлы
|
||||||
|
LichessClientTG_bot/docs/*.html
|
||||||
|
LichessClientTG_bot/docs/*.txt
|
||||||
|
LichessClientTG_bot/docs/*_files/
|
||||||
|
LichessWebServices/*.txt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
54
LichessClientTG_bot/.gitignore
vendored
54
LichessClientTG_bot/.gitignore
vendored
|
|
@ -1,54 +0,0 @@
|
||||||
# Python
|
|
||||||
__pycache__/
|
|
||||||
*.py[cod]
|
|
||||||
*$py.class
|
|
||||||
*.so
|
|
||||||
.Python
|
|
||||||
build/
|
|
||||||
develop-eggs/
|
|
||||||
dist/
|
|
||||||
downloads/
|
|
||||||
eggs/
|
|
||||||
.eggs/
|
|
||||||
lib/
|
|
||||||
lib64/
|
|
||||||
parts/
|
|
||||||
sdist/
|
|
||||||
var/
|
|
||||||
wheels/
|
|
||||||
*.egg-info/
|
|
||||||
.installed.cfg
|
|
||||||
*.egg
|
|
||||||
MANIFEST
|
|
||||||
|
|
||||||
# Virtual environments
|
|
||||||
.env
|
|
||||||
.venv
|
|
||||||
env/
|
|
||||||
venv/
|
|
||||||
ENV/
|
|
||||||
env.bak/
|
|
||||||
venv.bak/
|
|
||||||
|
|
||||||
# Database (commented out to include in git)
|
|
||||||
# *.db
|
|
||||||
# *.sqlite
|
|
||||||
# *.sqlite3
|
|
||||||
|
|
||||||
# Logs
|
|
||||||
*.log
|
|
||||||
logs/
|
|
||||||
|
|
||||||
# IDE
|
|
||||||
.vscode/
|
|
||||||
.idea/
|
|
||||||
*.swp
|
|
||||||
*.swo
|
|
||||||
*~
|
|
||||||
|
|
||||||
# OS
|
|
||||||
.DS_Store
|
|
||||||
Thumbs.db
|
|
||||||
|
|
||||||
# Docker
|
|
||||||
.dockerignore
|
|
||||||
58
LichessWebServices/.gitignore
vendored
58
LichessWebServices/.gitignore
vendored
|
|
@ -1,58 +0,0 @@
|
||||||
# Python
|
|
||||||
__pycache__/
|
|
||||||
*.py[cod]
|
|
||||||
*$py.class
|
|
||||||
*.so
|
|
||||||
.Python
|
|
||||||
build/
|
|
||||||
develop-eggs/
|
|
||||||
dist/
|
|
||||||
downloads/
|
|
||||||
eggs/
|
|
||||||
.eggs/
|
|
||||||
lib/
|
|
||||||
lib64/
|
|
||||||
parts/
|
|
||||||
sdist/
|
|
||||||
var/
|
|
||||||
wheels/
|
|
||||||
*.egg-info/
|
|
||||||
.installed.cfg
|
|
||||||
*.egg
|
|
||||||
MANIFEST
|
|
||||||
|
|
||||||
# Virtual environments
|
|
||||||
.env
|
|
||||||
.venv
|
|
||||||
env/
|
|
||||||
venv/
|
|
||||||
ENV/
|
|
||||||
env.bak/
|
|
||||||
venv.bak/
|
|
||||||
|
|
||||||
# IDE
|
|
||||||
.vscode/
|
|
||||||
.idea/
|
|
||||||
*.swp
|
|
||||||
*.swo
|
|
||||||
*~
|
|
||||||
|
|
||||||
# OS
|
|
||||||
.DS_Store
|
|
||||||
.DS_Store?
|
|
||||||
._*
|
|
||||||
.Spotlight-V100
|
|
||||||
.Trashes
|
|
||||||
ehthumbs.db
|
|
||||||
Thumbs.db
|
|
||||||
|
|
||||||
# Logs
|
|
||||||
*.log
|
|
||||||
logs/
|
|
||||||
|
|
||||||
# Docker
|
|
||||||
.dockerignore
|
|
||||||
|
|
||||||
# Temporary files
|
|
||||||
*.tmp
|
|
||||||
*.temp
|
|
||||||
28
LichessWebView/.gitignore
vendored
28
LichessWebView/.gitignore
vendored
|
|
@ -1,28 +0,0 @@
|
||||||
__pycache__/
|
|
||||||
*.py[cod]
|
|
||||||
*$py.class
|
|
||||||
*.so
|
|
||||||
.Python
|
|
||||||
env/
|
|
||||||
venv/
|
|
||||||
ENV/
|
|
||||||
build/
|
|
||||||
develop-eggs/
|
|
||||||
dist/
|
|
||||||
downloads/
|
|
||||||
eggs/
|
|
||||||
.eggs/
|
|
||||||
lib/
|
|
||||||
lib64/
|
|
||||||
parts/
|
|
||||||
sdist/
|
|
||||||
var/
|
|
||||||
wheels/
|
|
||||||
*.egg-info/
|
|
||||||
.installed.cfg
|
|
||||||
*.egg
|
|
||||||
.DS_Store
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
132
docs/ARCHITECTURE.md
Normal file
132
docs/ARCHITECTURE.md
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
# Архитектура проекта Lichess Statistics Ecosystem
|
||||||
|
|
||||||
|
## Обзор проекта
|
||||||
|
|
||||||
|
Проект состоит из трех основных компонентов:
|
||||||
|
|
||||||
|
1. **LichessWebServices** - REST API для получения статистики с Lichess
|
||||||
|
2. **LichessClientTG_bot** - Telegram бот для отслеживания игроков
|
||||||
|
3. **LichessWebView** - Веб-интерфейс для просмотра пользователей и игроков
|
||||||
|
|
||||||
|
## Структура базы данных
|
||||||
|
|
||||||
|
### Таблицы
|
||||||
|
|
||||||
|
#### telegram_users
|
||||||
|
Хранит информацию о пользователях Telegram бота.
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE telegram_users (
|
||||||
|
user_id INTEGER PRIMARY KEY, -- ID пользователя в Telegram
|
||||||
|
username TEXT, -- @username
|
||||||
|
first_name TEXT, -- Имя
|
||||||
|
last_name TEXT, -- Фамилия
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### gamers
|
||||||
|
Хранит информацию о игроках Lichess (уникальные).
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE gamers (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
username TEXT UNIQUE NOT NULL, -- Username на Lichess
|
||||||
|
token TEXT, -- DEPRECATED: старый глобальный токен
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Важно:** Поле `token` в таблице `gamers` больше не используется. Токены хранятся в `user_gamers`.
|
||||||
|
|
||||||
|
#### user_gamers
|
||||||
|
Связывает пользователей Telegram с игроками Lichess. Хранит индивидуальные настройки.
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE user_gamers (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
user_id INTEGER NOT NULL, -- ID пользователя Telegram
|
||||||
|
gamer_id INTEGER NOT NULL, -- ID игрока Lichess
|
||||||
|
token TEXT, -- Lichess API token для этого пользователя+игрока
|
||||||
|
is_active BOOLEAN DEFAULT FALSE, -- Активен ли игрок для пользователя
|
||||||
|
period_minutes INTEGER DEFAULT 0, -- Период уведомлений (0 = отключено)
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (user_id) REFERENCES telegram_users(user_id),
|
||||||
|
FOREIGN KEY (gamer_id) REFERENCES gamers(id),
|
||||||
|
UNIQUE(user_id, gamer_id)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ключевая особенность
|
||||||
|
|
||||||
|
Один пользователь может отслеживать несколько игроков, и у каждого пользователя **свой** активный игрок. Токены хранятся **отдельно для каждой пары** пользователь-игрок.
|
||||||
|
|
||||||
|
## Поток данных
|
||||||
|
|
||||||
|
### 1. Добавление игрока (`/adduser`)
|
||||||
|
|
||||||
|
```
|
||||||
|
Пользователь → /adduser → Ввод токена → Ввод username →
|
||||||
|
database.add_gamer(username) → database.add_user_gamer(user_id, gamer_id, token)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Получение статистики (`/today`, `/yesterday`, `/week`)
|
||||||
|
|
||||||
|
```
|
||||||
|
Пользователь → /today → database.get_user_active_gamer(user_id) →
|
||||||
|
lichess_api.get_stats(username, token) → Отправка в Telegram
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Периодические уведомления
|
||||||
|
|
||||||
|
```
|
||||||
|
Периодическая задача → database.get_all_gamers_with_periods() →
|
||||||
|
Для каждой пары (user_id, gamer_id):
|
||||||
|
lichess_api.get_stats(username, token из user_gamers) →
|
||||||
|
Проверка изменений → Отправка уведомления
|
||||||
|
```
|
||||||
|
|
||||||
|
## Миграция токенов
|
||||||
|
|
||||||
|
При старте бота автоматически запускается `_migrate_tokens_from_gamers()`:
|
||||||
|
|
||||||
|
1. Проверяет есть ли токены в `gamers.token`
|
||||||
|
2. Если есть и их нет в `user_gamers.token`, переносит их
|
||||||
|
3. Логирует сколько токенов перенесено
|
||||||
|
|
||||||
|
Это нужно для перехода со старой схемы (глобальный токен) на новую (токен на пару).
|
||||||
|
|
||||||
|
## API Endpoints
|
||||||
|
|
||||||
|
### Lichess Web Services
|
||||||
|
|
||||||
|
- `GET /api/user_ratings/{username}` - Получить рейтинги игрока
|
||||||
|
- `GET /api/user_stats/{username}?start_date={date}&end_date={date}&token={token}` - Статистика за период
|
||||||
|
- `GET /health` - Health check
|
||||||
|
|
||||||
|
## Команды бота
|
||||||
|
|
||||||
|
- `/start` - Приветствие и регистрация пользователя
|
||||||
|
- `/adduser` - Добавить игрока для отслеживания
|
||||||
|
- `/getgamers` - Выбрать активного игрока
|
||||||
|
- `/today` - Статистика за сегодня
|
||||||
|
- `/yesterday` - Статистика за вчера
|
||||||
|
- `/week` - Статистика за неделю
|
||||||
|
- `/setperiod` - Настроить периодические уведомления
|
||||||
|
|
||||||
|
## Docker Compose
|
||||||
|
|
||||||
|
Все три сервиса запускаются через единый `docker-compose.yml`:
|
||||||
|
|
||||||
|
- **lichess-api**: API на порту 8001
|
||||||
|
- **lichess-telegram-bot**: Бот (host network)
|
||||||
|
- **lichess-web-view**: Веб-интерфейс на порту 5000
|
||||||
|
|
||||||
|
База данных монтируется как bind mount: `./LichessClientTG_bot/data:/app/data`
|
||||||
|
|
||||||
|
## Безопасность
|
||||||
|
|
||||||
|
- Токены Lichess API хранятся в базе данных и используются только для запросов к Lichess
|
||||||
|
- Веб-интерфейс работает **без пароля** - доступен всем кто знает URL
|
||||||
|
- Telegram бот токен хранится в `config.py` (можно вынести в `.env`)
|
||||||
|
|
||||||
223
docs/USER_GUIDE.md
Normal file
223
docs/USER_GUIDE.md
Normal file
|
|
@ -0,0 +1,223 @@
|
||||||
|
# Инструкция для пользователя
|
||||||
|
|
||||||
|
## Первая установка на новом сервере
|
||||||
|
|
||||||
|
### Требования
|
||||||
|
|
||||||
|
- Docker и Docker Compose установлены
|
||||||
|
- Доступ к GitHub репозиторию
|
||||||
|
|
||||||
|
### Шаг 1: Клонирование репозитория
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/vrubelroman/LichessStatTgWeb.git
|
||||||
|
cd LichessStatTgWeb
|
||||||
|
```
|
||||||
|
|
||||||
|
### Шаг 2: Передача базы данных (опционально)
|
||||||
|
|
||||||
|
Если у вас есть бэкап базы данных с другого сервера:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Скопируйте файл базы на сервер
|
||||||
|
cp /path/to/backup.db LichessClientTG_bot/data/lichess_bot.db
|
||||||
|
```
|
||||||
|
|
||||||
|
Или импортируйте через скрипт:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod +x import_db.sh
|
||||||
|
./import_db.sh /path/to/backup.db
|
||||||
|
```
|
||||||
|
|
||||||
|
### Шаг 3: Запуск
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod +x start.sh
|
||||||
|
./start.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Скрипт автоматически:
|
||||||
|
- Остановит существующие контейнеры (если есть)
|
||||||
|
- Создаст бэкап базы данных
|
||||||
|
- Пересоберет Docker образы
|
||||||
|
- Запустит все три сервиса
|
||||||
|
|
||||||
|
### Шаг 4: Проверка
|
||||||
|
|
||||||
|
Откройте в браузере:
|
||||||
|
- **API документация**: http://localhost:8001/docs
|
||||||
|
- **Веб-интерфейс**: http://localhost:5000
|
||||||
|
|
||||||
|
Проверьте логи:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker logs lichess-telegram-bot -f
|
||||||
|
```
|
||||||
|
|
||||||
|
## Обновление до новой версии
|
||||||
|
|
||||||
|
### Шаг 1: Создайте бэкап (рекомендуется)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod +x export_db.sh
|
||||||
|
./export_db.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Это создаст копию базы в папке `backups/`.
|
||||||
|
|
||||||
|
### Шаг 2: Обновите код
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /path/to/LichessStatTgWeb
|
||||||
|
git pull
|
||||||
|
```
|
||||||
|
|
||||||
|
### Шаг 3: Перезапустите сервисы
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./start.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
**Важно:** `start.sh` **НЕ удалит** вашу базу данных. Она останется в `LichessClientTG_bot/data/`.
|
||||||
|
|
||||||
|
### Шаг 4: Проверка
|
||||||
|
|
||||||
|
Проверьте что все контейнеры запущены:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker ps | grep lichess
|
||||||
|
```
|
||||||
|
|
||||||
|
## Работа с бэкапами
|
||||||
|
|
||||||
|
### Экспорт базы данных
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./export_db.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Создает файл в `backups/export_lichess_bot_YYYYMMDD_HHMMSS.db`
|
||||||
|
|
||||||
|
### Импорт базы данных
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./import_db.sh backups/export_lichess_bot_20251028_123456.db
|
||||||
|
```
|
||||||
|
|
||||||
|
Автоматически создаст бэкап текущей базы перед импортом.
|
||||||
|
|
||||||
|
## Просмотр логов
|
||||||
|
|
||||||
|
### Все сервисы
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose logs -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### Только бот
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker logs lichess-telegram-bot -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### Только API
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker logs lichess-api -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### Только веб-интерфейс
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker logs lichess-web-view -f
|
||||||
|
```
|
||||||
|
|
||||||
|
## Остановка и удаление
|
||||||
|
|
||||||
|
### Остановка без удаления данных
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose down
|
||||||
|
```
|
||||||
|
|
||||||
|
База данных остается.
|
||||||
|
|
||||||
|
### Полное удаление (с данными!)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose down -v # ОПАСНО! Удалит volumes
|
||||||
|
rm -rf LichessClientTG_bot/data/ # Удалит базу данных
|
||||||
|
```
|
||||||
|
|
||||||
|
## Перенос на другой сервер
|
||||||
|
|
||||||
|
### На старом сервере:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./export_db.sh
|
||||||
|
# Скопируйте файл из backups/ на новый сервер
|
||||||
|
```
|
||||||
|
|
||||||
|
### На новом сервере:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/vrubelroman/LichessStatTgWeb.git
|
||||||
|
cd LichessStatTgWeb
|
||||||
|
chmod +x import_db.sh start.sh
|
||||||
|
./import_db.sh /path/to/backup.db
|
||||||
|
./start.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Устранение проблем
|
||||||
|
|
||||||
|
### Порт уже занят
|
||||||
|
|
||||||
|
Ошибка: `bind: address already in use`
|
||||||
|
|
||||||
|
Решение:
|
||||||
|
```bash
|
||||||
|
# Найдите процесс на порту
|
||||||
|
sudo fuser -k 8001/tcp # Для API
|
||||||
|
sudo fuser -k 5000/tcp # Для веб-интерфейса
|
||||||
|
|
||||||
|
# Перезапустите
|
||||||
|
./start.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Контейнер не запускается
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Проверьте логи
|
||||||
|
docker logs lichess-telegram-bot
|
||||||
|
|
||||||
|
# Удалите и пересоздайте
|
||||||
|
docker-compose down
|
||||||
|
docker-compose build --no-cache
|
||||||
|
./start.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### База данных повреждена
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Восстановите из бэкапа
|
||||||
|
./import_db.sh backups/export_lichess_bot_YYYYMMDD_HHMMSS.db
|
||||||
|
```
|
||||||
|
|
||||||
|
## Структура проекта
|
||||||
|
|
||||||
|
```
|
||||||
|
LichessStatTgWeb/
|
||||||
|
├── LichessWebServices/ # API сервис
|
||||||
|
├── LichessClientTG_bot/ # Telegram бот
|
||||||
|
│ └── data/
|
||||||
|
│ └── lichess_bot.db # База данных (НЕ в git!)
|
||||||
|
├── LichessWebView/ # Веб-интерфейс
|
||||||
|
├── backups/ # Бэкапы БД (НЕ в git!)
|
||||||
|
├── docs/ # Документация
|
||||||
|
├── docker-compose.yml # Конфигурация Docker
|
||||||
|
├── start.sh # Скрипт запуска
|
||||||
|
├── export_db.sh # Экспорт БД
|
||||||
|
└── import_db.sh # Импорт БД
|
||||||
|
```
|
||||||
|
|
||||||
57
import_db.sh
Executable file
57
import_db.sh
Executable file
|
|
@ -0,0 +1,57 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Проверяем аргументы
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
echo "📥 Импорт базы данных"
|
||||||
|
echo ""
|
||||||
|
echo "Использование:"
|
||||||
|
echo " ./import_db.sh <путь_к_файлу_базы>"
|
||||||
|
echo ""
|
||||||
|
echo "Пример:"
|
||||||
|
echo " ./import_db.sh backups/export_lichess_bot_20251028_123456.db"
|
||||||
|
echo " ./import_db.sh /path/to/lichess_bot.db"
|
||||||
|
echo ""
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
BACKUP_FILE="$1"
|
||||||
|
|
||||||
|
# Проверяем существует ли файл
|
||||||
|
if [ ! -f "$BACKUP_FILE" ]; then
|
||||||
|
echo "❌ Файл не найден: $BACKUP_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Проверяем что это SQLite файл
|
||||||
|
if ! file "$BACKUP_FILE" | grep -q "SQLite"; then
|
||||||
|
echo "❌ Это не файл базы данных SQLite!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "📥 Импорт базы данных из: $BACKUP_FILE"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Создаем директорию если не существует
|
||||||
|
mkdir -p LichessClientTG_bot/data
|
||||||
|
mkdir -p backups
|
||||||
|
|
||||||
|
# Делаем бэкап существующей базы если она есть
|
||||||
|
if [ -f "LichessClientTG_bot/data/lichess_bot.db" ]; then
|
||||||
|
echo "💾 Создание бэкапа существующей базы..."
|
||||||
|
cp LichessClientTG_bot/data/lichess_bot.db "backups/backup_before_import_$(date +%Y%m%d_%H%M%S).db"
|
||||||
|
echo "✅ Бэкап создан"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Копируем базу
|
||||||
|
echo "📋 Копирование базы данных..."
|
||||||
|
cp "$BACKUP_FILE" "LichessClientTG_bot/data/lichess_bot.db"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "✅ База данных импортирована!"
|
||||||
|
echo ""
|
||||||
|
echo "⚠️ Для применения изменений перезапустите бота:"
|
||||||
|
echo " docker-compose restart lichess-bot"
|
||||||
|
echo ""
|
||||||
|
echo " Или полный перезапуск всех сервисов:"
|
||||||
|
echo " ./start.sh"
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue