refacto site settings
Some checks failed
Dotnet build and test / build (pull_request) Failing after 8m29s

This commit is contained in:
Paul Schneider 2026-09-17 22:22:18 +01:00
commit 3bc4142383
Signed by: notazof
GPG key ID: 1DD5D838E5343B06
33 changed files with 399 additions and 315 deletions

View file

@ -27,12 +27,11 @@ public static class Config
throw new InvalidOperationException("Invalid Authority URL");
}
}
public static IConfigurationRoot? GoogleWebClientConfiguration { get; set; }
public static GoogleServiceAccount? GServiceAccount { get; set; }
public static SiteSettings SiteSetup { get; set; } = new SiteSettings();
public static FileServerOptions? UserFilesOptions { get; set; }
public static FileServerOptions? GitOptions { get; set; }
public static string AvatarsDirName { set; get; } = "Avatars";

View file

@ -21,11 +21,12 @@ namespace Yavsc.Helpers
return bill;
}
public static FileInfo GetBillInfo(this IBillable bill, IBillingService service)
public static FileInfo GetBillInfo(this IBillable bill,
IBillingService service, SiteSettings siteSettings)
{
var suffix = bill.GetIsAcquitted() ? "-ack":null;
var filename = bill.GetFileBaseName(service)+".pdf";
return new FileInfo(Path.Combine(AbstractFileSystemHelpers.UserBillsDirName, filename));
return new FileInfo(Path.Combine(siteSettings.Bills, filename));
}
}
}

View file

