Add Modpack feature: bundle installed mods into a player-downloadable zip#64
Merged
Conversation
… zip
New end-to-end feature: admin picks installed mods → names + versions a
"pack" → builds a zip → publishes it. Once published, an anonymous
download CTA appears top-right on the login page so players can grab
matching client-side mods without needing a panel account.
Spec (confirmed with Ada):
1. Single-server scope = exactly one modpack at a time
2. Latest version only — re-publishing overwrites the previous zip
3. Just the Mods/ folder contents — no config files, no other layers
4. Top-right CTA on the login page (not a full panel)
5. Three-state workflow: draft → published → archived
Backend surface:
Config/Migrations/010_modpack.sql
Singleton 'modpack' table. status, filename, size, mod_count, mod_list
(JSON array of folder names), description, download_count.
Data/Entities/Modpack.cs
Plain entity mirroring the table.
Data/Repositories/ModpackRepository.cs
GetCurrent / GetPublished / Upsert / IncrementDownloadCount / Delete.
Repo enforces singleton semantics (the table can structurally hold
many rows; this layer keeps it to one).
Services/ModpackService.cs
SaveDraft (validates mods exist on disk), BuildZip (atomic temp-file
pattern so partial writes can't leak to public downloaders),
Publish / Archive / Unarchive / Delete, OpenPublishedForDownload
(FileStream for the controller to pipe through StreamContent).
Zips land in <game-root>/KitsuneModpacks/ — same convention as
KitsuneBackups.
Web/Controllers/ModpackController.cs
[Authorize] for the admin endpoints, [AllowAnonymous] for the two
public-facing ones (GET /api/modpack/public for metadata, GET
/api/modpack/public/download for the zip stream).
Core/ServiceRegistry.cs
Registers ModpackService alongside BackupService.
Frontend surface:
api/modpack.ts
REST client for the 7 admin endpoints + 1 public metadata fetch.
Public download URL exported as a constant so anchor tags can hit
it directly without going through axios.
types/index.ts
Modpack, ModpackState, PublicModpack, ModpackStatus, etc.
views/ModsView.vue
Third tab on the existing Mods page ("Modpack"). Form fields for
name + version + mod picker (MultiSelect from installedMods) +
description. Status badge, action buttons gated by current state
(Save Draft / Build Zip / Publish / Archive / Unarchive / Delete).
Reveals the public download URL when published so admin can share
or smoke-test it.
views/LoginView.vue
Top-right CTA pill that renders only when getPublishedModpack()
resolves to a record. Shows name + version, opens the public
download endpoint in a new tab. Hidden cleanly when no pack is
published (silent failure on the anonymous metadata call).
i18n: full English in en.ts, native translations in de/fr/es, English
placeholders in ja/ko/zh-CN/zh-TW (consistent with prior locale
rollout pattern — translations TBD by community native speakers).
Tested locally: full build clean, vue-tsc structural check across all
8 locales passes, 0 C# errors, all expected files in dist/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 tasks
AdaInTheLab
added a commit
that referenced
this pull request
May 13, 2026
Rolls up everything since v2.6.2 into a single release tag. v2.6.3 was bumped on main (#63) but never tagged or released — folding it into v2.6.4 keeps the public release history clean. Marquee additions since v2.6.2: - Modpack (#64) — Bundle installed mods into a single zip players can download from the login page. No panel account required. Three-state workflow (draft → published → archived), top-right CTA on the login page, anonymous metadata + download endpoints. Atomic temp-file zip build so public downloaders can't grab a half-written archive. - Graceful Restart (#57, #58) — Scheduled daily restarts with player-friendly in-game countdown warnings. Configurable warning ladder, IANA-timezone schedule (DST-aware), krestart console command, REST endpoints, panel Settings tab. - Backups bug fix (#56) — BackupService had a redundant conn.Open() call that silently dropped writes due to a custom System.Data.SQLite quirk. Reads worked, so the bug went unnoticed until first hands-on test. Six call sites fixed, class doc comment added so future-me doesn't reintroduce it. - German / French / Spanish locales (#59, #61, #62) — Full polite-form translations across ~250 keys / 30 namespaces. Browser auto-detect picks the right one for de-* / fr-* / es-* visitors. Tooltip on the language switcher noting that in-game broadcast messages don't auto-translate. - Favicon (#60) — Regenerated all four favicon variants (svg / 16 / 32 / ico) from kitsune-command-logo-transparent.png so the browser tab matches the panel's sidebar logo. - README updates: features list now reflects all the above. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Like Minecraft modpacks: admin picks installed mods, names + versions a "pack," builds it, and publishes. Once published, an anonymous download CTA appears top-right on the login page so players can grab matching client-side mods without needing a panel account.
Spec (per your earlier answers)
Mods/folder contents — no config files, no other layersWhat it does
Admin flow (Mods view → new "Modpack" tab):
Public flow (login page, no auth):
Backend
Notable: zip builds use a `.building` temp file then atomic rename, so a public downloader can never grab a half-written archive mid-rebuild.
Frontend
Test plan
🤖 Generated with Claude Code