mirror of
https://github.com/gotson/komga.git
synced 2026-05-08 04:22:28 +02:00
update cicd flow with simplesoft-duongdt3/komga
This commit is contained in:
parent
c97b103b5b
commit
99b509d013
22 changed files with 1509 additions and 66 deletions
8
.dockerignore
Normal file
8
.dockerignore
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
**/node_modules
|
||||
**/.git
|
||||
**/.gradle
|
||||
**/build
|
||||
**/dist
|
||||
**/.idea
|
||||
**/.vscode
|
||||
*.log
|
||||
23
.github/workflows/dockerhub_description.yml
vendored
23
.github/workflows/dockerhub_description.yml
vendored
|
|
@ -1,23 +0,0 @@
|
|||
name: Update DockerHub description
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- 'DOCKERHUB.md'
|
||||
|
||||
jobs:
|
||||
update_docker_description:
|
||||
name: Update DockerHub description
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: DockerHub Description
|
||||
uses: peter-evans/dockerhub-description@v5.0.0
|
||||
env:
|
||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
DOCKERHUB_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
DOCKERHUB_REPOSITORY: gotson/komga
|
||||
README_FILEPATH: ./DOCKERHUB.md
|
||||
10
.github/workflows/release.yml
vendored
10
.github/workflows/release.yml
vendored
|
|
@ -27,7 +27,7 @@ on:
|
|||
default: true
|
||||
type: boolean
|
||||
docker_release:
|
||||
description: 'Push Docker images'
|
||||
description: 'Push Docker images to GitHub Container Registry'
|
||||
default: true
|
||||
type: boolean
|
||||
msstore_release:
|
||||
|
|
@ -103,13 +103,7 @@ jobs:
|
|||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v4
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Login to Docker Hub
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
registry: ghcr.io
|
||||
|
|
|
|||
727
.kilo/plans/1775635110293-nimble-eagle.md
Normal file
727
.kilo/plans/1775635110293-nimble-eagle.md
Normal file
|
|
@ -0,0 +1,727 @@
|
|||
# Plan: Optimize Dockerfile.local Build Performance
|
||||
|
||||
## Problem Statement
|
||||
Current `Dockerfile.local` builds both frontend (FE) and backend (BE) in a single stage, resulting in long build times. The FE build involves `npm install` and `npm run build`, which are time‑consuming and are triggered even when only BE code changes.
|
||||
|
||||
## Objectives
|
||||
- Reduce overall build time for production image builds using `Dockerfile.local`
|
||||
- Maximize Docker layer caching, especially for the frontend dependencies (`node_modules`)
|
||||
- Maintain compatibility with existing development and CI workflows
|
||||
- Ensure the final image remains functionally identical
|
||||
|
||||
## Current Analysis
|
||||
|
||||
### Usage Context
|
||||
- `Dockerfile.local` is **only for local development**, not used in CI production builds.
|
||||
- CI/CD uses `komga/docker/Dockerfile.tpl` via JReleaser for multi‑arch production images.
|
||||
- The main consumption is through `build‑local‑docker.sh` script and `docker‑compose.local.yml`.
|
||||
- No other scripts or workflows depend on the exact structure of `Dockerfile.local`.
|
||||
|
||||
### Dockerfile.local Structure
|
||||
1. **Single build stage** based on `eclipse‑temurin:21‑jdk`
|
||||
2. Installs Node.js 18 system‑wide
|
||||
3. Copies **all** source code (including `komga‑webui`) before running any build steps
|
||||
4. Runs `./gradlew :komga:prepareThymeLeaf :komga:bootJar`
|
||||
- `prepareThymeLeaf` depends on `npmInstall` → `npmBuild` → `copyWebDist`
|
||||
- This triggers a full frontend build on every Docker build
|
||||
|
||||
### Pain Points
|
||||
- `npm install` runs on every build because the `COPY` of the entire source tree invalidates the cache layer.
|
||||
- Changes to any source file (BE or FE) cause the frontend build to rerun.
|
||||
- No separation between FE and BE caching.
|
||||
- Frontend build (npm install + npm run build) is the most time‑consuming part.
|
||||
|
||||
## Proposed Solution
|
||||
|
||||
### Multi‑stage Build with Separate FE Stage
|
||||
Introduce a dedicated frontend build stage that caches `node_modules` independently.
|
||||
|
||||
```
|
||||
Stage 1: frontend‑builder
|
||||
- Use `node:18‑alpine` as base
|
||||
- Copy only `komga‑webui/package*.json`
|
||||
- Run `npm ci` (or `npm install`) → cached layer
|
||||
- Copy the rest of `komga‑webui`
|
||||
- Run `npm run build`
|
||||
- Output: `/app/dist` directory
|
||||
|
||||
Stage 2: backend‑builder
|
||||
- Use `eclipse‑temurin:21‑jdk` as base
|
||||
- Copy Gradle files (`gradle/`, `gradlew`, `*.gradle.kts`, etc.)
|
||||
- Copy backend source (`komga/`, `komga‑tray/`)
|
||||
- Copy the built frontend dist from Stage 1 into `komga/src/main/resources/public/`
|
||||
- Optionally inject ThymeLeaf tags (can be done with a simple `sed` or keep the Gradle task)
|
||||
- Run `./gradlew :komga:bootJar` (skip `prepareThymeLeaf` because dist is already present)
|
||||
|
||||
Stage 3: runtime
|
||||
- Use `eclipse‑temurin:21‑jre‑alpine`
|
||||
- Copy the JAR from Stage 2
|
||||
- Set up non‑root user and entrypoint
|
||||
```
|
||||
|
||||
### Cache Optimization Details
|
||||
1. **Frontend dependencies**: Layer depends only on `package*.json`. Changes to FE source code will not trigger `npm install` again.
|
||||
2. **Frontend build**: Layer depends on the whole `komga‑webui` source, but `node_modules` is cached.
|
||||
3. **Backend dependencies**: Gradle cache can be preserved by copying `~/.gradle/caches` (optional, but can be added as a volume in CI).
|
||||
4. **Backend build**: Copying the pre‑built frontend dist eliminates the need to run `npm` tasks inside the backend stage.
|
||||
|
||||
### Alternative: Two‑Phase Gradle Build
|
||||
Keep the single‑stage approach but reorder `COPY` instructions:
|
||||
- Copy `komga‑webui/package*.json` first, run `npm install`
|
||||
- Copy the rest of the source
|
||||
- Run `./gradlew :komga:prepareThymeLeaf :komga:bootJar`
|
||||
|
||||
This still runs the Gradle task that triggers `npmBuild`, but at least `npm install` is cached.
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
1. **Analyze existing Gradle tasks** to ensure skipping `prepareThymeLeaf` is safe.
|
||||
- Check if any other task depends on `prepareThymeLeaf` (e.g., `bootJar`).
|
||||
- Verify that `bootJar` includes the `public/` directory.
|
||||
|
||||
2. **Create new Dockerfile.local.optimized** (or modify the existing one) with the multi‑stage design.
|
||||
- Write the frontend stage using `node:18‑alpine`.
|
||||
- Ensure the ThymeLeaf tag injection is performed (either in the frontend stage or via a small script).
|
||||
- Test that the built JAR contains the correct frontend assets.
|
||||
|
||||
3. **Update docker‑compose.local.yml** (if necessary) to reference the new Dockerfile.
|
||||
|
||||
4. **Validate build time improvement** with a sample workflow:
|
||||
- Clean build
|
||||
- Rebuild after changing a backend source file
|
||||
- Rebuild after changing a frontend source file
|
||||
- Rebuild after changing `package.json`
|
||||
|
||||
5. **Ensure compatibility** with existing CI/CD (GitHub Actions) and local development commands.
|
||||
|
||||
## Risks & Mitigations
|
||||
|
||||
| Risk | Mitigation |
|
||||
|------|------------|
|
||||
| Skipping `prepareThymeLeaf` may break ThymeLeaf variable injection | Perform the same injection in the frontend stage using a simple script (or keep the Gradle task but make it depend only on the copied dist). |
|
||||
| Docker layer caching may not work as expected in CI environments | Use `--cache‑from` flags and consider storing `node_modules` as a separate build‑cache target. |
|
||||
| Increased complexity of the Dockerfile | Document each stage clearly and add comments. |
|
||||
|
||||
## Success Metrics
|
||||
- **Build time reduction**: At least 50% faster on clean builds (frontend dependencies already cached).
|
||||
- **Cache efficiency**: Changing a backend source file should not trigger frontend `npm install` or `npm build`.
|
||||
- **Image size**: Should not increase significantly.
|
||||
|
||||
## Deliverables
|
||||
1. Optimized `Dockerfile.local` (or `Dockerfile.local.optimized`).
|
||||
2. Updated documentation (if needed) in `ai‑docs/docker‑setup.md`.
|
||||
3. Validation script or instructions to verify the improvement.
|
||||
|
||||
## Implementation Choices & Decisions Made
|
||||
|
||||
### Chosen Approach: Option A – Multi‑stage with separate FE stage
|
||||
- **Rationale**: Maximum caching separation, frontend dependencies cached independently, clean separation of concerns.
|
||||
- **ThymeLeaf injection**: Will be performed in the frontend stage using a script that mimics the Gradle task's regex replacement.
|
||||
|
||||
### File Strategy
|
||||
- Create new `Dockerfile.local.optimized` alongside the existing `Dockerfile.local`.
|
||||
- Existing workflows continue to use the old file until migrated.
|
||||
|
||||
### Script Updates
|
||||
- Create separate optimized versions:
|
||||
- `docker‑compose.local.optimized.yml` that references `Dockerfile.local.optimized`.
|
||||
- Update `build‑local‑docker.sh` to optionally support the optimized version (via a flag or a separate script).
|
||||
- This allows gradual migration and testing.
|
||||
|
||||
## Detailed Implementation Plan
|
||||
|
||||
### Phase 1: Create Optimized Dockerfile
|
||||
1. Write `Dockerfile.local.optimized` with three stages:
|
||||
- **frontend‑builder**: `node:18‑alpine`, copy `package*.json`, `npm ci`, copy source, `npm run build`, inject ThymeLeaf tags.
|
||||
- **backend‑builder**: `eclipse‑temurin:21‑jdk`, copy Gradle files, copy backend source, copy pre‑built frontend dist, run `./gradlew :komga:bootJar`.
|
||||
- **runtime**: `eclipse‑temurin:21‑jre‑alpine`, copy JAR, set up user and entrypoint.
|
||||
|
||||
2. Implement ThymeLeaf tag injection script (shell or Node.js) that replicates the Gradle filter:
|
||||
```bash
|
||||
sed -i -E 's/((?:src|content|href)=")([\w]*/.*?)(")/\1 th:\2@{/\3}/g' index.html
|
||||
```
|
||||
(Exact regex to be validated against the Gradle task.)
|
||||
|
||||
### Phase 2: Create Supporting Files
|
||||
1. Create `docker‑compose.local.optimized.yml` that points to `dockerfile: Dockerfile.local.optimized`.
|
||||
2. Optionally create `build‑local‑docker‑optimized.sh` or modify the existing script to accept a `--optimized` flag.
|
||||
|
||||
### Phase 3: Validation
|
||||
1. Build the optimized image and verify it runs correctly.
|
||||
2. Compare build times:
|
||||
- Clean build (should be similar or slightly faster due to separate stage caching).
|
||||
- Rebuild after backend source change (should skip frontend `npm install` and `npm run build`).
|
||||
- Rebuild after frontend source change (should skip `npm install`).
|
||||
- Rebuild after `package.json` change (should re‑run `npm ci` but not backend build).
|
||||
|
||||
3. Verify ThymeLeaf tags are correctly injected and the web UI loads.
|
||||
|
||||
### Phase 4: Documentation
|
||||
1. Update `ai‑docs/docker‑setup.md` to mention the optimized option.
|
||||
2. Add a brief comparison of build times.
|
||||
|
||||
## Risks & Mitigations (Updated)
|
||||
|
||||
| Risk | Mitigation |
|
||||
|------|------------|
|
||||
| ThymeLeaf injection regex mismatch | Test with actual `index.html` from build and compare with output of Gradle task. |
|
||||
| Docker layer caching not effective due to copy ordering | Carefully order `COPY` commands to maximize cache hits. |
|
||||
| Increased image size due to multiple stages | Use `alpine` variants and clean up unnecessary files in each stage. |
|
||||
| Compatibility with existing local development workflows | Keep old files unchanged; new files are opt‑in. |
|
||||
|
||||
## Success Metrics
|
||||
- **Build time reduction**: At least 50% faster on incremental builds (backend‑only changes).
|
||||
- **Cache efficiency**: Changing a backend source file does not trigger `npm install` or `npm run build`.
|
||||
- **Image size**: Should not increase by more than 5%.
|
||||
|
||||
## Deliverables
|
||||
1. `Dockerfile.local.optimized`
|
||||
2. `docker‑compose.local.optimized.yml`
|
||||
3. Optional: updated `build‑local‑docker.sh` or new `build‑local‑docker‑optimized.sh`
|
||||
4. Validation results (timing comparisons)
|
||||
5. Updated documentation in `ai‑docs/docker‑setup.md`
|
||||
|
||||
## Brainstorm: Split FE and BE into Two Separate Docker Images
|
||||
|
||||
### Current Architecture Analysis
|
||||
- Komga is a monolithic Spring Boot app with embedded frontend assets
|
||||
- Frontend (Vue.js) built into `komga-webui/dist/` → copied to `komga/src/main/resources/public/`
|
||||
- Backend serves:
|
||||
- API endpoints under `/api/**`
|
||||
- OPDS feeds under `/opds/**`
|
||||
- Server-Sent Events under `/sse/**`
|
||||
- Static assets from `classpath:public/` (frontend)
|
||||
- SPA fallback: non-API routes forward to `index.html`
|
||||
- Frontend already configurable via `VUE_APP_KOMGA_API_URL` environment variable
|
||||
- CORS support exists for development (`dev` profile)
|
||||
|
||||
### Proposed Split Options
|
||||
|
||||
#### Option 1: Full Separation with Reverse Proxy
|
||||
- **FE Image**: `nginx:alpine` serving static files from `komga-webui/dist/`
|
||||
- **BE Image**: Spring Boot JAR only, no embedded frontend assets
|
||||
- **Reverse Proxy**: nginx/traefik/Caddy routing:
|
||||
- `/`, `/css/*`, `/js/*`, `/img/*`, `/fonts/*` → FE container
|
||||
- `/api/**`, `/opds/**`, `/sse/**` → BE container
|
||||
- **Pros**:
|
||||
- Complete separation, independent scaling
|
||||
- FE can be served via CDN
|
||||
- BE image smaller (no frontend assets)
|
||||
- Clear separation of concerns
|
||||
- **Cons**:
|
||||
- More complex deployment (3 containers)
|
||||
- Need to manage reverse proxy configuration
|
||||
- Session/cookie auth may need CORS configuration
|
||||
|
||||
#### Option 2: FE as Separate Container, BE Still Serves Assets
|
||||
- **FE Image**: Frontend build only, mounted as volume to BE container
|
||||
- **BE Image**: Spring Boot with frontend assets mounted at runtime
|
||||
- **Setup**: Docker volume mount `fe-dist:/app/resources/public`
|
||||
- **Pros**:
|
||||
- Minimal changes to BE code
|
||||
- FE can be updated independently
|
||||
- Simpler than full reverse proxy
|
||||
- **Cons**:
|
||||
- Still single point of entry (BE)
|
||||
- Volume mounting complexity
|
||||
- Less clean separation
|
||||
|
||||
#### Option 3: Hybrid - Conditional Serving
|
||||
- **FE Image**: Standalone nginx serving frontend
|
||||
- **BE Image**: Spring Boot with configurable static asset serving (enabled/disabled via profile)
|
||||
- **Routing**: Either reverse proxy or frontend proxies API calls (client-side routing)
|
||||
- **Pros**:
|
||||
- Flexible deployment options
|
||||
- Can run in both monolithic and separated modes
|
||||
- Gradual migration path
|
||||
- **Cons**:
|
||||
- More configuration options to maintain
|
||||
- Testing complexity
|
||||
|
||||
#### Option 4: Build-Time Separation Only
|
||||
- Keep single Docker image but build FE and BE separately
|
||||
- FE assets copied into BE image at build time (current multi-stage optimized approach)
|
||||
- **Pros**:
|
||||
- Simple, maintains current architecture
|
||||
- Good caching as implemented
|
||||
- No runtime complexity
|
||||
- **Cons**:
|
||||
- Not true separation
|
||||
- Can't update FE independently
|
||||
|
||||
### Technical Considerations
|
||||
|
||||
#### Frontend Changes Needed:
|
||||
1. **API URL configuration**: Already via `VUE_APP_KOMGA_API_URL`
|
||||
2. **Base path handling**: Vue Router uses `window.resourceBaseUrl`
|
||||
3. **Build process**: Need to inject environment variables at build time
|
||||
4. **Dockerfile**: Create `Dockerfile.fe` with nginx/alpine
|
||||
|
||||
#### Backend Changes Needed:
|
||||
1. **Conditional static resource serving**: Profile or property to disable
|
||||
2. **CORS configuration**: Enable for FE origin
|
||||
3. **SPA fallback**: Disable when FE served separately
|
||||
4. **Remove frontend assets from JAR**: Optional optimization
|
||||
|
||||
#### Deployment Options:
|
||||
1. **Docker Compose**: 3 services (fe, be, reverse-proxy)
|
||||
2. **Kubernetes**: Separate deployments + ingress
|
||||
3. **Single host with port mapping**: FE on port 80, BE on port 25600, nginx routing
|
||||
|
||||
### Evaluation of Solutions
|
||||
|
||||
#### Solution A: Multi-stage Single Image (Already Implemented)
|
||||
- **What**: `Dockerfile.local.optimized` with separate FE/BE build stages, single runtime image
|
||||
- **Build time impact**: High improvement for incremental builds
|
||||
- **Deployment**: Single container, simple
|
||||
- **Pros**:
|
||||
- Already implemented and working
|
||||
- Maximum Docker layer caching
|
||||
- No architectural changes
|
||||
- Compatible with all existing setups
|
||||
- **Cons**:
|
||||
- Still monolithic deployment
|
||||
- Can't update FE independently
|
||||
- Image contains both FE and BE
|
||||
|
||||
#### Solution B: Two Images, Reverse Proxy
|
||||
- **What**: FE image (nginx), BE image (Spring Boot), reverse proxy (nginx/traefik)
|
||||
- **Build time**: FE and BE build independently, parallel builds possible
|
||||
- **Deployment**: 3 containers, more complex
|
||||
- **Pros**:
|
||||
- True separation of concerns
|
||||
- Independent scaling and updates
|
||||
- FE can be served via CDN
|
||||
- BE image smaller (no frontend assets)
|
||||
- Modern microservices architecture
|
||||
- **Cons**:
|
||||
- Significant architectural change
|
||||
- Reverse proxy configuration needed
|
||||
- CORS and authentication complexity
|
||||
- More moving parts
|
||||
|
||||
#### Solution C: Two Images, FE Proxying to BE
|
||||
- **What**: FE image (nginx with proxy_pass to BE), BE image (Spring Boot)
|
||||
- **Build time**: Similar to Solution B
|
||||
- **Deployment**: 2 containers, FE acts as reverse proxy
|
||||
- **Pros**:
|
||||
- Simpler than separate reverse proxy
|
||||
- FE controls routing
|
||||
- Single entry point
|
||||
- **Cons**:
|
||||
- FE container more complex (nginx config)
|
||||
- FE needs to be redeployed for BE URL changes
|
||||
- Less flexible than dedicated reverse proxy
|
||||
|
||||
#### Solution D: Build-Time Separation, Runtime Combined
|
||||
- **What**: Build FE and BE separately, but combine in final image
|
||||
- **Build time**: Similar to Solution A
|
||||
- **Deployment**: Single container
|
||||
- **Pros**:
|
||||
- Clean build separation
|
||||
- Can version FE and BE independently
|
||||
- Single deployment unit
|
||||
- **Cons**:
|
||||
- Still monolithic runtime
|
||||
- Complex build pipeline
|
||||
|
||||
### Technical Deep Dive: What Would Change
|
||||
|
||||
#### For Solution B (Recommended for True Separation):
|
||||
|
||||
**Frontend Dockerfile** (`Dockerfile.fe`):
|
||||
```dockerfile
|
||||
FROM node:18-alpine AS builder
|
||||
WORKDIR /app
|
||||
COPY komga-webui/package*.json ./
|
||||
RUN npm ci
|
||||
COPY komga-webui/ ./
|
||||
ARG VUE_APP_KOMGA_API_URL=http://backend:25600
|
||||
ENV VUE_APP_KOMGA_API_URL=${VUE_APP_KOMGA_API_URL}
|
||||
RUN npm run build
|
||||
|
||||
FROM nginx:alpine
|
||||
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||
COPY komga-webui/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
EXPOSE 80
|
||||
```
|
||||
|
||||
**Backend Dockerfile** (`Dockerfile.be`):
|
||||
```dockerfile
|
||||
FROM eclipse-temurin:21-jdk AS builder
|
||||
WORKDIR /app
|
||||
COPY gradle/ ./gradle
|
||||
COPY gradlew ./
|
||||
COPY gradle.properties ./
|
||||
COPY settings.gradle ./
|
||||
COPY build.gradle.kts ./
|
||||
COPY komga/ ./komga
|
||||
COPY komga-tray/ ./komga-tray
|
||||
# Don't copy frontend assets
|
||||
RUN ./gradlew :komga:bootJar -x test -x generateGitProperties -x prepareThymeLeaf
|
||||
|
||||
FROM eclipse-temurin:21-jre-alpine
|
||||
WORKDIR /app
|
||||
COPY --from=builder /app/komga/build/libs/komga-*.jar komga.jar
|
||||
# Enable CORS and disable static serving via environment
|
||||
ENV SPRING_PROFILES_include=docker,separated-fe
|
||||
RUN addgroup -S komga && adduser -S komga -G komga
|
||||
USER komga
|
||||
EXPOSE 25600
|
||||
ENTRYPOINT ["java", "-Dspring.profiles.include=docker,separated-fe", "--enable-native-access=ALL-UNNAMED", "-jar", "komga.jar"]
|
||||
```
|
||||
|
||||
**Reverse Proxy Configuration** (nginx):
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
|
||||
# Frontend static files
|
||||
location / {
|
||||
proxy_pass http://frontend:80;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
|
||||
# Backend API
|
||||
location /api/ {
|
||||
proxy_pass http://backend:25600;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
|
||||
location /opds/ {
|
||||
proxy_pass http://backend:25600;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
|
||||
location /sse/ {
|
||||
proxy_pass http://backend:25600;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Backend Code Changes Needed**:
|
||||
1. New Spring profile `separated-fe` that:
|
||||
- Disables `ResourceNotFoundController` SPA fallback
|
||||
- Configures CORS for FE origin
|
||||
- Optionally skips static resource registration
|
||||
2. Update `application.yml` with conditional configuration
|
||||
|
||||
### Technical Implementation Deep Dive
|
||||
|
||||
#### Frontend Changes (All Options)
|
||||
|
||||
**Build Configuration** (`komga-webui/`):
|
||||
1. **Environment variables**: Already supports `VUE_APP_KOMGA_API_URL` in `urls.ts`
|
||||
2. **Base path**: `window.resourceBaseUrl` set by Thymeleaf injection; need alternative for standalone FE
|
||||
3. **Build-time injection**: Could use `envsubst` or templating to replace placeholders in `index.html`
|
||||
4. **Docker multi-stage build**: Need to install dependencies, build, and copy to nginx
|
||||
|
||||
**Potential issues**:
|
||||
- Frontend needs to know backend URL at build time (unless using runtime config)
|
||||
- Authentication cookies may need `SameSite=None; Secure` for cross-origin
|
||||
- WebSocket/SSE connections need proper CORS headers
|
||||
|
||||
#### Backend Changes (Option B - Full Separation)
|
||||
|
||||
**Spring Profile `separated-fe`**:
|
||||
```kotlin
|
||||
@Configuration
|
||||
@Profile("separated-fe")
|
||||
class SeparatedFeConfiguration {
|
||||
@Bean
|
||||
fun corsConfigurationSource(): CorsConfigurationSource {
|
||||
val configuration = CorsConfiguration()
|
||||
configuration.allowedOrigins = listOf("http://frontend:80", "http://localhost:8081")
|
||||
configuration.allowedMethods = listOf("GET", "POST", "PUT", "DELETE", "OPTIONS")
|
||||
configuration.allowCredentials = true
|
||||
configuration.allowedHeaders = listOf("*")
|
||||
val source = UrlBasedCorsConfigurationSource()
|
||||
source.registerCorsConfiguration("/api/**", configuration)
|
||||
source.registerCorsConfiguration("/opds/**", configuration)
|
||||
source.registerCorsConfiguration("/sse/**", configuration)
|
||||
return source
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
fun resourceNotFoundController(): ResourceNotFoundController? {
|
||||
// Return null bean to disable SPA fallback
|
||||
return null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Conditional Static Resource Registration**:
|
||||
- Modify `WebMvcConfiguration` to check for profile/property
|
||||
- Skip `addResourceHandlers` registration when `komga.webui.enabled=false`
|
||||
|
||||
**Application Configuration**:
|
||||
```yaml
|
||||
# application-separated-fe.yml
|
||||
komga:
|
||||
webui:
|
||||
enabled: false
|
||||
cors:
|
||||
allowed-origins: ${FE_ORIGIN:http://frontend:80}
|
||||
```
|
||||
|
||||
#### Docker Configuration Details
|
||||
|
||||
**Frontend Dockerfile** (`Dockerfile.fe`):
|
||||
```dockerfile
|
||||
# Stage 1: Build
|
||||
FROM node:18-alpine AS builder
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm ci
|
||||
COPY . .
|
||||
ARG VUE_APP_KOMGA_API_URL
|
||||
ENV VUE_APP_KOMGA_API_URL=${VUE_APP_KOMGA_API_URL}
|
||||
RUN npm run build
|
||||
|
||||
# Stage 2: Runtime
|
||||
FROM nginx:alpine
|
||||
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||
# Custom nginx config for SPA fallback
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD wget --no-verbose --tries=1 --spider http://localhost:80/ || exit 1
|
||||
EXPOSE 80
|
||||
```
|
||||
|
||||
**Frontend nginx.conf**:
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# Gzip compression
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1024;
|
||||
gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
|
||||
|
||||
# SPA fallback
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# Cache static assets
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Backend Dockerfile** (`Dockerfile.be`):
|
||||
```dockerfile
|
||||
# Stage 1: Build
|
||||
FROM eclipse-temurin:21-jdk AS builder
|
||||
WORKDIR /app
|
||||
# Copy Gradle files
|
||||
COPY gradle/ ./gradle
|
||||
COPY gradlew ./
|
||||
COPY gradle.properties ./
|
||||
COPY settings.gradle ./
|
||||
COPY build.gradle.kts ./
|
||||
# Copy only backend source
|
||||
COPY komga/ ./komga
|
||||
COPY komga-tray/ ./komga-tray
|
||||
# Build without frontend tasks
|
||||
RUN ./gradlew :komga:bootJar -x test -x generateGitProperties -x prepareThymeLeaf -x npmInstall -x npmBuild -x copyWebDist
|
||||
|
||||
# Stage 2: Runtime
|
||||
FROM eclipse-temurin:21-jre-alpine
|
||||
WORKDIR /app
|
||||
COPY --from=builder /app/komga/build/libs/komga-*.jar komga.jar
|
||||
# Environment variables for separated mode
|
||||
ENV SPRING_PROFILES_ACTIVE=docker,separated-fe
|
||||
ENV FE_ORIGIN=http://frontend:80
|
||||
# Non-root user
|
||||
RUN addgroup -S komga && adduser -S komga -G komga
|
||||
USER komga
|
||||
EXPOSE 25600
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
|
||||
CMD wget --no-verbose --tries=1 --spider http://localhost:25600/api/v1/health || exit 1
|
||||
ENTRYPOINT ["java", "-Dspring.profiles.active=${SPRING_PROFILES_ACTIVE}", "--enable-native-access=ALL-UNNAMED", "-jar", "komga.jar"]
|
||||
```
|
||||
|
||||
**Reverse Proxy Dockerfile** (`Dockerfile.reverse-proxy`):
|
||||
```dockerfile
|
||||
FROM nginx:alpine
|
||||
COPY nginx-reverse-proxy.conf /etc/nginx/nginx.conf
|
||||
EXPOSE 80
|
||||
```
|
||||
|
||||
**Docker Compose** (`docker-compose.separated.yml`):
|
||||
```yaml
|
||||
version: '3.8'
|
||||
services:
|
||||
frontend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.fe
|
||||
args:
|
||||
VUE_APP_KOMGA_API_URL: http://backend:25600
|
||||
environment:
|
||||
- VUE_APP_KOMGA_API_URL=http://backend:25600
|
||||
networks:
|
||||
- komga-network
|
||||
depends_on:
|
||||
- backend
|
||||
|
||||
backend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.be
|
||||
environment:
|
||||
- SPRING_PROFILES_ACTIVE=docker,separated-fe
|
||||
- FE_ORIGIN=http://frontend:80
|
||||
networks:
|
||||
- komga-network
|
||||
volumes:
|
||||
- komga_config:/config
|
||||
- ./data:/data:ro
|
||||
|
||||
reverse-proxy:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.reverse-proxy
|
||||
ports:
|
||||
- "25600:80"
|
||||
networks:
|
||||
- komga-network
|
||||
depends_on:
|
||||
- frontend
|
||||
- backend
|
||||
|
||||
volumes:
|
||||
komga_config:
|
||||
|
||||
networks:
|
||||
komga-network:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
#### Build Pipeline Changes
|
||||
|
||||
**CI/CD Considerations**:
|
||||
1. **Parallel builds**: FE and BE can build simultaneously
|
||||
2. **Version tagging**: Need consistent versioning across images (e.g., `komga:1.2.3-be`, `komga:1.2.3-fe`)
|
||||
3. **Dependency caching**: `node_modules` and Gradle caches should be preserved between builds
|
||||
4. **Multi-arch builds**: Both images need multi-architecture support for production
|
||||
|
||||
**Local Development**:
|
||||
1. **Hot reload**: FE development server (`npm run serve`) with proxy to backend
|
||||
2. **Mixed mode**: Option to run FE standalone + BE with embedded assets for debugging
|
||||
3. **Profile switching**: Easy toggle between monolithic and separated modes
|
||||
|
||||
#### Migration Strategy
|
||||
|
||||
**Phase 1 - Coexistence**:
|
||||
- Keep existing `Dockerfile.local` and `Dockerfile.local.optimized`
|
||||
- Add new Dockerfiles as experimental
|
||||
- Document both approaches
|
||||
|
||||
**Phase 2 - Feature Flags**:
|
||||
- Add Spring properties to control static serving
|
||||
- Make CORS configurable via environment
|
||||
- Update frontend to read config from multiple sources
|
||||
|
||||
**Phase 3 - Gradual Migration**:
|
||||
- Update documentation with new recommended approach
|
||||
- Provide migration scripts for existing users
|
||||
- Collect feedback from early adopters
|
||||
|
||||
**Phase 4 - Deprecation** (optional):
|
||||
- Mark monolithic mode as deprecated
|
||||
- Encourage new deployments to use separated architecture
|
||||
|
||||
### Recommendation
|
||||
|
||||
**For most users**: **Solution A (multi-stage single image)** is sufficient and already implemented. It provides:
|
||||
- 50%+ faster incremental builds
|
||||
- No architectural complexity
|
||||
- Simple deployment
|
||||
- Backward compatibility
|
||||
|
||||
**For advanced deployments**: **Solution B (two images + reverse proxy)** if you need:
|
||||
- Independent scaling of FE/BE
|
||||
- CDN for static assets
|
||||
- Microservices architecture
|
||||
- Team separation (frontend/backend teams)
|
||||
|
||||
**Implementation Priority**:
|
||||
1. **Short term**: Use Solution A (already done)
|
||||
2. **Medium term**: If needed, implement Solution B as optional configuration
|
||||
3. **Long term**: Maintain both options with feature flags
|
||||
|
||||
### Decision Matrix
|
||||
|
||||
| Factor | Solution A | Solution B | Solution C |
|
||||
|--------|------------|------------|------------|
|
||||
| Build speed | ✅ Excellent | ✅ Good | ✅ Good |
|
||||
| Deployment simplicity | ✅ Excellent | ⚠ Moderate | ⚠ Moderate |
|
||||
| Independent updates | ❌ No | ✅ Yes | ⚠ Partial |
|
||||
| Image size | ⚠ Medium | ✅ Small | ✅ Small |
|
||||
| Architectural change | ❌ None | ✅ Significant | ⚠ Moderate |
|
||||
| Learning curve | ✅ None | ⚠ High | ⚠ Medium |
|
||||
| Production readiness | ✅ Today | ⚠ Requires work | ⚠ Requires work |
|
||||
|
||||
### Next Steps
|
||||
Based on the brainstorming, which direction makes the most sense for your use case?
|
||||
|
||||
### Recommended Approach
|
||||
|
||||
Based on the analysis, **Option 1 (Full Separation with Reverse Proxy)** offers the cleanest architecture long-term, but **Option 3 (Hybrid)** provides more flexibility during transition.
|
||||
|
||||
**Suggested phased implementation**:
|
||||
|
||||
**Phase 1**: Create standalone FE Docker image + update BE to conditionally disable static serving
|
||||
**Phase 2**: Add reverse proxy for local development/demo
|
||||
**Phase 3**: Update CI/CD to build both images
|
||||
**Phase 4**: Documentation and migration guide
|
||||
|
||||
### Technical Brainstorming Questions
|
||||
|
||||
Based on the detailed technical analysis above, here are key questions to consider:
|
||||
|
||||
1. **Which technical challenges are most concerning?**
|
||||
- CORS configuration and authentication?
|
||||
- Reverse proxy setup and routing?
|
||||
- Build pipeline changes?
|
||||
- Backward compatibility?
|
||||
|
||||
2. **What's the priority for implementation?**
|
||||
- Quick wins (Solution A already done)
|
||||
- Medium-term separation (Solution B)
|
||||
- Long-term architectural evolution
|
||||
|
||||
3. **Which specific technical areas need more research?**
|
||||
- Spring Boot static resource conditional disabling
|
||||
- Vue.js runtime configuration vs build-time configuration
|
||||
- Docker networking and service discovery
|
||||
- Health checks and monitoring in separated architecture
|
||||
|
||||
4. **How should we handle the transition?**
|
||||
- Feature flags and gradual rollout?
|
||||
- Parallel support for both architectures?
|
||||
- Big-bang switch for new deployments only?
|
||||
|
||||
### Next Steps for Brainstorming
|
||||
|
||||
The technical implementation details have been explored in depth. Before proceeding with any implementation, we need to decide on:
|
||||
- **Goal**: What problem are we really trying to solve? (Build speed vs architectural separation)
|
||||
- **Scope**: How much change is acceptable? (Minimal vs comprehensive)
|
||||
- **Timeline**: When would this need to be implemented?
|
||||
|
||||
What aspect of the technical implementation would you like to dive deeper into, or are there other solutions you'd like to explore?
|
||||
16
DOCKERHUB.md
16
DOCKERHUB.md
|
|
@ -1,16 +0,0 @@
|
|||
[](https://opencollective.com/komga) 
|
||||
[](https://discord.gg/TdRpkDu)
|
||||
|
||||
[](https://github.com/gotson/komga/actions?query=workflow%3ACI+branch%3Amaster)
|
||||
[](https://github.com/gotson/komga/releases)
|
||||
[](https://hub.docker.com/r/gotson/komga)
|
||||
|
||||
[](https://hosted.weblate.org/engage/komga/)
|
||||
|
||||
#  Komga
|
||||
|
||||
[Komga](https://github.com/gotson/komga) is a media server for your comics, mangas, BDs, magazines and eBooks.
|
||||
|
||||
## Usage
|
||||
|
||||
Please refer to the [official documentation](https://komga.org/docs/installation/docker).
|
||||
71
Dockerfile.local.optimized
Normal file
71
Dockerfile.local.optimized
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
# Multi-stage optimized Docker build for Komga
|
||||
# Frontend and backend are built in separate stages for better caching
|
||||
|
||||
# Stage 1: Frontend builder
|
||||
FROM node:18-alpine AS frontend-builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files first for better layer caching
|
||||
COPY komga-webui/package*.json ./
|
||||
|
||||
# Install dependencies (use npm ci for reproducible builds)
|
||||
RUN npm ci
|
||||
|
||||
# Copy the rest of the frontend source
|
||||
COPY komga-webui/ ./
|
||||
|
||||
# Build the frontend
|
||||
RUN npm run build
|
||||
|
||||
# Inject ThymeLeaf th: tags into index.html (mimics Gradle's prepareThymeLeaf task)
|
||||
# This regex matches src="/path", href="/path", content="/path" and adds th: attributes
|
||||
RUN sed -i -E 's/(src|href|content)="(\/[^"]*)"/\1="\2" th:\1="@{\2}"/g' dist/index.html
|
||||
|
||||
|
||||
# Stage 2: Backend builder
|
||||
FROM eclipse-temurin:21-jdk AS backend-builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install Node.js 18 (required for Gradle's npm tasks, though we won't run them)
|
||||
RUN apt-get update && \
|
||||
apt-get install -y curl && \
|
||||
curl -fsSL https://deb.nodesource.com/setup_18.x | bash - && \
|
||||
apt-get install -y nodejs && \
|
||||
apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copy Gradle files
|
||||
COPY gradle ./gradle
|
||||
COPY gradlew ./
|
||||
COPY gradle.properties ./
|
||||
COPY settings.gradle ./
|
||||
COPY build.gradle.kts ./
|
||||
|
||||
# Copy backend source (excluding webui which is already built)
|
||||
COPY komga ./komga
|
||||
COPY komga-tray ./komga-tray
|
||||
|
||||
# Copy built frontend from frontend-builder stage
|
||||
COPY --from=frontend-builder /app/dist/ komga/src/main/resources/public/
|
||||
|
||||
# Build the backend JAR (skip prepareThymeLeaf since frontend is already built)
|
||||
ENV NODE_OPTIONS="--max-old-space-size=4096"
|
||||
RUN ./gradlew :komga:bootJar -x test -x generateGitProperties -x prepareThymeLeaf
|
||||
|
||||
|
||||
# Stage 3: Runtime
|
||||
FROM eclipse-temurin:21-jre-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy built artifact from backend-builder stage
|
||||
COPY --from=backend-builder /app/komga/build/libs/komga-*.jar komga.jar
|
||||
|
||||
# Create non-root user
|
||||
RUN addgroup -S komga && adduser -S komga -G komga
|
||||
USER komga
|
||||
|
||||
EXPOSE 25600
|
||||
|
||||
ENTRYPOINT ["java", "-Dspring.profiles.include=docker", "--enable-native-access=ALL-UNNAMED", "-jar", "komga.jar"]
|
||||
2
Dockerfile.test-minimal
Normal file
2
Dockerfile.test-minimal
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
FROM alpine:latest
|
||||
RUN echo "Test build successful"
|
||||
5
Dockerfile.test-partial
Normal file
5
Dockerfile.test-partial
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
FROM node:18-alpine AS frontend-builder
|
||||
WORKDIR /app
|
||||
COPY komga-webui/package*.json ./
|
||||
RUN echo "Package files copied successfully"
|
||||
RUN npm ci 2>&1 | tail -20
|
||||
137
ai-docs/docker-push-images.md
Normal file
137
ai-docs/docker-push-images.md
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
# Hướng dẫn Push Docker Images lên GitHub Container Registry
|
||||
|
||||
## Tổng quan
|
||||
|
||||
Hiện tại, dự án Komga fork sử dụng JReleaser để tự động build và push Docker images lên GitHub Container Registry (`ghcr.io/simplesoft-duongdt3/komga`) khi có release. Tài liệu này hướng dẫn cách để bạn tự push images lên repository của riêng bạn trên GitHub Container Registry.
|
||||
|
||||
Có hai phương pháp:
|
||||
1. **Phương pháp thủ công**: Build image, tag và push bằng lệnh Docker trực tiếp lên GitHub Container Registry.
|
||||
2. **Phương pháp sử dụng JReleaser**: Cấu hình JReleaser để push tự động lên GitHub Container Registry.
|
||||
|
||||
## Phương pháp thủ công
|
||||
|
||||
### Bước 1: Build Docker image
|
||||
|
||||
Sử dụng script `build-local-docker.sh` để build image local với version hiện tại (lấy từ `gradle.properties`):
|
||||
|
||||
```bash
|
||||
./build-local-docker.sh
|
||||
```
|
||||
|
||||
Script này sẽ build image với tag `komga-local:<version>`. Ví dụ: `komga-local:1.24.3`.
|
||||
|
||||
Nếu bạn muốn build với Dockerfile tối ưu (mặc định), script sử dụng `Dockerfile.local.optimized`. Bạn cũng có thể build trực tiếp bằng lệnh docker:
|
||||
|
||||
```bash
|
||||
docker build -f Dockerfile.local.optimized -t komga-local:$(grep 'version=' gradle.properties | cut -d'=' -f2) .
|
||||
```
|
||||
|
||||
### Bước 2: Tag image với repository đích
|
||||
|
||||
Để push lên GitHub Container Registry (thay `simplesoft-duongdt3` bằng tên tổ chức hoặc username GitHub):
|
||||
|
||||
```bash
|
||||
docker tag komga-local:<version> ghcr.io/simplesoft-duongdt3/komga:<version>
|
||||
docker tag komga-local:<version> ghcr.io/simplesoft-duongdt3/komga:latest
|
||||
```
|
||||
|
||||
### Bước 3: Đăng nhập vào registry
|
||||
|
||||
**GitHub Container Registry:**
|
||||
```bash
|
||||
echo $GHCR_TOKEN | docker login ghcr.io -u simplesoft-duongdt3 --password-stdin
|
||||
```
|
||||
|
||||
Trong đó `GHCR_TOKEN` là GitHub Personal Access Token với quyền `write:packages`. Bạn có thể tạo token tại https://github.com/settings/tokens.
|
||||
|
||||
### Bước 4: Push images
|
||||
|
||||
**Push lên GitHub Container Registry:**
|
||||
```bash
|
||||
docker push simplesoft-duongdt3/komga:<version>
|
||||
docker push simplesoft-duongdt3/komga:latest
|
||||
```
|
||||
|
||||
**Push lên GitHub Container Registry:**
|
||||
```bash
|
||||
docker push ghcr.io/simplesoft-duongdt3/komga:<version>
|
||||
docker push ghcr.io/simplesoft-duongdt3/komga:latest
|
||||
```
|
||||
|
||||
### Bước 5: Kiểm tra
|
||||
|
||||
Kiểm tra images đã được push lên GitHub Container Registry hoặc GHCR qua giao diện web hoặc lệnh:
|
||||
|
||||
```bash
|
||||
docker pull simplesoft-duongdt3/komga:latest
|
||||
```
|
||||
|
||||
## Phương pháp sử dụng JReleaser
|
||||
|
||||
JReleaser đã được cấu hình sẵn trong `build.gradle.kts` để build multi‑arch images (linux/amd64, linux/arm/v7, linux/arm64/v8) và push lên GitHub Container Registry và GHCR. Bạn có thể sửa cấu hình để thay đổi repository đích.
|
||||
|
||||
### Cấu hình JReleaser
|
||||
|
||||
Mở file `build.gradle.kts` và tìm phần `packagers.docker`. Sửa các mục sau:
|
||||
|
||||
1. **Thay đổi image names:** Tìm khối `imageNames` và thay thế bằng tên repository của bạn:
|
||||
|
||||
```kotlin
|
||||
imageNames =
|
||||
listOf(
|
||||
"simplesoft-duongdt3/komga:latest",
|
||||
"simplesoft-duongdt3/komga:{{projectVersion}}",
|
||||
"simplesoft-duongdt3/komga:{{projectVersionMajor}}.x",
|
||||
)
|
||||
```
|
||||
|
||||
Để push cùng lúc cả GitHub Container Registry và GHCR, bạn có thể thêm nhiều image names hoặc cấu hình thêm registry.
|
||||
|
||||
2. **Cấu hình registries:** Phần `registries` đã định nghĩa `ghcr.io` và `ghcr.io`. Nếu bạn muốn push lên GitHub Container Registry với username khác, bạn cần đảm bảo đăng nhập qua `docker login` trước khi chạy JReleaser. JReleaser sử dụng `externalLogin = true`, nghĩa là nó sẽ sử dụng thông tin đăng nhập từ Docker CLI.
|
||||
|
||||
Nếu bạn chỉ muốn push lên một registry, bạn có thể xóa registry không cần thiết.
|
||||
|
||||
3. **Label source:** Nếu muốn thay đổi label `org.opencontainers.image.source` trong image, bạn cần sửa file template `komga/docker/Dockerfile.tpl` (dòng cuối).
|
||||
|
||||
### Chạy JReleaser publish
|
||||
|
||||
Sau khi cấu hình, bạn có thể chạy lệnh sau để build và push images:
|
||||
|
||||
```bash
|
||||
./gradlew jreleaserPublish
|
||||
```
|
||||
|
||||
Lệnh này sẽ:
|
||||
- Build multi‑arch images sử dụng Docker Buildx.
|
||||
- Push images lên các registry đã cấu hình.
|
||||
|
||||
**Lưu ý:** JReleaser publish thường được kích hoạt trong workflow release khi `docker_release: true`. Bạn có thể chạy local nhưng cần đảm bảo đã login vào registry tương ứng.
|
||||
|
||||
### Cấu hình qua environment variables
|
||||
|
||||
JReleaser cho phép override một số cấu hình qua biến môi trường, ví dụ:
|
||||
|
||||
- `JRELEASER_DOCKER_IMAGENAMES`: danh sách image names phân tách bằng dấu phẩy.
|
||||
- `JRELEASER_DOCKER_REGISTRIES`: danh sách registries.
|
||||
|
||||
Tuy nhiên, cách đơn giản nhất là sửa file `build.gradle.kts`.
|
||||
|
||||
## Lưu ý quan trọng
|
||||
|
||||
1. **Version:** Version được lấy từ `gradle.properties`. Đảm bảo version đúng trước khi push.
|
||||
2. **Multi‑arch:** Nếu bạn muốn hỗ trợ nhiều kiến trúc (amd64, arm64, arm/v7), hãy sử dụng JReleaser hoặc Docker Buildx. Phương pháp thủ công chỉ build image cho kiến trúc hiện tại.
|
||||
3. **Quyền riêng tư:** Repository trên GitHub Container Registry và GHCR có thể là public hoặc private. Với private repository, bạn cần đăng nhập với token có quyền push.
|
||||
4. **Dung lượng:** Image size khoảng 200‑300MB. Đảm bảo bạn có đủ quota trên registry.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **Lỗi unauthorized:** Kiểm tra lại đăng nhập với `docker login`. Với GHCR, token cần có quyền `write:packages`.
|
||||
- **Lỗi denied: requested access to the resource is denied:** Đảm bảo tên repository đúng định dạng `username/reponame` và bạn có quyền push.
|
||||
- **Lỗi manifest invalid:** Khi push multi‑arch images, cần sử dụng Docker Buildx. JReleaser đã tích hợp sẵn.
|
||||
- **Lỗi không tìm thấy Dockerfile:** Khi dùng JReleaser, template Dockerfile nằm ở `komga/docker/Dockerfile.tpl`. Đảm bảo file tồn tại.
|
||||
|
||||
## Tham khảo
|
||||
|
||||
- [Docker Documentation](https://docs.docker.com/)
|
||||
- [GitHub Container Registry Documentation](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry)
|
||||
- [JReleaser Docker Packager](https://jreleaser.org/guide/latest/packagers/docker.html)
|
||||
|
|
@ -3,6 +3,8 @@
|
|||
## Overview
|
||||
Docker Compose setup để chạy Komga với PostgreSQL cho development và testing.
|
||||
|
||||
**Optimized Build**: Để build nhanh hơn với caching tốt hơn, sử dụng `Dockerfile.local.optimized` và `docker-compose.local.optimized.yml`.
|
||||
|
||||
## File Structure
|
||||
|
||||
### 1. docker-compose.yml (Production/Standard)
|
||||
|
|
@ -115,7 +117,69 @@ volumes:
|
|||
- Có thể timeout do download Gradle distribution
|
||||
- Migration fixes đã được áp dụng trong source code
|
||||
|
||||
### 3. docker-compose-test.yml (cho testing)
|
||||
### 2a. docker-compose.local.optimized.yml (Local Development với Build Optimized)
|
||||
```yaml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15-alpine
|
||||
container_name: komga-postgres
|
||||
environment:
|
||||
POSTGRES_DB: komga
|
||||
POSTGRES_USER: komga
|
||||
POSTGRES_PASSWORD: komga123
|
||||
ports:
|
||||
- "5433:5432"
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
- ./docker/postgres/init.sql:/docker-entrypoint-initdb.d/01-init.sql
|
||||
healthcheck:
|
||||
test: [ "CMD-SHELL", "pg_isready -U komga" ]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
komga:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.local.optimized
|
||||
container_name: komga-backend
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
SPRING_PROFILES_ACTIVE: docker
|
||||
KOMGA_DATABASE_TYPE: postgresql
|
||||
KOMGA_DATABASE_URL: jdbc:postgresql://postgres:5432/komga?connectTimeout=30000&socketTimeout=60000
|
||||
KOMGA_DATABASE_USERNAME: komga
|
||||
KOMGA_DATABASE_PASSWORD: komga123
|
||||
KOMGA_CONFIG_DIR: /config
|
||||
KOMGA_DATABASE_POOL_SIZE: 10
|
||||
KOMGA_DATABASE_MAX_POOL_SIZE: 10
|
||||
SPRING_FLYWAY_ENABLED: "true"
|
||||
SPRING_FLYWAY_BASELINE_ON_MIGRATE: "true"
|
||||
SPRING_FLYWAY_BASELINE_VERSION: "20250730173126"
|
||||
ports:
|
||||
- "25600:25600"
|
||||
volumes:
|
||||
- komga_config:/config
|
||||
- ./data:/data:ro
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
komga_config:
|
||||
```
|
||||
|
||||
**Lưu ý về Dockerfile.local.optimized:**
|
||||
- Multi-stage build tách biệt frontend và backend để tối ưu caching
|
||||
- Frontend (`npm install`, `npm run build`) được cache độc lập
|
||||
- Thay đổi backend không trigger rebuild frontend
|
||||
- Build nhanh hơn đáng kể cho incremental builds
|
||||
- Sử dụng: `docker-compose -f docker-compose.local.optimized.yml up -d`
|
||||
|
||||
### 4. docker-compose-test.yml (cho testing)
|
||||
```yaml
|
||||
version: '3.8'
|
||||
|
||||
|
|
@ -143,7 +207,7 @@ volumes:
|
|||
postgres_test_data:
|
||||
```
|
||||
|
||||
### 3. docker/postgres/init.sql
|
||||
### 5. docker/postgres/init.sql
|
||||
```sql
|
||||
-- PostgreSQL initialization script for Komga
|
||||
-- Creates necessary extensions and sets up database
|
||||
|
|
|
|||
281
ai-docs/frontend-build-process.md
Normal file
281
ai-docs/frontend-build-process.md
Normal file
|
|
@ -0,0 +1,281 @@
|
|||
# Frontend Build Process and Thymeleaf Integration
|
||||
|
||||
## Overview
|
||||
|
||||
Komga's frontend is built using Vue.js and integrated with the Spring Boot backend through Thymeleaf template processing. This document explains the build pipeline, Thymeleaf tag injection, and how static assets are served.
|
||||
|
||||
## Build Pipeline
|
||||
|
||||
### 1. Frontend Build (`komga-webui/`)
|
||||
- **Technology Stack**: Vue.js 2.6 with TypeScript, Vuetify, Vuex, Vue Router
|
||||
- **Build Tool**: Vue CLI with Webpack
|
||||
- **Entry Point**: `komga-webui/src/main.ts`
|
||||
- **Build Output**: `komga-webui/dist/` directory
|
||||
|
||||
### 2. Gradle Tasks (`komga/build.gradle.kts`)
|
||||
The build process is orchestrated by Gradle tasks:
|
||||
|
||||
```kotlin
|
||||
// 1. npmInstall - Install Node.js dependencies
|
||||
register<Exec>("npmInstall") {
|
||||
workingDir(webui) // $rootDir/komga-webui
|
||||
commandLine("npm", "install")
|
||||
}
|
||||
|
||||
// 2. npmBuild - Build frontend with Vue CLI
|
||||
register<Exec>("npmBuild") {
|
||||
dependsOn("npmInstall")
|
||||
workingDir(webui)
|
||||
commandLine("npm", "run", "build")
|
||||
}
|
||||
|
||||
// 3. copyWebDist - Copy built files to resources
|
||||
register<Sync>("copyWebDist") {
|
||||
dependsOn("npmBuild")
|
||||
from("$webui/dist/")
|
||||
into("$projectDir/src/main/resources/public/")
|
||||
}
|
||||
|
||||
// 4. prepareThymeLeaf - Inject Thymeleaf tags
|
||||
register<Copy>("prepareThymeLeaf") {
|
||||
dependsOn("copyWebDist")
|
||||
from("$webui/dist/index.html")
|
||||
into("$projectDir/src/main/resources/public/")
|
||||
filter { line ->
|
||||
line.replace("((?:src|content|href)=\")([\\w]*/.*?)(\")".toRegex()) {
|
||||
it.groups[0]?.value + " th:" + it.groups[1]?.value + "@{" +
|
||||
it.groups[2]?.value?.prefixIfNot("/") + "}" + it.groups[3]?.value
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Thymeleaf Tag Injection
|
||||
|
||||
### Purpose
|
||||
Thymeleaf tags are injected to make static resource URLs context-aware. When Komga is deployed under a subpath (e.g., `http://example.com/komga/`), the tags ensure assets load correctly without hardcoded paths.
|
||||
|
||||
### Injected Tags in `index.html`
|
||||
|
||||
```html
|
||||
<!-- Original (from Vue build) -->
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<script>window.resourceBaseUrl = '/'</script>
|
||||
|
||||
<!-- After prepareThymeLeaf task -->
|
||||
<link rel="icon" href="/favicon.ico" th:href="@{/favicon.ico}">
|
||||
<script th:inline="javascript">
|
||||
window.resourceBaseUrl = /*[(${"'" + baseUrl + "'"})]*/ '/'
|
||||
</script>
|
||||
```
|
||||
|
||||
### Tag Breakdown
|
||||
|
||||
1. **URL Rewriting Tags**:
|
||||
```html
|
||||
th:href="@{/favicon.ico}"
|
||||
th:content="@{/mstile-144x144.png}"
|
||||
th:href="@{/manifest.json}"
|
||||
```
|
||||
- `@{...}` syntax: Automatically prepends context path
|
||||
- Works for `href`, `src`, `content` attributes
|
||||
- Applies to: favicons, manifest, tile images
|
||||
|
||||
2. **JavaScript Inlining**:
|
||||
```html
|
||||
<script th:inline="javascript">
|
||||
window.resourceBaseUrl = /*[(${"'" + baseUrl + "'"})]*/ '/'
|
||||
</script>
|
||||
```
|
||||
- `th:inline="javascript"`: Enables Thymeleaf processing in JS
|
||||
- `/*[(${...})]*/`: Injects server-side `baseUrl` variable
|
||||
- `window.resourceBaseUrl`: Used by frontend for API calls
|
||||
|
||||
### Regex Pattern
|
||||
The `prepareThymeLeaf` task uses this regex to find and replace URLs:
|
||||
```regex
|
||||
((?:src|content|href)=\")([\\w]*/.*?)(\")
|
||||
```
|
||||
- Matches: `src="...`, `content="...`, `href="...`
|
||||
- Groups: Attribute name and URL value
|
||||
- Replacement: Adds `th:` prefix and `@{...}` wrapper
|
||||
|
||||
## Frontend Distribution (`frontend-dist/`)
|
||||
|
||||
### Directory Structure
|
||||
```
|
||||
frontend-dist/
|
||||
├── index.html # Main HTML with Thymeleaf tags
|
||||
├── js/
|
||||
│ ├── app.cd1e7444.js # Main application bundle
|
||||
│ ├── chunk-vendors.215f0888.js # Vendor bundle
|
||||
│ └── [chunk].js # Code-split chunks
|
||||
├── css/
|
||||
│ ├── app.c113e1ad.css # Application styles
|
||||
│ └── chunk-vendors.420551a9.css # Vendor styles
|
||||
├── fonts/ # Web fonts
|
||||
├── img/ # Static images
|
||||
└── manifest.json # PWA manifest
|
||||
```
|
||||
|
||||
### Building for Distribution
|
||||
The `build-local-docker.sh` script can export frontend files:
|
||||
```bash
|
||||
./build-local-docker.sh --export-dist ./frontend-dist
|
||||
```
|
||||
- Uses Docker multi-stage build to extract dist files
|
||||
- Creates standalone frontend distribution
|
||||
- Useful for testing or manual deployment
|
||||
|
||||
## Integration with Backend
|
||||
|
||||
### 1. Static Resource Serving
|
||||
- **Location**: `komga/src/main/resources/public/`
|
||||
- **Spring Boot**: Automatically serves files from `classpath:/public/`
|
||||
- **Access**: `http://localhost:25600/` serves `index.html`
|
||||
|
||||
### 2. Thymeleaf Controller
|
||||
```java
|
||||
// Komga uses Thymeleaf for serving index.html
|
||||
// The controller maps root path to index.html
|
||||
```
|
||||
|
||||
### 3. Base URL Handling
|
||||
- **Server-side**: `baseUrl` is determined from request context
|
||||
- **Client-side**: `window.resourceBaseUrl` used for API calls
|
||||
- **API Proxy**: Frontend routes API requests through correct path
|
||||
|
||||
## Development vs Production
|
||||
|
||||
### Development Mode
|
||||
- **Vue CLI Dev Server**: `npm run serve` on port 8081
|
||||
- **Hot Reload**: Instant updates without full rebuild
|
||||
- **API Proxy**: Dev server proxies API calls to backend (port 25600)
|
||||
|
||||
### Production Build
|
||||
- **Optimized Bundles**: Minified JS, CSS with hash filenames
|
||||
- **Code Splitting**: Dynamic imports for smaller initial load
|
||||
- **Tree Shaking**: Unused code removed
|
||||
|
||||
## Testing
|
||||
|
||||
### Unit Tests
|
||||
```bash
|
||||
cd komga-webui
|
||||
npm run test:unit
|
||||
```
|
||||
- **Framework**: Jest with Vue Test Utils
|
||||
- **Location**: `komga-webui/tests/unit/`
|
||||
- **Coverage**: Component functions and utilities
|
||||
|
||||
### Linting
|
||||
```bash
|
||||
npm run lint
|
||||
```
|
||||
- **Tools**: ESLint with Vue/TypeScript rules
|
||||
- **Configuration**: `.eslintrc.js`
|
||||
- **Pre-commit**: Runs automatically in CI
|
||||
|
||||
## Common Issues and Solutions
|
||||
|
||||
### 1. Missing Thymeleaf Tags
|
||||
**Symptom**: Assets fail to load when deployed under subpath
|
||||
**Solution**: Ensure `prepareThymeLeaf` task runs before `bootJar`
|
||||
```bash
|
||||
./gradlew :komga:prepareThymeLeaf :komga:bootJar
|
||||
```
|
||||
|
||||
### 2. Cached Assets with Hash Names
|
||||
**Symptom**: Browser caches old JS/CSS files
|
||||
**Solution**: Webpack hash filenames (e.g., `app.cd1e7444.js`) bust cache automatically
|
||||
|
||||
### 3. Base URL Incorrect
|
||||
**Symptom**: API calls go to wrong path
|
||||
**Debug**: Check `window.resourceBaseUrl` in browser console
|
||||
**Fix**: Verify Thymeleaf `baseUrl` injection
|
||||
|
||||
### 4. Docker Build Issues
|
||||
**Symptom**: Frontend not updated in Docker image
|
||||
**Solution**: Use optimized Docker build with separate frontend stage
|
||||
```bash
|
||||
./build-local-docker.sh --export-dist
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always run full build chain**:
|
||||
```bash
|
||||
./gradlew :komga:prepareThymeLeaf :komga:bootJar
|
||||
```
|
||||
|
||||
2. **Test with subpath deployment**:
|
||||
```bash
|
||||
# Deploy to /komga context
|
||||
server.servlet.context-path=/komga
|
||||
```
|
||||
|
||||
3. **Monitor bundle sizes**:
|
||||
- Check `frontend-dist/js/` directory size
|
||||
- Use Webpack Bundle Analyzer if needed
|
||||
|
||||
4. **Keep Thymeleaf tags minimal**:
|
||||
- Only inject where context-awareness needed
|
||||
- Avoid performance overhead
|
||||
|
||||
## Local Development Setup
|
||||
|
||||
### Quick Start with Pre-built Frontend
|
||||
|
||||
When you have a pre-built frontend in `frontend-dist/` (e.g., from Docker export or previous build), follow these steps to integrate with the Spring Boot backend:
|
||||
|
||||
1. **Copy static assets to public directory**:
|
||||
```bash
|
||||
mkdir -p komga/src/main/resources/public
|
||||
cp -r frontend-dist/* komga/src/main/resources/public/
|
||||
```
|
||||
|
||||
2. **Move index.html to templates directory** (required for Thymeleaf processing):
|
||||
```bash
|
||||
mkdir -p komga/src/main/resources/templates
|
||||
mv komga/src/main/resources/public/index.html komga/src/main/resources/templates/
|
||||
```
|
||||
|
||||
3. **Start backend excluding frontend build tasks**:
|
||||
```bash
|
||||
./gradlew :komga:bootRun -x npmInstall -x npmBuild -x copyWebDist -x prepareThymeLeaf
|
||||
```
|
||||
|
||||
4. **Verify frontend is served**:
|
||||
- Open http://localhost:25600/
|
||||
- Check browser console for errors
|
||||
- Static assets should load (JS, CSS, fonts)
|
||||
|
||||
### Using PostgreSQL (Optional)
|
||||
If you need PostgreSQL for development, start the database first:
|
||||
```bash
|
||||
docker-compose -f docker-compose.local.yml up -d postgres
|
||||
```
|
||||
Then set environment variables or activate `postgresql` profile.
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
**Thymeleaf template error "Error resolving template [index]"**:
|
||||
- Ensure `index.html` is in `komga/src/main/resources/templates/`
|
||||
- Remove duplicate `index.html` from `public/` directory
|
||||
|
||||
**Static assets not loading (404)**:
|
||||
- Verify JS/CSS files exist in `komga/src/main/resources/public/`
|
||||
- Check that Thymeleaf tags are properly injected (view page source)
|
||||
- Ensure `baseUrl` is correctly set (should be `/` for local development)
|
||||
|
||||
**Database errors during startup**:
|
||||
- Use SQLite default by not setting any database environment variables
|
||||
- Or ensure PostgreSQL is running and schema is applied
|
||||
|
||||
## Related Files
|
||||
|
||||
- `komga/build.gradle.kts`: Gradle build configuration
|
||||
- `komga-webui/vue.config.js`: Vue CLI configuration
|
||||
- `komga-webui/package.json`: Frontend dependencies
|
||||
- `build-local-docker.sh`: Docker build script
|
||||
- `frontend-dist/index.html`: Final HTML with Thymeleaf tags
|
||||
|
|
@ -3,26 +3,95 @@
|
|||
# Build Komga Docker image locally with multi-stage build
|
||||
set -e
|
||||
|
||||
# Default to optimized build
|
||||
OPTIMIZED=true
|
||||
DOCKERFILE="Dockerfile.local.optimized"
|
||||
BUILD_TYPE="Optimized multi-stage"
|
||||
EXPORT_DIST=false
|
||||
EXPORT_DIST_DIR="./frontend-dist"
|
||||
|
||||
# Parse command line arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--no-optimized)
|
||||
OPTIMIZED=false
|
||||
DOCKERFILE="Dockerfile.local"
|
||||
BUILD_TYPE="Standard"
|
||||
shift
|
||||
;;
|
||||
--export-dist)
|
||||
EXPORT_DIST=true
|
||||
if [[ -n "$2" && "$2" != --* ]]; then
|
||||
EXPORT_DIST_DIR="$2"
|
||||
shift
|
||||
else
|
||||
EXPORT_DIST_DIR="./frontend-dist"
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
--help|-h)
|
||||
echo "Usage: $0 [--no-optimized] [--export-dist [DIR]]"
|
||||
echo ""
|
||||
echo "Build Komga Docker image locally."
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --no-optimized Use the standard Dockerfile.local instead of optimized version"
|
||||
echo " --export-dist [DIR] Export frontend dist files to directory (default: ./frontend-dist)"
|
||||
echo " --help, -h Show this help message"
|
||||
echo ""
|
||||
echo "By default, uses Dockerfile.local.optimized for better build caching."
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1"
|
||||
echo "Use --help for usage information"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo "Building Komga Docker image locally..."
|
||||
echo "Build type: $BUILD_TYPE"
|
||||
echo "Dockerfile: $DOCKERFILE"
|
||||
echo ""
|
||||
|
||||
# Get version from gradle.properties
|
||||
VERSION=$(grep 'version=' gradle.properties | cut -d'=' -f2)
|
||||
echo "Building version: $VERSION"
|
||||
|
||||
# Build Docker image with multi-stage build (Node.js 18 + Java 21)
|
||||
docker build -f Dockerfile.local -t komga-local:$VERSION .
|
||||
# Build Docker image
|
||||
docker build -f $DOCKERFILE -t komga-local:$VERSION .
|
||||
|
||||
# Export frontend dist files if requested
|
||||
if [ "$EXPORT_DIST" = true ]; then
|
||||
if [ "$OPTIMIZED" = true ]; then
|
||||
echo "Exporting frontend dist files to $EXPORT_DIST_DIR"
|
||||
mkdir -p "$EXPORT_DIST_DIR"
|
||||
# Build frontend-builder stage and extract dist files
|
||||
docker build --target frontend-builder -t komga-frontend-builder-$VERSION -f $DOCKERFILE .
|
||||
docker run --rm komga-frontend-builder-$VERSION tar -czf - -C /app/dist . | tar -xzf - -C "$EXPORT_DIST_DIR"
|
||||
echo "Dist files exported to $EXPORT_DIST_DIR"
|
||||
docker rmi komga-frontend-builder-$VERSION 2>/dev/null || true
|
||||
else
|
||||
echo "Export dist is only supported with optimized build (--no-optimized not supported)"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Docker image built successfully:"
|
||||
echo " Image: komga-local:$VERSION"
|
||||
echo " Build: Multi-stage (Node.js 18 + Java 21)"
|
||||
echo " Build: $BUILD_TYPE (Node.js 18 + Java 21)"
|
||||
echo " Runtime: Java 21 (Temurin JRE)"
|
||||
echo ""
|
||||
echo "To run standalone container:"
|
||||
echo " docker run -p 25600:25600 -v /path/to/config:/config komga-local:$VERSION"
|
||||
echo ""
|
||||
echo "To run with docker-compose:"
|
||||
echo " docker-compose up -d"
|
||||
if [ "$OPTIMIZED" = true ]; then
|
||||
echo " docker-compose -f docker-compose.local.optimized.yml up -d"
|
||||
else
|
||||
echo " docker-compose -f docker-compose.local.yml up -d"
|
||||
fi
|
||||
echo ""
|
||||
echo "To tag with different name:"
|
||||
echo " docker tag komga-local:$VERSION komga:latest"
|
||||
|
|
@ -167,15 +167,14 @@ jreleaser {
|
|||
continueOnError = false
|
||||
templateDirectory = rootDir.resolve("komga/docker")
|
||||
repository.active = Active.NEVER
|
||||
buildArgs = listOf("--cache-from", "gotson/komga:latest")
|
||||
|
||||
imageNames =
|
||||
listOf(
|
||||
"komga:latest",
|
||||
"komga:{{projectVersion}}",
|
||||
"komga:{{projectVersionMajor}}.x",
|
||||
"simplesoft-duongdt3/komga:latest",
|
||||
"simplesoft-duongdt3/komga:{{projectVersion}}",
|
||||
"simplesoft-duongdt3/komga:{{projectVersionMajor}}.x",
|
||||
)
|
||||
registries {
|
||||
create("docker.io") { externalLogin = true }
|
||||
create("ghcr.io") { externalLogin = true }
|
||||
}
|
||||
buildx {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ include required("/stdlib/jdk/23/eclipse.conf")
|
|||
app {
|
||||
display-name = Komga
|
||||
fsname = komga
|
||||
vcs-url = "https://github.com/gotson/komga"
|
||||
vcs-url = "https://github.com/simplesoft-duongdt3/komga"
|
||||
vendor = "Gotson"
|
||||
description = "Media server for comics/mangas/BDs with API and OPDS support"
|
||||
license = MIT
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ services:
|
|||
command: ["postgres", "-c", "log_statement=all"] # Log all SQL for debugging
|
||||
|
||||
komga-test:
|
||||
image: gotson/komga:latest
|
||||
image: ghcr.io/simplesoft-duongdt3/komga:latest
|
||||
container_name: komga-test-backend
|
||||
depends_on:
|
||||
postgres-test:
|
||||
|
|
|
|||
51
docker-compose.local.optimized.yml
Normal file
51
docker-compose.local.optimized.yml
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
version: '3.8'
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15-alpine
|
||||
container_name: komga-postgres
|
||||
environment:
|
||||
POSTGRES_DB: komga
|
||||
POSTGRES_USER: komga
|
||||
POSTGRES_PASSWORD: komga123
|
||||
ports:
|
||||
- "5433:5432"
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
- ./docker/postgres/init.sql:/docker-entrypoint-initdb.d/01-init.sql
|
||||
healthcheck:
|
||||
test: [ "CMD-SHELL", "pg_isready -U komga" ]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
komga:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.local.optimized
|
||||
container_name: komga-backend
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
SPRING_PROFILES_ACTIVE: docker
|
||||
KOMGA_DATABASE_TYPE: postgresql
|
||||
KOMGA_DATABASE_URL: jdbc:postgresql://postgres:5432/komga?connectTimeout=30000&socketTimeout=60000
|
||||
KOMGA_DATABASE_USERNAME: komga
|
||||
KOMGA_DATABASE_PASSWORD: komga123
|
||||
KOMGA_CONFIG_DIR: /config
|
||||
KOMGA_DATABASE_POOL_SIZE: 10
|
||||
KOMGA_DATABASE_MAX_POOL_SIZE: 10
|
||||
SPRING_FLYWAY_ENABLED: "true"
|
||||
SPRING_FLYWAY_BASELINE_ON_MIGRATE: "true"
|
||||
SPRING_FLYWAY_BASELINE_VERSION: "20250730173126"
|
||||
ports:
|
||||
- "25600:25600"
|
||||
volumes:
|
||||
- komga_config:/config
|
||||
- ./data:/data:ro
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
komga_config:
|
||||
|
|
@ -20,7 +20,7 @@ services:
|
|||
retries: 5
|
||||
|
||||
komga:
|
||||
image: komga-local:1.24.3.2026060801
|
||||
image: komga-local:1.24.3
|
||||
container_name: komga-backend
|
||||
depends_on:
|
||||
postgres:
|
||||
|
|
|
|||
|
|
@ -53,4 +53,4 @@ ENV KOMGA_CONFIGDIR="/config"
|
|||
ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' LC_ALL='en_US.UTF-8'
|
||||
ENTRYPOINT ["java", "-Dspring.profiles.include=docker", "--enable-native-access=ALL-UNNAMED", "-jar", "application.jar", "--spring.config.additional-location=file:/config/"]
|
||||
EXPOSE 25600
|
||||
LABEL org.opencontainers.image.source="https://github.com/gotson/komga"
|
||||
LABEL org.opencontainers.image.source="https://github.com/simplesoft-duongdt3/komga"
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ class OpenApiConfiguration(
|
|||
Check the API reference:
|
||||
- on the [Komga website](https://komga.org/docs/openapi/komga-api)
|
||||
- on any running Komga instance at `/swagger-ui.html`
|
||||
- on [GitHub](https://raw.githubusercontent.com/gotson/komga/refs/heads/master/komga/docs/openapi.json)
|
||||
- on [GitHub](https://raw.githubusercontent.com/simplesoft-duongdt3/komga/refs/heads/master/komga/docs/openapi.json)
|
||||
|
||||
## Authentication
|
||||
|
||||
|
|
@ -119,7 +119,7 @@ class OpenApiConfiguration(
|
|||
|
||||
API endpoints marked as deprecated will be removed in the next major version.
|
||||
""".trimIndent(),
|
||||
).license(License().name("MIT").url("https://github.com/gotson/komga/blob/master/LICENSE")),
|
||||
).license(License().name("MIT").url("https://github.com/simplesoft-duongdt3/komga/blob/master/LICENSE")),
|
||||
).externalDocs(
|
||||
ExternalDocumentation()
|
||||
.description("Komga documentation")
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ class OpdsController(
|
|||
@Qualifier("pdfImageType")
|
||||
private val pdfImageType: ImageType,
|
||||
) {
|
||||
private val komgaAuthor = OpdsAuthor("Komga", URI("https://github.com/gotson/komga"))
|
||||
private val komgaAuthor = OpdsAuthor("Komga", URI("https://github.com/simplesoft-duongdt3/komga"))
|
||||
|
||||
private val decimalFormat = DecimalFormat("0.#")
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import org.springframework.web.reactive.function.client.WebClient
|
|||
import org.springframework.web.server.ResponseStatusException
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
private const val GITHUB_API = "https://api.github.com/repos/gotson/komga/releases"
|
||||
private const val GITHUB_API = "https://api.github.com/repos/simplesoft-duongdt3/komga/releases"
|
||||
|
||||
@RestController
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
|
|
|
|||
74
start-dev-with-frontend.sh
Executable file
74
start-dev-with-frontend.sh
Executable file
|
|
@ -0,0 +1,74 @@
|
|||
#!/bin/bash
|
||||
# Start Komga backend with pre-built frontend for local development
|
||||
set -e
|
||||
|
||||
FRONTEND_SOURCE="./frontend-dist"
|
||||
BACKEND_RESOURCES="./komga/src/main/resources"
|
||||
|
||||
echo "=== Komga Local Development with Frontend ==="
|
||||
echo "Frontend source: $FRONTEND_SOURCE"
|
||||
|
||||
# Check if frontend dist exists
|
||||
if [ ! -d "$FRONTEND_SOURCE" ]; then
|
||||
echo "ERROR: Frontend distribution not found at $FRONTEND_SOURCE"
|
||||
echo "Build frontend first: ./build-local-docker.sh --export-dist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Ensure backend resources directories exist
|
||||
mkdir -p "$BACKEND_RESOURCES/public"
|
||||
mkdir -p "$BACKEND_RESOURCES/templates"
|
||||
|
||||
echo "Copying static assets to $BACKEND_RESOURCES/public/"
|
||||
rsync -a --exclude="index.html" "$FRONTEND_SOURCE/" "$BACKEND_RESOURCES/public/" 2>/dev/null || cp -r "$FRONTEND_SOURCE/"* "$BACKEND_RESOURCES/public/" 2>/dev/null
|
||||
|
||||
echo "Moving index.html to $BACKEND_RESOURCES/templates/"
|
||||
if [ -f "$BACKEND_RESOURCES/public/index.html" ]; then
|
||||
mv "$BACKEND_RESOURCES/public/index.html" "$BACKEND_RESOURCES/templates/"
|
||||
elif [ -f "$FRONTEND_SOURCE/index.html" ]; then
|
||||
cp "$FRONTEND_SOURCE/index.html" "$BACKEND_RESOURCES/templates/"
|
||||
fi
|
||||
|
||||
# Check if PostgreSQL option is provided
|
||||
USE_POSTGRES=false
|
||||
if [[ "$1" == "--postgres" ]]; then
|
||||
USE_POSTGRES=true
|
||||
echo "Using PostgreSQL database"
|
||||
fi
|
||||
|
||||
if [ "$USE_POSTGRES" = true ]; then
|
||||
echo "Starting PostgreSQL container..."
|
||||
docker-compose -f docker-compose.local.yml up -d postgres
|
||||
|
||||
echo "Waiting for PostgreSQL to be ready..."
|
||||
until docker exec komga-postgres pg_isready -U komga -d komga; do
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo "Setting PostgreSQL environment variables..."
|
||||
export KOMGA_DATABASE_DRIVER_CLASS_NAME=org.postgresql.Driver
|
||||
export KOMGA_DATABASE_URL="jdbc:postgresql://localhost:5433/komga?user=komga&password=komga123"
|
||||
export KOMGA_DATABASE_USERNAME=komga
|
||||
export KOMGA_DATABASE_PASSWORD=komga123
|
||||
export KOMGA_DATABASE_TYPE=postgresql
|
||||
export KOMGA_DATABASE_POOL_SIZE=10
|
||||
export KOMGA_DATABASE_MAX_POOL_SIZE=10
|
||||
export SPRING_FLYWAY_BASELINE_ON_MIGRATE=true
|
||||
export SPRING_FLYWAY_BASELINE_VERSION=20250730173126
|
||||
export KOMGA_CONFIG_DIR="$HOME/.komga-postgres"
|
||||
export SPRING_PROFILES_ACTIVE=postgresql
|
||||
fi
|
||||
|
||||
echo "Starting Komga backend..."
|
||||
echo "Note: Frontend build tasks are skipped"
|
||||
echo "Access at: http://localhost:25600/"
|
||||
echo "Press Ctrl+C to stop"
|
||||
|
||||
# Run backend with excluded frontend tasks
|
||||
./gradlew :komga:bootRun -x npmInstall -x npmBuild -x copyWebDist -x prepareThymeLeaf
|
||||
|
||||
# Cleanup on exit
|
||||
if [ "$USE_POSTGRES" = true ]; then
|
||||
echo "Stopping PostgreSQL container..."
|
||||
docker-compose -f docker-compose.local.yml stop postgres
|
||||
fi
|
||||
Loading…
Reference in a new issue