@ -64,6 +64,7 @@ public static class EstimateSignatureFileHelper
long estimateId,
SignatureType type,
SignaturePadPayload payload,
SiteSettings siteSettings,
CancellationToken token = default)
{
if (user is null) throw new ArgumentNullException(nameof(user));
@ -77,7 +78,7 @@ public static class EstimateSignatureFileHelper
// (not user-derived), so we skip the IsValidYavscPath
// check that ReceiveUserFile performs on user-supplied
// subpaths.
var root = user.EnsureDestinationDirectory(SignaturesSubdir);
var root = user.EnsureDestinationDirectory(SignaturesSubdir, siteSettings);
var fileName = FileNameFormat(type, estimateId, DateTime.UtcNow.Ticks);
var fullPath = Path.Combine(root, fileName);

View file

@ -13,6 +13,7 @@ using Yavsc.Exceptions;
using Yavsc.Abstract.Helpers;
using ImageMagick;
using Yavsc.Server.Models.FileSystem;
using Yavsc.ViewModels.UserFiles;
namespace Yavsc.Server.Helpers
{
public static class FileSystemHelpers
@ -27,10 +28,12 @@ namespace Yavsc.Server.Helpers
}
}
}
public static async Task<FileReceivedInfo> ReceiveProSignatureAsync(this ClaimsPrincipal user, string billingCode, long estimateId, IFormFile formFile, string signType)
public static async Task<FileReceivedInfo> ReceiveProSignatureAsync(this ClaimsPrincipal user, string billingCode, long estimateId, IFormFile formFile, string signType,
SiteSettings settings
)
{
var item = new FileReceivedInfo(
Config.SiteSetup.Bills,
settings.Bills,
AbstractFileSystemHelpers.SignFileNameFormat("pro", billingCode, estimateId));
var fi = new FileInfo(item.FullName);
@ -39,16 +42,11 @@ namespace Yavsc.Server.Helpers
return item;
}
public static string GetAvatarUri(this ApplicationUser user)
{
return $"/{Config.SiteSetup.Avatars}/{user.UserName}.png";
}
public static string EnsureDestinationDirectory(
this ClaimsPrincipal user,
string subpath)
string subpath, SiteSettings siteSettings)
{
var root = Path.Combine(AbstractFileSystemHelpers.UserFilesDirName, user.Identity.Name);
var root = Path.Combine(siteSettings.Blog, user.Identity.Name);
if (!string.IsNullOrWhiteSpace(subpath))
{
if (!subpath.IsValidYavscPath())
@ -69,18 +67,18 @@ namespace Yavsc.Server.Helpers
/// <param name="user"></param>
/// <param name="fileName"></param>
public static void DeleteUserFile(this ApplicationUser user, string fileName)
public static void DeleteUserFile(this ApplicationUser user, string fileName, SiteSettings siteSettings)
{
var root = Path.Combine(AbstractFileSystemHelpers.UserFilesDirName, user.UserName);
var root = Path.Combine(siteSettings.Blog, user.UserName);
var fi = new FileInfo(Path.Combine(root, fileName));
if (!fi.Exists) return;
fi.Delete();
user.DiskUsage -= fi.Length;
}
public static FsOperationInfo DeleteUserDirOrFile(this ApplicationUser user, string dirName)
public static FsOperationInfo DeleteUserDirOrFile(this ApplicationUser user, string dirName, SiteSettings siteSettings)
{
var root = Path.Combine(AbstractFileSystemHelpers.UserFilesDirName, user.UserName);
var root = Path.Combine(siteSettings.Blog, user.UserName);
if (string.IsNullOrEmpty(dirName))
return new FsOperationInfo { Done = false, ErrorCode = ErrorCode.InvalidRequest, ErrorMessage = "specify a directory or file name" };
@ -101,9 +99,9 @@ namespace Yavsc.Server.Helpers
return new FsOperationInfo { Done = true };
}
public static FsOperationInfo MoveUserDir(this ApplicationUser user, string fromDirName, string toDirName)
public static FsOperationInfo MoveUserDir(this ApplicationUser user, string fromDirName, string toDirName, SiteSettings siteSettings)
{
var root = Path.Combine(AbstractFileSystemHelpers.UserFilesDirName, user.UserName);
var root = Path.Combine(siteSettings.Blog, user.UserName);
if (string.IsNullOrEmpty(fromDirName))
return new FsOperationInfo { Done = false, ErrorCode = ErrorCode.InvalidRequest, ErrorMessage = "specify a dir name " };
@ -127,28 +125,28 @@ namespace Yavsc.Server.Helpers
di.MoveTo(destPath);
return new FsOperationInfo { Done = true };
}
public static FsOperationInfo MoveUserFileToDir(this ApplicationUser user, string fileNameFrom, string fileNameDest)
public static FsOperationInfo MoveUserFileToDir(this ApplicationUser user, string fileNameFrom, string fileNameDest, SiteSettings siteSettings)
{
var root = Path.Combine(AbstractFileSystemHelpers.UserFilesDirName, user.UserName);
var root = Path.Combine(siteSettings.Blog, user.UserName);
var fi = new FileInfo(Path.Combine(root, fileNameFrom));
if (!fi.Exists) return new FsOperationInfo { ErrorCode = ErrorCode.NotFound, ErrorMessage = "no file to move" };
if (!fi.Exists) return new FsOperationInfo { Done = false, ErrorCode = ErrorCode.NotFound, ErrorMessage = "no file to move" };
string dest;
if (!string.IsNullOrEmpty(fileNameDest)) dest = Path.Combine(root, fileNameDest);
else dest = root;
var fo = new FileInfo(dest);
if (fo.Exists) return new FsOperationInfo { ErrorCode = ErrorCode.DestExists, ErrorMessage = "destination file name is an existing file" };
if (fo.Exists) return new FsOperationInfo { Done = false, ErrorCode = ErrorCode.DestExists, ErrorMessage = "destination file name is an existing file" };
var dout = new DirectoryInfo(dest);
if (!dout.Exists) dout.Create();
fi.MoveTo(Path.Combine(dout.FullName, fi.Name));
return new FsOperationInfo { Done = true };
}
public static FsOperationInfo MoveUserFile(this ApplicationUser user, string fileNameFrom, string fileNameDest)
public static FsOperationInfo MoveUserFile(this ApplicationUser user, string fileNameFrom, string fileNameDest, SiteSettings siteSettings)
{
var root = Path.Combine(AbstractFileSystemHelpers.UserFilesDirName, user.UserName);
var root = Path.Combine(siteSettings.Blog, user.UserName);
var fi = new FileInfo(Path.Combine(root, fileNameFrom));
if (!fi.Exists) return new FsOperationInfo { ErrorCode = ErrorCode.NotFound, ErrorMessage = "no file to move" };
if (!fi.Exists) return new FsOperationInfo { Done = false, ErrorCode = ErrorCode.NotFound, ErrorMessage = "no file to move" };
var fo = new FileInfo(Path.Combine(root, fileNameDest));
if (fo.Exists) return new FsOperationInfo { ErrorCode = ErrorCode.DestExists, ErrorMessage = "destination file name is an existing file" };
if (fo.Exists) return new FsOperationInfo { Done = false, ErrorCode = ErrorCode.DestExists, ErrorMessage = "destination file name is an existing file" };
fi.MoveTo(fo.FullName);
return new FsOperationInfo { Done = true };
}
@ -245,7 +243,9 @@ namespace Yavsc.Server.Helpers
}
public static FileReceivedInfo ReceiveAvatar(this ApplicationUser user, IFormFile formFile)
public static FileReceivedInfo ReceiveAvatar(this ApplicationUser user,
IFormFile formFile,
SiteSettings settings)
{
if (user == null) throw new ArgumentNullException(nameof(user));
if (formFile == null) throw new ArgumentNullException(nameof(formFile));
@ -256,7 +256,7 @@ namespace Yavsc.Server.Helpers
avatarsRequestPath = Constants.AvatarsPath;
}
var avatarsDirectory = Config.SiteSetup?.Avatars;
var avatarsDirectory = settings.Avatars;
if (string.IsNullOrWhiteSpace(avatarsDirectory))
{
avatarsDirectory = "avatars";
@ -306,5 +306,12 @@ namespace Yavsc.Server.Helpers
return $"{basename}-{flow.SequenceNumber}{ext}";
}
public static UserDirectoryInfo GetUserFiles(SiteSettings settings, string userId, string subdir)
{
UserDirectoryInfo di = new UserDirectoryInfo(settings.Blog, userId, subdir);
return di;
}
}
}

