This repository builds a personal ShareX hosting app defined in PLAN.md.
Implement the project end-to-end using PLAN.md as the source of truth.
- Single-user personal deployment.
- Blazor Server (.NET 10) app in Docker.
- Self-hosted Convex backend (TypeScript) for storage + metadata.
- ShareX upload routes for image/file/text.
- Public view/download routes by slug.
- Admin area with:
- Overview page (
/admin/dashboard) with real-time stats and cross-type top records - Per-type pages (
/admin/images,/admin/files,/admin/texts) with real-time updates - Tools page (
/admin/tools) for credential hash generation and ShareX uploader config
- Overview page (
- Security: rate limiting, security headers, upload validation, session hardening, proxy trust model.
- Multi-user auth, RBAC, or account recovery.
- Enterprise-grade abuse controls beyond basic correctness.
- Running Convex inside the .NET Docker image.
- Primary spec:
PLAN.md - If implementation and plan conflict, update implementation to match plan unless user says otherwise.
- If plan ambiguity is discovered, ask user once with a concrete recommendation, then proceed.
CONVEX_URLCONVEX_ADMIN_KEY(HTTP API auth header:Authorization: Convex <key>)INTERNAL_API_SECRET(shared secret injected into Convex mutation args for function-level auth)PUBLIC_BASE_URLADMIN_PASSWORD_HASH(PBKDF2-SHA256; random temporary password generated at startup if missing)SHAREX_AUTH_KEY_HASH(PBKDF2-SHA256; random temporary key generated at startup if missing)
PUBLIC_BASE_URL must be normalized by trimming trailing slash before generating returned URLs.
TRUSTED_PROXY_IPS(comma-separated IPs/CIDRs for trusted reverse proxies)ADMIN_IP_ALLOWLIST(comma-separated IPs/CIDRs allowed to access/admin*)RATE_LIMIT_UPLOAD_PER_MINUTE(default20)RATE_LIMIT_VIEW_PER_MINUTE_PER_SLUG(default60)RATE_LIMIT_DOWNLOAD_PER_MINUTE_PER_SLUG(default30)RATE_LIMIT_ADMIN_LOGIN_PER_15MIN(default5)MAX_IMAGE_MB(default20)MAX_FILE_MB(default100)MAX_TEXT_KB(default1024)
POST /i/multipart image upload (magic-byte validated)POST /f/multipart file uploadPOST /t/form post withtextfield (fallback tocontentfield; ShareX FormURLEncoded)
Upload return format:
- Image:
{ "url": "{PUBLIC_BASE_URL}/i/{slug}" } - File:
{ "url": "{PUBLIC_BASE_URL}/f/{slug}" } - Text:
{ "url": "{PUBLIC_BASE_URL}/t/{slug}" }
GET /content/i/{slug}- streams image with original content type, 1-year immutable cache, ETag, range requests
GET /download/i/{slug}GET /download/f/{slug}GET /download/t/{slug}
Behavior:
- Lookup slug.
- If found, increment downloads once.
- Serve/redirect content (forced
application/octet-streamto prevent stored XSS).
/i/{slug},/f/{slug},/t/{slug}:- Load by slug.
- If found, increment views once per request.
- If not found, render 404 component.
- Text uploads do not accept client filename.
texts:creategenerates/stores filename astext-{slug}.txt.
Upload flow must be rollback-safe:
- Generate upload URL.
- Upload bytes and get
storageId. - Call
{type}:create. - If create fails: call
storage:deleteStorageObject(storageId), then return error. - If partial success then later failure: call
{type}:deleteById(id)andstorage:deleteStorageObject(storageId), then return error. - Cleanup is best-effort. If cleanup also fails, log details and return HTTP 500.
- Functions live in
convex/functions/(configured viaconvex/convex.json). schema.tswith tables:images,files,texts,counters.- Upload table shared fields:
slug,storageId,fileName,contentType,fileSize,views,downloads. counterstable fields:table,count,views,downloads,storageBytes(pre-aggregated stats).by_slugindex on each upload table;by_tableindex on counters.auth.ts-requireApiSecret()validatesINTERNAL_API_SECRETenv var (called by all mutations and admin-facing queries).crud.ts-buildCrud(table)returns shared CRUD implementations:getBySlug(query)incrementViews(mutation, adjusts counter)incrementDownloads(mutation, adjusts counter)list(query, cursor pagination, default 50, max 50)deleteById(mutation, cleans up storage object, adjusts counter)
helpers.ts- slug generation (8-char alphanumeric, collision check), type/table mapping, limit normalization.- Per-type module (
images.ts,files.ts,texts.ts):createmutation (generates slug, inserts record, adjusts counter)- Re-exports shared CRUD from
buildCrud()
counters.ts:adjustCounter(internal helper for atomic counter updates)getCounter(internal helper for reading counter values)
storage.ts:generateUploadUrl(requires API secret)deleteStorageObject(requires API secret)
stats.ts:getOverviewStats(dashboard callouts from counters + cross-type top records)getTypeStats(type, cursor, limit)(per-type totals from counters + paginated records)getImageStatsLive/getFileStatsLive/getTextStatsLive(queries acceptingapiSecretfor WebSocket subscriptions)
- Services:
ConvexService.csfor query/mutation/upload operations (HTTP wrapper withAuthorization: Convex <key>+apiSecretarg injection for mutations).AuthService.csfor ShareX key and admin password validation (PBKDF2-SHA256 hashes only; generates random temporary secrets if env vars missing).UploadService.csorchestrating rollback-safe upload flow.ShareXUploaderService.csfor generating ShareX .sxcu configuration files.
- Features (vertical slice):
Features/Uploads/UploadEndpoints.cs- upload route handlers with magic-byte validation for images.Features/Downloads/DownloadEndpoints.cs- download + content route handlers.Features/Admin/AdminAuthEndpoints.cs- admin login/logout with anti-forgery.Features/Admin/AdminAccessMiddleware.cs- admin access control (session + IP allowlist).Features/Public/NotFoundRedirectMiddleware.cs- 404 redirect handling.
- Minimal API endpoints in
Program.csfor upload/download/admin login/logout + OpenAPI/Scalar UI. - Admin session auth via server-side session (single-instance assumptions are acceptable), with:
- PBKDF2-SHA256 password validation
- anti-forgery validation on admin login/logout
- login rate limiting
- optional admin IP allowlist
- session hardening (HttpOnly, SameSite=Strict, Secure, path-restricted, absolute 8-hour timeout)
- Security middleware:
- Rate limiting (per-route partitions: upload, view, download, admin login)
- Security headers (X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy, CSP)
- Configurable upload size limits per type
- Proxy trust model via
TRUSTED_PROXY_IPS
- UI pages:
- Home, ImageView, FileView, TextView, NotFound, Error
- AdminLogin, AdminDashboard, AdminImages, AdminFiles, AdminTexts, AdminTools
- Shared components:
AdminRecordList.razor- reusable record list with staggered row animationAdminNavigation.razor- admin navigation
- Convex deploy works.
- Upload tests for
/i/,/f/, and/t/return valid URLs. - Public view pages render expected content.
- Download routes work and increment counters.
- Abuse/rate-limit checks return HTTP 429 after threshold for upload and admin login routes.
- Auth checks:
- Missing upload auth returns 401.
- Unauthenticated admin route redirects to login.
- Admin overview shows callouts, top records, and links.
- Per-type admin pages show totals + paginated records with live updates.
- Pagination loads older records.
- Rollback test: create failure after upload does not orphan storage.
- Cleanup-failure test: logs failure and returns 500.
docker build && docker runworks with end-to-end checks.dotnet testpasses all unit tests.
- Implement, run tests, and report exact pass/fail outcomes.
- If blocked, state blocker clearly with the smallest required user input.
- Keep changes minimal, coherent, and aligned to
PLAN.md.