reinstall fixes and reorg

This commit is contained in:
Paul Schneider 2026-06-19 21:13:17 +01:00
commit 4398715004
10 changed files with 323 additions and 48 deletions

View file

@ -45,9 +45,12 @@ copy-binaries: build_publish_Org build_publish_Api build_publish_Blogs stop-serv
echo "$${project} -> $${LCAPI}" ; \ echo "$${project} -> $${LCAPI}" ; \
BINDIR="$(SLNDIR)/src/Yavsc.$${project}/bin/$(CONFIGURATION)/$(DOTNET_FRAMEWORK)/publish" ; \ BINDIR="$(SLNDIR)/src/Yavsc.$${project}/bin/$(CONFIGURATION)/$(DOTNET_FRAMEWORK)/publish" ; \
echo BINDIR: $${BINDIR} ; \ echo BINDIR: $${BINDIR} ; \
ls $${BINDIR}/appsettings* 2>&1 ; \ rm -f $${BINDIR}/appsettings-$${LCAPI}-dist.json ; \
mv $${BINDIR}/appsettings-$${LCAPI}.json \ mv -f $${BINDIR}/appsettings-$${LCAPI}.json \
$${BINDIR}/appsettings-$${LCAPI}-dist.json ; \ $${BINDIR}/appsettings-$${LCAPI}-dist.json ; \
rm -f $${BINDIR}/appsettings-$${LCAPI}.json ; \
sync ; \
ls $${BINDIR}/appsettings* 2>&1 ; \
sudo cp -a $${BINDIR}/* $(BASEAPPDIR) ; \ sudo cp -a $${BINDIR}/* $(BASEAPPDIR) ; \
done done
@sudo chown -R $(USER_AND_GROUP) $(BASEAPPDIR) @sudo chown -R $(USER_AND_GROUP) $(BASEAPPDIR)
@ -56,9 +59,12 @@ copy-binaries: build_publish_Org build_publish_Api build_publish_Blogs stop-serv
sudo cp $^ $@ sudo cp $^ $@
sudo chown root:root $@ sudo chown root:root $@
build_publish_%: build_publish_%: clean_publish_dir_%
@ASPNETCORE_ENV=$(CONFIGURATION) dotnet publish $(SLNDIR)/src/Yavsc.$*/Yavsc.$*.csproj @ASPNETCORE_ENV=$(CONFIGURATION) dotnet publish $(SLNDIR)/src/Yavsc.$*/Yavsc.$*.csproj
clean_publish_dir_%:
@rm -rf $(SLNDIR)/src/Yavsc.$*/bin/$(CONFIGURATION)/$(DOTNET_FRAMEWORK)/publish
install: build_publish copy-binaries copy-services install: build_publish copy-binaries copy-services
@sudo systemctl daemon-reload @sudo systemctl daemon-reload
@for project in $(APP_PROJECT_NAMES); \ @for project in $(APP_PROJECT_NAMES); \

View file

@ -18,7 +18,6 @@ using Yavsc.Interface;
using Yavsc.Models; using Yavsc.Models;
using Yavsc.Services; using Yavsc.Services;
using Yavsc.Server.Helpers; using Yavsc.Server.Helpers;
using Yavsc.Extensions;
namespace Yavsc.Blogs; namespace Yavsc.Blogs;
internal class Program internal class Program

View file

