The ignore rule `src/WishNinja/Data/` accidentally excluded the EF Core DbContext, entities, and migrations from the repo, breaking the CI build (WishNinja.Data namespace not found). Narrow the rule to ignore only the runtime artifacts (db/keys/uploads) and commit the missing source. Co-Authored-By: Claude Opus 4.8 <[email protected]>
🥷 WishNinja
A self-hosted gift wishlist manager for a closed group (family/friends). Create wishlists, add items with links, prices and images, and let others claim gifts so the owner never sees what's been claimed — preserving the surprise while preventing duplicate gifts.
Built with ASP.NET Core Blazor (.NET 10), EF Core + SQLite, and ASP.NET Core Identity. Ships as a single Docker image; all state lives in one mounted volume.
Features
- Wishlists & items — title/description, per-item link, price, priority, quantity and image.
- Three ways to add an image — upload a file, fetch from an image URL, or just
Ctrl+Va copied image. All images are stored locally on the data volume; pasted URLs are downloaded server-side and saved as files, so a wishlist never breaks when the source URL goes away. - Owner-hidden claims — when someone claims/reserves an item, the list owner can never see it. Other viewers see claims (and remaining quantity) to avoid double-gifting. Enforced at the query layer, not just hidden in the UI.
- Sharing — a list is visible to all members or only to specific people you choose.
- Invite-only accounts — no open registration. Admins invite by email; the recipient follows a single-use link to set their display name + password. Password resets via email.
- Admin area — manage users (promote/disable) and invitations.
Quick start (Docker Compose)
docker compose up --build
Then open http://localhost:8080, and sign in with the bootstrap admin credentials from
docker-compose.yml (SEED_ADMIN_EMAIL / SEED_ADMIN_PASSWORD). Change these before first run.
If SMTP isn't configured, emails aren't sent — but invite links are still shown directly in the admin Invitations page, so you can onboard users without an email server.
Configuration
All settings are environment variables (double-underscore maps to nested config).
| Variable | Purpose | Default |
|---|---|---|
WishNinja__DataPath |
Directory for the SQLite db, uploads and keys | /data (in container) |
WishNinja__BaseUrl |
Public URL, used in email links | https://localhost:7777 |
SEED_ADMIN_EMAIL |
First-run bootstrap admin email | — |
SEED_ADMIN_PASSWORD |
First-run bootstrap admin password | — |
WishNinja__Smtp__Host |
SMTP server (empty ⇒ emails logged, not sent) | empty |
WishNinja__Smtp__Port |
SMTP port | 587 |
WishNinja__Smtp__UseStartTls |
Use STARTTLS | true |
WishNinja__Smtp__User / __Password |
SMTP credentials | empty |
WishNinja__Smtp__FromAddress / __FromName |
Sender identity | — |
WishNinja__Uploads__MaxBytes |
Max upload size in bytes | 5242880 (5 MB) |
WishNinja__Invites__ExpiryHours |
Invite link lifetime | 168 (7 days) |
ConnectionStrings__DefaultConnection |
Override the SQLite connection string | derived from DataPath |
The container listens on port 8080 over plain HTTP — terminate TLS at your reverse proxy.
Running on Unraid
- Build & publish the image. Push to your Gitea repo; the included
.gitea/workflows/build.ymlruns the tests and pushesyour-gitea-host/youruser/wishninja:latestto Gitea's built-in container registry. (Or build locally and push manually.) - Add the container (Docker tab → Add Container, or import
unraid-template.xml):- Repository:
your-gitea-host/youruser/wishninja:latest - Port: host
8080→ container8080 - Path: host
/mnt/user/appdata/wishninja→ container/data - Env vars: at minimum
SEED_ADMIN_EMAIL,SEED_ADMIN_PASSWORD,WishNinja__BaseUrl, and SMTP settings.
- Repository:
- Reverse proxy (SWAG / Nginx Proxy Manager). Blazor's interactive server mode uses SignalR over WebSockets, so the proxy must have WebSocket support enabled (NPM: the "Websockets Support" toggle; SWAG: included in the default proxy-conf samples).
Everything (database, uploaded images, data-protection keys) lives under /data, so backing up
/mnt/user/appdata/wishninja backs up the whole app, and container updates preserve all state.
Development
# Run locally (uses ./Data for the SQLite db by default)
cd src/WishNinja
SEED_ADMIN_EMAIL=admin@local SEED_ADMIN_PASSWORD='Admin!2345' dotnet run
# Run the tests
dotnet test
EF Core migrations are applied automatically on startup. To add a migration after changing entities:
dotnet ef migrations add <Name> --project src/WishNinja/WishNinja.csproj --output-dir Data/Migrations
Project layout
src/WishNinja/ ASP.NET Core Blazor Web App
Data/ EF Core context, entities, migrations
Services/ EmailSender, InviteService, ImageService, WishlistService, seeding
Components/ Razor components (Pages, Account, Wishlists, Layout)
wwwroot/js/ clipboard-paste interop
tests/WishNinja.Tests/ xUnit tests (claim privacy, invite lifecycle, image validation)
Dockerfile multi-stage build → aspnet:10.0 runtime
docker-compose.yml local + Unraid reference
.gitea/workflows/ CI: test + build/push image