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:
@@ -3,7 +3,7 @@ name: code-reviewer
|
|||||||
description: Prüft Codequalität, Lesbarkeit und Konsistenz. Vor Commits einsetzen.
|
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)
|
## Checkliste Go (Backend)
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ name: dokumentar
|
|||||||
description: Pflegt Markdown-Dokumentation und Mermaid-Diagramme. Bei neuen Features, Architekturänderungen oder wenn Doku und Code auseinanderlaufen.
|
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
|
## Zu pflegende Dokumente
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
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
|
## Stack
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
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
|
## Prüf-Befehle
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
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
|
## Deine Aufgaben
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ name: tester
|
|||||||
description: Schreibt und führt Unit- und Integrationstests aus. Nach jeder Code-Änderung einsetzen.
|
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
|
## Test-Befehle
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|||||||
|
|
||||||
## Project Overview
|
## 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)
|
- `app/` — Android app (Kotlin + Jetpack Compose)
|
||||||
- `backend/` — Go REST API + server-side rendered Web UI
|
- `backend/` — Go REST API + server-side rendered Web UI
|
||||||
- `README.md` — single source of truth for architecture, requirements, and backlog
|
- `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):
|
**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`
|
- `LISTEN_ADDR` — `:8080`
|
||||||
- `UPLOAD_DIR` — `./uploads`
|
- `UPLOAD_DIR` — `./uploads`
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ cd app
|
|||||||
### Android Architecture
|
### Android Architecture
|
||||||
|
|
||||||
```
|
```
|
||||||
de.jacek.reisejournal/
|
de.jacek.pamietnik/
|
||||||
domain/ Trackpoint domain model
|
domain/ Trackpoint domain model
|
||||||
data/ Room entities, DAOs, local DB
|
data/ Room entities, DAOs, local DB
|
||||||
service/ Background location foreground service
|
service/ Background location foreground service
|
||||||
|
|||||||
24
Dockerfile
Normal file
24
Dockerfile
Normal 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"]
|
||||||
@@ -497,7 +497,7 @@ Nachteile:
|
|||||||
|
|
||||||
Widerruf/Revocation, Rotation und Lebensdauer-Management erhöhen Komplexität.
|
Widerruf/Revocation, Rotation und Lebensdauer-Management erhöhen Komplexität.
|
||||||
|
|
||||||
Empfehlung für RALPH
|
Empfehlung für Pamietnik
|
||||||
Website: Session Cookie.
|
Website: Session Cookie.
|
||||||
|
|
||||||
JWT optional später, falls API-first/SSO nötig wird.
|
JWT optional später, falls API-first/SSO nötig wird.
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "de.jacek.reisejournal"
|
namespace = "de.jacek.pamietnik"
|
||||||
compileSdk = 35
|
compileSdk = 35
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "de.jacek.reisejournal"
|
applicationId = "de.jacek.pamietnik"
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
versionCode = 1
|
versionCode = 1
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package de.jacek.reisejournal
|
package de.jacek.pamietnik
|
||||||
|
|
||||||
import androidx.test.platform.app.InstrumentationRegistry
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
@@ -11,6 +11,6 @@ class ExampleInstrumentedTest {
|
|||||||
@Test
|
@Test
|
||||||
fun useAppContext() {
|
fun useAppContext() {
|
||||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||||
assertEquals("de.jacek.reisejournal", appContext.packageName)
|
assertEquals("de.jacek.pamietnik", appContext.packageName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
package de.jacek.reisejournal
|
package de.jacek.pamietnik
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import de.jacek.reisejournal.ui.navigation.NavGraph
|
import de.jacek.pamietnik.ui.navigation.NavGraph
|
||||||
import de.jacek.reisejournal.ui.theme.RalphTheme
|
import de.jacek.pamietnik.ui.theme.RalphTheme
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package de.jacek.reisejournal
|
package de.jacek.pamietnik
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import androidx.hilt.work.HiltWorkerFactory
|
import androidx.hilt.work.HiltWorkerFactory
|
||||||
@@ -7,7 +7,7 @@ import dagger.hilt.android.HiltAndroidApp
|
|||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltAndroidApp
|
@HiltAndroidApp
|
||||||
class RalphApp : Application(), Configuration.Provider {
|
class PamietnikApp : Application(), Configuration.Provider {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var workerFactory: HiltWorkerFactory
|
lateinit var workerFactory: HiltWorkerFactory
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package de.jacek.reisejournal.domain
|
package de.jacek.pamietnik.domain
|
||||||
|
|
||||||
data class Trackpoint(
|
data class Trackpoint(
|
||||||
val eventId: String, // UUID, client-generated
|
val eventId: String, // UUID, client-generated
|
||||||
|
|||||||
@@ -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.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@@ -16,7 +16,7 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import de.jacek.reisejournal.R
|
import de.jacek.pamietnik.R
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package de.jacek.reisejournal.ui.home
|
package de.jacek.pamietnik.ui.home
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package de.jacek.reisejournal.ui.navigation
|
package de.jacek.pamietnik.ui.navigation
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import de.jacek.reisejournal.ui.home.HomeScreen
|
import de.jacek.pamietnik.ui.home.HomeScreen
|
||||||
|
|
||||||
const val HOME_ROUTE = "home"
|
const val HOME_ROUTE = "home"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package de.jacek.reisejournal.ui.theme
|
package de.jacek.pamietnik.ui.theme
|
||||||
|
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package de.jacek.reisejournal.ui.theme
|
package de.jacek.pamietnik.ui.theme
|
||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package de.jacek.reisejournal.ui.theme
|
package de.jacek.pamietnik.ui.theme
|
||||||
|
|
||||||
import androidx.compose.material3.Typography
|
import androidx.compose.material3.Typography
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package de.jacek.reisejournal
|
package de.jacek.pamietnik
|
||||||
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.Assert.*
|
import org.junit.Assert.*
|
||||||
|
|||||||
@@ -25,5 +25,5 @@ dependencyResolutionManagement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rootProject.name = "reisejournal"
|
rootProject.name = "pamietnik"
|
||||||
include(":app")
|
include(":app")
|
||||||
|
|||||||
2
backend
2
backend
Submodule backend updated: 55d2ffc203...d97196790d
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## 1. Einführung und Ziele
|
## 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
|
### Fachliches Zielbild
|
||||||
|
|
||||||
@@ -194,7 +194,7 @@ backend/
|
|||||||
### Ebene 2 — Android-Pakete
|
### Ebene 2 — Android-Pakete
|
||||||
|
|
||||||
```
|
```
|
||||||
app/app/src/main/kotlin/de/jacek/reisejournal/
|
app/app/src/main/kotlin/de/jacek/pamietnik/
|
||||||
├── domain/ Trackpoint Domain Model
|
├── domain/ Trackpoint Domain Model
|
||||||
├── data/ Room Entities, DAOs, lokale DB
|
├── data/ Room Entities, DAOs, lokale DB
|
||||||
├── service/ Background Location Foreground Service
|
├── service/ Background Location Foreground Service
|
||||||
|
|||||||
@@ -2,34 +2,30 @@ services:
|
|||||||
postgres:
|
postgres:
|
||||||
image: postgres:16-alpine
|
image: postgres:16-alpine
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_USER: ralph
|
POSTGRES_USER: pamietnik
|
||||||
POSTGRES_PASSWORD: ralph
|
POSTGRES_PASSWORD: pamietnik
|
||||||
POSTGRES_DB: ralph
|
POSTGRES_DB: pamietnik
|
||||||
volumes:
|
volumes:
|
||||||
- pgdata:/var/lib/postgresql/data
|
- pgdata:/var/lib/postgresql/data
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "pg_isready -U ralph"]
|
test: ["CMD-SHELL", "pg_isready -U pamietnik"]
|
||||||
interval: 5s
|
interval: 5s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 5
|
retries: 5
|
||||||
|
|
||||||
api:
|
api:
|
||||||
build: ./backend
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
ports:
|
||||||
|
- "9050:8080"
|
||||||
environment:
|
environment:
|
||||||
DATABASE_URL: postgres://ralph:ralph@postgres:5432/ralph?sslmode=disable
|
DATABASE_URL: postgres://pamietnik:pamietnik@postgres:5432/pamietnik?sslmode=disable
|
||||||
LISTEN_ADDR: :8080
|
LISTEN_ADDR: :8080
|
||||||
depends_on:
|
depends_on:
|
||||||
postgres:
|
postgres:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
webapp:
|
|
||||||
build: ./webapp
|
|
||||||
ports:
|
|
||||||
- "9050:80"
|
|
||||||
depends_on:
|
|
||||||
- api
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
pgdata:
|
pgdata:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Pamietnik Webapp
|
# 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
|
## Technologie
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,16 @@ server {
|
|||||||
listen 80;
|
listen 80;
|
||||||
|
|
||||||
# API und Auth-Endpunkte zum Backend proxieren
|
# 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/ {
|
location /v1/ {
|
||||||
proxy_pass http://api:8080;
|
proxy_pass http://api:8080;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
|
|||||||
@@ -50,9 +50,15 @@ async function get<T>(path: string): Promise<T> {
|
|||||||
|
|
||||||
export const api = {
|
export const api = {
|
||||||
getDays(from?: string, to?: string): Promise<DaySummary[]> {
|
getDays(from?: string, to?: string): Promise<DaySummary[]> {
|
||||||
const params = new URLSearchParams()
|
const now = new Date()
|
||||||
if (from) params.set('from', from)
|
const defaultTo = now.toISOString().slice(0, 10)
|
||||||
if (to) params.set('to', to)
|
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}`)
|
return get<DaySummary[]>(`/v1/days?${params}`)
|
||||||
},
|
},
|
||||||
getTrackpoints(date: string): Promise<Trackpoint[]> {
|
getTrackpoints(date: string): Promise<Trackpoint[]> {
|
||||||
|
|||||||
Reference in New Issue
Block a user