From a49416854ee801726901c9f8533a6c8ad9b3d2dd Mon Sep 17 00:00:00 2001 From: "Christoph K." Date: Mon, 6 Apr 2026 10:32:04 +0200 Subject: [PATCH] Remove nginx/webapp container; single Go server serves SPA + API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add root Dockerfile: node build → copy dist into Go embed path → distroless binary - Update docker-compose: one service (api on :9050), DB renamed ralph→pamietnik - Remove references to RALPH/reisejournal across all docs and configs Co-Authored-By: Claude Sonnet 4.6 --- .claude/agents/code-reviewer.md | 2 +- .claude/agents/dokumentar.md | 2 +- .claude/agents/programmierer.md | 2 +- .claude/agents/security-reviewer.md | 2 +- .claude/agents/software-architekt.md | 2 +- .claude/agents/tester.md | 2 +- CLAUDE.md | 6 ++--- Dockerfile | 24 +++++++++++++++++++ README.md | 2 +- app/app/build.gradle.kts | 4 ++-- .../reisejournal/ExampleInstrumentedTest.kt | 4 ++-- .../de/jacek/reisejournal/MainActivity.kt | 6 ++--- .../kotlin/de/jacek/reisejournal/RalphApp.kt | 4 ++-- .../jacek/reisejournal/domain/Trackpoint.kt | 2 +- .../jacek/reisejournal/ui/home/HomeScreen.kt | 4 ++-- .../reisejournal/ui/home/HomeViewModel.kt | 2 +- .../reisejournal/ui/navigation/NavGraph.kt | 4 ++-- .../de/jacek/reisejournal/ui/theme/Color.kt | 2 +- .../de/jacek/reisejournal/ui/theme/Theme.kt | 2 +- .../de/jacek/reisejournal/ui/theme/Type.kt | 2 +- .../de/jacek/reisejournal/ExampleUnitTest.kt | 2 +- app/settings.gradle.kts | 2 +- backend | 2 +- doc/architecture.md | 4 ++-- docker-compose.yml | 24 ++++++++----------- webapp/README.md | 2 +- webapp/nginx.conf | 10 ++++++++ webapp/src/api.ts | 12 +++++++--- 28 files changed, 87 insertions(+), 51 deletions(-) create mode 100644 Dockerfile diff --git a/.claude/agents/code-reviewer.md b/.claude/agents/code-reviewer.md index 8f63069..bd699ed 100644 --- a/.claude/agents/code-reviewer.md +++ b/.claude/agents/code-reviewer.md @@ -3,7 +3,7 @@ name: code-reviewer description: Prüft Codequalität, Lesbarkeit und Konsistenz. Vor Commits einsetzen. --- -Du bist Code-Reviewer für das Projekt Pamietnik (RALPH). +Du bist Code-Reviewer für das Projekt Pamietnik. ## Checkliste Go (Backend) diff --git a/.claude/agents/dokumentar.md b/.claude/agents/dokumentar.md index 35346b3..432066f 100644 --- a/.claude/agents/dokumentar.md +++ b/.claude/agents/dokumentar.md @@ -3,7 +3,7 @@ name: dokumentar description: Pflegt Markdown-Dokumentation und Mermaid-Diagramme. Bei neuen Features, Architekturänderungen oder wenn Doku und Code auseinanderlaufen. --- -Du bist Dokumentar für das Projekt Pamietnik (RALPH). +Du bist Dokumentar für das Projekt Pamietnik. ## Zu pflegende Dokumente diff --git a/.claude/agents/programmierer.md b/.claude/agents/programmierer.md index 73ae3fc..5b89e9f 100644 --- a/.claude/agents/programmierer.md +++ b/.claude/agents/programmierer.md @@ -3,7 +3,7 @@ name: programmierer description: Schreibt und ändert Code für Features und Bug-Fixes. Für Go-Backend und Android/Kotlin. Einsetzen bei konkreten Implementierungsaufgaben. --- -Du bist Programmierer für das Projekt Pamietnik (RALPH). +Du bist Programmierer für das Projekt Pamietnik. ## Stack diff --git a/.claude/agents/security-reviewer.md b/.claude/agents/security-reviewer.md index eeb2406..c52510d 100644 --- a/.claude/agents/security-reviewer.md +++ b/.claude/agents/security-reviewer.md @@ -3,7 +3,7 @@ name: security-reviewer description: Prüft OWASP Top 10, Dependency-Schwachstellen und Secrets. Vor Releases und bei Änderungen an externen APIs oder Auth-Code einsetzen. --- -Du bist Security-Reviewer für das Projekt Pamietnik (RALPH). +Du bist Security-Reviewer für das Projekt Pamietnik. ## Prüf-Befehle diff --git a/.claude/agents/software-architekt.md b/.claude/agents/software-architekt.md index 83ca7e3..470b807 100644 --- a/.claude/agents/software-architekt.md +++ b/.claude/agents/software-architekt.md @@ -3,7 +3,7 @@ name: software-architekt description: Analysiert Struktur, Abhängigkeiten und Architekturentscheidungen. Einsetzen vor größeren Änderungen, neuen Modulen oder wenn Komponenten-Grenzen unklar sind. --- -Du bist Software-Architekt für das Projekt Pamietnik (RALPH): Go-Backend + Android-App (Kotlin/Compose). +Du bist Software-Architekt für das Projekt Pamietnik: Go-Backend + Android-App (Kotlin/Compose). ## Deine Aufgaben diff --git a/.claude/agents/tester.md b/.claude/agents/tester.md index 64397ca..65affd6 100644 --- a/.claude/agents/tester.md +++ b/.claude/agents/tester.md @@ -3,7 +3,7 @@ name: tester description: Schreibt und führt Unit- und Integrationstests aus. Nach jeder Code-Änderung einsetzen. --- -Du bist Tester für das Projekt Pamietnik (RALPH). +Du bist Tester für das Projekt Pamietnik. ## Test-Befehle diff --git a/CLAUDE.md b/CLAUDE.md index d6e1b4f..6dc4af9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project Overview -**Pamietnik** (Codename: RALPH) is a life/travel journal consisting of three components: +**Pamietnik** is a life/travel journal consisting of three components: - `app/` — Android app (Kotlin + Jetpack Compose) - `backend/` — Go REST API + server-side rendered Web UI - `README.md` — single source of truth for architecture, requirements, and backlog @@ -28,7 +28,7 @@ go run ./cmd/server # starts API on :8080 (default) ``` **Env vars** (with defaults): -- `DATABASE_URL` — `postgres://ralph:ralph@localhost:5432/ralph?sslmode=disable` +- `DATABASE_URL` — `postgres://pamietnik:pamietnik@localhost:5432/pamietnik?sslmode=disable` - `LISTEN_ADDR` — `:8080` - `UPLOAD_DIR` — `./uploads` @@ -93,7 +93,7 @@ cd app ### Android Architecture ``` -de.jacek.reisejournal/ +de.jacek.pamietnik/ domain/ Trackpoint domain model data/ Room entities, DAOs, local DB service/ Background location foreground service diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2219417 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,24 @@ +# Stage 1: Build Vite SPA +FROM node:22-alpine AS webapp-builder +WORKDIR /webapp +COPY webapp/package.json webapp/package-lock.json ./ +RUN npm ci +COPY webapp/ ./ +RUN npm run build + +# Stage 2: Build Go server +FROM golang:1.25-alpine AS go-builder +WORKDIR /app +COPY backend/go.mod backend/go.sum ./ +RUN go mod download +COPY backend/ ./ +# Inject built SPA into embed path +COPY --from=webapp-builder /webapp/dist ./internal/api/webapp/ +RUN CGO_ENABLED=0 GOOS=linux go build -o /server ./cmd/server +RUN CGO_ENABLED=0 GOOS=linux go build -o /createuser ./cmd/createuser + +# Stage 3: Minimal runtime image +FROM gcr.io/distroless/static-debian12 +COPY --from=go-builder /server /server +COPY --from=go-builder /createuser /createuser +ENTRYPOINT ["/server"] diff --git a/README.md b/README.md index f7ded86..46aa83b 100644 --- a/README.md +++ b/README.md @@ -497,7 +497,7 @@ Nachteile: Widerruf/Revocation, Rotation und Lebensdauer-Management erhöhen Komplexität. -Empfehlung für RALPH +Empfehlung für Pamietnik Website: Session Cookie. JWT optional später, falls API-first/SSO nötig wird. diff --git a/app/app/build.gradle.kts b/app/app/build.gradle.kts index 2d32a5c..a21750e 100644 --- a/app/app/build.gradle.kts +++ b/app/app/build.gradle.kts @@ -7,11 +7,11 @@ plugins { } android { - namespace = "de.jacek.reisejournal" + namespace = "de.jacek.pamietnik" compileSdk = 35 defaultConfig { - applicationId = "de.jacek.reisejournal" + applicationId = "de.jacek.pamietnik" minSdk = 26 targetSdk = 35 versionCode = 1 diff --git a/app/app/src/androidTest/kotlin/de/jacek/reisejournal/ExampleInstrumentedTest.kt b/app/app/src/androidTest/kotlin/de/jacek/reisejournal/ExampleInstrumentedTest.kt index fe83988..bfe5a50 100644 --- a/app/app/src/androidTest/kotlin/de/jacek/reisejournal/ExampleInstrumentedTest.kt +++ b/app/app/src/androidTest/kotlin/de/jacek/reisejournal/ExampleInstrumentedTest.kt @@ -1,4 +1,4 @@ -package de.jacek.reisejournal +package de.jacek.pamietnik import androidx.test.platform.app.InstrumentationRegistry import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -11,6 +11,6 @@ class ExampleInstrumentedTest { @Test fun useAppContext() { val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("de.jacek.reisejournal", appContext.packageName) + assertEquals("de.jacek.pamietnik", appContext.packageName) } } diff --git a/app/app/src/main/kotlin/de/jacek/reisejournal/MainActivity.kt b/app/app/src/main/kotlin/de/jacek/reisejournal/MainActivity.kt index 43c5851..943ea12 100644 --- a/app/app/src/main/kotlin/de/jacek/reisejournal/MainActivity.kt +++ b/app/app/src/main/kotlin/de/jacek/reisejournal/MainActivity.kt @@ -1,12 +1,12 @@ -package de.jacek.reisejournal +package de.jacek.pamietnik import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import dagger.hilt.android.AndroidEntryPoint -import de.jacek.reisejournal.ui.navigation.NavGraph -import de.jacek.reisejournal.ui.theme.RalphTheme +import de.jacek.pamietnik.ui.navigation.NavGraph +import de.jacek.pamietnik.ui.theme.RalphTheme @AndroidEntryPoint class MainActivity : ComponentActivity() { diff --git a/app/app/src/main/kotlin/de/jacek/reisejournal/RalphApp.kt b/app/app/src/main/kotlin/de/jacek/reisejournal/RalphApp.kt index c344011..53786ff 100644 --- a/app/app/src/main/kotlin/de/jacek/reisejournal/RalphApp.kt +++ b/app/app/src/main/kotlin/de/jacek/reisejournal/RalphApp.kt @@ -1,4 +1,4 @@ -package de.jacek.reisejournal +package de.jacek.pamietnik import android.app.Application import androidx.hilt.work.HiltWorkerFactory @@ -7,7 +7,7 @@ import dagger.hilt.android.HiltAndroidApp import javax.inject.Inject @HiltAndroidApp -class RalphApp : Application(), Configuration.Provider { +class PamietnikApp : Application(), Configuration.Provider { @Inject lateinit var workerFactory: HiltWorkerFactory diff --git a/app/app/src/main/kotlin/de/jacek/reisejournal/domain/Trackpoint.kt b/app/app/src/main/kotlin/de/jacek/reisejournal/domain/Trackpoint.kt index 5863aea..eee2e68 100644 --- a/app/app/src/main/kotlin/de/jacek/reisejournal/domain/Trackpoint.kt +++ b/app/app/src/main/kotlin/de/jacek/reisejournal/domain/Trackpoint.kt @@ -1,4 +1,4 @@ -package de.jacek.reisejournal.domain +package de.jacek.pamietnik.domain data class Trackpoint( val eventId: String, // UUID, client-generated diff --git a/app/app/src/main/kotlin/de/jacek/reisejournal/ui/home/HomeScreen.kt b/app/app/src/main/kotlin/de/jacek/reisejournal/ui/home/HomeScreen.kt index 3cd1407..87895d2 100644 --- a/app/app/src/main/kotlin/de/jacek/reisejournal/ui/home/HomeScreen.kt +++ b/app/app/src/main/kotlin/de/jacek/reisejournal/ui/home/HomeScreen.kt @@ -1,4 +1,4 @@ -package de.jacek.reisejournal.ui.home +package de.jacek.pamietnik.ui.home import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -16,7 +16,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel -import de.jacek.reisejournal.R +import de.jacek.pamietnik.R @OptIn(ExperimentalMaterial3Api::class) @Composable diff --git a/app/app/src/main/kotlin/de/jacek/reisejournal/ui/home/HomeViewModel.kt b/app/app/src/main/kotlin/de/jacek/reisejournal/ui/home/HomeViewModel.kt index af9b43c..b983e30 100644 --- a/app/app/src/main/kotlin/de/jacek/reisejournal/ui/home/HomeViewModel.kt +++ b/app/app/src/main/kotlin/de/jacek/reisejournal/ui/home/HomeViewModel.kt @@ -1,4 +1,4 @@ -package de.jacek.reisejournal.ui.home +package de.jacek.pamietnik.ui.home import androidx.lifecycle.ViewModel import dagger.hilt.android.lifecycle.HiltViewModel diff --git a/app/app/src/main/kotlin/de/jacek/reisejournal/ui/navigation/NavGraph.kt b/app/app/src/main/kotlin/de/jacek/reisejournal/ui/navigation/NavGraph.kt index beeb41d..95fbb16 100644 --- a/app/app/src/main/kotlin/de/jacek/reisejournal/ui/navigation/NavGraph.kt +++ b/app/app/src/main/kotlin/de/jacek/reisejournal/ui/navigation/NavGraph.kt @@ -1,10 +1,10 @@ -package de.jacek.reisejournal.ui.navigation +package de.jacek.pamietnik.ui.navigation import androidx.compose.runtime.Composable import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController -import de.jacek.reisejournal.ui.home.HomeScreen +import de.jacek.pamietnik.ui.home.HomeScreen const val HOME_ROUTE = "home" diff --git a/app/app/src/main/kotlin/de/jacek/reisejournal/ui/theme/Color.kt b/app/app/src/main/kotlin/de/jacek/reisejournal/ui/theme/Color.kt index e05ead8..baf9bb2 100644 --- a/app/app/src/main/kotlin/de/jacek/reisejournal/ui/theme/Color.kt +++ b/app/app/src/main/kotlin/de/jacek/reisejournal/ui/theme/Color.kt @@ -1,4 +1,4 @@ -package de.jacek.reisejournal.ui.theme +package de.jacek.pamietnik.ui.theme import androidx.compose.ui.graphics.Color diff --git a/app/app/src/main/kotlin/de/jacek/reisejournal/ui/theme/Theme.kt b/app/app/src/main/kotlin/de/jacek/reisejournal/ui/theme/Theme.kt index 252d621..6ab6eaf 100644 --- a/app/app/src/main/kotlin/de/jacek/reisejournal/ui/theme/Theme.kt +++ b/app/app/src/main/kotlin/de/jacek/reisejournal/ui/theme/Theme.kt @@ -1,4 +1,4 @@ -package de.jacek.reisejournal.ui.theme +package de.jacek.pamietnik.ui.theme import android.os.Build import androidx.compose.foundation.isSystemInDarkTheme diff --git a/app/app/src/main/kotlin/de/jacek/reisejournal/ui/theme/Type.kt b/app/app/src/main/kotlin/de/jacek/reisejournal/ui/theme/Type.kt index 9a57238..404eb9c 100644 --- a/app/app/src/main/kotlin/de/jacek/reisejournal/ui/theme/Type.kt +++ b/app/app/src/main/kotlin/de/jacek/reisejournal/ui/theme/Type.kt @@ -1,4 +1,4 @@ -package de.jacek.reisejournal.ui.theme +package de.jacek.pamietnik.ui.theme import androidx.compose.material3.Typography import androidx.compose.ui.text.TextStyle diff --git a/app/app/src/test/kotlin/de/jacek/reisejournal/ExampleUnitTest.kt b/app/app/src/test/kotlin/de/jacek/reisejournal/ExampleUnitTest.kt index 16d998b..190c7e9 100644 --- a/app/app/src/test/kotlin/de/jacek/reisejournal/ExampleUnitTest.kt +++ b/app/app/src/test/kotlin/de/jacek/reisejournal/ExampleUnitTest.kt @@ -1,4 +1,4 @@ -package de.jacek.reisejournal +package de.jacek.pamietnik import org.junit.Test import org.junit.Assert.* diff --git a/app/settings.gradle.kts b/app/settings.gradle.kts index 242cafb..df6df1a 100644 --- a/app/settings.gradle.kts +++ b/app/settings.gradle.kts @@ -25,5 +25,5 @@ dependencyResolutionManagement { } } -rootProject.name = "reisejournal" +rootProject.name = "pamietnik" include(":app") diff --git a/backend b/backend index 55d2ffc..d971967 160000 --- a/backend +++ b/backend @@ -1 +1 @@ -Subproject commit 55d2ffc2036b1e96c154ef13899b6adaaea59645 +Subproject commit d97196790d22f6019284e0c41ebacda113d3ce60 diff --git a/doc/architecture.md b/doc/architecture.md index a7c5d2e..0ea1cf9 100644 --- a/doc/architecture.md +++ b/doc/architecture.md @@ -2,7 +2,7 @@ ## 1. Einführung und Ziele -**Pamietnik** (Codename RALPH) ist ein persönliches Lebens- und Reisejournal bestehend aus drei Komponenten: einer Android-App, einer Web-App und einem Go-Backend-Server. +**Pamietnik** ist ein persönliches Journal bestehend aus drei Komponenten: einer Android-App, einer Web-App und einem Go-Backend-Server. ### Fachliches Zielbild @@ -194,7 +194,7 @@ backend/ ### Ebene 2 — Android-Pakete ``` -app/app/src/main/kotlin/de/jacek/reisejournal/ +app/app/src/main/kotlin/de/jacek/pamietnik/ ├── domain/ Trackpoint Domain Model ├── data/ Room Entities, DAOs, lokale DB ├── service/ Background Location Foreground Service diff --git a/docker-compose.yml b/docker-compose.yml index a44b517..13195f8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,34 +2,30 @@ services: postgres: image: postgres:16-alpine environment: - POSTGRES_USER: ralph - POSTGRES_PASSWORD: ralph - POSTGRES_DB: ralph + POSTGRES_USER: pamietnik + POSTGRES_PASSWORD: pamietnik + POSTGRES_DB: pamietnik volumes: - pgdata:/var/lib/postgresql/data healthcheck: - test: ["CMD-SHELL", "pg_isready -U ralph"] + test: ["CMD-SHELL", "pg_isready -U pamietnik"] interval: 5s timeout: 5s retries: 5 api: - build: ./backend + build: + context: . + dockerfile: Dockerfile + ports: + - "9050:8080" environment: - DATABASE_URL: postgres://ralph:ralph@postgres:5432/ralph?sslmode=disable + DATABASE_URL: postgres://pamietnik:pamietnik@postgres:5432/pamietnik?sslmode=disable LISTEN_ADDR: :8080 depends_on: postgres: condition: service_healthy restart: unless-stopped - webapp: - build: ./webapp - ports: - - "9050:80" - depends_on: - - api - restart: unless-stopped - volumes: pgdata: diff --git a/webapp/README.md b/webapp/README.md index 891dc15..dc40ec7 100644 --- a/webapp/README.md +++ b/webapp/README.md @@ -1,6 +1,6 @@ # Pamietnik Webapp -Eigenständige Single-Page-Application für das Pamietnik-Reisejournal. Kommuniziert über REST mit dem Go-Backend. +Eigenständige Single-Page-Application für das Pamietnik. Kommuniziert über REST mit dem Go-Backend. ## Technologie diff --git a/webapp/nginx.conf b/webapp/nginx.conf index a9d5af1..591906e 100644 --- a/webapp/nginx.conf +++ b/webapp/nginx.conf @@ -2,6 +2,16 @@ server { listen 80; # API und Auth-Endpunkte zum Backend proxieren + location /healthz { + proxy_pass http://api:8080; + proxy_set_header Host $host; + } + + location /readyz { + proxy_pass http://api:8080; + proxy_set_header Host $host; + } + location /v1/ { proxy_pass http://api:8080; proxy_set_header Host $host; diff --git a/webapp/src/api.ts b/webapp/src/api.ts index 4e17065..bea5447 100644 --- a/webapp/src/api.ts +++ b/webapp/src/api.ts @@ -50,9 +50,15 @@ async function get(path: string): Promise { export const api = { getDays(from?: string, to?: string): Promise { - const params = new URLSearchParams() - if (from) params.set('from', from) - if (to) params.set('to', to) + const now = new Date() + const defaultTo = now.toISOString().slice(0, 10) + const past = new Date(now) + past.setDate(past.getDate() - 90) + const defaultFrom = past.toISOString().slice(0, 10) + const params = new URLSearchParams({ + from: from ?? defaultFrom, + to: to ?? defaultTo, + }) return get(`/v1/days?${params}`) }, getTrackpoints(date: string): Promise {