View file

@ -5,6 +5,8 @@ using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Yavsc;
using Yavsc.Blogspot;
using Yavsc.Models;
using Yavsc.Models.Access;
@ -19,72 +21,77 @@ public class BlogSpotService
private readonly ApplicationDbContext _context;
private readonly IAuthorizationService _authorizationService;
private readonly IFileSystemAuthManager fileSystemAuthManager;
private readonly SiteSettings siteSettings;
public BlogSpotService(
ApplicationDbContext context,
IAuthorizationService authorizationService,
IFileSystemAuthManager fileSystemAuthManager)
IFileSystemAuthManager fileSystemAuthManager,
IOptions<SiteSettings> siteSettings)
{
_authorizationService = authorizationService;
_context = context;
this.fileSystemAuthManager = fileSystemAuthManager;
this.siteSettings = siteSettings.Value;
}
public BlogPost Create(string userId, BlogPost post, IFormFileCollection files)
public void AttachFiles(IFormFileCollection files, string userId, long postId)
{
// Traiter les fichiers attaches s'il y en a
if (files != null && files.Count > 0)
{
var user = _context.Users.FirstOrDefault(u => u.Id == userId);
if (user == null)
throw new InvalidOperationException($"Utilisateur {userId} non trouvé.");
try
{
string blogFilesSubdir = $"blogs/{postId}";
string destDir = Path.Combine(
siteSettings.Blog,
user.UserName,
blogFilesSubdir);
var di = new DirectoryInfo(destDir);
if (!di.Exists) di.Create();
foreach (var formFile in files)
{
var fileInfo = user.ReceiveUserFile(destDir, formFile);
if (fileInfo != null && !fileInfo.QuotaOffense)
{
var uploadedFile = new UploadedFile
{
Path = fileInfo.FileName,
ContentType = formFile.ContentType,
Length = formFile.Length
};
_context.UploadedFiles.Add(uploadedFile);
_context.SaveChanges(userId);
var attachment = new BlogAttachedFile
{
PostId = postId,
FileId = uploadedFile.Id
};
_context.BlogAttachedFiles.Add(attachment);
}
}
_context.SaveChanges(userId);
}
catch (Exception ex)
{
Debug.WriteLine($"Erreur lors du traitement des fichiers : {ex.Message}");
}
}
}
public BlogPost Create(string userId, BlogPost post )
{
// Sauvegarder le post d'abord pour obtenir son ID
// Le createur vient de l'authentification, donc on ne le prend pas du post
post.AuthorId = userId;
_context.BlogSpot.Add(post);
_context.SaveChanges(userId);
// Traiter les fichiers attaches s'il y en a
if (files != null && files.Count > 0)
{
var user = _context.Users.FirstOrDefault(u => u.Id == userId);
if (user != null)
{
try
{
string blogFilesSubdir = $"blogs/{post.Id}";
string destDir = Path.Combine(
AbstractFileSystemHelpers.UserFilesDirName,
user.UserName,
blogFilesSubdir);
var di = new DirectoryInfo(destDir);
if (!di.Exists) di.Create();
foreach (var formFile in files)
{
var fileInfo = user.ReceiveUserFile(destDir, formFile);
if (fileInfo != null && !fileInfo.QuotaOffense)
{
var uploadedFile = new UploadedFile
{
Path = fileInfo.FileName,
ContentType = formFile.ContentType,
Length = formFile.Length
};
_context.UploadedFiles.Add(uploadedFile);
_context.SaveChanges(userId);
var attachment = new BlogAttachedFile
{
PostId = post.Id,
FileId = uploadedFile.Id
};
_context.BlogAttachedFiles.Add(attachment);
}
}
_context.SaveChanges(userId);
}
catch (Exception ex)
{
Debug.WriteLine($"Erreur lors du traitement des fichiers : {ex.Message}");
}
}
}
return post;
}
@ -186,58 +193,6 @@ public class BlogSpotService
_context.SaveChanges(user.GetUserId());
}
public async Task Modify(ClaimsPrincipal user, BlogPost blog, IFormFileCollection files)
{
await Modify(user, blog);
if (files == null || files.Count == 0)
return;
var userId = user.GetUserId();
var userEntity = _context.Users.FirstOrDefault(u => u.Id == userId);
if (userEntity == null)
return;
try
{
string blogFilesSubdir = $"blogs/{blog.Id}";
string destDir = Path.Combine(
AbstractFileSystemHelpers.UserFilesDirName,
userEntity.UserName,
blogFilesSubdir);
var di = new DirectoryInfo(destDir);
if (!di.Exists) di.Create();
foreach (var formFile in files)
{
var fileInfo = userEntity.ReceiveUserFile(destDir, formFile);
if (fileInfo != null && !fileInfo.QuotaOffense)
{
var uploadedFile = new UploadedFile
{
Path = fileInfo.FileName,
ContentType = formFile.ContentType,
Length = formFile.Length
};
_context.UploadedFiles.Add(uploadedFile);
_context.SaveChanges(userId);
var attachment = new BlogAttachedFile
{
PostId = blog.Id,
FileId = uploadedFile.Id
};
_context.BlogAttachedFiles.Add(attachment);
}
}
_context.SaveChanges(userId);
}
catch (Exception ex)
{
Debug.WriteLine($"Erreur lors du traitement des fichiers : {ex.Message}");
}
}
public async Task<IEnumerable<IBlogPost>> Index(ClaimsPrincipal user, string id, int skip = 0, int take = 25)
{

View file

@ -6,89 +6,72 @@ namespace Yavsc
{
public class SiteSettings
{
public string Title { get; set; } = "Yavsc";
public string Title { get; set; }
public string Slogan { get; set; } = "";
public string Banner { get; set; } = "";
public string Slogan { get; set; }
public string Banner { get; set; }
public string StyleSheet { get; set; } = "site.css";
public string FavIcon { get; set; } = "favicon.ico";
public string Logo { get; set; } = "logo.png";
public string StyleSheet { get; set; }
public string FavIcon { get; set; }
public string Logo { get; set; }
/// <summary>
/// Origins to allow via the CORS "default" policy.
/// Each entry must be a full origin (scheme + host [+ port]), e.g. "https://app.example.com".
/// </summary>
public IList<string> CorsAllowedOrigins { get; set; } = new List<string>
{
"https://localhost:5001"
};
public IList<string> CorsAllowedOrigins { get; set; }
/// <summary>
/// External Url
/// </summary>
/// <value></value>
public string ExternalUrl { get; set; } = "http://lua.pschneider.fr";
public string ExternalUrl { get; set; }
/// <summary>
/// Base URL of the API fronting this site.
/// </summary>
public string ApiUrl { get; set; } = "";
public string ApiUrl { get; set; }
/// <summary>
/// Must be a fqdn.
/// </summary>
/// <returns></returns>
public string Authority { get; set; } = "lua.pschneider.fr";
public string Authority { get; set; }
/// <summary>
/// Owner's email
/// </summary>
/// <returns></returns>
public StaticContact Owner { get; set; } = new StaticContact
{
EMail = "root@lua.pschneider.fr",
Name = "Root"
} ;
public StaticContact Owner { get; set; }
/// <summary>
/// Administrator's email
/// </summary>
/// <returns></returns>
public StaticContact Admin { get; set; } = new StaticContact
{
EMail = "root@lua.pschneider.fr",
Name = "Root"
} ;
public StaticContact Admin { get; set; }
public string DataDir { get; set; } = "data";
public string Avatars { get; set; } = "avatars";
public string DataDir { get; set; }
public string Avatars { get; set; }
public long Quota { get; set; }
public string Blog { get; set; } = "blogs";
public string Bills { get; set; } = "bills";
public string GitRepository { get; set; } = "sources";
public string Blog { get; set; }
public string Bills { get; set; }
public string GitRepository { get; set; }
public string BusinessName { get; set; } = "Yavsc";
public string? Street { get; set; }
public string? PostalCode { get; set; }
public string? CountryCode { get; set; }
public string HomeViewName { get; set; } = "Home";
/// <summary>
/// Specifies the directory where should be
/// generated pdf files using pandoc
/// </summary>
/// <returns>The temporary directory to use</returns>
public string TempDir { get; set; } = "temp";
public string TempDir { get; set; }
/// <summary>
/// Disk usage user list maximum length in memory
/// </summary>
/// <value></value>
public int DUUserListLen { get; set; } = 256;
public int DUUserListLen { get; set; }
/// <summary>
/// Default acl file name
/// </summary>
/// <value></value>
public string AccessListFileName { get; set; } = ".access";
public string AccessListFileName { get; set; }
}
}