DMS Layout mit Fehlern
All checks were successful
Build & Deploy PLDpro.Web Test to 192.168.1.100 / build-and-deploy (push) Successful in 1m15s

This commit is contained in:
2026-02-09 21:50:24 +01:00
parent f9fb791dca
commit cae77ef1e3
12 changed files with 709 additions and 1 deletions

View File

@@ -0,0 +1,20 @@
using Microsoft.AspNetCore.Components.Forms;
using Pldpro.Web.UI.Models;
namespace Pldpro.Web.UI.Services;
public interface IDocumentClient
{
Task<IReadOnlyList<string>> ListBucketsAsync(CancellationToken ct = default);
Task<(IReadOnlyList<DocumentListItem> Items, int Total)> SearchAsync(
string bucket, string? query, string? pathPrefix, int page, int pageSize, CancellationToken ct = default);
Task<DocumentDetail?> GetAsync(string bucket, string key, CancellationToken ct = default);
Task UploadAsync(string bucket, string? pathPrefix, IBrowserFile file, long streamLimit, CancellationToken ct = default);
Task DeleteAsync(string bucket, string key, CancellationToken ct = default);
string GetDownloadUrl(string bucket, string key);
}

View File

@@ -0,0 +1,95 @@
using Microsoft.AspNetCore.Components.Forms;
using Pldpro.Web.UI.Models;
using System.Net.Http.Json;
namespace Pldpro.Web.UI.Services;
public sealed class StorageDocumentClient(IHttpClientFactory factory) : IDocumentClient
{
private readonly HttpClient _http = factory.CreateClient("AppApi");
private sealed record BucketVm(string Name, DateTime? CreationDate);
private sealed record ObjectVm(string Key, long? Size, DateTime? LastModified);
public async Task<IReadOnlyList<string>> ListBucketsAsync(CancellationToken ct = default)
{
var list = await _http.GetFromJsonAsync<List<BucketVm>>("/api/storage/buckets", ct) ?? new();
return list.Select(b => b.Name).ToList();
}
public async Task<(IReadOnlyList<DocumentListItem> Items, int Total)> SearchAsync(
string bucket, string? query, string? pathPrefix, int page, int pageSize, CancellationToken ct = default)
{
var objs = await _http.GetFromJsonAsync<List<ObjectVm>>($"/api/storage/buckets/{Uri.EscapeDataString(bucket)}/objects", ct) ?? new();
IEnumerable<DocumentListItem> q = objs.Select(o => new DocumentListItem
{
Bucket = bucket,
Key = o.Key,
Size = o.Size,
LastModified = o.LastModified
});
if (!string.IsNullOrWhiteSpace(pathPrefix))
q = q.Where(d => d.Key.StartsWith(pathPrefix!.Trim('/') + "/", StringComparison.OrdinalIgnoreCase));
if (!string.IsNullOrWhiteSpace(query))
{
q = q.Where(d =>
d.FileName.Contains(query, StringComparison.OrdinalIgnoreCase) ||
d.PathPrefix.Contains(query, StringComparison.OrdinalIgnoreCase));
}
var total = q.Count();
var pageItems = q
.OrderByDescending(d => d.LastModified ?? DateTime.MinValue)
.Skip(page * pageSize)
.Take(pageSize)
.ToList();
return (pageItems, total);
}
public async Task<DocumentDetail?> GetAsync(string bucket, string key, CancellationToken ct = default)
{
// Da es keinen Einzel-Endpoint gibt, holen wir die Liste und picken das Objekt.
var (items, _) = await SearchAsync(bucket, null, null, 0, int.MaxValue, ct);
var d = items.FirstOrDefault(x => string.Equals(x.Key, key, StringComparison.Ordinal));
return d is null ? null : new DocumentDetail
{
Bucket = d.Bucket,
Key = d.Key,
Size = d.Size,
LastModified = d.LastModified,
Status = d.Status
};
}
public async Task UploadAsync(string bucket, string? pathPrefix, IBrowserFile file, long streamLimit, CancellationToken ct = default)
{
using var stream = file.OpenReadStream(streamLimit);
using var content = new MultipartFormDataContent();
content.Add(new StreamContent(stream), "file", file.Name);
if (!string.IsNullOrWhiteSpace(pathPrefix))
content.Add(new StringContent(pathPrefix!.Trim('/')), "path");
var resp = await _http.PostAsync($"/api/storage/buckets/{Uri.EscapeDataString(bucket)}/upload", content, ct);
resp.EnsureSuccessStatusCode();
}
public async Task DeleteAsync(string bucket, string key, CancellationToken ct = default)
{
var url = $"/api/storage/buckets/{Uri.EscapeDataString(bucket)}/objects/{EncodeKeyForPath(key)}";
var resp = await _http.DeleteAsync(url, ct);
resp.EnsureSuccessStatusCode();
}
public string GetDownloadUrl(string bucket, string key)
=> $"/api/storage/buckets/{Uri.EscapeDataString(bucket)}/download/{EncodeKeyForPath(key)}";
// Pfadsegment-weise encodieren: Slashes bleiben Trennzeichen
private static string EncodeKeyForPath(string key)
=> string.Join("/", (key ?? string.Empty)
.Split('/', StringSplitOptions.RemoveEmptyEntries)
.Select(Uri.EscapeDataString));
}