Skip to content

Commit 1e42966

Browse files
authored
feat!: migrate Patch Roulette from Spring to Cloudflare Workers (#108)
Replace the Spring/Gradle backend with a Cloudflare Worker that serves the SvelteKit UI and Hono API, backed by a Durable Object SQLite database. - add Drizzle schema, migrations, referential integrity, and Worker tests - use Cloudflare Access identities with per-request Svelte auth state - add legacy Spring export and one-time Worker import/claim flow - validate API payloads, reject duplicate/future legacy records, and serialize claims - update account/legacy-claim UI and replace Luxon with native Intl/Date formatting - consolidate frontend, deployment, CI, and Bun/Oxlint/Oxfmt tooling at the repo root - preserve patch lifecycle, duration/statistics, and version-management behavior BREAKING CHANGE: remove the Spring backend, Basic-auth login flow, old API endpoints, and Gradle build. Clients must use the new /api routes and Cloudflare Access/Managed OAuth authentication.
1 parent e0dd4fa commit 1e42966

106 files changed

Lines changed: 3713 additions & 2755 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitattributes

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
1-
#
2-
# https://help.github.com/articles/dealing-with-line-endings/
3-
#
4-
# Linux start script should use lf
5-
/gradlew text eol=lf
1+
# Documentation: https://www.git-scm.com/docs/gitattributes
62

7-
# These are Windows script files and should use crlf
8-
*.bat text eol=crlf
9-
10-
# Binary files should be left untouched
11-
*.jar binary
3+
# Set default behavior to automatically normalize line endings.
4+
* text=auto eol=lf
125

6+
# Enforce CRLF line endings in Windows batch scripts to avoid issues.
7+
*.cmd text eol=crlf
8+
*.bat text eol=crlf

.github/workflows/ci.yml

Lines changed: 24 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,29 @@
11
name: CI
22

33
on:
4-
push:
5-
branches: ["**"]
6-
pull_request:
4+
push:
5+
branches: ["**"]
6+
pull_request:
77

88
jobs:
9-
checks:
10-
name: Run checks
11-
# Only run on PRs if the source branch is on someone else's repo
12-
if: ${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }}
13-
runs-on: ubuntu-latest
14-
steps:
15-
- name: Checkout Repository
16-
uses: actions/checkout@v7
17-
- name: Set up JDK 25
18-
uses: actions/setup-java@v5
19-
with:
20-
distribution: "temurin"
21-
java-version: 25
22-
check-latest: true
23-
- name: Setup Gradle
24-
uses: gradle/actions/setup-gradle@v6
25-
- name: Setup Bun
26-
uses: oven-sh/setup-bun@v2
27-
- name: Install frontend dependencies
28-
run: cd web && bun install
29-
- name: Lint frontend
30-
run: cd web && bun run lint
31-
- name: Check frontend
32-
run: cd web && bun run check
33-
- name: Build frontend
34-
run: cd web && bun run build
35-
- name: Build with Gradle
36-
run: ./gradlew build --stacktrace
37-
38-
publish:
39-
name: Publish container
40-
needs: checks
41-
if: ${{ needs.checks.result == 'success' && github.event_name == 'push' && github.ref == 'refs/heads/master' }}
42-
runs-on: ubuntu-latest
43-
steps:
44-
- name: Checkout Repository
45-
uses: actions/checkout@v7
46-
- name: Set up JDK 25
47-
uses: actions/setup-java@v5
48-
with:
49-
distribution: "temurin"
50-
java-version: 25
51-
check-latest: true
52-
- name: Setup Gradle
53-
uses: gradle/actions/setup-gradle@v6
54-
- name: Setup Bun
55-
uses: oven-sh/setup-bun@v2
56-
- name: Install frontend dependencies
57-
run: cd web && bun install
58-
- name: Build frontend
59-
run: cd web && bun run build
60-
- name: Login to GitHub Container Registry
61-
uses: docker/login-action@v4
62-
with:
63-
registry: ghcr.io
64-
username: ${{ github.actor }}
65-
password: ${{ secrets.GITHUB_TOKEN }}
66-
- name: Build and publish container
67-
run: ./gradlew jib --stacktrace
9+
checks:
10+
name: Run checks
11+
if: ${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }}
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Checkout Repository
15+
uses: actions/checkout@v7
16+
- name: Setup Bun
17+
uses: oven-sh/setup-bun@v2
18+
- name: Install dependencies
19+
run: bun install
20+
- name: Check formatting
21+
run: bun run format:check
22+
- name: Lint
23+
run: bun run lint
24+
- name: Check
25+
run: bun run check
26+
- name: Build and test
27+
run: bun run test
28+
- name: Build deployment bundle
29+
run: bunx wrangler deploy --config wrangler.jsonc --dry-run

.github/workflows/gradle-dependency-submission.yml

Lines changed: 0 additions & 25 deletions
This file was deleted.

.gitignore

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
1-
# Ignore Gradle project-specific cache directory
2-
.gradle
1+
.idea/
2+
.DS_Store
33

4-
# Ignore Gradle build output directory
5-
build
4+
node_modules/
5+
.output/
6+
.vercel/
7+
.netlify/
8+
.wrangler/
9+
/.svelte-kit/
10+
/build/
611

7-
.idea/
8-
.kotlin/
12+
.env
13+
.env.*
14+
!.env.example
15+
!.env.test
916

