Allow beta-typed Modrinth builds for EasyAuth (easyauth:beta)

EasyAuth publishes its 26.x builds as beta on Modrinth, and the itzg
resolver only accepts release-typed versions by default, so the mc
container failed to start. Pin easyauth:beta in the default mod list and
teach the panel's mod manager to compare entries by base slug so version
qualifiers don't break protected/duplicate checks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Aryan SinghandClaude Fable 5 committed 2026-07-10 12:26:33 -05:00
1 parent 279d137ce5
commit 795c2af50b
6 files changed
+32 -11

No files matched your search

+3 -1
View File
@@ -24,7 +24,9 @@ MC_SEED=
# Server-side mods (Modrinth project slugs, comma-separated).
# Auto-resolved to the newest compatible release on every container start.
MODRINTH_PROJECTS=fabric-api,easyauth,spark,lithium,ferrite-core
# Append ":beta" to allow beta-typed builds for a project (EasyAuth ships
# its 26.x builds as beta on Modrinth).
MODRINTH_PROJECTS=fabric-api,easyauth:beta,spark,lithium,ferrite-core
# To run a full modpack instead, set its Modrinth slug (e.g. adrenaline)
# and see docs/mods-and-modpacks.md before enabling.
MODRINTH_MODPACK=
+8 -4
View File
@@ -31,6 +31,10 @@ type SearchHit = {
const PROTECTED = new Set(["fabric-api", "easyauth"]);
// Managed entries may carry a version qualifier ("easyauth:beta") — compare
// by the base slug.
const baseSlug = (entry: string) => entry.split(":")[0].toLowerCase();
export default function ModsPage() {
const { data, refresh } = useApi<ModsData>("/api/mods", 0);
const [query, setQuery] = useState("");
@@ -82,7 +86,7 @@ export default function ModsPage() {
}
};
const managedSet = new Set((data?.managed ?? []).map((s) => s.toLowerCase()));
const managedSet = new Set((data?.managed ?? []).map(baseSlug));
return (
<div className="space-y-4">
@@ -121,7 +125,7 @@ export default function ModsPage() {
)}
{hits?.length === 0 && <p className="py-6 text-center text-xs text-muted">No compatible mods found.</p>}
{hits?.map((h) => {
const already = managedSet.has(h.slug.toLowerCase());
const already = managedSet.has(baseSlug(h.slug));
return (
<div key={h.slug} className="flex items-center gap-3 rounded-lg border border-border bg-surface2 p-2.5">
{h.icon_url ? (
@@ -166,13 +170,13 @@ export default function ModsPage() {
{(data?.managed ?? []).map((slug) => (
<li key={slug} className="flex items-center gap-2 rounded-lg border border-border bg-surface2 px-3 py-2">
<span className="font-mono text-sm">{slug}</span>
{PROTECTED.has(slug.toLowerCase()) && (
{PROTECTED.has(baseSlug(slug)) && (
<Badge color="info">
<Lock size={9} /> required
</Badge>
)}
<span className="ml-auto">
{!PROTECTED.has(slug.toLowerCase()) && (
{!PROTECTED.has(baseSlug(slug)) && (
<Button
size="sm"
variant="danger"
+11 -3
View File
@@ -40,6 +40,10 @@ export async function searchMods(query: string): Promise<ModSearchResult[]> {
return data.hits;
}
/**
* Entries may carry a version qualifier after a colon (e.g. "easyauth:beta"
* allows beta-typed builds); comparisons always use the base slug.
*/
export function managedSlugs(): string[] {
return (readEnvFile()["MODRINTH_PROJECTS"] ?? "")
.split(",")
@@ -47,22 +51,26 @@ export function managedSlugs(): string[] {
.filter(Boolean);
}
export function baseSlug(entry: string): string {
return entry.split(":")[0].toLowerCase();
}
const PROTECTED_SLUGS = new Set(["fabric-api", "easyauth"]);
export function addManagedMod(slug: string): string[] {
if (!/^[a-z0-9][a-z0-9\-_]{0,63}$/i.test(slug)) throw new Error("Invalid Modrinth slug");
const slugs = managedSlugs();
if (slugs.some((s) => s.toLowerCase() === slug.toLowerCase())) return slugs;
if (slugs.some((s) => baseSlug(s) === slug.toLowerCase())) return slugs;
const next = [...slugs, slug.toLowerCase()];
writeEnvFile({ MODRINTH_PROJECTS: next.join(",") });
return next;
}
export function removeManagedMod(slug: string): string[] {
if (PROTECTED_SLUGS.has(slug.toLowerCase())) {
if (PROTECTED_SLUGS.has(baseSlug(slug))) {
throw new Error(`${slug} is required by the panel (auth/loader) and can't be removed here`);
}
const next = managedSlugs().filter((s) => s.toLowerCase() !== slug.toLowerCase());
const next = managedSlugs().filter((s) => baseSlug(s) !== baseSlug(slug));
writeEnvFile({ MODRINTH_PROJECTS: next.join(",") });
return next;
}
+4 -2
View File
@@ -33,8 +33,10 @@ services:
ENABLE_RCON: "true"
RCON_PASSWORD: ${RCON_PASSWORD:?generate with scripts/setup.sh}
# Server-side mods, auto-resolved for the current MC version on every start
MODRINTH_PROJECTS: ${MODRINTH_PROJECTS:-fabric-api,easyauth,spark,lithium,ferrite-core}
# Server-side mods, auto-resolved for the current MC version on every start.
# ":beta" allows beta-typed builds for that project (EasyAuth publishes
# its 26.x builds as beta).
MODRINTH_PROJECTS: ${MODRINTH_PROJECTS:-fabric-api,easyauth:beta,spark,lithium,ferrite-core}
MODRINTH_DOWNLOAD_DEPENDENCIES: required
# Modpack support (leave empty for lean Fabric; see docs/mods-and-modpacks.md)
+5 -1
View File
@@ -36,9 +36,13 @@ restart the server.
Managed mods are pinned by *project slug*, not version, in `.env`:
```
MODRINTH_PROJECTS=fabric-api,easyauth,spark,lithium,ferrite-core
MODRINTH_PROJECTS=fabric-api,easyauth:beta,spark,lithium,ferrite-core
```
A `:beta` (or `:alpha`) suffix widens which release types are accepted for that
project — EasyAuth publishes its 26.x builds as beta on Modrinth, so it needs the
suffix. Without one, only release-typed builds are considered.
Every time the server container (re)starts, the itzg image resolves each slug to the
**newest release compatible with your Minecraft version** and downloads it. So:
+1
View File
@@ -8,6 +8,7 @@
|---|---|
| `EULA` complaints | `EULA=TRUE` is set by compose — make sure you didn't override it |
| A mod failed to resolve for your version | Remove that slug on the Mods page, Apply. (New MC version + mod hasn't updated yet.) |
| `No candidate versions of '<mod>' … matched versionType=release` | The mod only ships beta builds for this MC version — append `:beta` to its entry in `MODRINTH_PROJECTS` (e.g. `easyauth:beta`) and `docker compose up -d mc` |
| `OutOfMemoryError` | Lower `MC_VIEW_DISTANCE`, or raise `MC_MEMORY`/`MC_CONTAINER_MEM_LIMIT` if the host has headroom |
| Port already in use | Something else on 25565 — change `MC_PORT` in `.env` |
| Crash right after "Done" | A mod conflict — disable recently added jars (Mods page → power icon) |