Compare commits

..
4 Commits
Author SHA1 Message Date
dmg 5e693e2cdd fix(auth): use public URL for magic-link redirects
CI / validate (push) Successful in 4m53s
Release / release (push) Successful in 6m30s
2026-08-01 16:44:01 -04:00
dmg 9440c651b6 docs(design): verify immutable release policy
CI / validate (push) Successful in 4m44s
Release / release (push) Successful in 4m52s
2026-08-01 16:06:07 -04:00
dmg ccb44fa253 fix(release): publish immutable image tags only
CI / validate (push) Successful in 5m5s
Release / release (push) Successful in 7m5s
2026-08-01 15:57:17 -04:00
dmg cee0378f8f docs(design): verify bot release artifact
CI / validate (push) Has been cancelled
Release / release (push) Has been cancelled
2026-08-01 15:56:02 -04:00
8 changed files with 32 additions and 19 deletions
-6
View File
@@ -130,10 +130,8 @@ jobs:
--target runner \
--build-arg VERSION="$VERSION" \
-t "git.garvis.dev/dmg/minecraft-account-manager:${VERSION}" \
-t git.garvis.dev/dmg/minecraft-account-manager:latest \
.
docker push "git.garvis.dev/dmg/minecraft-account-manager:${VERSION}"
docker push git.garvis.dev/dmg/minecraft-account-manager:latest
- name: Build and push Discord bot image
if: steps.release.outputs.created == 'true'
@@ -145,10 +143,8 @@ jobs:
--target bot \
--build-arg VERSION="$VERSION" \
-t "git.garvis.dev/dmg/minecraft-account-manager-bot:${VERSION}" \
-t git.garvis.dev/dmg/minecraft-account-manager-bot:latest \
.
docker push "git.garvis.dev/dmg/minecraft-account-manager-bot:${VERSION}"
docker push git.garvis.dev/dmg/minecraft-account-manager-bot:latest
- name: Build and push migration image
if: steps.release.outputs.created == 'true'
@@ -160,10 +156,8 @@ jobs:
--target migrate \
--build-arg VERSION="$VERSION" \
-t "git.garvis.dev/dmg/minecraft-account-manager-migrate:${VERSION}" \
-t git.garvis.dev/dmg/minecraft-account-manager-migrate:latest \
.
docker push "git.garvis.dev/dmg/minecraft-account-manager-migrate:${VERSION}"
docker push git.garvis.dev/dmg/minecraft-account-manager-migrate:latest
- name: Create Gitea release and upload Velocity JAR
if: steps.release.outputs.created == 'true'
+3 -2
View File
@@ -3,6 +3,7 @@ import { createAuthRepository, ipObservations, recordEvent } from "@minecraft-ac
import { getClientIp } from "@minecraft-account-manager/network";
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
import { applicationUrl } from "@/lib/application-url";
import { db } from "@/lib/database";
import { getIpIntelligence, toAuditIpData } from "@/lib/ip-intelligence";
@@ -38,7 +39,7 @@ export async function GET(request: NextRequest) {
]);
const destination = result.user.firstName ? "/account" : "/welcome";
const response = NextResponse.redirect(new URL(destination, request.url));
const response = NextResponse.redirect(applicationUrl(destination));
response.cookies.set(SESSION_COOKIE_NAME, result.sessionToken, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
@@ -49,7 +50,7 @@ export async function GET(request: NextRequest) {
return response;
} catch (error) {
if (error instanceof InvalidLoginCodeError) {
return NextResponse.redirect(new URL("/auth/error", request.url));
return NextResponse.redirect(applicationUrl("/auth/error"));
}
throw error;
}
+9
View File
@@ -0,0 +1,9 @@
import { describe, expect, it } from "vitest";
import { applicationUrl } from "./application-url";
describe("applicationUrl", () => {
it("builds browser redirects from the configured public application URL", () => {
expect(applicationUrl("/welcome", "https://portal.somc.club"))
.toEqual(new URL("https://portal.somc.club/welcome"));
});
});
+4
View File
@@ -0,0 +1,4 @@
export function applicationUrl(path: string, baseUrl = process.env.APP_URL) {
if (!baseUrl) throw new Error("APP_URL is required to build public application URLs");
return new URL(path, baseUrl);
}
+4
View File
@@ -2,6 +2,10 @@
## 2026-08-01
* **Fix**: Build magic-link redirects from the configured public portal URL instead of the reverse proxy's internal request origin.
* **Verify**: Confirmed `v1.1.1` left all pre-existing `latest` digests unchanged while publishing versioned artifacts.
* **Refine**: Removed mutable `latest` publication so all deployable artifacts use explicit semantic versions.
* **Verify**: Confirmed the `v1.1.0` Discord bot image and matching web, migration, and Velocity artifacts.
* **Extend**: Added a releasable Discord bot image and a dependency-free web health endpoint for Kubernetes deployment.
* **Verify**: Confirmed the initial `v1.0.0` release, public Velocity JAR, and versioned and `latest` web and migration image manifests.
* **Create**: Added Gitea CI and semantic-release pipelines for downloadable Velocity JARs and versioned web and migration images.
+4 -1
View File
@@ -3,7 +3,7 @@ type: User Story
title: Authenticate with a Discord magic link
description: Discord users receive private single-use links that establish secure portal sessions.
tags: [player, discord, authentication, security]
timestamp: 2026-08-01T18:43:58Z
timestamp: 2026-08-01T20:43:46Z
story_id: US-002
status: verified
---
@@ -19,6 +19,7 @@ As a Discord community member, I want `/register` and `/account` to issue a priv
- [x] Given a login token, then it expires after ten minutes and can be consumed only once.
- [x] Given repeated link requests, then requests are rate limited per Discord user and older active links are invalidated.
- [x] Given a valid link, when it is consumed, then the Discord user is created or refreshed and a secure seven-day session is established.
- [x] Given a magic-link result behind a reverse proxy, then the browser is redirected through the configured public application URL rather than an internal container address.
- [x] Given an invalid, expired, or consumed link, then the user sees a safe recovery page instructing them to request another link.
# Implementation
@@ -27,10 +28,12 @@ As a Discord community member, I want `/register` and `/account` to issue a priv
- [`packages/auth/src/index.ts`](../packages/auth/src/index.ts)
- [`packages/database/src/auth-repository.ts`](../packages/database/src/auth-repository.ts)
- [`apps/web/src/app/auth/discord/route.ts`](../apps/web/src/app/auth/discord/route.ts)
- [`apps/web/src/lib/application-url.ts`](../apps/web/src/lib/application-url.ts)
# Validation
- [`packages/auth/test/magic-link.test.ts`](../packages/auth/test/magic-link.test.ts)
- [`apps/web/src/lib/application-url.test.ts`](../apps/web/src/lib/application-url.test.ts)
- Discord command and authentication workspaces pass TypeScript validation.
# Related Stories
+7 -6
View File
@@ -3,9 +3,9 @@ type: User Story
title: Build and publish versioned releases
description: Gitea Actions validate every change and publish semantically versioned Velocity and container artifacts.
tags: [operations, ci, release, velocity, docker]
timestamp: 2026-08-01T19:46:09Z
timestamp: 2026-08-01T20:05:49Z
story_id: US-016
status: implemented
status: verified
---
# User Story
@@ -20,9 +20,10 @@ As a platform operator, I want automated validation and semantic releases, so th
- [x] Main-branch conventional commits determine the next semantic version and create a `vMAJOR.MINOR.PATCH` tag.
- [x] A release build embeds the semantic version in the Velocity plugin and JAR filename.
- [x] A public Gitea release exposes the versioned Velocity JAR as a downloadable asset.
- [x] Releases publish versioned and `latest` web runtime images to the Gitea registry.
- [ ] Releases publish versioned and `latest` Discord bot images to the Gitea registry.
- [x] Releases publish versioned and `latest` migration images that run versioned Drizzle migrations.
- [x] Releases publish semantically versioned web runtime images to the Gitea registry.
- [x] Releases publish semantically versioned Discord bot images to the Gitea registry.
- [x] Releases publish semantically versioned migration images that run versioned Drizzle migrations.
- [x] Releases do not publish mutable container tags such as `latest`.
- [x] Runtime containers use unprivileged users and exclude development source and secrets where practical.
- [x] Operators are told which repository secrets must be configured before the first push.
@@ -37,7 +38,7 @@ As a platform operator, I want automated validation and semantic releases, so th
# Validation
Local OKF, lint, typecheck, test, Next.js build, and versioned Velocity JAR checks pass. Initial Gitea CI and release runs succeeded. Release `v1.0.0` provides a publicly downloadable JAR whose Velocity metadata reports `1.0.0`. Registry manifests were resolved for versioned and `latest` web and migration images. Discord bot image publication is implemented for the next feature release. Pull-request commitlint configuration is present; its conditional execution will be exercised by the first pull request.
Local OKF, lint, typecheck, test, Next.js build, and versioned Velocity JAR checks pass. Initial Gitea CI and release runs succeeded. Release `v1.0.0` provides a publicly downloadable JAR whose Velocity metadata reports `1.0.0`. Registry manifests were resolved for the published semantic-version tags. Release `v1.1.0` also publishes resolvable versioned web, Discord bot, and migration manifests and a public Velocity JAR whose metadata reports `1.1.0`. Release `v1.1.1` published immutable semantic-version tags only; prior `latest` digests remained unchanged. Pull-request commitlint configuration is present; its conditional execution will be exercised by the first pull request.
# Related Stories
+1 -4
View File
@@ -32,13 +32,10 @@ Each release creates:
- Gitea release asset `minecraft-account-manager-velocity-VERSION.jar`
- `git.garvis.dev/dmg/minecraft-account-manager:VERSION`
- `git.garvis.dev/dmg/minecraft-account-manager:latest`
- `git.garvis.dev/dmg/minecraft-account-manager-bot:VERSION`
- `git.garvis.dev/dmg/minecraft-account-manager-bot:latest`
- `git.garvis.dev/dmg/minecraft-account-manager-migrate:VERSION`
- `git.garvis.dev/dmg/minecraft-account-manager-migrate:latest`
Use immutable version tags for deployments. `latest` is a convenience pointer to the newest release.
Only immutable semantic-version tags are published. Mutable tags such as `latest` must never be used in deployments or release-asset URLs.
## Discord bot