@ -10,6 +10,5 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" />
<ProjectReference Include="../Yavsc.Server/Yavsc.Server.csproj" /> <ProjectReference Include="../Yavsc.Server/Yavsc.Server.csproj" />
<ProjectReference Include="../Yavsc.Org/Yavsc.Org.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -121,7 +121,7 @@ namespace Yavsc.Org.Tests
{ {
var builder = WebApplication.CreateBuilder(); var builder = WebApplication.CreateBuilder();
builder.AddConfiguration("org").AddInMemoryCollection(new Dictionary<string, string?> builder.AddConfiguration(null).AddInMemoryCollection(new Dictionary<string, string?>
{ {
[$"ConnectionStrings:{YavscConstants.YavscConnectionStringName}"] = "InMemory" [$"ConnectionStrings:{YavscConstants.YavscConnectionStringName}"] = "InMemory"
}); });

View file

@ -48,7 +48,4 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" /> <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" />
</ItemGroup> </ItemGroup>
<Target Name="CopyStaticWebAssetsManifest" AfterTargets="Build">
<Copy SourceFiles="$(OutDir)Yavsc.Org.staticwebassets.endpoints.json" DestinationFiles="$(OutDir)$(MSBuildProjectName).staticwebassets.endpoints.json" SkipUnchangedFiles="true" Condition="Exists('$(OutDir)Yavsc.Org.staticwebassets.endpoints.json')" />
</Target>
</Project> </Project>

View file

@ -1,32 +0,0 @@
using Microsoft.EntityFrameworkCore;
using Yavsc.Models;
using Yavsc.Models.Blog;
namespace Yavsc.Helpers
{
public static class UserHelpers
{
public static IEnumerable<BlogPost> UserPosts(this ApplicationDbContext dbContext, string posterId, string? readerId)
{
if (readerId == null)
{
var userPosts = dbContext.blogSpotPublications.Include(
b => b.BlogPost
).Where(x => x.BlogPost.AuthorId == posterId)
.Select(x => x.BlogPost).ToArray();
return userPosts;
}
else
{
long[] readerCirclesMemberships =
dbContext.Circle.Include(c => c.Members)
.Where(c => c.Members.Any(m => m.MemberId == readerId))
.Select(c => c.Id).ToArray();
return dbContext.BlogSpot.Include(
b => b.Author
).Include(p => p.ACL).Where(x => x.Author.Id == posterId &&
(x.ACL.Count == 0 || x.ACL.Any(a => readerCirclesMemberships.Contains(a.CircleId))));
}
}
}
}

View file

@ -7,13 +7,23 @@ public static class ConfigHelpers
{ {
public static IConfigurationBuilder AddConfiguration(this WebApplicationBuilder builder, string appName) public static IConfigurationBuilder AddConfiguration(this WebApplicationBuilder builder, string appName)
{
if (string.IsNullOrEmpty(appName))
{ {
var rootConfig = builder.Configuration var rootConfig = builder.Configuration
.AddJsonFile($"appsettings-{appName}.json", optional: false, reloadOnChange: false); .AddJsonFile($"appsettings.json", optional: false, reloadOnChange: false);
rootConfig.AddJsonFile($"appsettings-{appName}.{builder.Environment.EnvironmentName}.json", optional: true, reloadOnChange: false); rootConfig.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true, reloadOnChange: false);
return rootConfig.AddEnvironmentVariables(); return rootConfig.AddEnvironmentVariables();
}
var specRootConfig = builder.Configuration
.AddJsonFile($"appsettings-{appName}.json", optional: false, reloadOnChange: false);
specRootConfig.AddJsonFile($"appsettings-{appName}.{builder.Environment.EnvironmentName}.json", optional: true, reloadOnChange: false);
return specRootConfig.AddEnvironmentVariables();
} }
} }

View file

@ -1,9 +1,35 @@
using Microsoft.EntityFrameworkCore;
using System.Security.Claims; using System.Security.Claims;
using Yavsc.Models;
using Yavsc.Models.Blog;
namespace Yavsc.Server.Helpers namespace Yavsc.Server.Helpers
{ {
public static class UserHelpers public static class UserHelpers
{ {
public static IEnumerable<BlogPost> UserPosts(this ApplicationDbContext dbContext, string posterId, string? readerId)
{
if (readerId == null)
{
var userPosts = dbContext.blogSpotPublications.Include(
b => b.BlogPost
).Where(x => x.BlogPost.AuthorId == posterId)
.Select(x => x.BlogPost).ToArray();
return userPosts;
}
else
{
long[] readerCirclesMemberships =
dbContext.Circle.Include(c => c.Members)
.Where(c => c.Members.Any(m => m.MemberId == readerId))
.Select(c => c.Id).ToArray();
return dbContext.BlogSpot.Include(
b => b.Author
).Include(p => p.ACL).Where(x => x.Author.Id == posterId &&
(x.ACL.Count == 0 || x.ACL.Any(a => readerCirclesMemberships.Contains(a.CircleId))));
}
}
public static string GetUserId(this ClaimsPrincipal user) public static string GetUserId(this ClaimsPrincipal user)
{ {
return user.FindFirstValue("sub"); return user.FindFirstValue("sub");

View file

@ -0,0 +1,268 @@
using System.Diagnostics;
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
using Microsoft.EntityFrameworkCore;
using Yavsc;
using Yavsc.Models;
using Yavsc.Models.Blog;
using Yavsc.Server.Exceptions;
using Yavsc.Server.Helpers;
using Yavsc.Services;
using Yavsc.ViewModels.Auth;
using Microsoft.AspNetCore.Http;
public class BlogSpotService
{
private readonly ApplicationDbContext _context;
private readonly IAuthorizationService _authorizationService;
private readonly IFileSystemAuthManager fileSystemAuthManager;
public BlogSpotService(ApplicationDbContext context,
IAuthorizationService authorizationService,
IFileSystemAuthManager fileSystemAuthManager)
{
_authorizationService = authorizationService;
_context = context;
this.fileSystemAuthManager = fileSystemAuthManager;
}
public BlogPost Create(string userId, 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(
AbstractFileSystemHelpers.UserFilesDirName,
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<BlogPostEditViewModel> 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);
return new BlogPostEditViewModel(blog, pub);
}
public async Task<BlogPost> Details(ClaimsPrincipal user, long blogPostId)
{
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);
}
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, 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<IEnumerable<IBlogPost>> Index(ClaimsPrincipal user, string id, int skip = 0, int take = 25)
{
IEnumerable<IBlogPost> posts;
if (user.Identity.IsAuthenticated)
{
string viewerId = user.GetUserId();
long[] userCircles = await _context.Circle.Include(c => c.Members).
Where(c => c.Members.Any(m => m.MemberId == viewerId))
.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 == viewerId)
|| (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 data = posts.OrderByDescending(p => p.DateModified)
.Skip(skip)
.Take(take);
return data;
}
public async Task Delete(ClaimsPrincipal user, long id)
{
var uid = user.GetUserId();
BlogPost blog = _context.BlogSpot.Single(m => m.Id == id);
_context.BlogSpot.Remove(blog);
_context.SaveChanges(user.GetUserId());
}
public async Task<IEnumerable<BlogPost>> 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<BlogPost>();
return _context.UserPosts(posterId, readerId);
}
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<BlogPost?> GetBlogPostAsync(long value)
{
return await _context.BlogSpot
.Include(b => b.Author)
.Include(b => b.ACL)
.SingleOrDefaultAsync(x => x.Id == value);
}
}

View file

@ -1,12 +1,14 @@
using System.Security.Claims; using System.Security.Claims;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Yavsc.Models; using Yavsc.Models;
using Yavsc.Models.Blog; using Yavsc.Models.Blog;
using Yavsc.Server.Helpers; using Yavsc.Server.Helpers;
using Yavsc.ViewModels.Auth; using Yavsc.ViewModels.Auth;
namespace Yavsc.Extensions; namespace Yavsc.Services;
public class PermissionHandler : IAuthorizationHandler public class PermissionHandler : IAuthorizationHandler
{ {