10-
database.mv.db
11-
database.trace.db
12-
db/
13-
!src/main/resources/db/
14-
!src/main/resources/db/migration/
15-
!src/main/resources/db/migration/**
16-
.DS_Store
17+
vite.config.js.timestamp-*
18+
vite.config.ts.timestamp-*
19+
20+
local-test-data/
File renamed without changes.

.oxfmtrc.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"$schema": "./node_modules/oxfmt/configuration_schema.json",
3+
"endOfLine": "lf",
4+
"useTabs": false,
5+
"tabWidth": 4,
6+
"printWidth": 160,
7+
"svelte": true,
8+
"sortTailwindcss": {
9+
"stylesheet": "./src/app.css"
10+
},
11+
"ignorePatterns": [
12+
"bun.lock",
13+
".agents/skills/kumo-design/**",
14+
".agents/skills/svelte-code-writer/**",
15+
".agents/skills/svelte-core-bestpractices/**",
16+
"skills-lock.json"
17+
]
18+
}

.oxlintrc.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"$schema": "./node_modules/oxlint/configuration_schema.json",
3+
"env": {
4+
"browser": true,
5+
"node": true
6+
},
7+
"categories": {
8+
"correctness": "error"
9+
},
10+
"options": {
11+
"typeAware": true
12+
},
13+
"ignorePatterns": [
14+
".agents/skills/kumo-design/**",
15+
".agents/skills/svelte-code-writer/**",
16+
".agents/skills/svelte-core-bestpractices/**",
17+
"skills-lock.json"
18+
]
19+
}

README.md

Lines changed: 44 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,67 @@
11
# Patch Roulette
22

3-
REST API and web interface for managing Paper updates.
3+
Patch Roulette manages Paper update work through a Cloudflare Worker, a Durable Object SQLite database, and a SvelteKit web interface.
44

5-
> [!NOTE]
6-
> This project is intended for internal use and does not guarantee stability, compatibility, support, or follow semantic versioning.
5+
> [!NOTE]
6+
> This project is intended for internal use and does not guarantee stability, compatibility, support, or semantic versioning.
77
8-
## Overview
8+
## Architecture
99

10-
### REST API
10+
- The Cloudflare Worker serves both the SvelteKit UI and the API.
11+
- API routes are under `/api` and are used directly by browser and CLI clients; see [docs/api.md](docs/api.md).
12+
- A single Durable Object owns the SQLite database and serializes patch claims.
13+
- Drizzle ORM manages the Durable Object schema and migrations.
14+
- Cloudflare Access protects every deployed hostname and every route, including `/api`.
15+
- Managed OAuth supplies CLI authentication. The application has no custom API-token system.
1116

12-
Powered by Spring Boot, backend for the web interface and `paperweight`. Routes are under `/api`.
17+
The Worker trusts Cloudflare Access as the authentication boundary. It extracts the `iss` and `sub` claims from `Cf-Access-Jwt-Assertion` and maps that external identity to an internal user. The deployed `workers.dev` or custom hostname must therefore be protected by the Access application; do not leave an alternate hostname unprotected.
1318

14-
### paperweight
19+
## Development
1520

16-
`paperweight` has tasks to interface with the REST API during the update process.
21+
Install the dependencies:
1722

18-
### Web Interface
23+
```sh
24+
bun install
25+
```
1926

20-
SvelteKit frontend using Kumo Svelte and Tailwind CSS for styling. Hosted as static files by the Spring Boot server.
27+
Run the Vite frontend and local Worker together:
2128

22-
#### Pages
29+
```sh
30+
bun run dev
31+
```
2332

24-
- [`/`](https://patch-roulette.papermc.io/): Management dashboard
25-
- [`/login`](https://patch-roulette.papermc.io/login) : Login page
33+
The local Worker uses a fixed development identity and local Durable Object SQLite state. Wrangler's normal local persistence is left enabled. Reset it with:
2634

27-
## Development
35+
```sh
36+
bun run db:reset
37+
```
2838

29-
### Setup
39+
Generate Drizzle migrations after changing `src/lib/db/schema.ts`:
3040

31-
- Install [Bun](https://bun.sh/) and execute `bun install` in `/web` to install the required dependencies for the frontend.
32-
- Install a JVM 25 or newer for the Gradle runtime (prefer a JDK to avoid extra downloads for a compiler).
41+
```sh
42+
bun run db:generate
43+
```
3344

34-
### Running Locally
45+
## Checks
3546

36-
- Run the frontend with `bun run devLocalServer` or `bun run devProdServer` in `/web`. `devLocalServer` will use localhost as the API, while `devProdServer` will use the production API at https://patch-roulette.papermc.io/api.
37-
- Run the backend with `./gradlew bootRun` in the project root.
47+
```sh
48+
bun run check
49+
bun run format:check
50+
bun run lint
51+
bun run test
52+
bunx wrangler deploy --config wrangler.jsonc --dry-run
53+
```
3854

39-
### Checks
55+
## Migration
4056

41-
- Run the frontend checks with `bun run lint`, `bun run check`, and `bun run build` in `/web`.
42-
- Run the backend checks and tests with `./gradlew build` in the project root.
57+
The temporary Spring-to-Worker migration procedure is documented in [docs/legacy.md](docs/legacy.md).
4358

44-
### Code Style
59+
## Deployment
4560

46-
- The frontend uses ESLint and Prettier for code style. Run `bun run format` to reformat and `bun run lint` to check style.
47-
- The backend uses Immaculate with Palantir Java Format. Run `./gradlew immaculateApply` to reformat and `./gradlew immaculateCheck` to check style.
61+
Build and deploy the Worker:
4862

49-
### Deployment
63+
```sh
64+
bun run deploy
65+
```
5066

51-
- Published to the GitHub Container Registry after successful pushes to `master` through the `publish` job in the CI workflow.
67+
Configure Cloudflare Access separately for the actual hostname used by the deployment. Protect the entire hostname, including `/api/*` and the Managed OAuth discovery endpoints. If both a `workers.dev` hostname and a custom hostname are reachable, protect both or disable the unused hostname.

0 commit comments

Comments
 (0)