From eebd46176df392801367de70f262f8c08940fca6 Mon Sep 17 00:00:00 2001 From: Paul Schneider Date: Fri, 18 Sep 2026 11:13:44 +0100 Subject: [PATCH] -Config.Temp --- src/Yavsc.Blogs.Tests/BlogApiTests.cs | 7 +- .../Fixtures/BlogsWebServerFixture.cs | 2 +- src/Yavsc.Org/Services/BlogSpotService.cs | 321 ------------------ src/Yavsc.Org/Startup.cs | 5 +- .../ViewComponents/BillViewComponent.cs | 10 +- src/Yavsc.Server/Config.cs | 49 ++- 6 files changed, 38 insertions(+), 356 deletions(-) delete mode 100644 src/Yavsc.Org/Services/BlogSpotService.cs diff --git a/src/Yavsc.Blogs.Tests/BlogApiTests.cs b/src/Yavsc.Blogs.Tests/BlogApiTests.cs index b2989ef50..13b5edb1b 100644 --- a/src/Yavsc.Blogs.Tests/BlogApiTests.cs +++ b/src/Yavsc.Blogs.Tests/BlogApiTests.cs @@ -364,6 +364,9 @@ public sealed class BlogApiTests : IClassFixture var created = (await postResponse.Content.ReadFromJsonAsync( TestContext.Current.CancellationToken))!; + var attachmentCount = CountAttachmentsForPost(created.Id); + Assert.True(attachmentCount == 0); + var update = new BlogPost { Id = created.Id, @@ -402,8 +405,8 @@ public sealed class BlogApiTests : IClassFixture using var detailsDoc = JsonDocument.Parse( await detailsResponse.Content.ReadAsStringAsync(TestContext.Current.CancellationToken)); Assert.Equal("Mis a jour via multipart", detailsDoc.RootElement.GetProperty("title").GetString()); - - Assert.True(CountAttachmentsForPost(created.Id) >= 1); + attachmentCount = CountAttachmentsForPost(created.Id); + Assert.True(attachmentCount >= 1); } finally { diff --git a/src/Yavsc.Blogs.Tests/Fixtures/BlogsWebServerFixture.cs b/src/Yavsc.Blogs.Tests/Fixtures/BlogsWebServerFixture.cs index f464a1caa..d677e47bb 100644 --- a/src/Yavsc.Blogs.Tests/Fixtures/BlogsWebServerFixture.cs +++ b/src/Yavsc.Blogs.Tests/Fixtures/BlogsWebServerFixture.cs @@ -71,7 +71,7 @@ public sealed class BlogsWebServerFixture : WebHostFixture /// A fresh per-run temp directory, so tests can assert on the /// physical file without touching the developer's real /// user-files tree. - public string BlogFilesRoot { get; private set; } = string.Empty; + public string BlogFilesRoot { get; private set; } = "files-root"; // A single SqliteConnection held open at the static level, // mirroring how Yavsc.Org.Tests.WebServerFixture hoists its diff --git a/src/Yavsc.Org/Services/BlogSpotService.cs b/src/Yavsc.Org/Services/BlogSpotService.cs deleted file mode 100644 index aa00d4121..000000000 --- a/src/Yavsc.Org/Services/BlogSpotService.cs +++ /dev/null @@ -1,321 +0,0 @@ -using System.Diagnostics; -using System.Security.Claims; -using Microsoft.AspNetCore.Authorization; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Yavsc; -using Yavsc.Blogspot; -using Yavsc.Models; -using Yavsc.Models.Access; -using Yavsc.Models.Blog; -using Yavsc.Server.Exceptions; -using Yavsc.Server.Helpers; -using Yavsc.Services; -using Yavsc.ViewModels.Auth; - - -[Obsolete] -public class OldBlogSpotService -{ - private readonly ApplicationDbContext _context; - private readonly IAuthorizationService _authorizationService; - private readonly IFileSystemAuthManager fileSystemAuthManager; - private readonly SiteSettings siteSettings; - - public OldBlogSpotService(ApplicationDbContext context, - IAuthorizationService authorizationService, - IFileSystemAuthManager fileSystemAuthManager, - IOptions siteSettings) - { - _authorizationService = authorizationService; - _context = context; - this.fileSystemAuthManager = fileSystemAuthManager; - this.siteSettings = siteSettings.Value; - } - - public Yavsc.Models.Blog.BlogPost Create(string userId, Yavsc.Models.Blog.BlogPost post, IFormFileCollection files) - { - // Sauvegarder le post d'abord pour obtenir son ID - _context.BlogSpot.Add(post); - _context.SaveChanges(userId); - - // Traiter les fichiers attachés s'il y en a - if (files != null && files.Count > 0) - { - var user = _context.Users.FirstOrDefault(u => u.Id == userId); - if (user != null) - { - try - { - // Créer un répertoire pour les fichiers du blog - string blogFilesSubdir = $"blogs/{post.Id}"; - string destDir = Path.Combine( - siteSettings.Blog, - user.UserName, - blogFilesSubdir - ); - var di = new DirectoryInfo(destDir); - if (!di.Exists) di.Create(); - - // Traiter chaque fichier - foreach (var formFile in files) - { - var fileInfo = user.ReceiveUserFile(destDir, formFile); - if (fileInfo != null && !fileInfo.QuotaOffense) - { - // Créer une entrée UploadedFile si nécessaire - var uploadedFile = new UploadedFile - { - Path = fileInfo.FileName, - ContentType = formFile.ContentType, - Length = formFile.Length - }; - _context.UploadedFiles.Add(uploadedFile); - _context.SaveChanges(userId); - - // Lier le fichier au post - var attachment = new BlogAttachedFile - { - PostId = post.Id, - FileId = uploadedFile.Id - }; - _context.BlogAttachedFiles.Add(attachment); - } - } - _context.SaveChanges(userId); - } - catch (Exception ex) - { - // Logger l'erreur mais ne pas échouer la création du post - System.Diagnostics.Debug.WriteLine($"Erreur lors du traitement des fichiers : {ex.Message}"); - } - } - } - - return post; - } - public async Task GetPostForEdition(ClaimsPrincipal user, long blogPostId) - { - var blog = await _context.BlogSpot.Include(x => x.Author).Include(x => x.ACL).SingleAsync(m => m.Id == blogPostId); - var auth = await _authorizationService.AuthorizeAsync(user, blog, new EditPermission()); - if (!auth.Succeeded) - { - throw new AuthorizationFailureException(auth); - } - var pub = await _context.blogSpotPublications.AnyAsync(x => x.BlogpostId == blog.Id); - ScrubAclForViewer(blog, user); - - return new BlogPostEditViewModel(blog, pub); - } - - public async Task Details(ClaimsPrincipal user, long blogPostId) - { - Yavsc.Models.Blog.BlogPost blog = await _context.BlogSpot - .Include(p => p.Author) - .Include(p => p.Tags) - .Include(p => p.Comments) - .Include(p => p.ACL) - .SingleAsync(m => m.Id == blogPostId); - if (blog == null) - { - return null; - } - var auth = await _authorizationService.AuthorizeAsync(user, blog, new ReadPermission()); - if (!auth.Succeeded) - { - throw new AuthorizationFailureException(auth); - } - ScrubAclForViewer(blog, user); - foreach (var c in blog.Comments) - { - c.Author = _context.Users.First(u => u.Id == c.AuthorId); - } - return blog; - } - - public async Task Modify(ClaimsPrincipal user, BlogPostEditViewModel blogEdit) - { - var blog = _context.BlogSpot.SingleOrDefault(b => b.Id == blogEdit.Id); - Debug.Assert(blog != null); - var auth = await _authorizationService.AuthorizeAsync(user, blog, new EditPermission()); - if (!auth.Succeeded) - { - throw new AuthorizationFailureException(auth); - } - blog.Article = blogEdit.Article; - blog.Title = blogEdit.Title; - blog.Photo = blogEdit.Photo; - blog.ACL = blogEdit.ACL; - // saves the change - _context.Update(blog); - var publication = await _context.blogSpotPublications.SingleOrDefaultAsync - (p => p.BlogpostId == blogEdit.Id); - if (publication != null) - { - if (!blogEdit.Publish) - { - _context.blogSpotPublications.Remove(publication); - } - } - else - { - if (blogEdit.Publish) - { - _context.blogSpotPublications.Add( - new BlogSpotPublication - { - BlogpostId = blogEdit.Id - } - ); - } - } - _context.SaveChanges(user.GetUserId()); - } - - public async Task Modify(ClaimsPrincipal user, Yavsc.Models.Blog.BlogPost blog) - { - var existing = await _context.BlogSpot.Include(b => b.ACL).SingleOrDefaultAsync(b => b.Id == blog.Id); - if (existing == null) - { - throw new InvalidOperationException($"Blog post {blog.Id} not found."); - } - - var auth = await _authorizationService.AuthorizeAsync(user, existing, new EditPermission()); - if (!auth.Succeeded) - { - throw new AuthorizationFailureException(auth); - } - - existing.Title = blog.Title; - existing.Article = blog.Article; - existing.Photo = blog.Photo; - existing.ACL = blog.ACL; - - _context.Update(existing); - _context.SaveChanges(user.GetUserId()); - } - - public async Task> Index(ClaimsPrincipal user, string id, int skip = 0, int take = 25) - { - string? viewerId = user.Identity?.IsAuthenticated == true ? user.GetUserId() : null; - IEnumerable posts; - - if (user.Identity.IsAuthenticated) - { - string viewerIdNonNull = viewerId!; - long[] userCircles = await _context.Circle.Include(c => c.Members). - Where(c => c.Members.Any(m => m.MemberId == viewerIdNonNull)) - .Select(c => c.Id).ToArrayAsync(); - - posts = _context.BlogSpot - .Include(b => b.Author) - .Include(p => p.ACL) - .Include(p => p.Tags) - .Include(p => p.Comments) - .Where(p => p.ACL == null - || p.ACL.Count == 0 - || (p.AuthorId == viewerIdNonNull) - || (userCircles != null && - p.ACL.Any(a => userCircles.Contains(a.CircleId))) - ); - } - else - { - posts = _context.blogSpotPublications - .Include(p => p.BlogPost) - .Include(b => b.BlogPost.Author) - .Include(p => p.BlogPost.ACL) - .Include(p => p.BlogPost.Tags) - .Include(p => p.BlogPost.Comments) - .Where(p => p.BlogPost.ACL == null - || p.BlogPost.ACL.Count == 0) - .Select(p => p.BlogPost).ToArray(); - } - - var materialised = posts.ToList(); - foreach (var post in materialised.OfType()) - ScrubAclForViewer(post, user); - - var data = materialised.OrderByDescending(p => p.DateModified) - .Skip(skip) - .Take(take); - return data; - } - - public async Task Delete(ClaimsPrincipal user, long id) - { - var uid = user.GetUserId(); - Yavsc.Models.Blog.BlogPost blog = _context.BlogSpot.Single(m => m.Id == id); - - _context.BlogSpot.Remove(blog); - _context.SaveChanges(user.GetUserId()); - } - - public async Task> UserPosts( - string posterName, - string? readerId, - int pageLen = 10, - int pageNum = 0) - { - string? posterId = (await _context.Users.SingleOrDefaultAsync(u => u.UserName == posterName))?.Id ?? null; - if (posterId == null) return Array.Empty(); - var posts = _context.UserPosts(posterId, readerId).ToList(); - var viewerId = string.Equals(readerId, posterId, StringComparison.Ordinal) ? readerId : null; - foreach (var post in posts) - ScrubAclForViewer(post, viewerId); - return posts; - } - - public object? GetTitle(string title) - { - return _context.BlogSpot.Include( - b => b.Author - ).Where(x => x.Title == title).OrderByDescending( - x => x.DateCreated - ).ToList(); - } - - public async Task GetBlogPostAsync(long value) - { - return await _context.BlogSpot - .Include(b => b.Author) - .Include(b => b.ACL) - .SingleOrDefaultAsync(x => x.Id == value); - } - - private static void ScrubAclForViewer(Yavsc.Models.Blog.BlogPost post, ClaimsPrincipal? user) - { - if (!IsOwner(post, user)) - post.ACL = new List(); - } - - private static void ScrubAclForViewer(Yavsc.Models.Blog.BlogPost post, string? viewerId) - { - if (!string.Equals(post.AuthorId, viewerId, StringComparison.Ordinal) - && !string.Equals(post.Author?.Id, viewerId, StringComparison.Ordinal)) - post.ACL = new List(); - } - - private static bool IsOwner(Yavsc.Models.Blog.BlogPost post, ClaimsPrincipal? user) - { - if (user?.Identity?.IsAuthenticated != true) return false; - - var viewerId = user.GetUserId(); - var viewerName = user.GetUserName() ?? user.Identity?.Name; - - if (!string.IsNullOrWhiteSpace(viewerId)) - { - if (string.Equals(post.AuthorId, viewerId, StringComparison.Ordinal)) return true; - if (string.Equals(post.Author?.Id, viewerId, StringComparison.Ordinal)) return true; - } - - if (!string.IsNullOrWhiteSpace(viewerName)) - { - if (string.Equals(post.AuthorId, viewerName, StringComparison.OrdinalIgnoreCase)) return true; - if (string.Equals(post.Author?.UserName, viewerName, StringComparison.OrdinalIgnoreCase)) return true; - } - - return false; - } - -} diff --git a/src/Yavsc.Org/Startup.cs b/src/Yavsc.Org/Startup.cs index 497ee6389..ee5c13af2 100644 --- a/src/Yavsc.Org/Startup.cs +++ b/src/Yavsc.Org/Startup.cs @@ -25,14 +25,15 @@ public class Startup Config.Authority = siteSettings.Value.Authority; string blogsDir = siteSettings.Value.Blog ?? throw new Exception("blogsDir is not set."); string billsDir = siteSettings.Value.Bills ?? throw new Exception("billsDir is not set."); - Config.Temp = siteSettings.Value.TempDir; + string tempDir = siteSettings.Value.TempDir ?? throw new Exception("tempDir is not set."); + Config.PayPalSettings = payPalSettings.Value; AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); // TODO implement an installation & upgrade procedure // Create required directories - foreach (string dir in new string[] { blogsDir, billsDir, Config.Temp }) + foreach (string dir in new string[] { blogsDir, billsDir, tempDir }) { if (dir == null) { diff --git a/src/Yavsc.Org/ViewComponents/BillViewComponent.cs b/src/Yavsc.Org/ViewComponents/BillViewComponent.cs index a4698e2f6..0217ebcf2 100644 --- a/src/Yavsc.Org/ViewComponents/BillViewComponent.cs +++ b/src/Yavsc.Org/ViewComponents/BillViewComponent.cs @@ -32,9 +32,9 @@ namespace Yavsc.ViewComponents public async Task InvokeAsync( string code, - IBillable billable, - OutputFormat format, - bool asBill, + IBillable billable, + OutputFormat format, + bool asBill, SiteSettings settings ) { @@ -87,9 +87,9 @@ namespace Yavsc.ViewComponents var pdfGenerationViewModel = new PdfGenerationViewModel { - Temp = Config.Temp, + Temp = siteSettings.TempDir, TeXSource = tex, - DestDir = settings.Blog, + DestDir = siteSettings.Bills, BaseFileName = billable.GetFileBaseName(billing) }; if (settings.GenerateEstimatePdf(pdfGenerationViewModel)) { diff --git a/src/Yavsc.Server/Config.cs b/src/Yavsc.Server/Config.cs index e6be2a59b..175e66a41 100644 --- a/src/Yavsc.Server/Config.cs +++ b/src/Yavsc.Server/Config.cs @@ -1,6 +1,6 @@ #nullable enable annotations - + using IdentityServer8; using IdentityServer8.Models; using Microsoft.AspNetCore.Builder; @@ -14,43 +14,42 @@ public static class Config /// /// Authority URL for IdentityServer, used for authentication and authorization. /// - public static string Authority { get; set; } + public static string Authority { get; set; } - public static string AuthorityDomain + public static string AuthorityDomain { get + { + if (Uri.TryCreate(Authority, UriKind.Absolute, out var uri)) { - if (Uri.TryCreate(Authority, UriKind.Absolute, out var uri)) - { - return uri.GetLeftPart(UriPartial.Authority); - } - throw new InvalidOperationException("Invalid Authority URL"); + return uri.GetLeftPart(UriPartial.Authority); } + throw new InvalidOperationException("Invalid Authority URL"); + } } - public static IConfigurationRoot? GoogleWebClientConfiguration { get; set; } - public static GoogleServiceAccount? GServiceAccount { get; set; } + public static IConfigurationRoot? GoogleWebClientConfiguration { get; set; } + public static GoogleServiceAccount? GServiceAccount { get; set; } - public static FileServerOptions? UserFilesOptions { get; set; } - public static FileServerOptions? GitOptions { get; set; } - public static string AvatarsDirName { set; get; } = "Avatars"; - public static string GitDirName { set; get; } = "Git"; + public static FileServerOptions? UserFilesOptions { get; set; } + public static FileServerOptions? GitOptions { get; set; } + public static string AvatarsDirName { set; get; } = "Avatars"; + public static string GitDirName { set; get; } = "Git"; - public static GoogleAuthSettings? GoogleSettings { get; set; } - public static SmtpSettings? SmtpSetup { get; set; } - public static string? Temp { get; set; } - public static FileServerOptions? AvatarsOptions { get; set; } - public static string UserBillsDirName { set; get; } = "Bills"; - public static string UserFilesDirName { set; get; } = "Files"; + public static GoogleAuthSettings? GoogleSettings { get; set; } + public static SmtpSettings? SmtpSetup { get; set; } + public static FileServerOptions? AvatarsOptions { get; set; } + public static string UserBillsDirName { set; get; } = "Bills"; + public static string UserFilesDirName { set; get; } = "Files"; - /// - /// Lists Available user profile classes, - /// populated at startup, using reflection. - /// - public static List ProfileTypes = new List(); + /// + /// Lists Available user profile classes, + /// populated at startup, using reflection. + /// + public static List ProfileTypes = new List(); public static IEnumerable IdentityResources =>