Scope
Sponsor logos (admin-managed in v1) and the public gallery feeding the home/sponsors pages.
| Method |
Path |
Auth |
Notes |
| POST |
/admin/sponsors/{id}/logo/sign |
admin |
Cloudinary signed-upload payload (same hand-rolled SHA-1 signer pattern as Members avatars — module owns its own signer instance/folder; never proxy image bytes) |
| PUT |
/admin/sponsors/{id} |
admin |
set LogoUrl (validate https + allowed host, mirroring avatar URL validation) + ShowInGallery toggle |
| GET |
/sponsorship/gallery |
anon |
cacheable; Active sponsors with a logo and ShowInGallery == true; name + logo + tier only |
Error codes
sponsorship.not_found, sponsorship.invalid_logo_url, sponsorship.signing_unconfigured (503, mirroring members.avatar_signing_unconfigured)
Dependencies
Blocked by #14; admin endpoints need #1's policy/group.
Acceptance criteria
Conventions (project-wide, non-negotiable)
- .NET 9, records for immutable shapes, file-scoped namespaces, primary constructors where they read well. Minimal-API endpoints grouped per module via
IEndpointModule.MapEndpoints.
Result<T> (SharedKernel) instead of exception-driven control flow. Endpoint results map failures to ProblemDetails with the stable error codes listed above — the frontend keys off them.
- Module owns its EF Core
DbContext mapped to its own Postgres schema. Modules never reference each other's internals — cross-module needs go through a public contract interface or an in-process domain event (IEventPublisher).
- All external calls (GitHub, Stripe, Brevo, Cloudinary, Meilisearch) behind interfaces owned by the consuming module.
- Every list endpoint paginated (offset is fine). xUnit tests in
tests/CommunityPro.Tests/<Module>/ following the existing harness patterns (see Members/MembersTestHarness.cs).
Scope
Sponsor logos (admin-managed in v1) and the public gallery feeding the home/sponsors pages.
/admin/sponsors/{id}/logo/sign/admin/sponsors/{id}LogoUrl(validate https + allowed host, mirroring avatar URL validation) +ShowInGallerytoggle/sponsorship/galleryActivesponsors with a logo andShowInGallery == true; name + logo + tier onlyError codes
sponsorship.not_found,sponsorship.invalid_logo_url,sponsorship.signing_unconfigured(503, mirroringmembers.avatar_signing_unconfigured)Dependencies
Blocked by #14; admin endpoints need #1's policy/group.
Acceptance criteria
Conventions (project-wide, non-negotiable)
IEndpointModule.MapEndpoints.Result<T>(SharedKernel) instead of exception-driven control flow. Endpoint results map failures to ProblemDetails with the stable error codes listed above — the frontend keys off them.DbContextmapped to its own Postgres schema. Modules never reference each other's internals — cross-module needs go through a public contract interface or an in-process domain event (IEventPublisher).tests/CommunityPro.Tests/<Module>/following the existing harness patterns (seeMembers/MembersTestHarness.cs).