mirror of
https://github.com/seanmorley15/AdventureLog.git
synced 2025-08-04 04:35:19 +02:00
Merge 9f58b98737
into 2412754350
This commit is contained in:
commit
4ae72bed80
33 changed files with 1302 additions and 657 deletions
63
.env.example
63
.env.example
|
@ -1,47 +1,50 @@
|
|||
# 🌐 Frontend
|
||||
FRONTEND_PORT=8015 # Don't forget to update CSRF_TRUSTED_ORIGINS in backend and FRONTEND_URL in backend and ORIGIN in frontend if you make changes to this.
|
||||
PUBLIC_SERVER_URL=http://server:8000 # PLEASE DON'T CHANGE :) - Should be the service name of the backend with port 8000, even if you change the port in the backend service. Only change if you are using a custom more complex setup.
|
||||
ORIGIN=http://localhost:8015
|
||||
BODY_SIZE_LIMIT=Infinity
|
||||
FRONTEND_PORT=8015
|
||||
|
||||
# 🐘 PostgreSQL Database
|
||||
PGHOST=db
|
||||
POSTGRES_DB=database
|
||||
POSTGRES_USER=adventure
|
||||
PGHOST=db # Supposed to match the host of the postgis database
|
||||
POSTGRES_DB=adventurelog
|
||||
POSTGRES_USER=adventurelog
|
||||
POSTGRES_PASSWORD=changeme123
|
||||
# POSTGRES_PASSWORD_FILE=/run/secrets/postgres-password # Uncomment this block if you'd rather use docker secrets (more-secure)
|
||||
|
||||
# 🔒 Django Backend
|
||||
SECRET_KEY=changeme123
|
||||
DJANGO_ADMIN_USERNAME=admin
|
||||
DJANGO_ADMIN_PASSWORD=admin
|
||||
DJANGO_ADMIN_EMAIL=admin@example.com
|
||||
PUBLIC_URL=http://localhost:8016 # Match the outward port, used for the creation of image urls
|
||||
CSRF_TRUSTED_ORIGINS=http://localhost:8016,http://localhost:8015
|
||||
DEBUG=False
|
||||
FRONTEND_URL=http://localhost:8015 # Used for email generation. This should be the url of the frontend
|
||||
BACKEND_PORT=8016
|
||||
SECRET_KEY=changeme123 # Replace with the actual secret key
|
||||
# SECRET_KEY_FILE=/run/secrets/secret-key # Uncomment this block if you'd rather use docker secrets (more-secure)
|
||||
DJANGO_ADMIN_USERNAME=admin # Replace with the actual admin username
|
||||
DJANGO_ADMIN_PASSWORD=admin # Replace with the actual admin password
|
||||
# DJANGO_ADMIN_PASSWORD_FILE=/run/secrets/django-admin-password # Uncomment this block if you'd rather use docker secrets (more-secure)
|
||||
DJANGO_ADMIN_EMAIL=admin@example.com # Replace with the actual admin email
|
||||
BACKEND_PORT=8016 # Don't forget to update CSRF_TRUSTED_ORIGINS and PUBLIC_URL in backend, and PUBLIC_SERVER_URL in frontend if you make changes to this.
|
||||
PUBLIC_URL=http://localhost:8016 # Replace with your domain where the backend is accessible publicly, used for the creation of image urls
|
||||
CSRF_TRUSTED_ORIGINS=http://localhost:8016,http://localhost:8015 # Replace with your domain, if applicable
|
||||
FRONTEND_URL=http://localhost:8015 # Replace with your domain where the frontend is accessible, used for email generation.
|
||||
# Disabling Registration: https://adventurelog.app/docs/configuration/disable_registration.html
|
||||
# DISABLE_REGISTRATION=False
|
||||
# DISABLE_REGISTRATION_MESSAGE='Registration is disabled for this instance of AdventureLog.'
|
||||
|
||||
# Optional: use Google Maps integration
|
||||
# https://adventurelog.app/docs/configuration/google_maps_integration.html
|
||||
# GOOGLE_MAPS_API_KEY=your_google_maps_api_key
|
||||
|
||||
# Optional: disable registration
|
||||
# https://adventurelog.app/docs/configuration/disable_registration.html
|
||||
DISABLE_REGISTRATION=False
|
||||
# DISABLE_REGISTRATION_MESSAGE=Registration is disabled for this instance of AdventureLog.
|
||||
|
||||
# Optional: Use email
|
||||
# https://adventurelog.app/docs/configuration/email.html
|
||||
# Integrations
|
||||
## Email: https://adventurelog.app/docs/configuration/email.html
|
||||
# EMAIL_BACKEND=email
|
||||
# EMAIL_HOST=smtp.gmail.com
|
||||
# EMAIL_USE_TLS=True
|
||||
# EMAIL_HOST=smtp.mail.com
|
||||
# EMAIL_USE_TLS=False
|
||||
# EMAIL_PORT=587
|
||||
# EMAIL_USE_SSL=False
|
||||
# EMAIL_USE_SSL=True
|
||||
# EMAIL_HOST_USER=user
|
||||
# EMAIL_HOST_PASSWORD=password
|
||||
# DEFAULT_FROM_EMAIL=user@example.com
|
||||
# # EMAIL_HOST_PASSWORD_FILE=/run/secrets/email-host-password # Uncomment this block if you'd rather use docker secrets (more-secure)
|
||||
# DEFAULT_FROM_EMAIL=adventurelog@example.com
|
||||
|
||||
# Optional: Use Umami for analytics
|
||||
# https://adventurelog.app/docs/configuration/analytics.html
|
||||
## Google Maps: https://adventurelog.app/docs/configuration/google_maps_integration.html
|
||||
# GOOGLE_MAPS_API_KEY=your_google_maps_api_key
|
||||
|
||||
## Umami for analytics: https://adventurelog.app/docs/configuration/analytics.html
|
||||
# PUBLIC_UMAMI_SRC=https://cloud.umami.is/script.js # If you are using the hosted version of Umami
|
||||
# PUBLIC_UMAMI_WEBSITE_ID=
|
||||
# PUBLIC_UMAMI_WEBSITE_ID=
|
||||
|
||||
# Extra configuration
|
||||
DEBUG=false # Use to enable debugging
|
4
.github/.docker-compose-database.yml
vendored
4
.github/.docker-compose-database.yml
vendored
|
@ -6,8 +6,8 @@ services:
|
|||
ports:
|
||||
- "127.0.0.1:5432:5432"
|
||||
environment:
|
||||
POSTGRES_DB: database
|
||||
POSTGRES_USER: adventure
|
||||
POSTGRES_DB: adventurelog
|
||||
POSTGRES_USER: adventurelog
|
||||
POSTGRES_PASSWORD: changeme123
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data/
|
||||
|
|
43
.github/workflows/backend-beta.yml
vendored
43
.github/workflows/backend-beta.yml
vendored
|
@ -36,11 +36,42 @@ jobs:
|
|||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: set lower case owner name
|
||||
run: |
|
||||
echo "REPO_OWNER=${OWNER,,}" >>${GITHUB_ENV}
|
||||
- name: Set build metadata
|
||||
id: vars
|
||||
env:
|
||||
OWNER: "${{ github.repository_owner }}"
|
||||
OWNER: '${{ github.repository_owner }}'
|
||||
run: |
|
||||
echo "GIT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
echo "GIT_TAG=$(git describe --tags --abbrev=0 --exact-match 2>/dev/null || echo unknown)" >> $GITHUB_OUTPUT
|
||||
echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
|
||||
echo "REPO_OWNER=${OWNER,,}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build Docker images
|
||||
run: docker buildx build --platform linux/amd64,linux/arm64 --push -t ghcr.io/$REPO_OWNER/$IMAGE_NAME:beta -t ${{ secrets.DOCKERHUB_USERNAME }}/$IMAGE_NAME:beta ./backend
|
||||
- name: Build and push Docker images
|
||||
run: |
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--build-arg GIT_SHA=${{ steps.vars.outputs.GIT_SHA }} \
|
||||
--build-arg GIT_TAG=${{ steps.vars.outputs.GIT_TAG }} \
|
||||
--build-arg BUILD_DATE=${{ steps.vars.outputs.BUILD_DATE }} \
|
||||
-t ghcr.io/$REPO_OWNER/$IMAGE_NAME:beta \
|
||||
-t ${{ secrets.DOCKERHUB_USERNAME }}/$IMAGE_NAME:beta \
|
||||
./backend
|
||||
|
||||
# Notify on success
|
||||
# - name: Notify Discord on success
|
||||
# if: success()
|
||||
# run: |
|
||||
# curl -H "Content-Type: application/json" \
|
||||
# -X POST \
|
||||
# -d "{\"username\": \"CI Bot\", \"content\": \"✅ Build and push succeeded for backend beta\"}" \
|
||||
# ${{ secrets.DISCORD_WEBHOOK_BUILD }}
|
||||
|
||||
# # Notify on failure
|
||||
# - name: Notify Discord on failure
|
||||
# if: failure()
|
||||
# run: |
|
||||
# curl -H "Content-Type: application/json" \
|
||||
# -X POST \
|
||||
# -d "{\"username\": \"CI Bot\", \"content\": \"❌ Build or push failed for backend beta\"}" \
|
||||
# ${{ secrets.DISCORD_WEBHOOK_BUILD }}
|
||||
|
|
43
.github/workflows/backend-latest.yml
vendored
43
.github/workflows/backend-latest.yml
vendored
|
@ -35,12 +35,43 @@ jobs:
|
|||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: set lower case owner name
|
||||
run: |
|
||||
echo "REPO_OWNER=${OWNER,,}" >>${GITHUB_ENV}
|
||||
|
||||
- name: Set build metadata
|
||||
id: vars
|
||||
env:
|
||||
OWNER: '${{ github.repository_owner }}'
|
||||
run: |
|
||||
echo "GIT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
echo "GIT_TAG=$(git describe --tags --abbrev=0 --exact-match 2>/dev/null || echo unknown)" >> $GITHUB_OUTPUT
|
||||
echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
|
||||
echo "REPO_OWNER=${OWNER,,}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build Docker images
|
||||
run: docker buildx build --platform linux/amd64,linux/arm64 --push -t ghcr.io/$REPO_OWNER/$IMAGE_NAME:latest -t ${{ secrets.DOCKERHUB_USERNAME }}/$IMAGE_NAME:latest ./backend
|
||||
- name: Build and push Docker images
|
||||
run: |
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--build-arg GIT_SHA=${{ steps.vars.outputs.GIT_SHA }} \
|
||||
--build-arg GIT_TAG=${{ steps.vars.outputs.GIT_TAG }} \
|
||||
--build-arg BUILD_DATE=${{ steps.vars.outputs.BUILD_DATE }} \
|
||||
-t ghcr.io/${{ steps.vars.outputs.REPO_OWNER }}/$IMAGE_NAME:latest \
|
||||
-t ${{ secrets.DOCKERHUB_USERNAME }}/$IMAGE_NAME:latest \
|
||||
./backend
|
||||
|
||||
# Notify on success
|
||||
# - name: Notify Discord on success
|
||||
# if: success()
|
||||
# run: |
|
||||
# curl -H "Content-Type: application/json" \
|
||||
# -X POST \
|
||||
# -d "{\"username\": \"CI Bot\", \"content\": \"✅ Build and push succeeded for backend latest\"}" \
|
||||
# ${{ secrets.DISCORD_WEBHOOK_BUILD }}
|
||||
|
||||
# # Notify on failure
|
||||
# - name: Notify Discord on failure
|
||||
# if: failure()
|
||||
# run: |
|
||||
# curl -H "Content-Type: application/json" \
|
||||
# -X POST \
|
||||
# -d "{\"username\": \"CI Bot\", \"content\": \"❌ Build or push failed for backend latest\"}" \
|
||||
# ${{ secrets.DISCORD_WEBHOOK_BUILD }}
|
||||
|
|
44
.github/workflows/backend-release.yml
vendored
44
.github/workflows/backend-release.yml
vendored
|
@ -33,11 +33,43 @@ jobs:
|
|||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: set lower case owner name
|
||||
run: |
|
||||
echo "REPO_OWNER=${OWNER,,}" >>${GITHUB_ENV}
|
||||
- name: Set build metadata
|
||||
id: vars
|
||||
env:
|
||||
OWNER: "${{ github.repository_owner }}"
|
||||
OWNER: '${{ github.repository_owner }}'
|
||||
run: |
|
||||
echo "GIT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
echo "GIT_TAG=$(git describe --tags --abbrev=0 --exact-match 2>/dev/null || echo unknown)" >> $GITHUB_OUTPUT
|
||||
echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
|
||||
echo "REPO_OWNER=${OWNER,,}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build Docker images
|
||||
run: docker buildx build --platform linux/amd64,linux/arm64 --push -t ghcr.io/$REPO_OWNER/$IMAGE_NAME:${{ github.event.release.tag_name }} -t ${{ secrets.DOCKERHUB_USERNAME }}/$IMAGE_NAME:${{ github.event.release.tag_name }} ./backend
|
||||
- name: Build and push Docker images
|
||||
run: |
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--build-arg GIT_SHA=${{ steps.vars.outputs.GIT_SHA }} \
|
||||
--build-arg GIT_TAG=${{ steps.vars.outputs.GIT_TAG }} \
|
||||
--build-arg BUILD_DATE=${{ steps.vars.outputs.BUILD_DATE }} \
|
||||
-t ghcr.io/${{ steps.vars.outputs.REPO_OWNER }}/$IMAGE_NAME:${{ github.event.release.tag_name }} \
|
||||
-t ${{ secrets.DOCKERHUB_USERNAME }}/$IMAGE_NAME:${{ github.event.release.tag_name }} \
|
||||
./backend
|
||||
|
||||
|
||||
# # Notify on success
|
||||
# - name: Notify Discord on success
|
||||
# if: success()
|
||||
# run: |
|
||||
# curl -H "Content-Type: application/json" \
|
||||
# -X POST \
|
||||
# -d "{\"username\": \"CI Bot\", \"content\": \"✅ Build and push succeeded for backend release ${{ github.event.release.tag_name }}\"}" \
|
||||
# ${{ secrets.DISCORD_WEBHOOK_BUILD }}
|
||||
|
||||
# # Notify on failure
|
||||
# - name: Notify Discord on failure
|
||||
# if: failure()
|
||||
# run: |
|
||||
# curl -H "Content-Type: application/json" \
|
||||
# -X POST \
|
||||
# -d "{\"username\": \"CI Bot\", \"content\": \"❌ Build or push failed for backend release ${{ github.event.release.tag_name }}\"}" \
|
||||
# ${{ secrets.DISCORD_WEBHOOK_BUILD }}
|
||||
|
|
8
.github/workflows/backend-test.yml
vendored
8
.github/workflows/backend-test.yml
vendored
|
@ -37,10 +37,10 @@ jobs:
|
|||
working-directory: backend/server
|
||||
env:
|
||||
PGHOST: "127.0.0.1"
|
||||
PGDATABASE: "database"
|
||||
PGUSER: "adventure"
|
||||
PGPASSWORD: "changeme123"
|
||||
SECRET_KEY: "changeme123"
|
||||
POSTGRES_DB: "adventurelog"
|
||||
POSTGRES_USER: "adventurelog"
|
||||
POSTGRES_PASSWORD: "changeme123"
|
||||
SECRET_KEY: "secretkey"
|
||||
DJANGO_ADMIN_USERNAME: "admin"
|
||||
DJANGO_ADMIN_PASSWORD: "admin"
|
||||
DJANGO_ADMIN_EMAIL: "admin@example.com"
|
||||
|
|
39
.github/workflows/cdn-beta.yml
vendored
39
.github/workflows/cdn-beta.yml
vendored
|
@ -36,11 +36,40 @@ jobs:
|
|||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: set lower case owner name
|
||||
run: |
|
||||
echo "REPO_OWNER=${OWNER,,}" >>${GITHUB_ENV}
|
||||
- name: Set build metadata
|
||||
id: vars
|
||||
env:
|
||||
OWNER: "${{ github.repository_owner }}"
|
||||
OWNER: '${{ github.repository_owner }}'
|
||||
run: |
|
||||
echo "GIT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
|
||||
echo "REPO_OWNER=${OWNER,,}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build Docker images
|
||||
run: docker buildx build --platform linux/amd64,linux/arm64 --push -t ghcr.io/$REPO_OWNER/$IMAGE_NAME:beta -t ${{ secrets.DOCKERHUB_USERNAME }}/$IMAGE_NAME:beta ./cdn
|
||||
run: |
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--build-arg GIT_SHA=${{ steps.vars.outputs.GIT_SHA }} \
|
||||
--build-arg BUILD_DATE=${{ steps.vars.outputs.BUILD_DATE }} \
|
||||
-t ghcr.io/${{ steps.vars.outputs.REPO_OWNER }}/$IMAGE_NAME:beta \
|
||||
-t ${{ secrets.DOCKERHUB_USERNAME }}/$IMAGE_NAME:beta \
|
||||
./cdn
|
||||
|
||||
# Notify on success
|
||||
# - name: Notify Discord on success
|
||||
# if: success()
|
||||
# run: |
|
||||
# curl -H "Content-Type: application/json" \
|
||||
# -X POST \
|
||||
# -d "{\"username\": \"CI Bot\", \"content\": \"✅ Build and push succeeded for CDN beta\"}" \
|
||||
# ${{ secrets.DISCORD_WEBHOOK_BUILD }}
|
||||
|
||||
# # Notify on failure
|
||||
# - name: Notify Discord on failure
|
||||
# if: failure()
|
||||
# run: |
|
||||
# curl -H "Content-Type: application/json" \
|
||||
# -X POST \
|
||||
# -d "{\"username\": \"CI Bot\", \"content\": \"❌ Build or push failed for CDN beta\"}" \
|
||||
# ${{ secrets.DISCORD_WEBHOOK_BUILD }}
|
||||
|
|
39
.github/workflows/cdn-latest.yml
vendored
39
.github/workflows/cdn-latest.yml
vendored
|
@ -36,11 +36,40 @@ jobs:
|
|||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: set lower case owner name
|
||||
run: |
|
||||
echo "REPO_OWNER=${OWNER,,}" >>${GITHUB_ENV}
|
||||
- name: Set build metadata
|
||||
id: vars
|
||||
env:
|
||||
OWNER: "${{ github.repository_owner }}"
|
||||
OWNER: '${{ github.repository_owner }}'
|
||||
run: |
|
||||
echo "GIT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
|
||||
echo "REPO_OWNER=${OWNER,,}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build Docker images
|
||||
run: docker buildx build --platform linux/amd64,linux/arm64 --push -t ghcr.io/$REPO_OWNER/$IMAGE_NAME:latest -t ${{ secrets.DOCKERHUB_USERNAME }}/$IMAGE_NAME:latest ./cdn
|
||||
run: |
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--build-arg GIT_SHA=${{ steps.vars.outputs.GIT_SHA }} \
|
||||
--build-arg BUILD_DATE=${{ steps.vars.outputs.BUILD_DATE }} \
|
||||
-t ghcr.io/${{ steps.vars.outputs.REPO_OWNER }}/$IMAGE_NAME:latest \
|
||||
-t ${{ secrets.DOCKERHUB_USERNAME }}/$IMAGE_NAME:latest \
|
||||
./cdn
|
||||
|
||||
# Notify on success
|
||||
# - name: Notify Discord on success
|
||||
# if: success()
|
||||
# run: |
|
||||
# curl -H "Content-Type: application/json" \
|
||||
# -X POST \
|
||||
# -d "{\"username\": \"CI Bot\", \"content\": \"✅ Build and push succeeded for CDN latest\"}" \
|
||||
# ${{ secrets.DISCORD_WEBHOOK_BUILD }}
|
||||
|
||||
# # Notify on failure
|
||||
# - name: Notify Discord on failure
|
||||
# if: failure()
|
||||
# run: |
|
||||
# curl -H "Content-Type: application/json" \
|
||||
# -X POST \
|
||||
# -d "{\"username\": \"CI Bot\", \"content\": \"❌ Build or push failed for CDN latest\"}" \
|
||||
# ${{ secrets.DISCORD_WEBHOOK_BUILD }}
|
||||
|
|
40
.github/workflows/cdn-release.yml
vendored
40
.github/workflows/cdn-release.yml
vendored
|
@ -33,11 +33,41 @@ jobs:
|
|||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: set lower case owner name
|
||||
run: |
|
||||
echo "REPO_OWNER=${OWNER,,}" >>${GITHUB_ENV}
|
||||
- name: Set build metadata
|
||||
id: vars
|
||||
env:
|
||||
OWNER: "${{ github.repository_owner }}"
|
||||
OWNER: '${{ github.repository_owner }}'
|
||||
run: |
|
||||
echo "GIT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
|
||||
echo "REPO_OWNER=${OWNER,,}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build Docker images
|
||||
run: docker buildx build --platform linux/amd64,linux/arm64 --push -t ghcr.io/$REPO_OWNER/$IMAGE_NAME:${{ github.event.release.tag_name }} -t ${{ secrets.DOCKERHUB_USERNAME }}/$IMAGE_NAME:${{ github.event.release.tag_name }} ./cdn
|
||||
run: |
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--build-arg GIT_SHA=${{ steps.vars.outputs.GIT_SHA }} \
|
||||
--build-arg BUILD_DATE=${{ steps.vars.outputs.BUILD_DATE }} \
|
||||
-t ghcr.io/${{ steps.vars.outputs.REPO_OWNER }}/$IMAGE_NAME:${{ github.event.release.tag_name }} \
|
||||
-t ${{ secrets.DOCKERHUB_USERNAME }}/$IMAGE_NAME:${{ github.event.release.tag_name }} \
|
||||
./cdn
|
||||
|
||||
# Notify on success
|
||||
# - name: Notify Discord on success
|
||||
# if: success()
|
||||
# run: |
|
||||
# curl -H "Content-Type: application/json" \
|
||||
# -X POST \
|
||||
# -d "{\"username\": \"CI Bot\", \"content\": \"✅ Build and push succeeded for CDN release ${{ github.event.release.tag_name }}\"}" \
|
||||
# ${{ secrets.DISCORD_WEBHOOK_BUILD }}
|
||||
|
||||
# # Notify on failure
|
||||
# - name: Notify Discord on failure
|
||||
# if: failure()
|
||||
# run: |
|
||||
# curl -H "Content-Type: application/json" \
|
||||
# -X POST \
|
||||
# -d "{\"username\": \"CI Bot\", \"content\": \"❌ Build or push failed for CDN release ${{ github.event.release.tag_name }}\"}" \
|
||||
# ${{ secrets.DISCORD_WEBHOOK_BUILD }}
|
||||
|
||||
|
|
43
.github/workflows/frontend-beta.yml
vendored
43
.github/workflows/frontend-beta.yml
vendored
|
@ -36,11 +36,42 @@ jobs:
|
|||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: set lower case owner name
|
||||
run: |
|
||||
echo "REPO_OWNER=${OWNER,,}" >>${GITHUB_ENV}
|
||||
- name: Set build metadata
|
||||
id: vars
|
||||
env:
|
||||
OWNER: "${{ github.repository_owner }}"
|
||||
OWNER: '${{ github.repository_owner }}'
|
||||
run: |
|
||||
echo "GIT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
echo "GIT_TAG=$(git describe --tags --abbrev=0 --exact-match 2>/dev/null || echo unknown)" >> $GITHUB_OUTPUT
|
||||
echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
|
||||
echo "REPO_OWNER=${OWNER,,}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build Docker images
|
||||
run: docker buildx build --platform linux/amd64,linux/arm64 --push -t ghcr.io/$REPO_OWNER/$IMAGE_NAME:beta -t ${{ secrets.DOCKERHUB_USERNAME }}/$IMAGE_NAME:beta ./frontend
|
||||
- name: Build and push Docker images
|
||||
run: |
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--build-arg GIT_SHA=${{ steps.vars.outputs.GIT_SHA }} \
|
||||
--build-arg GIT_TAG=${{ steps.vars.outputs.GIT_TAG }} \
|
||||
--build-arg BUILD_DATE=${{ steps.vars.outputs.BUILD_DATE }} \
|
||||
-t ghcr.io/$REPO_OWNER/$IMAGE_NAME:beta \
|
||||
-t ${{ secrets.DOCKERHUB_USERNAME }}/$IMAGE_NAME:beta \
|
||||
./frontend
|
||||
|
||||
# Notify on success
|
||||
# - name: Notify Discord on success
|
||||
# if: success()
|
||||
# run: |
|
||||
# curl -H "Content-Type: application/json" \
|
||||
# -X POST \
|
||||
# -d "{\"username\": \"CI Bot\", \"content\": \"✅ Build and push succeeded for frontend beta\"}" \
|
||||
# ${{ secrets.DISCORD_WEBHOOK_BUILD }}
|
||||
|
||||
# # Notify on failure
|
||||
# - name: Notify Discord on failure
|
||||
# if: failure()
|
||||
# run: |
|
||||
# curl -H "Content-Type: application/json" \
|
||||
# -X POST \
|
||||
# -d "{\"username\": \"CI Bot\", \"content\": \"❌ Build or push failed for frontend beta\"}" \
|
||||
# ${{ secrets.DISCORD_WEBHOOK_BUILD }}
|
||||
|
|
43
.github/workflows/frontend-latest.yml
vendored
43
.github/workflows/frontend-latest.yml
vendored
|
@ -35,12 +35,43 @@ jobs:
|
|||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: set lower case owner name
|
||||
run: |
|
||||
echo "REPO_OWNER=${OWNER,,}" >>${GITHUB_ENV}
|
||||
|
||||
- name: Set build metadata
|
||||
id: vars
|
||||
env:
|
||||
OWNER: '${{ github.repository_owner }}'
|
||||
run: |
|
||||
echo "GIT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
echo "GIT_TAG=$(git describe --tags --abbrev=0 --exact-match 2>/dev/null || echo unknown)" >> $GITHUB_OUTPUT
|
||||
echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
|
||||
echo "REPO_OWNER=${OWNER,,}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build Docker images
|
||||
run: docker buildx build --platform linux/amd64,linux/arm64 --push -t ghcr.io/$REPO_OWNER/$IMAGE_NAME:latest -t ${{ secrets.DOCKERHUB_USERNAME }}/$IMAGE_NAME:latest ./frontend
|
||||
- name: Build and push Docker images
|
||||
run: |
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--build-arg GIT_SHA=${{ steps.vars.outputs.GIT_SHA }} \
|
||||
--build-arg GIT_TAG=${{ steps.vars.outputs.GIT_TAG }} \
|
||||
--build-arg BUILD_DATE=${{ steps.vars.outputs.BUILD_DATE }} \
|
||||
-t ghcr.io/$REPO_OWNER/$IMAGE_NAME:latest \
|
||||
-t ${{ secrets.DOCKERHUB_USERNAME }}/$IMAGE_NAME:latest \
|
||||
./frontend
|
||||
|
||||
# Notify on success
|
||||
# - name: Notify Discord on success
|
||||
# if: success()
|
||||
# run: |
|
||||
# curl -H "Content-Type: application/json" \
|
||||
# -X POST \
|
||||
# -d "{\"username\": \"CI Bot\", \"content\": \"✅ Build and push succeeded for frontend beta\"}" \
|
||||
# ${{ secrets.DISCORD_WEBHOOK_BUILD }}
|
||||
|
||||
# # Notify on failure
|
||||
# - name: Notify Discord on failure
|
||||
# if: failure()
|
||||
# run: |
|
||||
# curl -H "Content-Type: application/json" \
|
||||
# -X POST \
|
||||
# -d "{\"username\": \"CI Bot\", \"content\": \"❌ Build or push failed for frontend beta\"}" \
|
||||
# ${{ secrets.DISCORD_WEBHOOK_BUILD }}
|
||||
|
|
43
.github/workflows/frontend-release.yml
vendored
43
.github/workflows/frontend-release.yml
vendored
|
@ -33,11 +33,42 @@ jobs:
|
|||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: set lower case owner name
|
||||
run: |
|
||||
echo "REPO_OWNER=${OWNER,,}" >>${GITHUB_ENV}
|
||||
- name: Set build metadata
|
||||
id: vars
|
||||
env:
|
||||
OWNER: "${{ github.repository_owner }}"
|
||||
OWNER: '${{ github.repository_owner }}'
|
||||
run: |
|
||||
echo "GIT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
echo "GIT_TAG=$(git describe --tags --abbrev=0 --exact-match 2>/dev/null || echo unknown)" >> $GITHUB_OUTPUT
|
||||
echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
|
||||
echo "REPO_OWNER=${OWNER,,}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build Docker images
|
||||
run: docker buildx build --platform linux/amd64,linux/arm64 --push -t ghcr.io/$REPO_OWNER/$IMAGE_NAME:${{ github.event.release.tag_name }} -t ${{ secrets.DOCKERHUB_USERNAME }}/$IMAGE_NAME:${{ github.event.release.tag_name }} ./frontend
|
||||
- name: Build and push Docker images
|
||||
run: |
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--build-arg GIT_SHA=${{ steps.vars.outputs.GIT_SHA }} \
|
||||
--build-arg GIT_TAG=${{ steps.vars.outputs.GIT_TAG }} \
|
||||
--build-arg BUILD_DATE=${{ steps.vars.outputs.BUILD_DATE }} \
|
||||
-t ghcr.io/$REPO_OWNER/$IMAGE_NAME:${{ github.event.release.tag_name }} \
|
||||
-t ${{ secrets.DOCKERHUB_USERNAME }}/$IMAGE_NAME:${{ github.event.release.tag_name }} \
|
||||
./frontend
|
||||
|
||||
# Notify on success
|
||||
# - name: Notify Discord on success
|
||||
# if: success()
|
||||
# run: |
|
||||
# curl -H "Content-Type: application/json" \
|
||||
# -X POST \
|
||||
# -d "{\"username\": \"CI Bot\", \"content\": \"✅ Build and push succeeded for frontend release ${{ github.event.release.tag_name }}\"}" \
|
||||
# ${{ secrets.DISCORD_WEBHOOK_BUILD }}
|
||||
|
||||
# # Notify on failure
|
||||
# - name: Notify Discord on failure
|
||||
# if: failure()
|
||||
# run: |
|
||||
# curl -H "Content-Type: application/json" \
|
||||
# -X POST \
|
||||
# -d "{\"username\": \"CI Bot\", \"content\": \"❌ Build or push failed for frontend release ${{ github.event.release.tag_name }}\"}" \
|
||||
# ${{ secrets.DISCORD_WEBHOOK_BUILD }}
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,3 +3,4 @@
|
|||
.vscode/settings.json
|
||||
.pnpm-store/
|
||||
.env
|
||||
.secrets
|
||||
|
|
7
backend/.dockerignore
Normal file
7
backend/.dockerignore
Normal file
|
@ -0,0 +1,7 @@
|
|||
.env.example
|
||||
.devcontainer
|
||||
.github
|
||||
server/build_files.sh
|
||||
.gitignore
|
||||
Dockerfile
|
||||
.dockerignore
|
|
@ -1,7 +1,49 @@
|
|||
# Use the official Python slim image as the base image
|
||||
FROM python:3.13-slim
|
||||
# -------------------
|
||||
# Stage 1: Build Stage
|
||||
# -------------------
|
||||
FROM docker.io/python:3.13-alpine AS builder
|
||||
|
||||
# Build-time metadata
|
||||
ARG GIT_SHA=unknown
|
||||
ARG GIT_TAG=latest
|
||||
ARG BUILD_DATE=unknown
|
||||
|
||||
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
PYTHONPATH=/install
|
||||
|
||||
# Install build dependencies for Alpine
|
||||
RUN apk add --no-cache \
|
||||
build-base \
|
||||
libpq \
|
||||
libpq-dev \
|
||||
gdal \
|
||||
gdal-dev \
|
||||
geos \
|
||||
geos-dev \
|
||||
postgresql-dev \
|
||||
musl-dev \
|
||||
python3-dev \
|
||||
linux-headers \
|
||||
git
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /build
|
||||
|
||||
# Copy entire project into /tmp
|
||||
COPY . /tmp/
|
||||
|
||||
# Install Python dependencies into /install
|
||||
RUN pip install --upgrade pip && \
|
||||
pip install --target=/install -r /tmp/server/requirements.txt && \
|
||||
python3 /tmp/server/manage.py collectstatic --noinput --verbosity 2
|
||||
|
||||
|
||||
# -------------------------
|
||||
# Stage 2: Final Production
|
||||
# -------------------------
|
||||
FROM docker.io/python:3.13-alpine AS final
|
||||
|
||||
# Metadata labels for the AdventureLog image
|
||||
LABEL maintainer="Sean Morley" \
|
||||
version="v0.10.0" \
|
||||
description="AdventureLog — the ultimate self-hosted travel companion." \
|
||||
|
@ -12,49 +54,51 @@ LABEL maintainer="Sean Morley" \
|
|||
org.opencontainers.image.url="https://raw.githubusercontent.com/seanmorley15/AdventureLog/refs/heads/main/brand/banner.png" \
|
||||
org.opencontainers.image.source="https://github.com/seanmorley15/AdventureLog" \
|
||||
org.opencontainers.image.vendor="Sean Morley" \
|
||||
org.opencontainers.image.created="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" \
|
||||
org.opencontainers.image.version="${GIT_TAG}" \
|
||||
org.opencontainers.image.revision="${GIT_SHA}" \
|
||||
org.opencontainers.image.created="${BUILD_DATE}" \
|
||||
org.opencontainers.image.licenses="GPL-3.0"
|
||||
|
||||
# Set environment variables
|
||||
ENV PYTHONDONTWRITEBYTECODE=1
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
PYTHONPATH="/install:/code" \
|
||||
PATH="/install/bin:${PATH}" \
|
||||
GDAL_LIBRARY_PATH="/usr/lib/libgdal.so.36.3.10.3" \
|
||||
GEOS_LIBRARY_PATH="/usr/lib/libgeos_c.so"
|
||||
|
||||
# Set the working directory
|
||||
|
||||
# Copy all from builder, organize in one RUN step
|
||||
COPY --from=builder /install /install
|
||||
COPY --from=builder /tmp /tmp
|
||||
|
||||
RUN apk add --no-cache \
|
||||
nginx\
|
||||
supervisor\
|
||||
bash\
|
||||
curl\
|
||||
libpq\
|
||||
gdal\
|
||||
gdal-dev\
|
||||
geos\
|
||||
geos-dev\
|
||||
postgresql-client && \
|
||||
mv /tmp/server /code && \
|
||||
mkdir -p /code/static /code/media && \
|
||||
mv /tmp/nginx.conf /etc/nginx/nginx.conf && \
|
||||
mkdir -p /etc/supervisor/conf.d/ && \
|
||||
mv /tmp/supervisord.conf /etc/supervisor/conf.d/supervisord.conf && \
|
||||
mv /tmp/entrypoint.sh /code/entrypoint.sh && \
|
||||
chmod +x /code/entrypoint.sh && \
|
||||
rm -rf /tmp && \
|
||||
addgroup -g 349 adventurelog && \
|
||||
adduser -D -u 349 -G adventurelog adventurelog && \
|
||||
chown -R 349:349 /code
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
# Install system dependencies (Nginx included)
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y git postgresql-client gdal-bin libgdal-dev nginx supervisor \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Python dependencies
|
||||
COPY ./server/requirements.txt /code/
|
||||
RUN pip install --upgrade pip \
|
||||
&& pip install -r requirements.txt
|
||||
|
||||
# Create necessary directories
|
||||
RUN mkdir -p /code/static /code/media
|
||||
# RUN mkdir -p /code/staticfiles /code/media
|
||||
|
||||
# Copy the Django project code into the Docker image
|
||||
COPY ./server /code/
|
||||
|
||||
# Copy Nginx configuration
|
||||
COPY ./nginx.conf /etc/nginx/nginx.conf
|
||||
|
||||
# Copy Supervisor configuration
|
||||
COPY ./supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
||||
|
||||
# Collect static files
|
||||
RUN python3 manage.py collectstatic --noinput --verbosity 2
|
||||
|
||||
# Set the entrypoint script
|
||||
COPY ./entrypoint.sh /code/entrypoint.sh
|
||||
RUN chmod +x /code/entrypoint.sh
|
||||
|
||||
# Expose ports for NGINX and Gunicorn
|
||||
EXPOSE 80 8000
|
||||
|
||||
# Command to start Supervisor (which starts Nginx and Gunicorn)
|
||||
CMD ["supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
|
||||
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=20s --retries=5 \
|
||||
CMD curl --fail --silent --show-error http://localhost:80/ || exit 1
|
|
@ -1,4 +1,17 @@
|
|||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Load environment variables from *_FILE if set
|
||||
for file_var in $(env | grep -E '^[A-Z0-9_]+_FILE=' | sed -E 's/=.*//'); do
|
||||
var_name="${file_var%_FILE}"
|
||||
file_path="${!file_var}"
|
||||
|
||||
if [ -r "$file_path" ]; then
|
||||
export "$var_name"="$(< "$file_path")"
|
||||
unset "$file_var"
|
||||
else
|
||||
>&2 echo "Warning: Cannot read file for $file_var: $file_path"
|
||||
fi
|
||||
done
|
||||
|
||||
# Function to check PostgreSQL availability
|
||||
# Helper to get the first non-empty environment variable
|
||||
|
@ -13,22 +26,27 @@ get_env() {
|
|||
}
|
||||
|
||||
check_postgres() {
|
||||
if ! command -v psql > /dev/null 2>&1; then
|
||||
>&2 echo "psql command not found — PostgreSQL client is not installed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local db_host
|
||||
local db_user
|
||||
local db_name
|
||||
local db_pass
|
||||
|
||||
db_host=$(get_env PGHOST)
|
||||
db_user=$(get_env PGUSER POSTGRES_USER)
|
||||
db_name=$(get_env PGDATABASE POSTGRES_DB)
|
||||
db_pass=$(get_env PGPASSWORD POSTGRES_PASSWORD)
|
||||
db_host="$(get_env PGHOST)"
|
||||
db_user="$(get_env PGUSER POSTGRES_USER)"
|
||||
db_name="$(get_env PGDATABASE POSTGRES_DB)"
|
||||
db_pass="$(get_env PGPASSWORD POSTGRES_PASSWORD)"
|
||||
|
||||
PGPASSWORD="$db_pass" psql -h "$db_host" -U "$db_user" -d "$db_name" -c '\q' >/dev/null 2>&1
|
||||
}
|
||||
|
||||
|
||||
# Wait for PostgreSQL to become available
|
||||
until check_postgres; do
|
||||
until check_postgres; do
|
||||
>&2 echo "PostgreSQL is unavailable - sleeping"
|
||||
sleep 1
|
||||
done
|
||||
|
@ -73,8 +91,6 @@ else:
|
|||
EOF
|
||||
fi
|
||||
|
||||
|
||||
# Sync the countries and world travel regions
|
||||
# Sync the countries and world travel regions
|
||||
python manage.py download-countries
|
||||
if [ $? -eq 137 ]; then
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
PGHOST=''
|
||||
PGDATABASE=''
|
||||
PGUSER=''
|
||||
PGPASSWORD=''
|
||||
|
||||
SECRET_KEY='pleasechangethisbecauseifyoudontitwillbeverybadandyouwillgethackedinlessthanaminuteguaranteed'
|
||||
|
||||
PUBLIC_URL='http://127.0.0.1:8000'
|
||||
|
||||
DEBUG=True
|
||||
|
||||
FRONTEND_URL='http://localhost:3000'
|
||||
|
||||
EMAIL_BACKEND='console'
|
||||
|
||||
# EMAIL_BACKEND='email'
|
||||
# EMAIL_HOST='smtp.gmail.com'
|
||||
# EMAIL_USE_TLS=False
|
||||
# EMAIL_PORT=587
|
||||
# EMAIL_USE_SSL=True
|
||||
# EMAIL_HOST_USER='user'
|
||||
# EMAIL_HOST_PASSWORD='password'
|
||||
# DEFAULT_FROM_EMAIL='user@example.com'
|
||||
|
||||
# GOOGLE_MAPS_API_KEY='key'
|
||||
|
||||
|
||||
# ------------------- #
|
||||
# For Developers to start a Demo Database
|
||||
# docker run --name adventurelog-development -e POSTGRES_USER=admin -e POSTGRES_PASSWORD=admin -e POSTGRES_DB=adventurelog -p 5432:5432 -d postgis/postgis:15-3.3
|
||||
|
||||
# PGHOST='localhost'
|
||||
# PGDATABASE='adventurelog'
|
||||
# PGUSER='admin'
|
||||
# PGPASSWORD='admin'
|
||||
# ------------------- #
|
|
@ -20,12 +20,19 @@ load_dotenv()
|
|||
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
|
||||
|
||||
# GDAL library version path
|
||||
GDAL_LIBRARY_PATH = getenv('GDAL_LIBRARY_PATH', None)
|
||||
|
||||
# GEOS library version path
|
||||
GEOS_LIBRARY_PATH = getenv('GEOS_LIBRARY_PATH', None)
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = getenv('SECRET_KEY')
|
||||
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = getenv('DEBUG', 'true').lower() == 'true'
|
||||
|
||||
|
|
|
@ -3,12 +3,14 @@ nodaemon=true
|
|||
|
||||
[program:nginx]
|
||||
command=/usr/sbin/nginx -g "daemon off;"
|
||||
user=nginx
|
||||
autorestart=true
|
||||
stdout_logfile=/dev/stdout
|
||||
stderr_logfile=/dev/stderr
|
||||
|
||||
[program:gunicorn]
|
||||
command=/code/entrypoint.sh
|
||||
user=adventurelog
|
||||
autorestart=true
|
||||
stdout_logfile=/dev/stdout
|
||||
stderr_logfile=/dev/stderr
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# This script will create a backup of the adventurelog_media volume and store it in the current directory as adventurelog-backup.tar.gz
|
||||
|
||||
docker run --rm \
|
||||
-v adventurelog_adventurelog_media:/backup-volume \
|
||||
-v adventurelog_media:/backup-volume \
|
||||
-v "$(pwd)":/backup \
|
||||
busybox \
|
||||
tar -zcvf /backup/adventurelog-backup.tar.gz /backup-volume
|
|
@ -1,36 +1,46 @@
|
|||
# Use an official Python image as a base
|
||||
FROM python:3.11-slim
|
||||
# ---------------------
|
||||
# Stage 1: Data builder
|
||||
# ---------------------
|
||||
FROM docker.io/python:3.11-slim AS builder
|
||||
|
||||
# Set the working directory
|
||||
ARG GIT_SHA=unknown
|
||||
ARG BUILD_DATE=unknown
|
||||
|
||||
# Set workdir and environment
|
||||
WORKDIR /app
|
||||
ENV PYTHONDONTWRITEBYTECODE=1
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
|
||||
# Install required Python packages
|
||||
RUN pip install --no-cache-dir requests osm2geojson
|
||||
|
||||
# Copy the script into the container
|
||||
# Copy and run data generation script
|
||||
COPY main.py /app/main.py
|
||||
|
||||
# Run the script to generate the data folder and GeoJSON files (this runs inside the container)
|
||||
RUN python -u /app/main.py
|
||||
|
||||
# Install Nginx
|
||||
RUN apt update && apt install -y nginx && rm -rf /var/lib/apt/lists/*
|
||||
# ------------------------
|
||||
# Stage 2: Final Nginx-only
|
||||
# ------------------------
|
||||
FROM docker.io/nginx:alpine
|
||||
|
||||
# Copy the entire generated data folder to the Nginx serving directory
|
||||
RUN mkdir -p /var/www/html/data && cp -r /app/data/* /var/www/html/data/
|
||||
ARG GIT_SHA=unknown
|
||||
ARG BUILD_DATE=unknown
|
||||
|
||||
# Copy Nginx configuration
|
||||
# Optional metadata
|
||||
LABEL org.opencontainers.image.revision="${GIT_SHA}" \
|
||||
org.opencontainers.image.created="${BUILD_DATE}"
|
||||
|
||||
# Create destination directory
|
||||
RUN mkdir -p /var/www/html/data
|
||||
|
||||
# Copy generated data and static files
|
||||
COPY --from=builder /app/data /var/www/html/data
|
||||
COPY index.html /usr/share/nginx/html/index.html
|
||||
COPY nginx.conf /etc/nginx/nginx.conf
|
||||
|
||||
# Copy the index.html file to the Nginx serving directory
|
||||
COPY index.html /usr/share/nginx/html/index.html
|
||||
|
||||
# Expose port 80 for Nginx
|
||||
EXPOSE 80
|
||||
|
||||
# Copy the entrypoint script into the container
|
||||
# Add entrypoint
|
||||
COPY entrypoint.sh /app/entrypoint.sh
|
||||
RUN chmod +x /app/entrypoint.sh
|
||||
|
||||
# Set the entrypoint script as the default command
|
||||
EXPOSE 80
|
||||
ENTRYPOINT ["/app/entrypoint.sh"]
|
||||
|
|
|
@ -5,4 +5,4 @@ docker compose pull
|
|||
echo "Stating containers"
|
||||
docker compose up -d
|
||||
echo "All set!"
|
||||
docker logs adventurelog-backend --follow
|
||||
docker compose logs --follow
|
|
@ -1,78 +0,0 @@
|
|||
version: "3.9"
|
||||
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:v2.11
|
||||
command:
|
||||
- "--api.insecure=true" # Enable Traefik dashboard (remove in production)
|
||||
- "--providers.docker=true"
|
||||
- "--entrypoints.web.address=:80"
|
||||
- "--entrypoints.websecure.address=:443"
|
||||
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
|
||||
- "--certificatesresolvers.letsencrypt.acme.email=your-email@example.com" # Replace with your email
|
||||
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||
- "traefik-letsencrypt:/letsencrypt"
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
|
||||
db:
|
||||
image: postgis/postgis:15-3.3
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: database
|
||||
POSTGRES_USER: adventure
|
||||
POSTGRES_PASSWORD: your_postgres_password # Replace with the actual password
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data/
|
||||
|
||||
web:
|
||||
image: ghcr.io/seanmorley15/adventurelog-frontend:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
PUBLIC_SERVER_URL: "http://server:8000"
|
||||
BODY_SIZE_LIMIT: "100000"
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.adventurelogweb.entrypoints=websecure"
|
||||
- "traefik.http.routers.adventurelogweb.rule=Host(`yourdomain.com`) && !(PathPrefix(`/media`) || PathPrefix(`/admin`) || PathPrefix(`/static`) || PathPrefix(`/accounts`))" # Replace with your domain
|
||||
- "traefik.http.routers.adventurelogweb.tls=true"
|
||||
- "traefik.http.routers.adventurelogweb.tls.certresolver=letsencrypt"
|
||||
depends_on:
|
||||
- server
|
||||
|
||||
server:
|
||||
image: ghcr.io/seanmorley15/adventurelog-backend:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
PGHOST: "db"
|
||||
PGDATABASE: "database"
|
||||
PGUSER: "adventure"
|
||||
PGPASSWORD: your_postgres_password # Replace with the actual password
|
||||
SECRET_KEY: your_secret_key # Replace with the actual secret key
|
||||
DJANGO_ADMIN_USERNAME: "admin"
|
||||
DJANGO_ADMIN_PASSWORD: your_admin_password # Replace with the actual admin password
|
||||
DJANGO_ADMIN_EMAIL: "adventurelog-admin@yourdomain.com" # Replace with your email
|
||||
PUBLIC_URL: "https://yourdomain.com" # Replace with your domain
|
||||
CSRF_TRUSTED_ORIGINS: "https://yourdomain.com" # Replace with your domain
|
||||
DEBUG: "false"
|
||||
FRONTEND_URL: "https://yourdomain.com" # Replace with your domain
|
||||
volumes:
|
||||
- adventurelog-media:/code/media
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.adventurelogserver.entrypoints=websecure"
|
||||
- "traefik.http.routers.adventurelogserver.rule=Host(`yourdomain.com`) && (PathPrefix(`/media`) || PathPrefix(`/admin`) || PathPrefix(`/static`) || PathPrefix(`/accounts`))" # Replace with your domain
|
||||
- "traefik.http.routers.adventurelogserver.tls=true"
|
||||
- "traefik.http.routers.adventurelogserver.tls.certresolver=letsencrypt"
|
||||
depends_on:
|
||||
- db
|
||||
|
||||
volumes:
|
||||
postgres-data:
|
||||
adventurelog-media:
|
||||
traefik-letsencrypt:
|
|
@ -1,36 +1,149 @@
|
|||
name: adventurelog
|
||||
|
||||
# Common DB variables used by both backend and db
|
||||
x-env-common: &env-common
|
||||
POSTGRES_DB: adventurelog
|
||||
POSTGRES_USER: adventurelog
|
||||
POSTGRES_PASSWORD: changeme123
|
||||
# POSTGRES_PASSWORD_FILE: /run/secrets/postgres-password # Uncomment this block if you'd rather use docker secrets (more-secure)
|
||||
|
||||
# Environment variables for the backend service
|
||||
x-env-backend: &env-backend
|
||||
# Database Configuration
|
||||
PGHOST: db # Supposed to match the host of the postgis database
|
||||
<<: *env-common
|
||||
# AdventureLog base configuration
|
||||
SECRET_KEY: changeme123 # Replace with the actual secret key
|
||||
# SECRET_KEY_FILE: /run/secrets/secret-key # Uncomment this block if you'd rather use docker secrets (more-secure)
|
||||
DJANGO_ADMIN_USERNAME: admin # Replace with the actual admin username
|
||||
DJANGO_ADMIN_PASSWORD: admin # Replace with the actual admin password
|
||||
# DJANGO_ADMIN_PASSWORD_FILE: /run/secrets/django-admin-password # Uncomment this block if you'd rather use docker secrets (more-secure)
|
||||
DJANGO_ADMIN_EMAIL: admin@example.com # Replace with the actual admin email
|
||||
BACKEND_PORT: 8016 # Don't forget to update CSRF_TRUSTED_ORIGINS and PUBLIC_URL in backend, and PUBLIC_SERVER_URL in frontend if you make changes to this.
|
||||
PUBLIC_URL: http://localhost:8016 # Replace with your domain where the backend is accessible publicly, used for the creation of image URLs
|
||||
CSRF_TRUSTED_ORIGINS: http://localhost:8016,http://localhost:8015 # Replace with your domain, if applicable
|
||||
FRONTEND_URL: http://localhost:8015 # Replace with your domain where the frontend is accessible, used for email generation.
|
||||
|
||||
# Optional integrations (commented out by default)
|
||||
# DISABLE_REGISTRATION: False
|
||||
# DISABLE_REGISTRATION_MESSAGE: Registration is disabled for this instance of AdventureLog.
|
||||
# EMAIL_BACKEND: email
|
||||
# EMAIL_HOST: smtp.mail.com
|
||||
# EMAIL_USE_TLS: False
|
||||
# EMAIL_PORT: 587
|
||||
# EMAIL_USE_SSL: True
|
||||
# EMAIL_HOST_USER: user
|
||||
# EMAIL_HOST_PASSWORD: password
|
||||
# # EMAIL_HOST_PASSWORD_FILE: /run/secrets/email-host-password # Uncomment this block if you'd rather use docker secrets (more-secure)
|
||||
# DEFAULT_FROM_EMAIL: adventurelog@example.com
|
||||
# GOOGLE_MAPS_API_KEY: your_google_maps_api_key
|
||||
# PUBLIC_UMAMI_SRC: https://cloud.umami.is/script.js # If you are using the hosted version of Umami
|
||||
# PUBLIC_UMAMI_WEBSITE_ID:
|
||||
|
||||
# Extra configuration
|
||||
DEBUG: false # Use to enable debugging
|
||||
|
||||
# Environment variables for the frontend service
|
||||
x-env-frontend: &env-frontend
|
||||
FRONTEND_PORT: 8015 # Don't forget to update CSRF_TRUSTED_ORIGINS in backend and FRONTEND_URL in backend and ORIGIN in frontend if you make changes to this.
|
||||
PUBLIC_SERVER_URL: http://backend:8000 # PLEASE DON'T CHANGE :) - Should be the service name of the backend with port 8000, even if you change the port in the backend service. Only change if you are using a custom more complex setup.
|
||||
ORIGIN: http://localhost:8015
|
||||
BODY_SIZE_LIMIT: Infinity
|
||||
|
||||
services:
|
||||
web:
|
||||
#build: ./frontend/
|
||||
image: ghcr.io/seanmorley15/adventurelog-frontend:latest
|
||||
container_name: adventurelog-frontend
|
||||
restart: unless-stopped
|
||||
env_file: .env
|
||||
ports:
|
||||
- "${FRONTEND_PORT:-8015}:3000"
|
||||
depends_on:
|
||||
- server
|
||||
|
||||
db:
|
||||
image: postgis/postgis:16-3.5
|
||||
container_name: adventurelog-db
|
||||
image: docker.io/postgis/postgis:16-3.5
|
||||
restart: unless-stopped
|
||||
env_file: .env
|
||||
#env_file: .env # Disabled for security reasons, as all containers have access to these env vars.
|
||||
environment: # Anything defined here (including the env-backend) overrides what's in the env_file.
|
||||
<<: *env-common
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data/
|
||||
- data:/var/lib/postgresql/data/
|
||||
# secrets: # Uncomment this block if you'd rather use docker secrets (more-secure)
|
||||
# - postgres-password
|
||||
networks:
|
||||
- default
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER}"]
|
||||
interval: 20s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
start_period: 10s
|
||||
|
||||
server:
|
||||
#build: ./backend/
|
||||
image: ghcr.io/seanmorley15/adventurelog-backend:latest
|
||||
container_name: adventurelog-backend
|
||||
frontend:
|
||||
#build: ./frontend/
|
||||
image: localhost/adventurelog-frontend:testing
|
||||
restart: unless-stopped
|
||||
env_file: .env
|
||||
# env_file: .env # Disabled for security reasons, as all containers have access to these env vars.
|
||||
environment: # Anything defined here (including the env-backend) overrides what's in the env_file.
|
||||
<<: *env-frontend
|
||||
ports:
|
||||
- "${BACKEND_PORT:-8016}:80"
|
||||
- "8015:3000"
|
||||
networks:
|
||||
- default
|
||||
depends_on:
|
||||
- db
|
||||
backend:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "/nodejs/bin/node", "-e", "require('net').connect(3000).on('connect', () => process.exit(0)).on('error', () => process.exit(1))"]
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
# labels:
|
||||
# Traefik example: Refer to https://doc.traefik.io/traefik/user-guides/docker-compose/basic-example/ for help on deploying Traefik.
|
||||
# - "traefik.enable=true"
|
||||
# - "traefik.http.routers.adventurelogweb.entrypoints=websecure"
|
||||
# - "traefik.http.routers.adventurelogweb.rule=Host(`yourdomain.com`) && !(PathPrefix(`/media`) || PathPrefix(`/admin`) || PathPrefix(`/static`) || PathPrefix(`/accounts`))" # Replace with your domain
|
||||
# - "traefik.http.routers.adventurelogweb.tls=true"
|
||||
# - "traefik.http.routers.adventurelogweb.tls.certresolver=letsencrypt"
|
||||
|
||||
|
||||
backend:
|
||||
#build: ./backend/
|
||||
image: localhost/adventurelog-backend:testing
|
||||
restart: unless-stopped
|
||||
# env_file: .env # Disabled for security reasons, as all containers have access to these env vars.
|
||||
environment: # Anything defined here (including the env-backend) overrides what's in the env_file.
|
||||
<<: *env-backend
|
||||
networks:
|
||||
- default
|
||||
ports:
|
||||
- "8016:80"
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- adventurelog_media:/code/media/
|
||||
- media:/code/media/
|
||||
# secrets: # Uncomment this block if you'd rather use docker secrets (more-secure)
|
||||
# - postgres-password
|
||||
# - secret-key
|
||||
# # - email-host-password
|
||||
# - django-admin-password
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "--fail", "--silent", "--show-error", "http://localhost:80/"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
start_period: 20s
|
||||
retries: 5
|
||||
# labels:
|
||||
# Traefik example: Refer to https://doc.traefik.io/traefik/user-guides/docker-compose/basic-example/ for help on deploying Traefik.
|
||||
# - "traefik.enable=true"
|
||||
# - "traefik.http.routers.adventurelogserver.entrypoints=websecure"
|
||||
# - "traefik.http.routers.adventurelogserver.rule=Host(`yourdomain.com`) && (PathPrefix(`/media`) || PathPrefix(`/admin`) || PathPrefix(`/static`) || PathPrefix(`/accounts`))" # Replace with your domain
|
||||
# - "traefik.http.routers.adventurelogserver.tls=true"
|
||||
# - "traefik.http.routers.adventurelogserver.tls.certresolver=letsencrypt"
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
adventurelog_media:
|
||||
data:
|
||||
media:
|
||||
|
||||
# secrets: # Uncomment this block if you'd rather use docker secrets (more-secure)
|
||||
# postgres-password:
|
||||
# file: .secrets/postgres-password.secret
|
||||
# secret-key:
|
||||
# file: .secrets/secret-key.secret
|
||||
# # email-host-password: # Uncomment this if you want to use secrets with the email host password
|
||||
# # file: .secrets/email-host-password.secret
|
||||
# django-admin-password:
|
||||
# file: .secrets/django-admin-password.secret
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Docker is the preferred way to run AdventureLog on your local machine. It is a lightweight containerization technology that allows you to run applications in isolated environments called containers.
|
||||
|
||||
> **Note**: This guide mainly focuses on installation with a Linux-based host machine, but the steps are similar for other operating systems.
|
||||
> **Note**: This guide mainly focuses on installation with a Linux-based host machine, but the steps are similar for other operating systems or when using Podman.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
|
@ -29,30 +29,22 @@ If running on an ARM based machine, you will need to use a different PostGIS Ima
|
|||
|
||||
## Configuration
|
||||
|
||||
The `.env` file contains all the configuration settings for your AdventureLog instance. Here’s a breakdown of each section:
|
||||
|
||||
### 🌐 Frontend (web)
|
||||
|
||||
| Name | Required | Description | Default Value |
|
||||
| ------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------- | ----------------------- |
|
||||
| `PUBLIC_SERVER_URL` | Yes | Used by the frontend SSR server to connect to the backend. Almost every user user will **never have to change this from default**! | `http://server:8000` |
|
||||
| `ORIGIN` | Sometimes | Needed only if not using HTTPS. Set it to the domain or IP you'll use to access the frontend. | `http://localhost:8015` |
|
||||
| `BODY_SIZE_LIMIT` | Yes | Maximum upload size in bytes. | `Infinity` |
|
||||
| `FRONTEND_PORT` | Yes | Port that the frontend will run on inside Docker. | `8015` |
|
||||
The `docker-compose.yml` file contains all the configuration settings for your AdventureLog instance. Here’s a breakdown of each environment variable (at the top of the file):
|
||||
|
||||
### 🐘 PostgreSQL Database
|
||||
|
||||
| Name | Required | Description | Default Value |
|
||||
| ------------------- | -------- | --------------------- | ------------- |
|
||||
| `PGHOST` | Yes | Internal DB hostname. | `db` |
|
||||
| `POSTGRES_DB` | Yes | DB name. | `database` |
|
||||
| `POSTGRES_USER` | Yes | DB user. | `adventure` |
|
||||
| `POSTGRES_DB` | Yes | DB name. | `adventurelog` |
|
||||
| `POSTGRES_USER` | Yes | DB user. | `adventurelog` |
|
||||
| `POSTGRES_PASSWORD` | Yes | DB password. | `changeme123` |
|
||||
|
||||
### 🔒 Backend (server)
|
||||
|
||||
| Name | Required | Description | Default Value |
|
||||
| ----------------------- | -------- | ---------------------------------------------------------------------------------- | --------------------------------------------- |
|
||||
| `PHHOST` | Yes | The hostname of the postgres container. | `pg` |
|
||||
| `SECRET_KEY` | Yes | Django secret key. Change this in production! | `changeme123` |
|
||||
| `DJANGO_ADMIN_USERNAME` | Yes | Default Django admin username. | `admin` |
|
||||
| `DJANGO_ADMIN_PASSWORD` | Yes | Default Django admin password. | `admin` |
|
||||
|
@ -63,6 +55,15 @@ The `.env` file contains all the configuration settings for your AdventureLog in
|
|||
| `BACKEND_PORT` | Yes | Port that the backend will run on inside Docker. | `8016` |
|
||||
| `DEBUG` | No | Should be `False` in production. | `False` |
|
||||
|
||||
### 🌐 Frontend (web)
|
||||
|
||||
| Name | Required | Description | Default Value |
|
||||
| ------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------- | ----------------------- |
|
||||
| `PUBLIC_SERVER_URL` | Yes | Used by the frontend SSR server to connect to the backend. Almost every user user will **never have to change this from default**! | `http://backend:8000` |
|
||||
| `ORIGIN` | Sometimes | Needed only if not using HTTPS. Set it to the domain or IP you'll use to access the frontend. | `http://localhost:8015` |
|
||||
| `BODY_SIZE_LIMIT` | Yes | Maximum upload size in bytes. | `Infinity` |
|
||||
| `FRONTEND_PORT` | Yes | Port that the frontend will run on inside Docker. | `8015` |
|
||||
|
||||
## Optional Configuration
|
||||
|
||||
- [Disable Registration](../configuration/disable_registration.md)
|
||||
|
@ -71,9 +72,50 @@ The `.env` file contains all the configuration settings for your AdventureLog in
|
|||
- [Immich Integration](../configuration/immich_integration.md)
|
||||
- [Umami Analytics](../configuration/analytics.md)
|
||||
|
||||
## Additional Configuration Methods
|
||||
|
||||
### Secret Files
|
||||
If you're not comfortable storing sensitive values like `SECRET_KEY` or any `PASSWORD` directly in the environment file, you can either use Docker secrets or bind-mount a text file containing the secret value. In this case, append `_FILE` to the environment variable name and set its value to the file path.
|
||||
|
||||
For example, instead of:
|
||||
|
||||
```yml
|
||||
SECRET_KEY: your_secret_value
|
||||
```
|
||||
|
||||
You can use:
|
||||
|
||||
```yml
|
||||
SECRET_KEY_FILE: /run/secrets/secret_key # For a docker secret
|
||||
SECRET_KEY_FILE: /some/directory/secret_key # For a volume bind mount
|
||||
```
|
||||
|
||||
Make sure the any secret files are **not** committed to version control to keep your secrets safe.
|
||||
|
||||
### .env File
|
||||
|
||||
If you prefer not to hardcode sensitive values like `SECRET_KEY` or any `PASSWORD` directly in your `docker-compose.yml` file, you can reference them from a `.env` file. Docker Compose will automatically read this file and substitute the values into your configuration. An example .env file is found [on GitHub](https://github.com/seanmorley15/AdventureLog/blob/main/.env.example).
|
||||
|
||||
For example, in your `.env` file:
|
||||
|
||||
```
|
||||
|
||||
SECRET\_KEY=your\_secret\_value
|
||||
|
||||
````
|
||||
|
||||
And in your `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
- SECRET_KEY=${SECRET_KEY}
|
||||
````
|
||||
|
||||
Make sure the `.env` file is **not** committed to version control to keep your secrets safe.
|
||||
|
||||
## Running the Containers
|
||||
|
||||
Once you've configured `.env`, you can start AdventureLog with:
|
||||
Once you've configured `docker-compose.yml`, you can start AdventureLog with:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
|
|
|
@ -5,7 +5,7 @@ Install **AdventureLog** in seconds using our automated script.
|
|||
## 🧪 One-Liner Install
|
||||
|
||||
```bash
|
||||
curl -sSL https://get.adventurelog.app | bash
|
||||
bash -c "$(curl -sSL https://get.adventurelog.app)"
|
||||
```
|
||||
|
||||
This will:
|
||||
|
@ -27,7 +27,7 @@ This will:
|
|||
The script automatically:
|
||||
|
||||
1. Verifies Docker is installed and running
|
||||
2. Downloads `docker-compose.yml` and `.env`
|
||||
2. Downloads `docker-compose.yml`
|
||||
3. Prompts you for domain and port settings
|
||||
4. Waits for services to start
|
||||
5. Prints success info with next steps
|
||||
|
|
|
@ -4,4 +4,4 @@ Traefik is a modern HTTP reverse proxy and load balancer that makes deploying mi
|
|||
|
||||
AdventureLog has a built-in Traefik configuration that makes it easy to deploy and manage your AdventureLog instance.
|
||||
|
||||
The most recent version of the Traefik `docker-compose.yml` file can be found in the [AdventureLog GitHub repository](https://github.com/seanmorley15/AdventureLog/blob/main/docker-compose-traefik.yaml).
|
||||
The most recent version of [docker-compose.yml](https://github.com/seanmorley15/AdventureLog/blob/main/docker-compose.yml) file includes example Traefik container labels. To deploy Traefik itself, consult the [Traefik v3.x documentation](https://doc.traefik.io/traefik/).
|
||||
|
|
6
frontend/.dockerignore
Normal file
6
frontend/.dockerignore
Normal file
|
@ -0,0 +1,6 @@
|
|||
.env.example
|
||||
.prettierignore
|
||||
.prettierrc
|
||||
.gitignore
|
||||
Dockerfile
|
||||
.dockerignore
|
|
@ -1,7 +0,0 @@
|
|||
PUBLIC_SERVER_URL=http://127.0.0.1:8000
|
||||
BODY_SIZE_LIMIT=Infinity
|
||||
|
||||
|
||||
# OPTIONAL VARIABLES FOR UMAMI ANALYTICS
|
||||
PUBLIC_UMAMI_SRC=
|
||||
PUBLIC_UMAMI_WEBSITE_ID=
|
|
@ -1,49 +1,65 @@
|
|||
# Use this image as the platform to build the app
|
||||
FROM node:22-alpine AS external-website
|
||||
# ----------------------
|
||||
# Stage 1: Build the app
|
||||
# ----------------------
|
||||
FROM docker.io/node:22-alpine AS builder
|
||||
|
||||
ARG GIT_SHA=unknown
|
||||
ARG GIT_TAG=latest
|
||||
ARG BUILD_DATE=unknown
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
COPY . ./
|
||||
# Install pnpm globally
|
||||
RUN npm install -g pnpm && \
|
||||
pnpm install --frozen-lockfile && \
|
||||
rm -f .env && \
|
||||
pnpm run build && \
|
||||
mv /app/package.json /app/build/package.json;\
|
||||
ls -lah .; \
|
||||
ls -lah ./build
|
||||
|
||||
# ----------------------------
|
||||
# Stage 2: Install Production Dependencies
|
||||
# ----------------------------
|
||||
FROM docker.io/node:22-alpine AS deps
|
||||
|
||||
WORKDIR /app
|
||||
COPY --from=builder /app/build/package.json ./
|
||||
RUN npm install --omit=dev && \
|
||||
addgroup -g 349 adventurelog && \
|
||||
adduser -D -u 349 -G adventurelog adventurelog && \
|
||||
chown -R 349:349 /app
|
||||
|
||||
# ----------------------------
|
||||
# Stage 3: Distroless Runtime
|
||||
# ----------------------------
|
||||
FROM gcr.io/distroless/nodejs20
|
||||
|
||||
# Metadata labels for the AdventureLog image
|
||||
LABEL maintainer="Sean Morley" \
|
||||
version="v0.10.0" \
|
||||
description="AdventureLog — the ultimate self-hosted travel companion." \
|
||||
org.opencontainers.image.title="AdventureLog" \
|
||||
org.opencontainers.image.description="AdventureLog is a self-hosted travel companion that helps you plan, track, and share your adventures." \
|
||||
org.opencontainers.image.version="v0.10.0" \
|
||||
org.opencontainers.image.authors="Sean Morley" \
|
||||
org.opencontainers.image.url="https://raw.githubusercontent.com/seanmorley15/AdventureLog/refs/heads/main/brand/banner.png" \
|
||||
org.opencontainers.image.source="https://github.com/seanmorley15/AdventureLog" \
|
||||
org.opencontainers.image.vendor="Sean Morley" \
|
||||
org.opencontainers.image.created="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" \
|
||||
org.opencontainers.image.version="${GIT_TAG}" \
|
||||
org.opencontainers.image.revision="${GIT_SHA}" \
|
||||
org.opencontainers.image.created="${BUILD_DATE}" \
|
||||
org.opencontainers.image.licenses="GPL-3.0"
|
||||
|
||||
# The WORKDIR instruction sets the working directory for everything that will happen next
|
||||
USER 349:349
|
||||
WORKDIR /app
|
||||
|
||||
# Install pnpm globally first
|
||||
RUN npm install -g pnpm
|
||||
COPY --from=builder /etc/passwd /etc/passwd
|
||||
COPY --from=builder /etc/group /etc/group
|
||||
COPY --from=builder /app/build ./
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
|
||||
# Copy package files first for better Docker layer caching
|
||||
COPY package.json pnpm-lock.yaml* ./
|
||||
CMD ["index.js"]
|
||||
|
||||
# Clean install all node modules using pnpm with frozen lockfile
|
||||
RUN pnpm install --frozen-lockfile
|
||||
|
||||
# Copy the rest of the application files
|
||||
COPY . .
|
||||
|
||||
# Remove the development .env file if present
|
||||
RUN rm -f .env
|
||||
|
||||
# Build SvelteKit app
|
||||
RUN pnpm run build
|
||||
|
||||
# Make startup script executable
|
||||
RUN chmod +x ./startup.sh
|
||||
|
||||
# Change to non-root user for security
|
||||
USER node:node
|
||||
|
||||
# Expose the port that the app is listening on
|
||||
EXPOSE 3000
|
||||
|
||||
# Run startup.sh instead of the default command
|
||||
CMD ["./startup.sh"]
|
||||
HEALTHCHECK --interval=10s --timeout=3s --retries=3 \
|
||||
CMD /nodejs/bin/node -e "require('net').connect(3000).on('connect', () => process.exit(0)).on('error', () => process.exit(1))"
|
|
@ -1,5 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
echo "The origin to be set is: $ORIGIN"
|
||||
# Start the application
|
||||
ORIGIN=$ORIGIN exec node build
|
|
@ -1,4 +1,4 @@
|
|||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# =============================================================================
|
||||
|
@ -20,6 +20,16 @@ declare -g ADMIN_PASSWORD=""
|
|||
declare -g DB_PASSWORD=""
|
||||
declare -g FRONTEND_PORT=""
|
||||
declare -g BACKEND_PORT=""
|
||||
declare -g TRAEFIK_ENABLED=""
|
||||
declare -g USE_EMAIL=""
|
||||
declare -g USE_DOCKER_SECRETS=""
|
||||
declare -g EMAIL_HOST=""
|
||||
declare -g EMAIL_USE_TLS=""
|
||||
declare -g EMAIL_PORT=""
|
||||
declare -g EMAIL_USE_SSL=""
|
||||
declare -g EMAIL_HOST_USER=""
|
||||
declare -g EMAIL_HOST_PASSWORD=""
|
||||
declare -g FINISHED_CORRECTLY="true"
|
||||
|
||||
# Color codes for beautiful output
|
||||
readonly RED='\033[0;31m'
|
||||
|
@ -56,6 +66,11 @@ log_header() {
|
|||
echo -e "${PURPLE}$1${NC}"
|
||||
}
|
||||
|
||||
# pause(){
|
||||
# read -s -n 1 -p "Press any key to continue . . ."
|
||||
# echo ""
|
||||
# }
|
||||
|
||||
print_banner() {
|
||||
cat << 'EOF'
|
||||
╔═════════════════════════════════════════════════════════════════════════╗
|
||||
|
@ -183,18 +198,11 @@ check_dependencies() {
|
|||
log_info "Checking system dependencies..."
|
||||
|
||||
local missing_deps=()
|
||||
|
||||
if ! command -v curl &>/dev/null; then
|
||||
missing_deps+=("curl")
|
||||
fi
|
||||
|
||||
if ! command -v docker &>/dev/null; then
|
||||
missing_deps+=("docker")
|
||||
fi
|
||||
|
||||
if ! command -v docker-compose &>/dev/null && ! docker compose version &>/dev/null; then
|
||||
missing_deps+=("docker-compose")
|
||||
fi
|
||||
for bin in curl docker docker-compose tr sed; do
|
||||
if ! command -v "$bin" >/dev/null 2>&1; then
|
||||
missing_deps+=("$bin")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#missing_deps[@]} -ne 0 ]; then
|
||||
log_error "Missing dependencies: ${missing_deps[*]}"
|
||||
|
@ -205,6 +213,12 @@ check_dependencies() {
|
|||
"curl")
|
||||
echo " • curl: apt-get install curl (Ubuntu/Debian) or brew install curl (macOS)"
|
||||
;;
|
||||
"tr")
|
||||
echo " • curl: apt-get install coreutils (Ubuntu/Debian) and should be installed by default on macOS"
|
||||
;;
|
||||
"sed")
|
||||
echo " • curl: apt-get install sed (Ubuntu/Debian) or brew install gnu-sed (macOS)"
|
||||
;;
|
||||
"docker")
|
||||
echo " • Docker: https://docs.docker.com/get-docker/"
|
||||
;;
|
||||
|
@ -282,12 +296,93 @@ download_files() {
|
|||
exit 1
|
||||
fi
|
||||
log_success "docker-compose.yml downloaded"
|
||||
|
||||
if ! curl -fsSL --connect-timeout 10 --max-time 30 "$ENV_FILE_URL" -o .env; then
|
||||
log_error "Failed to download .env template"
|
||||
exit 1
|
||||
fi
|
||||
log_success ".env template downloaded"
|
||||
}
|
||||
|
||||
prompt_email_configuration() {
|
||||
echo ""
|
||||
log_header "📧 SMTP Email Configuration"
|
||||
echo ""
|
||||
echo "Please enter the required SMTP settings for sending emails from AdventureLog."
|
||||
echo ""
|
||||
|
||||
# SMTP Host
|
||||
while true; do
|
||||
read -r -p "📨 SMTP Host: " EMAIL_HOST
|
||||
if [[ -n "$EMAIL_HOST" ]]; then
|
||||
break
|
||||
else
|
||||
log_error "SMTP Host cannot be empty."
|
||||
fi
|
||||
done
|
||||
|
||||
# Use TLS
|
||||
while true; do
|
||||
read -r -p "🔐 Use TLS? (y/n): " input_tls
|
||||
case "${input_tls,,}" in
|
||||
y | yes)
|
||||
EMAIL_USE_TLS=true
|
||||
break
|
||||
;;
|
||||
n | no)
|
||||
EMAIL_USE_TLS=false
|
||||
break
|
||||
;;
|
||||
*)
|
||||
log_error "Invalid input. Please enter y or n."
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Use SSL
|
||||
while true; do
|
||||
read -r -p "🔐 Use SSL? (y/n): " input_ssl
|
||||
case "${input_ssl,,}" in
|
||||
y | yes)
|
||||
EMAIL_USE_SSL=true
|
||||
break
|
||||
;;
|
||||
n | no)
|
||||
EMAIL_USE_SSL=false
|
||||
break
|
||||
;;
|
||||
*)
|
||||
log_error "Invalid input. Please enter y or n."
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# SMTP Port
|
||||
while true; do
|
||||
read -r -p "🔌 SMTP Port: " EMAIL_PORT
|
||||
if [[ "$EMAIL_PORT" =~ ^[0-9]+$ ]]; then
|
||||
break
|
||||
else
|
||||
log_error "Please enter a valid numeric port."
|
||||
fi
|
||||
done
|
||||
|
||||
# SMTP Username
|
||||
while true; do
|
||||
read -r -p "👤 SMTP Username: " EMAIL_HOST_USER
|
||||
if [[ -n "$EMAIL_HOST_USER" ]]; then
|
||||
break
|
||||
else
|
||||
log_error "SMTP Username cannot be empty."
|
||||
fi
|
||||
done
|
||||
|
||||
# SMTP Password
|
||||
while true; do
|
||||
read -r -s -p "🔑 SMTP Password: " EMAIL_HOST_PASSWORD
|
||||
echo
|
||||
if [[ -n "$EMAIL_HOST_PASSWORD" ]]; then
|
||||
break
|
||||
else
|
||||
log_error "Passwords do not match or are empty. Please try again."
|
||||
fi
|
||||
done
|
||||
|
||||
log_success "SMTP configuration completed."
|
||||
}
|
||||
|
||||
prompt_configuration() {
|
||||
|
@ -333,233 +428,330 @@ prompt_configuration() {
|
|||
# Check port availability
|
||||
check_port_availability "$FRONTEND_PORT" "frontend"
|
||||
check_port_availability "$BACKEND_PORT" "backend"
|
||||
|
||||
while true; do
|
||||
read -r -p "🚦 Use SMTP email with AdventureLog? (Y/n): " input_email
|
||||
case "${input_email,,}" in
|
||||
y | yes | "")
|
||||
USE_EMAIL=true
|
||||
prompt_email_configuration
|
||||
break
|
||||
;;
|
||||
n | no)
|
||||
USE_EMAIL=false
|
||||
break
|
||||
;;
|
||||
*)
|
||||
log_error "Invalid input. Please enter Y or N."
|
||||
;;
|
||||
esac
|
||||
done
|
||||
log_success "Using SMTP email with AdventureLog?: $USE_EMAIL"
|
||||
|
||||
while true; do
|
||||
read -r -p "🚦 Enable Traefik labels? (Y/n): " input_traefik
|
||||
case "${input_traefik,,}" in
|
||||
y | yes | "")
|
||||
TRAEFIK_ENABLED=true
|
||||
break
|
||||
;;
|
||||
n | no)
|
||||
TRAEFIK_ENABLED=false
|
||||
break
|
||||
;;
|
||||
*)
|
||||
log_error "Invalid input. Please enter Y or N."
|
||||
;;
|
||||
esac
|
||||
done
|
||||
log_success "Traefik enabled?: $TRAEFIK_ENABLED"
|
||||
|
||||
while true; do
|
||||
read -r -p "🚦 Use docker secrets, which is more secure? (Y/n): " input_secrets
|
||||
case "${input_secrets,,}" in
|
||||
y | yes | "")
|
||||
USE_DOCKER_SECRETS=true
|
||||
break
|
||||
;;
|
||||
n | no)
|
||||
USE_DOCKER_SECRETS=false
|
||||
break
|
||||
;;
|
||||
*)
|
||||
log_error "Invalid input. Please enter Y or N."
|
||||
;;
|
||||
esac
|
||||
done
|
||||
log_success "Using Docker Secrets enabled?: $USE_DOCKER_SECRETS"
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
configure_environment_fallback() {
|
||||
log_info "Using simple configuration approach..."
|
||||
|
||||
# Generate simple passwords using a basic method
|
||||
DB_PASSWORD="$(date +%s | sha256sum | base64 | head -c 32)"
|
||||
ADMIN_PASSWORD="$(date +%s | sha256sum | base64 | head -c 24)"
|
||||
|
||||
log_info "Generated passwords using fallback method"
|
||||
|
||||
# Create backup
|
||||
cp .env .env.backup
|
||||
|
||||
# Use simple string replacement with perl if available
|
||||
if command -v perl &>/dev/null; then
|
||||
log_info "Using perl for configuration..."
|
||||
# Fix: Update BOTH password variables for database consistency
|
||||
perl -pi -e "s/^POSTGRES_PASSWORD=.*/POSTGRES_PASSWORD=$DB_PASSWORD/" .env
|
||||
perl -pi -e "s/^DATABASE_PASSWORD=.*/DATABASE_PASSWORD=$DB_PASSWORD/" .env
|
||||
perl -pi -e "s/^DJANGO_ADMIN_PASSWORD=.*/DJANGO_ADMIN_PASSWORD=$ADMIN_PASSWORD/" .env
|
||||
perl -pi -e "s|^ORIGIN=.*|ORIGIN=$FRONTEND_ORIGIN|" .env
|
||||
perl -pi -e "s|^PUBLIC_URL=.*|PUBLIC_URL=$BACKEND_URL|" .env
|
||||
perl -pi -e "s|^CSRF_TRUSTED_ORIGINS=.*|CSRF_TRUSTED_ORIGINS=$FRONTEND_ORIGIN,$BACKEND_URL|" .env
|
||||
perl -pi -e "s|^FRONTEND_URL=.*|FRONTEND_URL=$FRONTEND_ORIGIN|" .env
|
||||
# Add port configuration
|
||||
perl -pi -e "s/^FRONTEND_PORT=.*/FRONTEND_PORT=$FRONTEND_PORT/" .env
|
||||
perl -pi -e "s/^BACKEND_PORT=.*/BACKEND_PORT=$BACKEND_PORT/" .env
|
||||
|
||||
# Add port variables if they don't exist
|
||||
if ! grep -q "^FRONTEND_PORT=" .env; then
|
||||
echo "FRONTEND_PORT=$FRONTEND_PORT" >> .env
|
||||
fi
|
||||
if ! grep -q "^BACKEND_PORT=" .env; then
|
||||
echo "BACKEND_PORT=$BACKEND_PORT" >> .env
|
||||
fi
|
||||
|
||||
if grep -q "POSTGRES_PASSWORD=$DB_PASSWORD" .env; then
|
||||
log_success "Configuration completed successfully"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Manual approach - create .env from scratch with key variables
|
||||
log_info "Creating minimal .env configuration..."
|
||||
cat > .env << EOF
|
||||
# Database Configuration
|
||||
POSTGRES_DB=adventurelog
|
||||
POSTGRES_USER=adventurelog
|
||||
POSTGRES_PASSWORD=$DB_PASSWORD
|
||||
DATABASE_PASSWORD=$DB_PASSWORD
|
||||
|
||||
# Django Configuration
|
||||
DJANGO_ADMIN_USERNAME=admin
|
||||
DJANGO_ADMIN_PASSWORD=$ADMIN_PASSWORD
|
||||
SECRET_KEY=$(openssl rand -base64 32 2>/dev/null || echo "change-this-secret-key-$(date +%s)")
|
||||
|
||||
# URL Configuration
|
||||
ORIGIN=$FRONTEND_ORIGIN
|
||||
PUBLIC_URL=$BACKEND_URL
|
||||
FRONTEND_URL=$FRONTEND_ORIGIN
|
||||
CSRF_TRUSTED_ORIGINS=$FRONTEND_ORIGIN,$BACKEND_URL
|
||||
|
||||
# Port Configuration
|
||||
FRONTEND_PORT=$FRONTEND_PORT
|
||||
BACKEND_PORT=$BACKEND_PORT
|
||||
|
||||
# Additional Settings
|
||||
DEBUG=False
|
||||
EOF
|
||||
|
||||
log_success "Created minimal .env configuration"
|
||||
return 0
|
||||
}
|
||||
|
||||
configure_environment() {
|
||||
log_info "Generating secure configuration..."
|
||||
|
||||
# Debug: Test password generation first
|
||||
log_info "Testing password generation..."
|
||||
if ! command -v tr &>/dev/null; then
|
||||
log_error "tr command not found - required for password generation"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Generate secure passwords with error checking
|
||||
log_info "Generating database password..."
|
||||
log_info "Verifying required tools..."
|
||||
|
||||
for cmd in tr sed cp grep wc mkdir; do
|
||||
if ! command -v "$cmd" &>/dev/null; then
|
||||
log_error "$cmd is required but not found."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
log_info "Generating secure secrets..."
|
||||
DB_PASSWORD=$(generate_secure_password 32)
|
||||
if [[ -z "$DB_PASSWORD" ]]; then
|
||||
log_error "Failed to generate database password"
|
||||
exit 1
|
||||
fi
|
||||
log_success "Database password generated (${#DB_PASSWORD} characters)"
|
||||
|
||||
log_info "Generating admin password..."
|
||||
ADMIN_PASSWORD=$(generate_secure_password 24)
|
||||
if [[ -z "$ADMIN_PASSWORD" ]]; then
|
||||
log_error "Failed to generate admin password"
|
||||
SECRET_KEY=$(generate_secure_password 50)
|
||||
|
||||
if [[ -z "$DB_PASSWORD" || -z "$ADMIN_PASSWORD" || -z "$SECRET_KEY" ]]; then
|
||||
log_error "❌ Failed to generate secure credentials"
|
||||
exit 1
|
||||
fi
|
||||
log_success "Admin password generated (${#ADMIN_PASSWORD} characters)"
|
||||
|
||||
# Debug: Check if .env file exists and is readable
|
||||
log_info "Checking .env file..."
|
||||
if [[ ! -f ".env" ]]; then
|
||||
log_error ".env file not found"
|
||||
|
||||
log_success "✅ Secrets generated: DB(${#DB_PASSWORD}), Admin(${#ADMIN_PASSWORD}), Key(${#SECRET_KEY})"
|
||||
|
||||
FRONTEND_PORT="${FRONTEND_PORT:-8015}"
|
||||
BACKEND_PORT="${BACKEND_PORT:-8016}"
|
||||
FRONTEND_ORIGIN="${FRONTEND_ORIGIN:-"http://localhost:$FRONTEND_PORT"}"
|
||||
BACKEND_URL="${BACKEND_URL:-"http://localhost:$BACKEND_PORT"}"
|
||||
CSRF_TRUSTED="$BACKEND_URL,$FRONTEND_ORIGIN"
|
||||
|
||||
if [[ ! -f "docker-compose.yml" ]]; then
|
||||
log_error "docker-compose.yml not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -r ".env" ]]; then
|
||||
log_error ".env file is not readable"
|
||||
|
||||
cp docker-compose.yml docker-compose.yml.bak || {
|
||||
log_error "Failed to back up docker-compose.yml"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "File check passed - .env exists and is readable ($(wc -l < .env) lines)"
|
||||
|
||||
# Try fallback method first (simpler and more reliable)
|
||||
log_info "Attempting configuration..."
|
||||
if configure_environment_fallback; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_warning "Fallback method failed, trying advanced processing..."
|
||||
|
||||
# Fallback to bash processing
|
||||
# Create backup of original .env
|
||||
cp .env .env.backup
|
||||
|
||||
# Create a new .env file by processing the original line by line
|
||||
local temp_file=".env.temp"
|
||||
local processed_lines=0
|
||||
local updated_lines=0
|
||||
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
((processed_lines++))
|
||||
case "$line" in
|
||||
POSTGRES_PASSWORD=*)
|
||||
echo "POSTGRES_PASSWORD=$DB_PASSWORD"
|
||||
((updated_lines++))
|
||||
;;
|
||||
DATABASE_PASSWORD=*)
|
||||
echo "DATABASE_PASSWORD=$DB_PASSWORD"
|
||||
((updated_lines++))
|
||||
;;
|
||||
DJANGO_ADMIN_PASSWORD=*)
|
||||
echo "DJANGO_ADMIN_PASSWORD=$ADMIN_PASSWORD"
|
||||
((updated_lines++))
|
||||
;;
|
||||
ORIGIN=*)
|
||||
echo "ORIGIN=$FRONTEND_ORIGIN"
|
||||
((updated_lines++))
|
||||
;;
|
||||
PUBLIC_URL=*)
|
||||
echo "PUBLIC_URL=$BACKEND_URL"
|
||||
((updated_lines++))
|
||||
;;
|
||||
CSRF_TRUSTED_ORIGINS=*)
|
||||
echo "CSRF_TRUSTED_ORIGINS=$FRONTEND_ORIGIN,$BACKEND_URL"
|
||||
((updated_lines++))
|
||||
;;
|
||||
FRONTEND_URL=*)
|
||||
echo "FRONTEND_URL=$FRONTEND_ORIGIN"
|
||||
((updated_lines++))
|
||||
;;
|
||||
FRONTEND_PORT=*)
|
||||
echo "FRONTEND_PORT=$FRONTEND_PORT"
|
||||
((updated_lines++))
|
||||
;;
|
||||
BACKEND_PORT=*)
|
||||
echo "BACKEND_PORT=$BACKEND_PORT"
|
||||
((updated_lines++))
|
||||
;;
|
||||
*)
|
||||
echo "$line"
|
||||
;;
|
||||
esac
|
||||
done < .env > "$temp_file"
|
||||
|
||||
# Add port variables if they weren't found in the original file
|
||||
if ! grep -q "^FRONTEND_PORT=" "$temp_file"; then
|
||||
echo "FRONTEND_PORT=$FRONTEND_PORT" >> "$temp_file"
|
||||
((updated_lines++))
|
||||
fi
|
||||
if ! grep -q "^BACKEND_PORT=" "$temp_file"; then
|
||||
echo "BACKEND_PORT=$BACKEND_PORT" >> "$temp_file"
|
||||
((updated_lines++))
|
||||
fi
|
||||
|
||||
log_info "Processed $processed_lines lines, updated $updated_lines configuration values"
|
||||
|
||||
# Check if temp file was created successfully
|
||||
if [[ ! -f "$temp_file" ]]; then
|
||||
log_error "Failed to create temporary configuration file"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Replace the original .env with the configured one
|
||||
if mv "$temp_file" .env; then
|
||||
log_success "Environment configured with secure passwords and port settings"
|
||||
}
|
||||
|
||||
log_info "Applying configuration to docker-compose.yml..."
|
||||
|
||||
# Handle Docker secrets if enabled
|
||||
if [[ "$USE_DOCKER_SECRETS" == "true" ]]; then
|
||||
log_info "Enabling Docker secrets..."
|
||||
|
||||
mkdir -p .secrets
|
||||
|
||||
# Save secrets to files
|
||||
echo "$DB_PASSWORD" > .secrets/postgres-password.secret || FINISHED_CORRECTLY=false
|
||||
echo "$SECRET_KEY" > .secrets/secret-key.secret || FINISHED_CORRECTLY=false
|
||||
echo "$ADMIN_PASSWORD" > .secrets/django-admin-password.secret || FINISHED_CORRECTLY=false
|
||||
|
||||
# Uncomment secret references in docker-compose.yml
|
||||
sed -i '' -E \
|
||||
-e 's/^([[:space:]]*)#[[:space:]]*(secrets:)/\1\2/' \
|
||||
-e 's/^([[:space:]]*)#[[:space:]]*( - postgres-password)/\1\2/' \
|
||||
-e 's/^([[:space:]]*)#[[:space:]]*( - secret-key)/\1\2/' \
|
||||
-e 's/^([[:space:]]*)#[[:space:]]*( - django-admin-password)/\1\2/' \
|
||||
-e 's/^([[:space:]]*)#[[:space:]]*#( - email-host-password)/\1# \2/' \
|
||||
-e 's/^([[:space:]]*)#[[:space:]](secrets:[[:space:]]*#.*)/\1\2/' \
|
||||
-e 's/^([[:space:]]*)#[[:space:]]*(postgres-password:)/\1 \2/' \
|
||||
-e 's/^([[:space:]]*)#[[:space:]]*(file: .secrets\/postgres-password\.secret)/\1 \2/' \
|
||||
-e 's/^([[:space:]]*)#[[:space:]]*(secret-key:)/\1 \2/' \
|
||||
-e 's/^([[:space:]]*)#[[:space:]]*(file: .secrets\/secret-key\.secret)/\1 \2/' \
|
||||
-e 's/^([[:space:]]*)#[[:space:]]*#[[:space:]]*(email-host-password:)/\1 # \2/' \
|
||||
-e 's/^([[:space:]]*)#[[:space:]]*#[[:space:]]*(file: .secrets\/email-host-password\.secret)/\1 # \2/' \
|
||||
-e 's/^([[:space:]]*)#[[:space:]]*(django-admin-password:)/\1 \2/' \
|
||||
-e 's/^([[:space:]]*)#[[:space:]]*(file: .secrets\/django-admin-password\.secret)/\1 \2/' \
|
||||
docker-compose.yml || FINISHED_CORRECTLY=false
|
||||
# for swapping normal variables to secrets
|
||||
awk '
|
||||
{
|
||||
lines[NR] = $0
|
||||
|
||||
# Match lines like: [whitespace]#[whitespace]VARIABLE_FILE:
|
||||
if ($0 ~ /^[[:space:]]*#[[:space:]]*[A-Z0-9_]+_FILE:/) {
|
||||
line = $0
|
||||
indent = line
|
||||
sub(/[^[:space:]]+.*/, "", indent) # Get leading whitespace
|
||||
|
||||
# Remove leading `#` and any spaces after
|
||||
sub(/^[[:space:]]*#[[:space:]]*/, "", line)
|
||||
split(line, parts, ":")
|
||||
split(parts[1], name_parts, "_FILE")
|
||||
varname = name_parts[1]
|
||||
|
||||
uncomment_line[NR] = indent line
|
||||
comment_var[varname] = 1
|
||||
}
|
||||
}
|
||||
END {
|
||||
for (i = 1; i <= NR; i++) {
|
||||
line = lines[i]
|
||||
|
||||
# Replace commented *_FILE line with uncommented version (with indent)
|
||||
if (i in uncomment_line) {
|
||||
print uncomment_line[i]
|
||||
continue
|
||||
}
|
||||
|
||||
# Comment out original VARIABLE: line if a *_FILE was found
|
||||
if (line ~ /^[[:space:]]*[A-Z0-9_]+:/) {
|
||||
indent = line
|
||||
sub(/[^[:space:]]+.*/, "", indent)
|
||||
split(line, parts, ":")
|
||||
var = parts[1]
|
||||
gsub(/^[[:space:]]*/, "", var)
|
||||
|
||||
if (var in comment_var) {
|
||||
print indent "# " substr(line, length(indent)+1)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
# Default: print line as-is
|
||||
print line
|
||||
}
|
||||
}
|
||||
' docker-compose.yml > docker-compose.yml.tmp && {
|
||||
mv docker-compose.yml.tmp docker-compose.yml
|
||||
} || FINISHED_CORRECTLY=false
|
||||
|
||||
# Handle email password secret comments
|
||||
awk '
|
||||
{
|
||||
lines[NR] = $0
|
||||
}
|
||||
|
||||
END {
|
||||
for (i = 1; i <= NR; i++) {
|
||||
line1 = lines[i]
|
||||
line2 = lines[i + 1]
|
||||
|
||||
# Match: line1 = single-commented VAR, line2 = double-commented VAR_FILE
|
||||
if (line1 ~ /^[[:space:]]*#[[:space:]]*[A-Z0-9_]+:[[:space:]]*/ &&
|
||||
line2 ~ /^[[:space:]]*#[[:space:]]*#[[:space:]]*[A-Z0-9_]+_FILE:/) {
|
||||
|
||||
# Extract indent from line1
|
||||
indent1 = line1
|
||||
sub(/[^[:space:]]+.*/, "", indent1)
|
||||
|
||||
# Clean VAR line for recommenting
|
||||
clean1 = line1
|
||||
gsub(/^[[:space:]]*#[[:space:]]*/, "", clean1)
|
||||
|
||||
# Print double-commented VAR line
|
||||
print indent1 "# # " clean1
|
||||
|
||||
# Extract indent from line2
|
||||
indent2 = line2
|
||||
sub(/[^[:space:]]+.*/, "", indent2)
|
||||
|
||||
# Clean VAR_FILE line to single-comment
|
||||
clean2 = line2
|
||||
gsub(/^[[:space:]]*#[[:space:]]*/, "", clean2) # Remove first #
|
||||
gsub(/^[[:space:]]*#[[:space:]]*/, "", clean2) # Remove second #
|
||||
|
||||
# Print single-commented VAR_FILE line
|
||||
print indent2 "# " clean2
|
||||
|
||||
i++ # Skip next line since we just processed it
|
||||
continue
|
||||
}
|
||||
|
||||
# Default: print original line
|
||||
print lines[i]
|
||||
}
|
||||
}
|
||||
' docker-compose.yml > docker-compose.yml.tmp && {
|
||||
mv docker-compose.yml.tmp docker-compose.yml
|
||||
} || FINISHED_CORRECTLY=false
|
||||
|
||||
log_success "Docker secrets configured"
|
||||
else
|
||||
log_error "Failed to replace .env file"
|
||||
log_info "Restoring backup and exiting"
|
||||
mv .env.backup .env
|
||||
rm -f "$temp_file"
|
||||
# Default env var replacements (no secrets)
|
||||
sed -i '' -E \
|
||||
-e "s|^([[:space:]]*POSTGRES_PASSWORD:).*|\1 $DB_PASSWORD|" \
|
||||
-e "s|^([[:space:]]*DJANGO_ADMIN_PASSWORD:).*|\1 $ADMIN_PASSWORD|" \
|
||||
-e "s|^([[:space:]]*SECRET_KEY:).*|\1 $SECRET_KEY|" \
|
||||
-e "s|^([[:space:]]*EMAIL_HOST_PASSWORD:).*|\1 $EMAIL_HOST_PASSWORD|" \
|
||||
docker-compose.yml || FINISHED_CORRECTLY=false
|
||||
|
||||
# if [[ "$USE_EMAIL" == "true" ]]; then
|
||||
# fi
|
||||
fi
|
||||
# Handle email config if enabled
|
||||
if [[ "$USE_EMAIL" == "true" ]]; then
|
||||
log_info "Enabling email configuration..."
|
||||
sed -i '' -E \
|
||||
-e "s/^([[:space:]]*)#[[:space:]]*(EMAIL_BACKEND:).*/\1\2 email/" \
|
||||
-e "s/^([[:space:]]*)#[[:space:]]*(EMAIL_HOST:).*/\1\2 $EMAIL_HOST/" \
|
||||
-e "s/^([[:space:]]*)#[[:space:]]*(EMAIL_USE_TLS:).*/\1\2 $EMAIL_USE_TLS/" \
|
||||
-e "s/^([[:space:]]*)#[[:space:]]*(EMAIL_PORT:).*/\1\2 $EMAIL_PORT/" \
|
||||
-e "s/^([[:space:]]*)#[[:space:]]*(EMAIL_USE_SSL:).*/\1\2 $EMAIL_USE_SSL/" \
|
||||
-e "s/^([[:space:]]*)#[[:space:]]*(EMAIL_HOST_USER:).*/\1\2 $EMAIL_HOST_USER/" \
|
||||
docker-compose.yml || FINISHED_CORRECTLY=false
|
||||
|
||||
if [[ "$USE_DOCKER_SECRETS" == "true" ]]; then
|
||||
sed -i '' -E \
|
||||
-e "s/^([[:space:]]*)#[[:space:]]*(# EMAIL_HOST_PASSWORD:).*/\1\2/" \
|
||||
-e "s/^([[:space:]]*)#[[:space:]]*(EMAIL_HOST_PASSWORD_FILE:).*/\1\2 \/run\/secrets\/email-host-password/" \
|
||||
-e "s/^([[:space:]]*)#[[:space:]]*(- email-host-password)/\1 \2/" \
|
||||
-e "s/^([[:space:]]*)#[[:space:]]*(email-host-password:)/\1\2/" \
|
||||
-e "s/^([[:space:]]*)#[[:space:]]*(file: .secrets\/email-host-password\.secret)/\1 \2/" \
|
||||
docker-compose.yml || FINISHED_CORRECTLY=false
|
||||
else
|
||||
sed -i '' -E \
|
||||
-e "s/^([[:space:]]*)#[[:space:]]*(EMAIL_HOST_PASSWORD:).*/\1\2 $EMAIL_HOST_PASSWORD/" \
|
||||
docker-compose.yml || FINISHED_CORRECTLY=false
|
||||
fi
|
||||
|
||||
log_success "Email configuration applied"
|
||||
fi
|
||||
|
||||
# Replace other standard values
|
||||
sed -i '' -E \
|
||||
-e "s|^( *BACKEND_PORT:).*|\1 $BACKEND_PORT|" \
|
||||
-e "s|^( *FRONTEND_PORT:).*|\1 $FRONTEND_PORT|" \
|
||||
-e "s|^( *PUBLIC_URL:).*|\1 $BACKEND_URL|" \
|
||||
-e "s|^( *FRONTEND_URL:).*|\1 $FRONTEND_ORIGIN|" \
|
||||
-e "s|^( *ORIGIN:).*|\1 $FRONTEND_ORIGIN|" \
|
||||
-e "s|^( *CSRF_TRUSTED_ORIGINS:).*|\1 $CSRF_TRUSTED|" \
|
||||
-e "s|([[:space:]]+-[[:space:]]*\")([0-9]+)(:3000\")|\1$FRONTEND_PORT\3|" \
|
||||
-e "s|([[:space:]]+-[[:space:]]*\")([0-9]+)(:80\")|\1$BACKEND_PORT\3|" \
|
||||
docker-compose.yml || FINISHED_CORRECTLY=false
|
||||
|
||||
if [[ "$TRAEFIK_ENABLED" == "true" ]]; then
|
||||
sed -i '' -E \
|
||||
-e 's/^([[:space:]]*)#([[:space:]]*labels:)/\1 \2/' \
|
||||
-e 's/^([[:space:]]*)#([[:space:]]*-?[[:space:]]*"traefik\..*")/\1 \2/' \
|
||||
docker-compose.yml || FINISHED_CORRECTLY=false
|
||||
fi
|
||||
|
||||
if [[ "$FINISHED_CORRECTLY" == "false" ]]; then
|
||||
log_error "configuration failed"
|
||||
log_info "Restoring backup..."
|
||||
mv docker-compose.yml.bak docker-compose.yml
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify critical configuration was applied
|
||||
if grep -q "POSTGRES_PASSWORD=$DB_PASSWORD" .env && (grep -q "DATABASE_PASSWORD=$DB_PASSWORD" .env || grep -q "POSTGRES_PASSWORD=$DB_PASSWORD" .env); then
|
||||
log_success "Configuration verification passed - database password variables set"
|
||||
|
||||
log_success "docker-compose.yml updated"
|
||||
|
||||
log_info "🔍 Verifying updated values in docker-compose.yml:"
|
||||
diff -y docker-compose.yml.bak docker-compose.yml || :
|
||||
|
||||
# Basic verification of values present
|
||||
if [[ "$USE_DOCKER_SECRETS" == "false" ]]; then
|
||||
if grep -q "$DB_PASSWORD" docker-compose.yml && grep -q "$FRONTEND_PORT" docker-compose.yml && grep -q "$BACKEND_PORT" docker-compose.yml; then
|
||||
log_success "✅ Configuration verification passed"
|
||||
else
|
||||
log_warning "⚠️ Some configuration values may not have applied properly"
|
||||
log_info "Restoring backup for safety... (renaming auto-configured docker-compose.yml to docker-compose.yml.install)"
|
||||
mv docker-compose.yml docker-compose.yml.install
|
||||
mv docker-compose.yml.bak docker-compose.yml
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
log_error "Configuration verification failed - database passwords not properly configured"
|
||||
log_info "Showing database-related lines in .env for debugging:"
|
||||
grep -E "(POSTGRES_PASSWORD|DATABASE_PASSWORD)" .env | while read -r line; do
|
||||
echo " $line"
|
||||
done
|
||||
mv .env.backup .env
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify port configuration
|
||||
if grep -q "FRONTEND_PORT=$FRONTEND_PORT" .env && grep -q "BACKEND_PORT=$BACKEND_PORT" .env; then
|
||||
log_success "Port configuration verified - frontend: $FRONTEND_PORT, backend: $BACKEND_PORT"
|
||||
else
|
||||
log_warning "Port configuration may not be complete - check .env file manually"
|
||||
if grep -q "postgres-password" docker-compose.yml && grep -q "$FRONTEND_PORT" docker-compose.yml && grep -q "$BACKEND_PORT" docker-compose.yml; then
|
||||
log_success "✅ Configuration verification passed"
|
||||
else
|
||||
log_warning "⚠️ Some configuration values may not have applied properly"
|
||||
log_info "Restoring backup for safety... (renaming auto-configured docker-compose.yml to docker-compose.yml.install)"
|
||||
mv docker-compose.yml docker-compose.yml.install
|
||||
mv docker-compose.yml.bak docker-compose.yml
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
|
|
|
@ -1,78 +1,63 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: example-name
|
||||
name: adventurelog
|
||||
labels:
|
||||
app: adventure
|
||||
app: adventurelog
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: adventure
|
||||
app: adventurelog
|
||||
template:
|
||||
metadata:
|
||||
name: adventure
|
||||
labels:
|
||||
app: adventure
|
||||
app: adventurelog
|
||||
spec:
|
||||
volumes:
|
||||
- name: adventure-journal
|
||||
- name: media
|
||||
persistentVolumeClaim:
|
||||
claimName: adventure-journal-pvc
|
||||
- name: adventure-journal-db
|
||||
claimName: media-pvc
|
||||
- name: data
|
||||
persistentVolumeClaim:
|
||||
claimName: adventure-journal-db-pvc
|
||||
claimName: data-pvc
|
||||
containers:
|
||||
- name: adventure-frontend
|
||||
- name: frontend
|
||||
image: ghcr.io/seanmorley15/adventurelog-frontend:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 3000
|
||||
env:
|
||||
- name: FRONTEND_PORT
|
||||
value: "8015"
|
||||
- name: PUBLIC_SERVER_URL
|
||||
value: "http://internally-and-externally.reachable.io:80"
|
||||
value: "http://backend:8000"
|
||||
- name: ORIGIN
|
||||
value: "http://url-typed-into-browser.io:80"
|
||||
value: "http://localhost:8015"
|
||||
- name: BODY_SIZE_LIMIT
|
||||
value: "Infinity"
|
||||
readinessProbe:
|
||||
exec:
|
||||
command: ["/nodejs/bin/node", "-e", "require('net').connect(3000).on('connect', () => process.exit(0)).on('error', () => process.exit(1))"]
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 3
|
||||
|
||||
- name: adventure-db
|
||||
image: postgis/postgis:15-3.3
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 5432
|
||||
volumeMounts:
|
||||
- name: adventure-journal-db
|
||||
mountPath: /var/lib/postgresql/data
|
||||
env:
|
||||
- name: POSTGRES_DB
|
||||
value: database
|
||||
- name: PGDATA
|
||||
value: /var/lib/postgresql/data/pgdata/subdir
|
||||
- name: POSTGRES_USER
|
||||
value: adventure
|
||||
- name: POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: adventurelog-secret
|
||||
key: adventure-postgres-password
|
||||
|
||||
- name: adventure-backend
|
||||
- name: backend
|
||||
image: ghcr.io/seanmorley15/adventurelog-backend:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 80
|
||||
- containerPort: 8000
|
||||
volumeMounts:
|
||||
- name: adventure-journal
|
||||
- name: media
|
||||
mountPath: /code/media
|
||||
env:
|
||||
- name: PGHOST
|
||||
value: "adventure-db-svc"
|
||||
- name: PGDATABASE
|
||||
value: "database"
|
||||
- name: PGUSER
|
||||
value: "adventure"
|
||||
value: "db"
|
||||
- name: POSTGRES_DB
|
||||
value: "adventurelog"
|
||||
- name: POSTGRES_USER
|
||||
value: "adventurelog"
|
||||
- name: PGPASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
|
@ -82,58 +67,101 @@ spec:
|
|||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: adventurelog-secret
|
||||
key: adventure-postgres-password
|
||||
- name: PUBLIC_URL
|
||||
value: "http://internally-and-externally.reachable.io:80" # Match the outward port, used for the creation of image urls
|
||||
- name: FRONTEND_URL
|
||||
value: "http://url-typed-into-browser.io:80"
|
||||
- name: CSRF_TRUSTED_ORIGINS
|
||||
value: "http://url-typed-into-browser.io:80, http://internally-and-externally.reachable.io:80"
|
||||
key: secret-key
|
||||
- name: DJANGO_ADMIN_USERNAME
|
||||
value: "admin"
|
||||
- name: DJANGO_ADMIN_PASSWORD
|
||||
value: "admin"
|
||||
- name: DJANGO_ADMIN_EMAIL
|
||||
value: "admin@example.com"
|
||||
- name: BACKEND_PORT
|
||||
value: "8016"
|
||||
- name: PUBLIC_URL
|
||||
value: "http://localhost:8016"
|
||||
- name: FRONTEND_URL
|
||||
value: "http://localhost:8015"
|
||||
- name: CSRF_TRUSTED_ORIGINS
|
||||
value: "http://localhost:8016,http://localhost:8015"
|
||||
- name: DEBUG
|
||||
value: "True"
|
||||
value: "false"
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 80
|
||||
initialDelaySeconds: 20
|
||||
periodSeconds: 30
|
||||
timeoutSeconds: 10
|
||||
failureThreshold: 5
|
||||
|
||||
- name: db
|
||||
image: postgis/postgis:16-3.5
|
||||
ports:
|
||||
- containerPort: 5432
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /var/lib/postgresql/data
|
||||
env:
|
||||
- name: POSTGRES_DB
|
||||
value: "adventurelog"
|
||||
- name: POSTGRES_USER
|
||||
value: "adventurelog"
|
||||
- name: POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: adventurelog-secret
|
||||
key: adventure-postgres-password
|
||||
readinessProbe:
|
||||
exec:
|
||||
command: ["pg_isready", "-U", "adventurelog"]
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 20
|
||||
timeoutSeconds: 10
|
||||
failureThreshold: 5
|
||||
restartPolicy: Always
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: adventure-db-svc
|
||||
name: backend
|
||||
spec:
|
||||
selector:
|
||||
app: adventure
|
||||
app: adventurelog
|
||||
ports:
|
||||
- name: db
|
||||
protocol: TCP
|
||||
port: 5432
|
||||
targetPort: 5432
|
||||
- name: http
|
||||
port: 80
|
||||
targetPort: 80
|
||||
- name: backend
|
||||
port: 8000
|
||||
targetPort: 8000
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: server
|
||||
name: frontend
|
||||
spec:
|
||||
selector:
|
||||
app: adventure
|
||||
app: adventurelog
|
||||
ports:
|
||||
- name: http
|
||||
protocol: TCP
|
||||
port: 80
|
||||
targetPort: 80
|
||||
- name: base
|
||||
protocol: TCP
|
||||
port: 8000
|
||||
targetPort: 8000
|
||||
port: 3000
|
||||
targetPort: 3000
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: db
|
||||
spec:
|
||||
selector:
|
||||
app: adventurelog
|
||||
ports:
|
||||
- name: postgres
|
||||
port: 5432
|
||||
targetPort: 5432
|
||||
---
|
||||
# If you aren't automatically provisioning PVCs (i.e. with Longhorn, you'll need to also create the PV's)
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: adventure-journal-pvc
|
||||
name: media-pvc
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
|
@ -144,10 +172,19 @@ spec:
|
|||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: adventure-journal-db-pvc
|
||||
name: data-pvc
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: adventurelog-secret
|
||||
type: Opaque
|
||||
stringData:
|
||||
adventure-postgres-password: changeme123
|
||||
secret-key: changeme123
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue