A login's storage is counted, and can have a quota
MediaAttachment.Size was stored and never summed: nobody, the administrator included, could tell what media took, and nothing bounded it. Counted.Media and MediaOfRoot sum what is kept (not trashed). A login sees what its personas' uploads and pictures take at /clientapi/user/storage (ViewStorage), with its quota when the server sets one; Media:QuotaBytesPerRoot (0, no quota, by default) refuses an upload or a picture over it with 422, whichever persona sends it; the statistics overview gains the media totals, the proxy cache and the trash (ViewMediaTotals). Tests: a login's storage counts what its personas keep and forgets what is trashed; two uploads from two personas of one login are refused together past the quota. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
This commit is contained in:
1 parent
5252b8f320
commit
dc63fa57b8
11 files changed
+134
-3
No files matched your search
@@ -120,6 +120,8 @@ namespace PrivaPub.Domain.Media
|
||||
return MediaOutcome.Fail(StatusCodes.Status422UnprocessableEntity, "Validation failed: File can't be blank");
|
||||
var options = _options.CurrentValue;
|
||||
var contentType = file.ContentType?.Split(';')[0].Trim().ToLowerInvariant();
|
||||
if (await OverQuota(owner.Id, file.Length, token) is { } full)
|
||||
return full;
|
||||
|
||||
var attachment = new MediaAttachment
|
||||
{
|
||||
@@ -203,6 +205,8 @@ namespace PrivaPub.Domain.Media
|
||||
return MediaOutcome.Fail(StatusCodes.Status422UnprocessableEntity, "Validation failed: File type is not supported");
|
||||
if (file.Length > _options.CurrentValue.MaxImageBytes)
|
||||
return MediaOutcome.Fail(StatusCodes.Status422UnprocessableEntity, "Validation failed: File is too big");
|
||||
if (await OverQuota(avatarId, file.Length, token) is { } full)
|
||||
return full;
|
||||
var input = await Read(file, token);
|
||||
if (Refusal(input, _options.CurrentValue) is { } refused)
|
||||
return refused;
|
||||
@@ -241,6 +245,19 @@ namespace PrivaPub.Domain.Media
|
||||
return new MediaOutcome(attachment);
|
||||
}
|
||||
|
||||
// a login over its quota (Media:QuotaBytesPerRoot; none when 0) uploads nothing more
|
||||
async Task<MediaOutcome> OverQuota(string avatarId, long incoming, CancellationToken token)
|
||||
{
|
||||
var quota = _options.CurrentValue.QuotaBytesPerRoot;
|
||||
if (quota <= 0)
|
||||
return default;
|
||||
var rootId = (await DB.Default.Find<Models.User.RootToAvatar>().Match(r => r.AvatarId == avatarId).ExecuteFirstAsync(token))?.RootId;
|
||||
if (rootId == default)
|
||||
return default;
|
||||
var (bytes, _) = await Privacy.Counted.MediaOfRoot(rootId, token);
|
||||
return bytes + incoming > quota ? MediaOutcome.Fail(StatusCodes.Status422UnprocessableEntity, "Validation failed: Your storage is full") : default;
|
||||
}
|
||||
|
||||
public async Task<long> Trash(System.Linq.Expressions.Expression<Func<MediaAttachment, bool>> which, string reason, CancellationToken token)
|
||||
{
|
||||
var trashed = 0L;
|
||||
|
||||
Reference in new issue
Block a user