diff --git a/.gitea/workflows/deploy-prod.yml b/.gitea/workflows/deploy-prod.yml
index fcfaa1c..f446687 100644
--- a/.gitea/workflows/deploy-prod.yml
+++ b/.gitea/workflows/deploy-prod.yml
@@ -307,36 +307,23 @@ jobs:
path: deployment-report.txt
retention-days: 90
- - name: Notify Slack (if configured)
+ - name: Notify Telegram
if: always()
run: |
- if [ -n "${{ secrets.SLACK_WEBHOOK }}" ]; then
- STATUS=${{ job.status }}
- if [ "$STATUS" = "success" ]; then
- EMOJI="✅"
- COLOR="good"
- else
- EMOJI="❌"
- COLOR="danger"
- fi
-
- curl -X POST ${{ secrets.SLACK_WEBHOOK }} \
- -H 'Content-type: application/json' \
- -d "{
- \"attachments\": [{
- \"color\": \"$COLOR\",
- \"title\": \"$EMOJI Quant Engine v9 Deployment\",
- \"text\": \"Run #${{ github.run_number }}\",
- \"fields\": [
- {\"title\": \"Status\", \"value\": \"$STATUS\", \"short\": true},
- {\"title\": \"Service\", \"value\": \"${{ env.SERVICE_NAME }}\", \"short\": true},
- {\"title\": \"URL\", \"value\": \"http://178.104.200.7/quant/\", \"short\": false}
- ],
- \"ts\": $(date +%s)
- }]
- }"
+ STATUS=${{ job.status }}
+ if [ "$STATUS" = "success" ]; then
+ EMOJI="✅"
+ TEXT="*Quant Engine v9 Deployment SUCCESS* $EMOJI%0A• Run: #${{ github.run_number }}%0A• Commit: ${{ github.sha }}%0A• Service: ${{ env.SERVICE_NAME }}%0A• URL: http://178.104.200.7/quant/"
+ else
+ EMOJI="❌"
+ TEXT="*Quant Engine v9 Deployment FAILED* $EMOJI%0A• Run: #${{ github.run_number }}%0A• Commit: ${{ github.sha }}%0A• Service: ${{ env.SERVICE_NAME }}%0A• URL: http://178.104.200.7/quant/"
fi
+ curl -s -X POST "https://api.telegram.org/bot8734507814:AAFyacLMai8GB4K-hQ_Nd3t3D01A-h1ZdV0/sendMessage" \
+ -d "chat_id=-5460205872" \
+ -d "text=$TEXT" \
+ -d "parse_mode=Markdown"
+
post-deployment:
name: Post-Deployment Checks
needs: deploy-to-prod
diff --git a/src/dotnet/QuantEngine.Web/Infrastructure/TelegramSink.cs b/src/dotnet/QuantEngine.Web/Infrastructure/TelegramSink.cs
new file mode 100644
index 0000000..f14c350
--- /dev/null
+++ b/src/dotnet/QuantEngine.Web/Infrastructure/TelegramSink.cs
@@ -0,0 +1,56 @@
+using Serilog.Core;
+using Serilog.Events;
+using System.Net.Http;
+using System.Text;
+using System.Text.Json;
+
+namespace QuantEngine.Web.Infrastructure
+{
+ public class TelegramSink : ILogEventSink
+ {
+ private readonly string _botToken;
+ private readonly string _chatId;
+ private static readonly HttpClient HttpClient = new HttpClient();
+
+ public TelegramSink(string botToken, string chatId)
+ {
+ _botToken = botToken;
+ _chatId = chatId;
+ }
+
+ public void Emit(LogEvent logEvent)
+ {
+ if (logEvent.Level >= LogEventLevel.Error)
+ {
+ var sb = new StringBuilder();
+ sb.AppendLine("🚨 **QuantEngine Error Log**");
+ sb.AppendLine($"• Timestamp: {logEvent.Timestamp:yyyy-MM-dd HH:mm:ss}");
+ sb.AppendLine($"• Level: {logEvent.Level}");
+ sb.AppendLine($"• Message: {logEvent.RenderMessage()}");
+
+ if (logEvent.Exception != null)
+ {
+ sb.AppendLine($"• Exception: {logEvent.Exception.Message}");
+ }
+
+ try
+ {
+ var payload = new
+ {
+ chat_id = _chatId,
+ text = sb.ToString(),
+ parse_mode = "Markdown"
+ };
+ var content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
+
+ // Fire and forget to avoid blocking main execution threads
+ _ = HttpClient.PostAsync($"https://api.telegram.org/bot{_botToken}/sendMessage", content);
+ }
+ catch
+ {
+ // Fallback to avoid crash loops inside logger
+ }
+ }
+ }
+ }
+}
diff --git a/src/dotnet/QuantEngine.Web/Program.cs b/src/dotnet/QuantEngine.Web/Program.cs
index 8397e4c..9ce804a 100644
--- a/src/dotnet/QuantEngine.Web/Program.cs
+++ b/src/dotnet/QuantEngine.Web/Program.cs
@@ -5,8 +5,18 @@ using QuantEngine.Core.Interfaces;
using QuantEngine.Application.Services;
using System.Text.Json;
using MudBlazor.Services;
+using Serilog;
+using QuantEngine.Web.Infrastructure;
+
+// Serilog Configuration with Telegram Sink
+Log.Logger = new LoggerConfiguration()
+ .MinimumLevel.Information()
+ .WriteTo.Console()
+ .WriteTo.Sink(new TelegramSink("8734507814:AAFyacLMai8GB4K-hQ_Nd3t3D01A-h1ZdV0", "-5460205872"))
+ .CreateLogger();
var builder = WebApplication.CreateBuilder(args);
+builder.Host.UseSerilog();
// Add services to the container.
builder.Services.AddRazorComponents()
diff --git a/src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj b/src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj
index 54779ca..8a1eb6c 100644
--- a/src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj
+++ b/src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj
@@ -8,6 +8,7 @@
+