Remove nginx/webapp container; single Go server serves SPA + API

- 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 <noreply@anthropic.com>
This commit is contained in:
Christoph K.
2026-04-06 10:32:04 +02:00
parent d1436abca8
commit a49416854e
28 changed files with 87 additions and 51 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

24
Dockerfile Normal file
View File

@@ -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"]

View File

@@ -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.

View File

@@ -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

View File

@@ -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)
}
}

View File

@@ -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() {

View File

@@ -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

View File

@@ -1,4 +1,4 @@
package de.jacek.reisejournal.domain
package de.jacek.pamietnik.domain
data class Trackpoint(
val eventId: String, // UUID, client-generated

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -1,4 +1,4 @@
package de.jacek.reisejournal.ui.theme
package de.jacek.pamietnik.ui.theme
import androidx.compose.ui.graphics.Color

View File

@@ -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

View File

@@ -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

View File

@@ -1,4 +1,4 @@
package de.jacek.reisejournal
package de.jacek.pamietnik
import org.junit.Test
import org.junit.Assert.*

View File

@@ -25,5 +25,5 @@ dependencyResolutionManagement {
}
}
rootProject.name = "reisejournal"
rootProject.name = "pamietnik"
include(":app")

Submodule backend updated: 55d2ffc203...d97196790d

View File

@@ -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

View File

@@ -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:

View File

@@ -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

View File

@@ -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;

View File

@@ -50,9 +50,15 @@ async function get<T>(path: string): Promise<T> {
export const api = {
getDays(from?: string, to?: string): Promise<DaySummary[]> {
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<DaySummary[]>(`/v1/days?${params}`)
},
getTrackpoints(date: string): Promise<Trackpoint[]> {