-
Notifications
You must be signed in to change notification settings - Fork 0
Chore/image moderation #42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,4 +1,5 @@ | ||||||||||||||||||||||||||
| using Azure; | ||||||||||||||||||||||||||
| using Azure.AI.ContentSafety; | ||||||||||||||||||||||||||
| using Azure.Storage.Blobs; | ||||||||||||||||||||||||||
| using Azure.Storage.Blobs.Models; | ||||||||||||||||||||||||||
| using Evently.Server.Common.Domains.Interfaces; | ||||||||||||||||||||||||||
|
|
@@ -11,6 +12,9 @@ namespace Evently.Server.Features.Files.Services; | |||||||||||||||||||||||||
| public sealed class ObjectStorageService(IOptions<Settings> settings, ILogger<ObjectStorageService> logger) : IObjectStorageService { | ||||||||||||||||||||||||||
| private readonly BlobServiceClient _blobServiceClient = | ||||||||||||||||||||||||||
| new(settings.Value.StorageAccount.AzureStorageConnectionString); | ||||||||||||||||||||||||||
| private readonly ContentSafetyClient _contentSafetyClient = new( | ||||||||||||||||||||||||||
| endpoint: new Uri(settings.Value.AzureAiFoundry.ContentSafetyEndpoint), | ||||||||||||||||||||||||||
| credential: new AzureKeyCredential(settings.Value.AzureAiFoundry.ContentSafetyKey)); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| public async Task<Uri> UploadFile(string containerName, string fileName, BinaryData binaryData, | ||||||||||||||||||||||||||
| string mimeType = "application/octet-stream") { | ||||||||||||||||||||||||||
|
|
@@ -65,4 +69,23 @@ public async Task<BinaryData> GetFile(string containerName, string fileName) { | |||||||||||||||||||||||||
| BinaryData data = BinaryData.FromBytes(bytes); | ||||||||||||||||||||||||||
| return data; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| public async Task<bool> PassesContentModeration(BinaryData binaryData) { | ||||||||||||||||||||||||||
| ContentSafetyImageData image = new(binaryData); | ||||||||||||||||||||||||||
| AnalyzeImageOptions request = new(image); | ||||||||||||||||||||||||||
| Response<AnalyzeImageResult> response; | ||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||
| response = await _contentSafetyClient.AnalyzeImageAsync(request); | ||||||||||||||||||||||||||
| } catch (RequestFailedException ex) { | ||||||||||||||||||||||||||
| Console.WriteLine("Analyze image failed.\nStatus code: {0}, Error code: {1}, Error message: {2}", ex.Status, ex.ErrorCode, ex.Message); | ||||||||||||||||||||||||||
| throw; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| AnalyzeImageResult result = response.Value; | ||||||||||||||||||||||||||
| int? score = result.CategoriesAnalysis | ||||||||||||||||||||||||||
| .Select(v => v.Severity) | ||||||||||||||||||||||||||
| .Aggregate((a, b) => a + b) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
| .Aggregate((a, b) => a + b) | |
| .Sum() |
Copilot
AI
Oct 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The null-conditional operator ?? is applied to the wrong expression. If result.CategoriesAnalysis is empty, Aggregate will throw an exception before the null check. Consider using DefaultIfEmpty() or checking if the collection is empty first.
| .Aggregate((a, b) => a + b) | |
| ?? 0; | |
| .DefaultIfEmpty(0) | |
| .Aggregate((a, b) => a + b); |
Copilot
AI
Oct 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic for determining content safety is incorrect. When CategoriesAnalysis.Count is 0, the method returns false (content is unsafe), but when there are no categories to analyze, the content should be considered safe. Also, the aggregate will throw an exception if the collection is empty.
| int? score = result.CategoriesAnalysis | |
| .Select(v => v.Severity) | |
| .Aggregate((a, b) => a + b) | |
| ?? 0; | |
| return result.CategoriesAnalysis.Count != 0 && score == 0; | |
| if (result.CategoriesAnalysis.Count == 0) | |
| { | |
| // No categories to analyze, consider content safe | |
| return true; | |
| } | |
| int score = result.CategoriesAnalysis.Sum(v => v.Severity); | |
| return score == 0; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -115,6 +115,10 @@ public async Task<ActionResult<Gathering>> DeleteGathering(long gatheringId) { | |
| private async Task<Uri> UploadCoverImage(long gatheringId, IFormFile coverImg) { | ||
| string fileName = $"gatherings/{gatheringId}/cover-image{Path.GetExtension(coverImg.FileName)}"; | ||
| BinaryData binaryData = await coverImg.ToBinaryData(); | ||
| bool isContentSafe = await objectStorageService.PassesContentModeration(binaryData); | ||
| if (!isContentSafe) { | ||
| return new Uri(string.Empty, UriKind.RelativeOrAbsolute); | ||
|
||
| } | ||
| return await objectStorageService.UploadFile(_containerName, | ||
| fileName, | ||
| binaryData, | ||
|
|
||
This file was deleted.
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -5,5 +5,26 @@ | |||||||||
| "Microsoft.AspNetCore": "Warning" | ||||||||||
| } | ||||||||||
| }, | ||||||||||
| "AllowedHosts": "*" | ||||||||||
| "AllowedHosts": "*", | ||||||||||
| "ConnectionStrings": { | ||||||||||
| "WebApiDatabase": "Data Source=(localdb)\\MSSQLLocalDB;Database=evently;Integrated Security=True;Persist Security Info=False;Pooling=False;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Application Name=\"SQL Server Management Studio\";Command Timeout=0;" | ||||||||||
| }, | ||||||||||
| "StorageAccount": { | ||||||||||
| "AccountName": "evently-dev-images", | ||||||||||
| "AzureStorageConnectionString": "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;QueueEndpoint=http://127.0.0.1:10001/devstoreaccount1;TableEndpoint=http://127.0.0.1:10002/devstoreaccount1;" | ||||||||||
| }, | ||||||||||
| "AzureAIFoundry": { | ||||||||||
| "ContentSafetyKey": "<ai-foundary-content-safety-key>", | ||||||||||
| "ContentSafetyEndpoint": "<ai-foundary-content-safety-api>" | ||||||||||
|
eugbyte marked this conversation as resolved.
Outdated
|
||||||||||
| "ContentSafetyKey": "<ai-foundary-content-safety-key>", | |
| "ContentSafetyEndpoint": "<ai-foundary-content-safety-api>" | |
| "ContentSafetyKey": "<ai-foundry-content-safety-key>", | |
| "ContentSafetyEndpoint": "<ai-foundry-content-safety-api>" |
Uh oh!
There was an error while loading. Please reload this page.