mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 07:13:12 +00:00
The Elastic Beanstalk nginx proxy kept its 1 MB default body limit, so every media upload over ~1 MB got an nginx 413 before reaching the API. Ship a .platform nginx override (120M) in the bundle and assert it in the bundle contract. Apply the client-confirmed contract: photos up to 10 MB (JPEG/PNG/HEIC), videos up to 100 MB (MP4/MOV), at most 10 photos and 3 videos per work order, with stable generic rejection messages. The request ceiling (110 MB) sits between the per-kind caps and the proxy so oversize files get the generic message. The vendor portal accepts the same photo/video types and caps.
84 lines
4.1 KiB
C#
84 lines
4.1 KiB
C#
namespace SeaHaven.Services.Helpers
|
|
{
|
|
/// <summary>
|
|
/// SH-116 client-confirmed media contract (2026-09-22): photos up to 10 MB (JPEG/PNG/HEIC),
|
|
/// videos up to 100 MB and 90 seconds (MP4/MOV), at most 10 photos and 3 videos per work
|
|
/// order, across the dispatcher media modal, the completion-doc media tab and the vendor
|
|
/// portal upload. Documents (PDF/DOC/DOCX) keep the 50 MB document cap. Duration is only
|
|
/// checkable client-side (the server cannot probe it cheaply), so it is enforced in the
|
|
/// browser and documented here as part of the same contract.
|
|
/// </summary>
|
|
public static class WorkOrderMediaContract
|
|
{
|
|
public const long MaxPhotoBytes = 10_000_000;
|
|
public const long MaxVideoBytes = 100_000_000;
|
|
public const long MaxDocumentBytes = 50_000_000;
|
|
public const int MaxPhotosPerWorkOrder = 10;
|
|
public const int MaxVideosPerWorkOrder = 3;
|
|
public const int MaxVideoDurationSeconds = 90;
|
|
|
|
/// <summary>
|
|
/// Request-level ceiling for media endpoints (RequestSizeLimit / multipart limit). It sits
|
|
/// above <see cref="MaxVideoBytes"/> plus multipart overhead so an oversize file reaches the
|
|
/// per-kind check and gets its generic message instead of a framework 413. The EB nginx
|
|
/// proxy (.platform/nginx/conf.d/01_upload_body_size.conf) must stay above this value.
|
|
/// </summary>
|
|
public const long MaxUploadRequestBytes = 110_000_000;
|
|
|
|
public enum UploadKind
|
|
{
|
|
Photo,
|
|
Video,
|
|
Document,
|
|
Unknown
|
|
}
|
|
|
|
public static UploadKind ResolveKind(string? contentType, string? fileName)
|
|
{
|
|
var type = (contentType ?? string.Empty).Trim();
|
|
var extension = Path.GetExtension(fileName ?? string.Empty);
|
|
|
|
if (type.StartsWith("video/", StringComparison.OrdinalIgnoreCase)
|
|
|| extension.Equals(".mp4", StringComparison.OrdinalIgnoreCase)
|
|
|| extension.Equals(".mov", StringComparison.OrdinalIgnoreCase))
|
|
return UploadKind.Video;
|
|
|
|
if (type.StartsWith("image/", StringComparison.OrdinalIgnoreCase)
|
|
|| extension.Equals(".jpg", StringComparison.OrdinalIgnoreCase)
|
|
|| extension.Equals(".jpeg", StringComparison.OrdinalIgnoreCase)
|
|
|| extension.Equals(".png", StringComparison.OrdinalIgnoreCase)
|
|
|| extension.Equals(".heic", StringComparison.OrdinalIgnoreCase))
|
|
return UploadKind.Photo;
|
|
|
|
if (type.Equals("application/pdf", StringComparison.OrdinalIgnoreCase)
|
|
|| type.Equals("application/msword", StringComparison.OrdinalIgnoreCase)
|
|
|| type.Equals(
|
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
StringComparison.OrdinalIgnoreCase)
|
|
|| extension.Equals(".pdf", StringComparison.OrdinalIgnoreCase)
|
|
|| extension.Equals(".doc", StringComparison.OrdinalIgnoreCase)
|
|
|| extension.Equals(".docx", StringComparison.OrdinalIgnoreCase))
|
|
return UploadKind.Document;
|
|
|
|
return UploadKind.Unknown;
|
|
}
|
|
|
|
/// <summary>Stable, generic per-kind size message; never echoes file metadata.</summary>
|
|
public static string? ValidateSize(long length, UploadKind kind)
|
|
{
|
|
return kind switch
|
|
{
|
|
UploadKind.Photo when length > MaxPhotoBytes => "Photos must be 10 MB or smaller.",
|
|
UploadKind.Video when length > MaxVideoBytes => "Videos must be 100 MB or smaller.",
|
|
UploadKind.Document when length > MaxDocumentBytes =>
|
|
"Documents must be 50 MB or smaller.",
|
|
UploadKind.Unknown when length > MaxVideoBytes =>
|
|
"Videos must be 100 MB or smaller.",
|
|
_ => null
|
|
};
|
|
}
|
|
|
|
public static string? ValidateSize(string? contentType, string? fileName, long length)
|
|
=> ValidateSize(length, ResolveKind(contentType, fileName));
|
|
}
|
|
}
|