This commit is contained in:
Dmitri Shimanski
2025-07-30 01:37:10 +03:00
parent 2d36a60f5d
commit 8c212321e0
8 changed files with 74 additions and 17 deletions

View File

@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.2"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1"/>
<PackageReference Include="NUnit" Version="4.2.2"/>
<PackageReference Include="NUnit.Analyzers" Version="4.3.0"/>
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0"/>
</ItemGroup>
<ItemGroup>
<Using Include="NUnit.Framework"/>
</ItemGroup>
</Project>

View File

@@ -8,6 +8,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
README.md = README.md
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aoyo.Taiga.Tests", "Aoyo.Taiga.Tests\Aoyo.Taiga.Tests.csproj", "{D36CA783-EA93-4B54-B3A4-B911B00D0887}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -18,5 +20,9 @@ Global
{CAFC85B2-1606-44DF-A3CD-9DEAA07ADB78}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CAFC85B2-1606-44DF-A3CD-9DEAA07ADB78}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CAFC85B2-1606-44DF-A3CD-9DEAA07ADB78}.Release|Any CPU.Build.0 = Release|Any CPU
{D36CA783-EA93-4B54-B3A4-B911B00D0887}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D36CA783-EA93-4B54-B3A4-B911B00D0887}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D36CA783-EA93-4B54-B3A4-B911B00D0887}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D36CA783-EA93-4B54-B3A4-B911B00D0887}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@@ -1,11 +1,14 @@
using System.Diagnostics.CodeAnalysis;
using System.Security.Cryptography;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
using Aoyo.Taiga.WebHookTypes;
using Discord;
using Discord.WebSocket;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
namespace Aoyo.Taiga.Controllers;
@@ -40,17 +43,20 @@ public class TaigaWebHook : Controller
{
try
{
var signature = Request.Headers["X-TAIGA-WEBHOOK-SIGNATURE"].First()!;
var signature = Request.Headers["X-TAIGA-WEBHOOK-SIGNATURE"].First();
if (signature == null) return BadRequest("Provide signature first");
using var reader = new StreamReader(Request.Body);
var data = await reader.ReadToEndAsync();
_logger.LogInformation(data);
var hash = VerifySignature(_key, data);
if (_config.GetValue<string>("ASPNETCORE_ENVIRONMENT") == "Production")
if (!VerifySignature(_key, data, signature))
{
return BadRequest("Invalid signature");
}
if (!string.Equals(signature, hash, StringComparison.OrdinalIgnoreCase))
{
return BadRequest($"Invalid signature {hash}");
}
var webhookType = DetermineWebhookType(data);
@@ -66,6 +72,8 @@ public class TaigaWebHook : Controller
case "task":
await HandleTaskWebhook(data);
break;
case "test":
return Ok("test success!");
default:
_logger.LogWarning("Unsupported type of webhook: {WebhookType}", webhookType);
return BadRequest($"Unsupported type of webhook: {webhookType}");
@@ -80,13 +88,12 @@ public class TaigaWebHook : Controller
}
}
private bool VerifySignature(string key, string data, string signature)
private string VerifySignature(string key, string data)
{
using HMACSHA1 hmac = new HMACSHA1(Encoding.UTF8.GetBytes(key));
var hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(data));
var computedHash = Convert.ToHexString(hashBytes).ToLower();
return computedHash.Length == signature.Length;
using var hmac = new HMACSHA1(Encoding.UTF8.GetBytes(key));
var hashBytes = hmac.ComputeHash(Encoding.ASCII.GetBytes(data));
var computedSignature = BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
return computedSignature;
}
private async Task HandleUserStoryWebhook(string json)
@@ -386,13 +393,13 @@ public class TaigaWebHook : Controller
if (_logChannel == null)
{
_logger.LogError("Не удалось получить Discord канал с ID: {ChannelId}", _id);
_logger.LogError("Can`t get channel with ID: {ChannelId}", _id);
return;
}
await _logChannel.SendMessageAsync(embed:
new EmbedBuilder()
.WithAuthor(payload.By.Username, payload.By.Photo)
.WithAuthor(payload.By.Username, payload.By.GravatarId != null ? "https://gravatar.com/avatar/" + payload.By.GravatarId : payload.By.Photo)
.WithTitle($"{payload.Action} {payload.Type.ToLower()}")
.WithDescription(payload.Description)
.WithColor(payload.Color)

View File

@@ -22,7 +22,7 @@ WORKDIR /app
COPY --from=publish /app/publish .
RUN apt-get update && \
apt-get install -y curl && \
rm -rf /var/lib/apt/lists/* \
rm -rf /var/lib/apt/lists/*
HEALTHCHECK --interval=30s --timeout=3s --retries=3 CMD curl -f http://localhost:8080/aoyo/health || exit 1

View File

@@ -9,6 +9,11 @@ public class Program
WebHost.
CreateDefaultBuilder(args).
UseStartup<Startup>().
ConfigureAppConfiguration(k =>
k.
AddJsonFile("appsettings.json").
AddJsonFile("appsettings.Development.json").
AddEnvironmentVariables()).
UseKestrel(
l =>
l.ListenAnyIP(8080)

View File

@@ -18,4 +18,6 @@ public class TaigaUser
[JsonPropertyName("photo")]
public string Photo { get; set; }
[JsonPropertyName("gravatar_id")]
public string? GravatarId { get; set; }
}

View File

@@ -4,5 +4,12 @@
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"Discord": {
"Token": "as",
"Id": 0
},
"Taiga": {
"Key": "123"
}
}

7
compose.yaml Normal file
View File

@@ -0,0 +1,7 @@
services:
aoyo.taiga:
image: aoyo.taiga
build:
context: .
dockerfile: Aoyo.Taiga/Dockerfile