This commit is contained in:
13
.env.example
Normal file
13
.env.example
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Database Configuration
|
||||||
|
DB_HOST=localhost
|
||||||
|
DB_PORT=5432
|
||||||
|
DB_NAME=chatbot
|
||||||
|
DB_USER=postgres
|
||||||
|
DB_PASSWORD=postgres
|
||||||
|
|
||||||
|
# Telegram Bot Configuration
|
||||||
|
TELEGRAM_BOT_TOKEN=your_telegram_bot_token_here
|
||||||
|
|
||||||
|
# Ollama Configuration
|
||||||
|
OLLAMA_URL=https://ai.api.home/
|
||||||
|
OLLAMA_DEFAULT_MODEL=gemma3:4b
|
||||||
@@ -22,7 +22,7 @@ public class ISessionStorageTests : UnitTestBase
|
|||||||
// GetOrCreateAsync method
|
// GetOrCreateAsync method
|
||||||
var getOrCreateMethod = methods.FirstOrDefault(m => m.Name == "GetOrCreateAsync");
|
var getOrCreateMethod = methods.FirstOrDefault(m => m.Name == "GetOrCreateAsync");
|
||||||
getOrCreateMethod.Should().NotBeNull();
|
getOrCreateMethod.Should().NotBeNull();
|
||||||
getOrCreateMethod!.ReturnType.Should().Be(typeof(Task<ChatSession>));
|
getOrCreateMethod!.ReturnType.Should().Be<Task<ChatSession>>();
|
||||||
getOrCreateMethod.GetParameters().Should().HaveCount(3);
|
getOrCreateMethod.GetParameters().Should().HaveCount(3);
|
||||||
getOrCreateMethod.GetParameters()[0].ParameterType.Should().Be<long>();
|
getOrCreateMethod.GetParameters()[0].ParameterType.Should().Be<long>();
|
||||||
getOrCreateMethod.GetParameters()[1].ParameterType.Should().Be<string>();
|
getOrCreateMethod.GetParameters()[1].ParameterType.Should().Be<string>();
|
||||||
@@ -31,14 +31,14 @@ public class ISessionStorageTests : UnitTestBase
|
|||||||
// GetAsync method
|
// GetAsync method
|
||||||
var getMethod = methods.FirstOrDefault(m => m.Name == "GetAsync");
|
var getMethod = methods.FirstOrDefault(m => m.Name == "GetAsync");
|
||||||
getMethod.Should().NotBeNull();
|
getMethod.Should().NotBeNull();
|
||||||
getMethod!.ReturnType.Should().Be(typeof(Task<ChatSession?>));
|
getMethod!.ReturnType.Should().Be<Task<ChatSession?>>();
|
||||||
getMethod.GetParameters().Should().HaveCount(1);
|
getMethod.GetParameters().Should().HaveCount(1);
|
||||||
getMethod.GetParameters()[0].ParameterType.Should().Be<long>();
|
getMethod.GetParameters()[0].ParameterType.Should().Be<long>();
|
||||||
|
|
||||||
// RemoveAsync method
|
// RemoveAsync method
|
||||||
var removeMethod = methods.FirstOrDefault(m => m.Name == "RemoveAsync");
|
var removeMethod = methods.FirstOrDefault(m => m.Name == "RemoveAsync");
|
||||||
removeMethod.Should().NotBeNull();
|
removeMethod.Should().NotBeNull();
|
||||||
removeMethod!.ReturnType.Should().Be(typeof(Task<bool>));
|
removeMethod!.ReturnType.Should().Be<Task<bool>>();
|
||||||
removeMethod.GetParameters().Should().HaveCount(1);
|
removeMethod.GetParameters().Should().HaveCount(1);
|
||||||
removeMethod.GetParameters()[0].ParameterType.Should().Be<long>();
|
removeMethod.GetParameters()[0].ParameterType.Should().Be<long>();
|
||||||
|
|
||||||
@@ -47,13 +47,13 @@ public class ISessionStorageTests : UnitTestBase
|
|||||||
m.Name == "GetActiveSessionsCountAsync"
|
m.Name == "GetActiveSessionsCountAsync"
|
||||||
);
|
);
|
||||||
getActiveSessionsCountMethod.Should().NotBeNull();
|
getActiveSessionsCountMethod.Should().NotBeNull();
|
||||||
getActiveSessionsCountMethod!.ReturnType.Should().Be(typeof(Task<int>));
|
getActiveSessionsCountMethod!.ReturnType.Should().Be<Task<int>>();
|
||||||
getActiveSessionsCountMethod.GetParameters().Should().BeEmpty();
|
getActiveSessionsCountMethod.GetParameters().Should().BeEmpty();
|
||||||
|
|
||||||
// CleanupOldSessionsAsync method
|
// CleanupOldSessionsAsync method
|
||||||
var cleanupOldSessionsMethod = methods.FirstOrDefault(m => m.Name == "CleanupOldSessionsAsync");
|
var cleanupOldSessionsMethod = methods.FirstOrDefault(m => m.Name == "CleanupOldSessionsAsync");
|
||||||
cleanupOldSessionsMethod.Should().NotBeNull();
|
cleanupOldSessionsMethod.Should().NotBeNull();
|
||||||
cleanupOldSessionsMethod!.ReturnType.Should().Be(typeof(Task<int>));
|
cleanupOldSessionsMethod!.ReturnType.Should().Be<Task<int>>();
|
||||||
cleanupOldSessionsMethod.GetParameters().Should().HaveCount(1);
|
cleanupOldSessionsMethod.GetParameters().Should().HaveCount(1);
|
||||||
cleanupOldSessionsMethod.GetParameters()[0].ParameterType.Should().Be<int>();
|
cleanupOldSessionsMethod.GetParameters()[0].ParameterType.Should().Be<int>();
|
||||||
|
|
||||||
|
|||||||
28
ChatBot/.dockerignore
Normal file
28
ChatBot/.dockerignore
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# Build artifacts
|
||||||
|
bin/
|
||||||
|
obj/
|
||||||
|
out/
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# IDE and editor files
|
||||||
|
.vs/
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# Environment files (will be injected via secrets)
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs/
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Test results
|
||||||
|
TestResults/
|
||||||
|
*.trx
|
||||||
|
*.coverage
|
||||||
42
ChatBot/Dockerfile
Normal file
42
ChatBot/Dockerfile
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# Build stage
|
||||||
|
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
|
||||||
|
WORKDIR /src
|
||||||
|
|
||||||
|
# Copy project file
|
||||||
|
COPY ChatBot.csproj ./
|
||||||
|
|
||||||
|
# Restore dependencies
|
||||||
|
RUN dotnet restore
|
||||||
|
|
||||||
|
# Copy all source files
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Build the application
|
||||||
|
RUN dotnet build -c Release -o /app/build
|
||||||
|
|
||||||
|
# Publish stage
|
||||||
|
FROM build AS publish
|
||||||
|
RUN dotnet publish -c Release -o /app/publish /p:UseAppHost=false
|
||||||
|
|
||||||
|
# Runtime stage
|
||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS final
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install PostgreSQL client for healthcheck (optional)
|
||||||
|
RUN apt-get update && apt-get install -y postgresql-client && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Copy published application
|
||||||
|
COPY --from=publish /app/publish .
|
||||||
|
|
||||||
|
# Create directory for logs
|
||||||
|
RUN mkdir -p /app/logs && chmod 777 /app/logs
|
||||||
|
|
||||||
|
# Expose ports (if needed for health checks or metrics)
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
|
||||||
|
CMD dotnet ChatBot.dll --health-check || exit 1
|
||||||
|
|
||||||
|
# Run the application
|
||||||
|
ENTRYPOINT ["dotnet", "ChatBot.dll"]
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using ChatBot.Services.Telegram.Interfaces;
|
using ChatBot.Services.Telegram.Interfaces;
|
||||||
using ChatBot.Services.Telegram.Services;
|
using ChatBot.Services.Telegram.Services;
|
||||||
|
using Telegram.Bot.Types;
|
||||||
|
|
||||||
namespace ChatBot.Services.Telegram.Commands
|
namespace ChatBot.Services.Telegram.Commands
|
||||||
{
|
{
|
||||||
@@ -58,51 +59,13 @@ namespace ChatBot.Services.Telegram.Commands
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Получаем информацию о боте
|
|
||||||
var botInfo = await _botInfoService.GetBotInfoAsync(cancellationToken);
|
var botInfo = await _botInfoService.GetBotInfoAsync(cancellationToken);
|
||||||
|
|
||||||
// Проверяем, нужно ли отвечать на реплай
|
if (!ShouldProcessMessage(messageText, chatId, replyInfo, botInfo))
|
||||||
if (replyInfo != null)
|
|
||||||
{
|
{
|
||||||
_logger.LogInformation(
|
return string.Empty;
|
||||||
"Reply detected: ReplyToUserId={ReplyToUserId}, BotId={BotId}, ChatId={ChatId}",
|
|
||||||
replyInfo.UserId,
|
|
||||||
botInfo?.Id,
|
|
||||||
chatId
|
|
||||||
);
|
|
||||||
|
|
||||||
if (botInfo != null && replyInfo.UserId != botInfo.Id)
|
|
||||||
{
|
|
||||||
_logger.LogInformation(
|
|
||||||
"Ignoring reply to user {ReplyToUserId} (not bot {BotId}) in chat {ChatId}",
|
|
||||||
replyInfo.UserId,
|
|
||||||
botInfo.Id,
|
|
||||||
chatId
|
|
||||||
);
|
|
||||||
return string.Empty; // Не отвечаем на реплаи другим пользователям
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Если это не реплай, проверяем, обращаются ли к боту или нет упоминаний других пользователей
|
|
||||||
if (botInfo != null)
|
|
||||||
{
|
|
||||||
bool hasBotMention = messageText.Contains($"@{botInfo.Username}");
|
|
||||||
bool hasOtherMentions = messageText.Contains('@') && !hasBotMention;
|
|
||||||
|
|
||||||
if (!hasBotMention && hasOtherMentions)
|
|
||||||
{
|
|
||||||
_logger.LogInformation(
|
|
||||||
"Ignoring message with other user mentions in chat {ChatId}: {MessageText}",
|
|
||||||
chatId,
|
|
||||||
messageText
|
|
||||||
);
|
|
||||||
return string.Empty; // Не отвечаем на сообщения с упоминанием других пользователей
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Создаем контекст команды
|
|
||||||
var context = TelegramCommandContext.Create(
|
var context = TelegramCommandContext.Create(
|
||||||
chatId,
|
chatId,
|
||||||
username,
|
username,
|
||||||
@@ -112,27 +75,11 @@ namespace ChatBot.Services.Telegram.Commands
|
|||||||
replyInfo
|
replyInfo
|
||||||
);
|
);
|
||||||
|
|
||||||
// Ищем команду, которая может обработать сообщение
|
return await ExecuteCommandOrProcessMessageAsync(
|
||||||
var command = _commandRegistry.FindCommandForMessage(messageText);
|
context,
|
||||||
if (command != null)
|
messageText,
|
||||||
{
|
|
||||||
_logger.LogDebug(
|
|
||||||
"Executing command {CommandName} for chat {ChatId}",
|
|
||||||
command.CommandName,
|
|
||||||
chatId
|
|
||||||
);
|
|
||||||
return await command.ExecuteAsync(context, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Если команда не найдена, обрабатываем как обычное сообщение
|
|
||||||
_logger.LogDebug(
|
|
||||||
"No command found, processing as regular message for chat {ChatId}",
|
|
||||||
chatId
|
|
||||||
);
|
|
||||||
return await _chatService.ProcessMessageAsync(
|
|
||||||
chatId,
|
chatId,
|
||||||
username,
|
username,
|
||||||
messageText,
|
|
||||||
chatType,
|
chatType,
|
||||||
chatTitle,
|
chatTitle,
|
||||||
cancellationToken
|
cancellationToken
|
||||||
@@ -159,5 +106,101 @@ namespace ChatBot.Services.Telegram.Commands
|
|||||||
return "Произошла непредвиденная ошибка. Попробуйте еще раз.";
|
return "Произошла непредвиденная ошибка. Попробуйте еще раз.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool ShouldProcessMessage(
|
||||||
|
string messageText,
|
||||||
|
long chatId,
|
||||||
|
ReplyInfo? replyInfo,
|
||||||
|
User? botInfo
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (replyInfo != null)
|
||||||
|
{
|
||||||
|
return ShouldProcessReply(replyInfo, botInfo, chatId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ShouldProcessNonReplyMessage(messageText, botInfo, chatId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ShouldProcessReply(ReplyInfo replyInfo, User? botInfo, long chatId)
|
||||||
|
{
|
||||||
|
_logger.LogInformation(
|
||||||
|
"Reply detected: ReplyToUserId={ReplyToUserId}, BotId={BotId}, ChatId={ChatId}",
|
||||||
|
replyInfo.UserId,
|
||||||
|
botInfo?.Id,
|
||||||
|
chatId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (botInfo != null && replyInfo.UserId != botInfo.Id)
|
||||||
|
{
|
||||||
|
_logger.LogInformation(
|
||||||
|
"Ignoring reply to user {ReplyToUserId} (not bot {BotId}) in chat {ChatId}",
|
||||||
|
replyInfo.UserId,
|
||||||
|
botInfo.Id,
|
||||||
|
chatId
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ShouldProcessNonReplyMessage(string messageText, User? botInfo, long chatId)
|
||||||
|
{
|
||||||
|
if (botInfo == null)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasBotMention = messageText.Contains($"@{botInfo.Username}");
|
||||||
|
bool hasOtherMentions = messageText.Contains('@') && !hasBotMention;
|
||||||
|
|
||||||
|
if (!hasBotMention && hasOtherMentions)
|
||||||
|
{
|
||||||
|
_logger.LogInformation(
|
||||||
|
"Ignoring message with other user mentions in chat {ChatId}: {MessageText}",
|
||||||
|
chatId,
|
||||||
|
messageText
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> ExecuteCommandOrProcessMessageAsync(
|
||||||
|
TelegramCommandContext context,
|
||||||
|
string messageText,
|
||||||
|
long chatId,
|
||||||
|
string username,
|
||||||
|
string chatType,
|
||||||
|
string chatTitle,
|
||||||
|
CancellationToken cancellationToken
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var command = _commandRegistry.FindCommandForMessage(messageText);
|
||||||
|
if (command != null)
|
||||||
|
{
|
||||||
|
_logger.LogDebug(
|
||||||
|
"Executing command {CommandName} for chat {ChatId}",
|
||||||
|
command.CommandName,
|
||||||
|
chatId
|
||||||
|
);
|
||||||
|
return await command.ExecuteAsync(context, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogDebug(
|
||||||
|
"No command found, processing as regular message for chat {ChatId}",
|
||||||
|
chatId
|
||||||
|
);
|
||||||
|
return await _chatService.ProcessMessageAsync(
|
||||||
|
chatId,
|
||||||
|
username,
|
||||||
|
messageText,
|
||||||
|
chatType,
|
||||||
|
chatTitle,
|
||||||
|
cancellationToken
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
189
DOCKER_README.md
Normal file
189
DOCKER_README.md
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
# Docker Deployment Guide
|
||||||
|
|
||||||
|
## Структура файлов
|
||||||
|
|
||||||
|
```
|
||||||
|
ChatBot/
|
||||||
|
├── ChatBot/
|
||||||
|
│ ├── Dockerfile # Dockerfile для сборки приложения
|
||||||
|
│ └── .dockerignore # Исключения для Docker build
|
||||||
|
├── docker-compose.yml # Композиция для локального запуска
|
||||||
|
├── .env.example # Пример переменных окружения
|
||||||
|
└── .gitea/workflows/
|
||||||
|
└── deploy.yml # CI/CD pipeline для автоматического развертывания
|
||||||
|
```
|
||||||
|
|
||||||
|
## Локальный запуск с Docker Compose
|
||||||
|
|
||||||
|
### 1. Подготовка
|
||||||
|
|
||||||
|
Скопируйте `.env.example` в `.env` и заполните необходимые значения:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
Отредактируйте `.env`:
|
||||||
|
```env
|
||||||
|
TELEGRAM_BOT_TOKEN=your_actual_bot_token
|
||||||
|
OLLAMA_URL=https://your-ollama-instance/
|
||||||
|
OLLAMA_DEFAULT_MODEL=gemma3:4b
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Запуск
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Запуск всех сервисов (PostgreSQL + ChatBot)
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
# Просмотр логов
|
||||||
|
docker-compose logs -f chatbot
|
||||||
|
|
||||||
|
# Остановка
|
||||||
|
docker-compose down
|
||||||
|
|
||||||
|
# Остановка с удалением volumes
|
||||||
|
docker-compose down -v
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Проверка статуса
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Статус контейнеров
|
||||||
|
docker-compose ps
|
||||||
|
|
||||||
|
# Логи приложения
|
||||||
|
docker-compose logs chatbot
|
||||||
|
|
||||||
|
# Логи базы данных
|
||||||
|
docker-compose logs postgres
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ручная сборка и запуск
|
||||||
|
|
||||||
|
### Сборка образа
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ChatBot
|
||||||
|
docker build -t chatbot:latest .
|
||||||
|
```
|
||||||
|
|
||||||
|
### Запуск контейнера
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -d \
|
||||||
|
--name chatbot-app \
|
||||||
|
-e DB_HOST=your_db_host \
|
||||||
|
-e DB_PORT=5432 \
|
||||||
|
-e DB_NAME=chatbot \
|
||||||
|
-e DB_USER=postgres \
|
||||||
|
-e DB_PASSWORD=your_password \
|
||||||
|
-e TELEGRAM_BOT_TOKEN=your_token \
|
||||||
|
-e OLLAMA_URL=https://your-ollama/ \
|
||||||
|
-e OLLAMA_DEFAULT_MODEL=gemma3:4b \
|
||||||
|
chatbot:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
## CI/CD Pipeline
|
||||||
|
|
||||||
|
### Настройка секретов в Gitea
|
||||||
|
|
||||||
|
Для работы CI/CD pipeline необходимо настроить следующие секреты в Gitea (Settings → Secrets):
|
||||||
|
|
||||||
|
| Секрет | Описание | Пример |
|
||||||
|
|--------|----------|--------|
|
||||||
|
| `CHATBOT_DB_HOST` | Хост базы данных | `postgres` или `your-db-host` |
|
||||||
|
| `CHATBOT_DB_PORT` | Порт базы данных | `5432` |
|
||||||
|
| `CHATBOT_DB_NAME` | Имя базы данных | `chatbot` |
|
||||||
|
| `CHATBOT_DB_USER` | Пользователь БД | `postgres` |
|
||||||
|
| `CHATBOT_DB_PASSWORD` | Пароль БД | `your_secure_password` |
|
||||||
|
| `CHATBOT_TELEGRAM_BOT_TOKEN` | Токен Telegram бота | `123456:ABC-DEF...` |
|
||||||
|
| `CHATBOT_OLLAMA_URL` | URL Ollama API | `https://ai.api.home/` |
|
||||||
|
| `CHATBOT_OLLAMA_DEFAULT_MODEL` | Модель по умолчанию | `gemma3:4b` |
|
||||||
|
|
||||||
|
### Workflow триггеры
|
||||||
|
|
||||||
|
Pipeline запускается автоматически при:
|
||||||
|
- Push в ветки `master` или `develop`
|
||||||
|
- Создании Pull Request в ветку `master`
|
||||||
|
|
||||||
|
### Этапы pipeline
|
||||||
|
|
||||||
|
1. **Build Docker Image** - сборка Docker образа
|
||||||
|
2. **Stop existing container** - остановка существующего тестового контейнера
|
||||||
|
3. **Run test container** - запуск нового контейнера с секретами
|
||||||
|
4. **Health check** - проверка работоспособности приложения
|
||||||
|
5. **Cleanup** - очистка старых образов
|
||||||
|
|
||||||
|
### Мониторинг deployment
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Просмотр логов контейнера
|
||||||
|
docker logs chatbot-test -f
|
||||||
|
|
||||||
|
# Проверка статуса
|
||||||
|
docker ps | grep chatbot-test
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
docker exec chatbot-test dotnet ChatBot.dll --health-check
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Контейнер не запускается
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Проверьте логи
|
||||||
|
docker logs chatbot-app
|
||||||
|
|
||||||
|
# Проверьте переменные окружения
|
||||||
|
docker inspect chatbot-app | grep -A 20 Env
|
||||||
|
```
|
||||||
|
|
||||||
|
### Проблемы с подключением к БД
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Проверьте доступность PostgreSQL
|
||||||
|
docker exec chatbot-postgres pg_isready
|
||||||
|
|
||||||
|
# Проверьте сетевое подключение
|
||||||
|
docker network inspect chatbot-network
|
||||||
|
```
|
||||||
|
|
||||||
|
### Очистка
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Удалить все остановленные контейнеры
|
||||||
|
docker container prune
|
||||||
|
|
||||||
|
# Удалить неиспользуемые образы
|
||||||
|
docker image prune -a
|
||||||
|
|
||||||
|
# Удалить неиспользуемые volumes
|
||||||
|
docker volume prune
|
||||||
|
```
|
||||||
|
|
||||||
|
## Production Deployment
|
||||||
|
|
||||||
|
Для production рекомендуется:
|
||||||
|
|
||||||
|
1. Использовать Docker registry (например, GitHub Container Registry)
|
||||||
|
2. Настроить мониторинг (Prometheus + Grafana)
|
||||||
|
3. Использовать orchestration (Docker Swarm или Kubernetes)
|
||||||
|
4. Настроить backup базы данных
|
||||||
|
5. Использовать secrets management (Docker Secrets, Vault)
|
||||||
|
6. Настроить reverse proxy (Nginx, Traefik)
|
||||||
|
|
||||||
|
### Пример с Docker Swarm
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Инициализация swarm
|
||||||
|
docker swarm init
|
||||||
|
|
||||||
|
# Создание секретов
|
||||||
|
echo "your_token" | docker secret create telegram_token -
|
||||||
|
echo "your_password" | docker secret create db_password -
|
||||||
|
|
||||||
|
# Deploy stack
|
||||||
|
docker stack deploy -c docker-compose.yml chatbot
|
||||||
|
```
|
||||||
55
SECRETS_SETUP.md
Normal file
55
SECRETS_SETUP.md
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
# Настройка секретов для CI/CD
|
||||||
|
|
||||||
|
## Gitea Secrets
|
||||||
|
|
||||||
|
Перейдите в настройки репозитория: **Settings → Secrets → Actions**
|
||||||
|
|
||||||
|
### Обязательные секреты с префиксом CHATBOT_
|
||||||
|
|
||||||
|
| Имя секрета | Значение | Описание |
|
||||||
|
|-------------|----------|----------|
|
||||||
|
| `CHATBOT_DB_HOST` | `postgres` | Хост PostgreSQL |
|
||||||
|
| `CHATBOT_DB_PORT` | `5432` | Порт PostgreSQL |
|
||||||
|
| `CHATBOT_DB_NAME` | `chatbot` | Имя базы данных |
|
||||||
|
| `CHATBOT_DB_USER` | `postgres` | Пользователь БД |
|
||||||
|
| `CHATBOT_DB_PASSWORD` | `your_secure_password` | Пароль БД |
|
||||||
|
| `CHATBOT_TELEGRAM_BOT_TOKEN` | `123456:ABC-DEF...` | Токен Telegram бота |
|
||||||
|
| `CHATBOT_OLLAMA_URL` | `https://ai.api.home/` | URL Ollama API |
|
||||||
|
| `CHATBOT_OLLAMA_DEFAULT_MODEL` | `gemma3:4b` | Модель по умолчанию |
|
||||||
|
|
||||||
|
## Как добавить секрет в Gitea
|
||||||
|
|
||||||
|
1. Откройте репозиторий в Gitea
|
||||||
|
2. Перейдите в **Settings** (⚙️)
|
||||||
|
3. Выберите **Secrets** → **Actions**
|
||||||
|
4. Нажмите **Add Secret**
|
||||||
|
5. Введите:
|
||||||
|
- **Name**: имя секрета (например, `CHATBOT_DB_HOST`)
|
||||||
|
- **Value**: значение секрета
|
||||||
|
6. Нажмите **Add Secret**
|
||||||
|
|
||||||
|
## Проверка секретов
|
||||||
|
|
||||||
|
После добавления всех секретов, workflow `.gitea/workflows/deploy.yml` будет использовать их автоматически при каждом push в ветки `master` или `develop`.
|
||||||
|
|
||||||
|
## Локальная разработка
|
||||||
|
|
||||||
|
Для локальной разработки используйте файл `.env`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Скопируйте пример
|
||||||
|
cp .env.example .env
|
||||||
|
|
||||||
|
# Отредактируйте значения
|
||||||
|
nano .env # или любой другой редактор
|
||||||
|
```
|
||||||
|
|
||||||
|
**Важно**: Файл `.env` добавлен в `.gitignore` и не должен попадать в репозиторий!
|
||||||
|
|
||||||
|
## Безопасность
|
||||||
|
|
||||||
|
- ✅ Никогда не коммитьте файл `.env` с реальными данными
|
||||||
|
- ✅ Используйте сложные пароли для production
|
||||||
|
- ✅ Регулярно ротируйте токены и пароли
|
||||||
|
- ✅ Ограничьте доступ к секретам в Gitea
|
||||||
|
- ✅ Используйте разные токены для dev/test/prod окружений
|
||||||
58
docker-compose.yml
Normal file
58
docker-compose.yml
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
container_name: chatbot-postgres
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: ${DB_NAME:-chatbot}
|
||||||
|
POSTGRES_USER: ${DB_USER:-postgres}
|
||||||
|
POSTGRES_PASSWORD: ${DB_PASSWORD:-postgres}
|
||||||
|
ports:
|
||||||
|
- "${DB_PORT:-5432}:5432"
|
||||||
|
volumes:
|
||||||
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-postgres}"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
networks:
|
||||||
|
- chatbot-network
|
||||||
|
|
||||||
|
chatbot:
|
||||||
|
build:
|
||||||
|
context: ./ChatBot
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: chatbot-app
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
environment:
|
||||||
|
- DB_HOST=postgres
|
||||||
|
- DB_PORT=5432
|
||||||
|
- DB_NAME=${DB_NAME:-chatbot}
|
||||||
|
- DB_USER=${DB_USER:-postgres}
|
||||||
|
- DB_PASSWORD=${DB_PASSWORD:-postgres}
|
||||||
|
- TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}
|
||||||
|
- OLLAMA_URL=${OLLAMA_URL}
|
||||||
|
- OLLAMA_DEFAULT_MODEL=${OLLAMA_DEFAULT_MODEL:-gemma3:4b}
|
||||||
|
volumes:
|
||||||
|
- ./ChatBot/logs:/app/logs
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- chatbot-network
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "dotnet", "ChatBot.dll", "--health-check"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres_data:
|
||||||
|
driver: local
|
||||||
|
|
||||||
|
networks:
|
||||||
|
chatbot-network:
|
||||||
|
driver: bridge
|
||||||
Reference in New Issue
Block a user