Compare commits
12 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7963928d1 | ||
|
|
861e5b00c4 | ||
|
|
a847c7bf5c | ||
|
|
f7d1a2cce0 | ||
|
|
9901887233 | ||
|
|
62ac2e4dc6 | ||
|
|
d8f26497c2 | ||
|
|
bd10a3f377 | ||
|
|
7659c15787 | ||
|
|
7c7edae110 | ||
|
|
be8a20786f | ||
|
|
10b3b69572 |
19 changed files with 732 additions and 76 deletions
13
.coderabbit.yaml
Normal file
13
.coderabbit.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
# CodeRabbit Configuration
|
||||||
|
# Inherits from: https://github.com/host-uk/coderabbit/.coderabbit.yaml
|
||||||
|
|
||||||
|
reviews:
|
||||||
|
review_status: false
|
||||||
|
|
||||||
|
path_instructions:
|
||||||
|
- path: "**/Dockerfile*"
|
||||||
|
instructions: "Check for security best practices, multi-stage builds, and pinned versions"
|
||||||
|
- path: "**/*.yml"
|
||||||
|
instructions: "Ansible/Docker Compose - validate syntax and idempotency"
|
||||||
|
- path: "**/*.sh"
|
||||||
|
instructions: "Shell scripts - check for shellcheck compliance and proper error handling"
|
||||||
143
.github/workflows/build.yml
vendored
143
.github/workflows/build.yml
vendored
|
|
@ -1,15 +1,24 @@
|
||||||
|
# Host UK Container Images
|
||||||
|
#
|
||||||
|
# Dev branch: ghcr.io/host-uk/{core-dev,server-php}:dev
|
||||||
|
# Tags/Main: ghcr.io/host-uk/{core-dev,server-php}:latest + lthn/{core-dev,server-php}:latest
|
||||||
|
|
||||||
name: Build Images
|
name: Build Images
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [dev, main]
|
||||||
tags: ['v*']
|
tags: ['v*']
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [main]
|
branches: [dev, main]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
env:
|
env:
|
||||||
REGISTRY: ghcr.io
|
GHCR_REGISTRY: ghcr.io
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|
@ -23,10 +32,18 @@ jobs:
|
||||||
packages: write
|
packages: write
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
image:
|
image:
|
||||||
- developer
|
- developer
|
||||||
- server-php
|
- server-php
|
||||||
|
include:
|
||||||
|
- image: developer
|
||||||
|
ghcr_name: core-dev
|
||||||
|
dockerhub_name: core-dev
|
||||||
|
- image: server-php
|
||||||
|
ghcr_name: server-php
|
||||||
|
dockerhub_name: server-php
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
|
|
@ -42,21 +59,57 @@ jobs:
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.GHCR_REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Extract metadata
|
- name: Login to Docker Hub
|
||||||
id: meta
|
if: github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Determine if release build
|
||||||
|
id: release
|
||||||
|
run: |
|
||||||
|
if [[ "${{ github.ref }}" == "refs/heads/main" ]] || [[ "${{ github.ref }}" == refs/tags/v* ]]; then
|
||||||
|
echo "is_release=true" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "is_release=false" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Extract metadata (GHCR only - dev builds)
|
||||||
|
if: steps.release.outputs.is_release == 'false'
|
||||||
|
id: meta-dev
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
images: ${{ env.REGISTRY }}/host-uk/${{ matrix.image == 'developer' && 'core-dev' || matrix.image }}
|
images: |
|
||||||
|
${{ env.GHCR_REGISTRY }}/host-uk/${{ matrix.ghcr_name }}
|
||||||
tags: |
|
tags: |
|
||||||
type=ref,event=branch
|
type=ref,event=branch
|
||||||
type=ref,event=pr
|
type=ref,event=pr
|
||||||
|
flavor: |
|
||||||
|
latest=false
|
||||||
|
|
||||||
|
- name: Extract metadata (GHCR + Docker Hub - release builds)
|
||||||
|
if: steps.release.outputs.is_release == 'true'
|
||||||
|
id: meta-release
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: |
|
||||||
|
${{ env.GHCR_REGISTRY }}/host-uk/${{ matrix.ghcr_name }}
|
||||||
|
lthn/${{ matrix.dockerhub_name }}
|
||||||
|
tags: |
|
||||||
|
# main branch -> latest
|
||||||
|
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
|
||||||
|
# Semver tags (v1.0.0 -> 1.0.0, 1.0, 1, latest)
|
||||||
type=semver,pattern={{version}}
|
type=semver,pattern={{version}}
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
type=raw,value=latest,enable={{is_default_branch}}
|
type=semver,pattern={{major}},enable=${{ !startsWith(github.ref, 'refs/tags/v0.') }}
|
||||||
|
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||||
|
flavor: |
|
||||||
|
latest=false
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
|
|
@ -64,62 +117,64 @@ jobs:
|
||||||
context: ./${{ matrix.image }}
|
context: ./${{ matrix.image }}
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta-dev.outputs.tags || steps.meta-release.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta-dev.outputs.labels || steps.meta-release.outputs.labels }}
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# Build LinuxKit Images
|
# Build LinuxKit Images (only on tags)
|
||||||
# ============================================================
|
# ============================================================
|
||||||
linuxkit:
|
linuxkit:
|
||||||
name: LinuxKit (${{ matrix.image }}-${{ matrix.arch }})
|
name: LinuxKit (${{ matrix.image }}-${{ matrix.arch }})
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: docker # Needs Docker images to be built first
|
needs: docker
|
||||||
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
image: [developer, server-php]
|
image: [developer, server-php]
|
||||||
arch: [amd64, arm64]
|
arch: [amd64, arm64]
|
||||||
format: [qcow2-bios, iso-bios]
|
format: [qcow2-bios, iso-bios]
|
||||||
|
include:
|
||||||
|
- image: developer
|
||||||
|
output_name: core-dev
|
||||||
|
- image: server-php
|
||||||
|
output_name: server-php
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install Core CLI
|
- name: Install LinuxKit
|
||||||
run: |
|
run: |
|
||||||
# Download latest core binary
|
curl -fsSL "https://github.com/linuxkit/linuxkit/releases/download/v1.5.3/linuxkit-linux-amd64" -o linuxkit
|
||||||
curl -fsSL "https://github.com/host-uk/core/releases/latest/download/core-linux-amd64.tar.gz" -o core.tar.gz
|
chmod +x linuxkit
|
||||||
tar -xzf core.tar.gz
|
sudo mv linuxkit /usr/local/bin/
|
||||||
sudo mv core /usr/local/bin/core
|
|
||||||
chmod +x /usr/local/bin/core
|
|
||||||
core --version
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
- name: Login to GHCR
|
- name: Login to GHCR
|
||||||
if: github.event_name != 'pull_request'
|
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.GHCR_REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build LinuxKit Image
|
- name: Build LinuxKit Image
|
||||||
run: |
|
run: |
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
core build --type linuxkit \
|
linuxkit build \
|
||||||
--config ./${{ matrix.image }}/linuxkit.yml \
|
|
||||||
--format ${{ matrix.format }} \
|
--format ${{ matrix.format }} \
|
||||||
--arch ${{ matrix.arch }} \
|
--name ./dist/${{ matrix.output_name }}-${{ matrix.arch }} \
|
||||||
-o ./dist/${{ matrix.image == 'developer' && 'core-dev' || matrix.image }}-${{ matrix.arch }}
|
./${{ matrix.image }}/linuxkit.yml
|
||||||
|
|
||||||
- name: Upload Artifact
|
- name: Upload Artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.image == 'developer' && 'core-dev' || matrix.image }}-${{ matrix.arch }}-${{ matrix.format }}
|
name: ${{ matrix.output_name }}-${{ matrix.arch }}-${{ matrix.format }}
|
||||||
path: ./dist/*
|
path: ./dist/*
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|
@ -152,39 +207,3 @@ jobs:
|
||||||
dist/*
|
dist/*
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# Build TIM Bundles (when core build --type tim is ready)
|
|
||||||
# ============================================================
|
|
||||||
# tim:
|
|
||||||
# name: TIM (${{ matrix.image }})
|
|
||||||
# runs-on: ubuntu-latest
|
|
||||||
# needs: docker
|
|
||||||
#
|
|
||||||
# strategy:
|
|
||||||
# matrix:
|
|
||||||
# image: [developer, server-php]
|
|
||||||
# os: [linux, darwin]
|
|
||||||
# arch: [amd64, arm64]
|
|
||||||
#
|
|
||||||
# steps:
|
|
||||||
# - uses: actions/checkout@v4
|
|
||||||
#
|
|
||||||
# - name: Install Core
|
|
||||||
# run: |
|
|
||||||
# curl -fsSL https://github.com/host-uk/core/releases/latest/download/core-linux-amd64 -o /usr/local/bin/core
|
|
||||||
# chmod +x /usr/local/bin/core
|
|
||||||
#
|
|
||||||
# - name: Build TIM
|
|
||||||
# run: |
|
|
||||||
# core build --type tim \
|
|
||||||
# --borgfile ./${{ matrix.image }}/Borgfile \
|
|
||||||
# --os ${{ matrix.os }} \
|
|
||||||
# --arch ${{ matrix.arch }} \
|
|
||||||
# -o ./dist/${{ matrix.image }}-${{ matrix.os }}-${{ matrix.arch }}.tim
|
|
||||||
#
|
|
||||||
# - name: Upload artifact
|
|
||||||
# uses: actions/upload-artifact@v4
|
|
||||||
# with:
|
|
||||||
# name: ${{ matrix.image }}-${{ matrix.os }}-${{ matrix.arch }}
|
|
||||||
# path: ./dist/*.tim
|
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,9 @@ ENV LC_ALL=C.UTF-8
|
||||||
ENV TERM=xterm-256color
|
ENV TERM=xterm-256color
|
||||||
ENV EDITOR=nvim
|
ENV EDITOR=nvim
|
||||||
ENV SHELL=/bin/zsh
|
ENV SHELL=/bin/zsh
|
||||||
|
ENV CODEX_HOME=/root/.codex
|
||||||
|
ENV CORE_DEV_HOST_HOME=/host-home
|
||||||
|
ENV CODEX_AUTH_SYNC=1
|
||||||
ENV GOPATH=/root/go
|
ENV GOPATH=/root/go
|
||||||
ENV CARGO_HOME=/root/.cargo
|
ENV CARGO_HOME=/root/.cargo
|
||||||
ENV RUSTUP_HOME=/root/.rustup
|
ENV RUSTUP_HOME=/root/.rustup
|
||||||
|
|
@ -65,8 +68,9 @@ RUN apk add --no-cache \
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# VCS & Git Tools
|
# VCS & Git Tools
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
# Note: git-delta not in Alpine repos, install via cargo if needed
|
||||||
RUN apk add --no-cache \
|
RUN apk add --no-cache \
|
||||||
git git-lfs github-cli lazygit git-delta
|
git git-lfs github-cli lazygit
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# Node.js Ecosystem
|
# Node.js Ecosystem
|
||||||
|
|
@ -123,13 +127,13 @@ RUN ln -sf /usr/bin/php84 /usr/bin/php
|
||||||
RUN curl -sS https://getcomposer.org/installer | php -- \
|
RUN curl -sS https://getcomposer.org/installer | php -- \
|
||||||
--install-dir=/usr/bin --filename=composer
|
--install-dir=/usr/bin --filename=composer
|
||||||
|
|
||||||
# PHP tools via Composer
|
# Allow Pest plugin and install PHP tools globally
|
||||||
RUN composer global require --no-interaction \
|
RUN composer global config allow-plugins.pestphp/pest-plugin true && \
|
||||||
|
composer global require --no-interaction \
|
||||||
phpunit/phpunit:^11.0 \
|
phpunit/phpunit:^11.0 \
|
||||||
pestphp/pest:^3.0 \
|
pestphp/pest:^3.0 \
|
||||||
phpstan/phpstan:^2.0 \
|
phpstan/phpstan:^2.0 \
|
||||||
laravel/pint:^1.0 \
|
laravel/pint:^1.0
|
||||||
phpdocumentor/phpdocumentor:^3.0
|
|
||||||
|
|
||||||
# FrankenPHP (static binary)
|
# FrankenPHP (static binary)
|
||||||
RUN curl -fsSL "https://github.com/dunglas/frankenphp/releases/latest/download/frankenphp-linux-$(uname -m | sed 's/aarch64/arm64/' | sed 's/x86_64/x86_64/')" -o /usr/local/bin/frankenphp && \
|
RUN curl -fsSL "https://github.com/dunglas/frankenphp/releases/latest/download/frankenphp-linux-$(uname -m | sed 's/aarch64/arm64/' | sed 's/x86_64/x86_64/')" -o /usr/local/bin/frankenphp && \
|
||||||
|
|
@ -140,12 +144,13 @@ RUN curl -fsSL "https://github.com/dunglas/frankenphp/releases/latest/download/f
|
||||||
# ============================================================
|
# ============================================================
|
||||||
RUN apk add --no-cache go
|
RUN apk add --no-cache go
|
||||||
|
|
||||||
RUN go install golang.org/x/tools/gopls@latest && \
|
# Note: Some tools require newer Go versions, so we pin compatible versions
|
||||||
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest && \
|
# or make installations optional with || true
|
||||||
go install github.com/go-task/task/v3/cmd/task@latest && \
|
RUN go install golang.org/x/tools/gopls@v0.17.1 || true && \
|
||||||
go install github.com/casey/just@latest && \
|
go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.2 && \
|
||||||
go install github.com/watchexec/watchexec/cmd/watchexec@latest 2>/dev/null || true && \
|
go install github.com/go-task/task/v3/cmd/task@v3.40.1 && \
|
||||||
go install github.com/boyter/scc/v3@latest
|
go install github.com/casey/just@latest || true && \
|
||||||
|
go install github.com/boyter/scc/v3@v3.4.0 || true
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# Rust Ecosystem
|
# Rust Ecosystem
|
||||||
|
|
@ -281,7 +286,7 @@ COPY --chmod=644 config/aliases.sh /etc/profile.d/aliases.sh
|
||||||
COPY --chmod=755 scripts/entrypoint.sh /usr/local/bin/entrypoint.sh
|
COPY --chmod=755 scripts/entrypoint.sh /usr/local/bin/entrypoint.sh
|
||||||
|
|
||||||
# Create directories
|
# Create directories
|
||||||
RUN mkdir -p /root/.config /root/.claude /workspace
|
RUN mkdir -p /root/.config /root/.claude /root/.codex /workspace
|
||||||
|
|
||||||
WORKDIR /workspace
|
WORKDIR /workspace
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,28 @@
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
sync_dir() {
|
||||||
|
src="$1"
|
||||||
|
dst="$2"
|
||||||
|
|
||||||
|
if [ ! -d "$src" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$dst"
|
||||||
|
# Copy host state into container state each boot so auth/extensions are portable.
|
||||||
|
cp -a "$src"/. "$dst"/
|
||||||
|
}
|
||||||
|
|
||||||
|
# Sync Codex auth/state from host profile if mounted.
|
||||||
|
# Example: -v "$HOME:/host-home:ro"
|
||||||
|
if [ "${CODEX_AUTH_SYNC:-1}" = "1" ]; then
|
||||||
|
HOST_HOME="${CORE_DEV_HOST_HOME:-/host-home}"
|
||||||
|
HOST_CODEX_HOME="${HOST_HOME}/.codex"
|
||||||
|
CONTAINER_CODEX_HOME="${CODEX_HOME:-${HOME}/.codex}"
|
||||||
|
sync_dir "$HOST_CODEX_HOME" "$CONTAINER_CODEX_HOME"
|
||||||
|
fi
|
||||||
|
|
||||||
# Run pre-start hooks if they exist
|
# Run pre-start hooks if they exist
|
||||||
if [ -d "/root/.config/core-dev/hooks/pre-start" ]; then
|
if [ -d "/root/.config/core-dev/hooks/pre-start" ]; then
|
||||||
for hook in /root/.config/core-dev/hooks/pre-start/*; do
|
for hook in /root/.config/core-dev/hooks/pre-start/*; do
|
||||||
|
|
@ -26,8 +48,8 @@ fi
|
||||||
# Setup SSH agent if keys exist
|
# Setup SSH agent if keys exist
|
||||||
if [ -d "$HOME/.ssh" ] && [ -z "$SSH_AUTH_SOCK" ]; then
|
if [ -d "$HOME/.ssh" ] && [ -z "$SSH_AUTH_SOCK" ]; then
|
||||||
eval "$(ssh-agent -s)" > /dev/null 2>&1
|
eval "$(ssh-agent -s)" > /dev/null 2>&1
|
||||||
for key in $HOME/.ssh/id_* ; do
|
for key in "$HOME"/.ssh/id_*; do
|
||||||
[ -f "$key" ] && [ ! -f "$key.pub" ] || ssh-add "$key" 2>/dev/null || true
|
[ -f "$key" ] && [ ! -f "$key.pub" ] && ssh-add "$key" 2>/dev/null || true
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
||||||
182
server-php/config/conf.d/unified.conf
Normal file
182
server-php/config/conf.d/unified.conf
Normal file
|
|
@ -0,0 +1,182 @@
|
||||||
|
# Unified nginx configuration
|
||||||
|
# Routes traffic based on domain:
|
||||||
|
# - host.uk.com → Laravel Host Hub (PHP-FPM)
|
||||||
|
# - *.host.uk.com → WordPress (PHP-FPM)
|
||||||
|
|
||||||
|
# Map for allowed CORS origins (WordPress REST API)
|
||||||
|
map $http_origin $cors_origin {
|
||||||
|
default "";
|
||||||
|
"~^https?://host\.uk\.com$" $http_origin;
|
||||||
|
"~^https?://social\.host\.uk\.com$" $http_origin;
|
||||||
|
"~^https?://link\.host\.uk\.com$" $http_origin;
|
||||||
|
"~^https?://analytics\.host\.uk\.com$" $http_origin;
|
||||||
|
"~^https?://trust\.host\.uk\.com$" $http_origin;
|
||||||
|
"~^https?://notify\.host\.uk\.com$" $http_origin;
|
||||||
|
"~^https?://localhost(:[0-9]+)?$" $http_origin;
|
||||||
|
"~^https?://127\.0\.0\.1(:[0-9]+)?$" $http_origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# LARAVEL HOST HUB - Apex Domain
|
||||||
|
# ============================================
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
listen [::]:80;
|
||||||
|
server_name host.uk.com www.host.uk.com;
|
||||||
|
|
||||||
|
root /app/public;
|
||||||
|
index index.php;
|
||||||
|
|
||||||
|
client_max_body_size 64M;
|
||||||
|
|
||||||
|
# Security headers
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
|
||||||
|
# Health check - returns 200 if nginx is up (no PHP needed)
|
||||||
|
location = /healthz {
|
||||||
|
access_log off;
|
||||||
|
add_header Content-Type text/plain;
|
||||||
|
return 200 "ok\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
# WordPress REST API proxy
|
||||||
|
# host.uk.com/api/wordpress/* → WordPress /wp-json/*
|
||||||
|
# Same-origin, no CORS needed
|
||||||
|
location ~ ^/api/wordpress/(.*)$ {
|
||||||
|
# Pass to WordPress index.php with rest_route
|
||||||
|
fastcgi_pass unix:/run/php-fpm.sock;
|
||||||
|
fastcgi_param SCRIPT_FILENAME /var/www/html/index.php;
|
||||||
|
fastcgi_param REQUEST_URI /wp-json/$1$is_args$args;
|
||||||
|
fastcgi_param HTTP_X_FORWARDED_PROTO $http_x_forwarded_proto;
|
||||||
|
fastcgi_index index.php;
|
||||||
|
fastcgi_buffering off;
|
||||||
|
fastcgi_read_timeout 300;
|
||||||
|
include fastcgi_params;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Laravel routing
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.php?$query_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
# PHP-FPM for Laravel
|
||||||
|
location ~ \.php$ {
|
||||||
|
try_files $uri =404;
|
||||||
|
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||||
|
fastcgi_pass unix:/run/php-fpm.sock;
|
||||||
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||||
|
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||||
|
fastcgi_param HTTP_X_FORWARDED_PROTO $http_x_forwarded_proto;
|
||||||
|
fastcgi_index index.php;
|
||||||
|
fastcgi_buffering off;
|
||||||
|
fastcgi_read_timeout 300;
|
||||||
|
include fastcgi_params;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Livewire and Flux - must go to Laravel (not static files)
|
||||||
|
location ~ ^/(admin|flux)/ {
|
||||||
|
try_files $uri $uri/ /index.php?$query_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Laravel static assets (build, vendor directories only)
|
||||||
|
location ~* ^/(build|vendor)/.*\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||||
|
expires max;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
log_not_found off;
|
||||||
|
access_log off;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Deny hidden files
|
||||||
|
location ~ /\. {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
|
||||||
|
# PHP-FPM status (internal only)
|
||||||
|
location ~ ^/(fpm-status|fpm-ping)$ {
|
||||||
|
access_log off;
|
||||||
|
allow 127.0.0.1;
|
||||||
|
deny all;
|
||||||
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_pass unix:/run/php-fpm.sock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# LARAVEL SATELLITES - Subdomains
|
||||||
|
# ============================================
|
||||||
|
server {
|
||||||
|
listen 80 default_server;
|
||||||
|
listen [::]:80 default_server;
|
||||||
|
server_name *.host.uk.com;
|
||||||
|
|
||||||
|
root /app/public;
|
||||||
|
index index.php;
|
||||||
|
|
||||||
|
client_max_body_size 64M;
|
||||||
|
|
||||||
|
# Security headers
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
location = /healthz {
|
||||||
|
access_log off;
|
||||||
|
add_header Content-Type text/plain;
|
||||||
|
return 200 "ok\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
# WordPress REST API (for internal content sync)
|
||||||
|
# Routes /wp-json/* requests to WordPress
|
||||||
|
# Host header determines which multisite blog to serve
|
||||||
|
location ~ ^/wp-json/(.*)$ {
|
||||||
|
fastcgi_pass unix:/run/php-fpm.sock;
|
||||||
|
fastcgi_param SCRIPT_FILENAME /var/www/html/index.php;
|
||||||
|
fastcgi_param REQUEST_URI /wp-json/$1$is_args$args;
|
||||||
|
fastcgi_param HTTP_X_FORWARDED_PROTO $http_x_forwarded_proto;
|
||||||
|
fastcgi_index index.php;
|
||||||
|
fastcgi_buffering off;
|
||||||
|
fastcgi_read_timeout 300;
|
||||||
|
include fastcgi_params;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Laravel routing
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.php?$query_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
# PHP-FPM for Laravel
|
||||||
|
location ~ \.php$ {
|
||||||
|
try_files $uri =404;
|
||||||
|
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||||
|
fastcgi_pass unix:/run/php-fpm.sock;
|
||||||
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||||
|
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||||
|
fastcgi_param HTTP_X_FORWARDED_PROTO $http_x_forwarded_proto;
|
||||||
|
fastcgi_index index.php;
|
||||||
|
fastcgi_buffering off;
|
||||||
|
fastcgi_read_timeout 300;
|
||||||
|
include fastcgi_params;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Livewire and Flux
|
||||||
|
location ~ ^/(admin|flux)/ {
|
||||||
|
try_files $uri $uri/ /index.php?$query_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Static assets
|
||||||
|
location ~* ^/(build|vendor)/.*\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||||
|
expires max;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
log_not_found off;
|
||||||
|
access_log off;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Deny hidden files
|
||||||
|
location ~ /\. {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}
|
||||||
155
server-php/config/conf.d/wordpress.conf
Normal file
155
server-php/config/conf.d/wordpress.conf
Normal file
|
|
@ -0,0 +1,155 @@
|
||||||
|
# WordPress Multisite server configuration
|
||||||
|
# Map for allowed CORS origins
|
||||||
|
map $http_origin $cors_origin {
|
||||||
|
default "";
|
||||||
|
"~^https?://host\.uk\.com$" $http_origin;
|
||||||
|
"~^https?://social\.host\.uk\.com$" $http_origin;
|
||||||
|
"~^https?://link\.host\.uk\.com$" $http_origin;
|
||||||
|
"~^https?://analytics\.host\.uk\.com$" $http_origin;
|
||||||
|
"~^https?://trust\.host\.uk\.com$" $http_origin;
|
||||||
|
"~^https?://notify\.host\.uk\.com$" $http_origin;
|
||||||
|
"~^https?://localhost(:[0-9]+)?$" $http_origin;
|
||||||
|
"~^https?://127\.0\.0\.1(:[0-9]+)?$" $http_origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen [::]:80 default_server;
|
||||||
|
listen 80 default_server;
|
||||||
|
|
||||||
|
# Only accept subdomain traffic (*.host.uk.com), not apex domain
|
||||||
|
# The apex domain (host.uk.com) should route to Host Hub (Laravel)
|
||||||
|
server_name ~^(?<subdomain>.+)\.host\.uk\.com$ hestia.host.uk.com *.host.uk.com;
|
||||||
|
|
||||||
|
# Serve error page for apex domain - this shouldn't hit WordPress
|
||||||
|
# If it does, Coolify routing is misconfigured
|
||||||
|
error_page 503 /wp-content/routing-error.html;
|
||||||
|
if ($host = "host.uk.com") {
|
||||||
|
return 503;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Reject completely unknown hosts with connection close
|
||||||
|
if ($host !~ "\.host\.uk\.com$") {
|
||||||
|
return 444;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendfile off;
|
||||||
|
tcp_nodelay on;
|
||||||
|
absolute_redirect off;
|
||||||
|
|
||||||
|
root /var/www/html;
|
||||||
|
index index.php;
|
||||||
|
|
||||||
|
client_max_body_size 64M;
|
||||||
|
|
||||||
|
# Security headers
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
|
||||||
|
# WordPress multisite rewrite rules
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.php?$args;
|
||||||
|
}
|
||||||
|
|
||||||
|
# REST API with CORS headers for headless operation
|
||||||
|
location /wp-json/ {
|
||||||
|
# Handle preflight OPTIONS requests
|
||||||
|
if ($request_method = 'OPTIONS') {
|
||||||
|
add_header 'Access-Control-Allow-Origin' $cors_origin always;
|
||||||
|
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE, OPTIONS' always;
|
||||||
|
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-WP-Nonce' always;
|
||||||
|
add_header 'Access-Control-Allow-Credentials' 'true' always;
|
||||||
|
add_header 'Access-Control-Max-Age' 86400 always;
|
||||||
|
add_header 'Content-Length' 0;
|
||||||
|
add_header 'Content-Type' 'text/plain';
|
||||||
|
return 204;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add CORS headers to actual requests
|
||||||
|
add_header 'Access-Control-Allow-Origin' $cors_origin always;
|
||||||
|
add_header 'Access-Control-Allow-Credentials' 'true' always;
|
||||||
|
add_header 'Access-Control-Expose-Headers' 'X-WP-Total, X-WP-TotalPages, Link' always;
|
||||||
|
|
||||||
|
try_files $uri $uri/ /index.php?$args;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Pass the PHP scripts to PHP-FPM listening on unix socket
|
||||||
|
location ~ \.php$ {
|
||||||
|
try_files $uri =404;
|
||||||
|
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||||
|
fastcgi_pass unix:/run/php-fpm.sock;
|
||||||
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||||
|
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||||
|
fastcgi_param HTTP_X_FORWARDED_PROTO $http_x_forwarded_proto;
|
||||||
|
fastcgi_index index.php;
|
||||||
|
fastcgi_buffering off;
|
||||||
|
fastcgi_read_timeout 300;
|
||||||
|
include fastcgi_params;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Cache static assets
|
||||||
|
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||||
|
expires max;
|
||||||
|
log_not_found off;
|
||||||
|
access_log off;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /favicon.ico {
|
||||||
|
log_not_found off;
|
||||||
|
access_log off;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /robots.txt {
|
||||||
|
allow all;
|
||||||
|
log_not_found off;
|
||||||
|
access_log off;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Block XML-RPC by default, allow with secret token
|
||||||
|
# Usage: /xmlrpc.php?token=YOUR_XMLRPC_TOKEN
|
||||||
|
location = /xmlrpc.php {
|
||||||
|
set $xmlrpc_allowed 0;
|
||||||
|
|
||||||
|
# Allow if valid token provided (set in environment or change here)
|
||||||
|
if ($arg_token = "xrpc-9f8e7d6c5b4a") {
|
||||||
|
set $xmlrpc_allowed 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Block if no valid token
|
||||||
|
if ($xmlrpc_allowed = 0) {
|
||||||
|
return 403;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Pass to PHP if allowed
|
||||||
|
try_files $uri =404;
|
||||||
|
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||||
|
fastcgi_pass unix:/run/php-fpm.sock;
|
||||||
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||||
|
fastcgi_index index.php;
|
||||||
|
include fastcgi_params;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Deny access to hidden files
|
||||||
|
location ~ /\. {
|
||||||
|
deny all;
|
||||||
|
access_log off;
|
||||||
|
log_not_found off;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Deny access to backup files
|
||||||
|
location ~ ~$ {
|
||||||
|
access_log off;
|
||||||
|
log_not_found off;
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Allow fpm ping and status from localhost
|
||||||
|
location ~ ^/(fpm-status|fpm-ping)$ {
|
||||||
|
access_log off;
|
||||||
|
allow 127.0.0.1;
|
||||||
|
deny all;
|
||||||
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_pass unix:/run/php-fpm.sock;
|
||||||
|
}
|
||||||
|
}
|
||||||
43
server-php/config/fpm-pool.conf.template
Normal file
43
server-php/config/fpm-pool.conf.template
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
[global]
|
||||||
|
; Log to stderr
|
||||||
|
error_log = /dev/stderr
|
||||||
|
|
||||||
|
[www]
|
||||||
|
; User and group for PHP-FPM processes
|
||||||
|
user = nobody
|
||||||
|
group = nobody
|
||||||
|
|
||||||
|
; The address on which to accept FastCGI requests.
|
||||||
|
listen = /run/php-fpm.sock
|
||||||
|
|
||||||
|
; Set permissions for unix socket
|
||||||
|
listen.owner = nobody
|
||||||
|
listen.group = nobody
|
||||||
|
listen.mode = 0666
|
||||||
|
|
||||||
|
; Enable status page
|
||||||
|
pm.status_path = /fpm-status
|
||||||
|
|
||||||
|
; Ondemand process manager
|
||||||
|
pm = ondemand
|
||||||
|
|
||||||
|
; The maximum number of child processes
|
||||||
|
pm.max_children = 100
|
||||||
|
|
||||||
|
; The number of seconds after which an idle process will be killed.
|
||||||
|
pm.process_idle_timeout = 10s
|
||||||
|
|
||||||
|
; The number of requests each child process should execute before respawning.
|
||||||
|
pm.max_requests = 1000
|
||||||
|
|
||||||
|
; Make sure the FPM workers can reach the environment variables for configuration
|
||||||
|
clear_env = no
|
||||||
|
|
||||||
|
; Catch output from PHP
|
||||||
|
catch_workers_output = yes
|
||||||
|
|
||||||
|
; Remove the 'child 10 said into stderr' prefix in the log and only show the actual message
|
||||||
|
decorate_workers_output = no
|
||||||
|
|
||||||
|
; Enable ping page to use in healthcheck
|
||||||
|
ping.path = /fpm-ping
|
||||||
22
server-php/config/nginx-performance.conf
Normal file
22
server-php/config/nginx-performance.conf
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
# Production performance optimizations
|
||||||
|
gzip on;
|
||||||
|
gzip_vary on;
|
||||||
|
gzip_proxied any;
|
||||||
|
gzip_comp_level 6;
|
||||||
|
gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml;
|
||||||
|
|
||||||
|
# Brotli (if available)
|
||||||
|
brotli on;
|
||||||
|
brotli_comp_level 6;
|
||||||
|
brotli_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml;
|
||||||
|
|
||||||
|
# Cache static files
|
||||||
|
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff|woff2|ttf|svg)$ {
|
||||||
|
expires 30d;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Security headers
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
63
server-php/config/nginx.conf
Normal file
63
server-php/config/nginx.conf
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
upstream php-fpm {
|
||||||
|
server host-uk-dev-wordpress:9000;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
root /var/www/html;
|
||||||
|
index index.php;
|
||||||
|
|
||||||
|
client_max_body_size 64M;
|
||||||
|
|
||||||
|
# Security headers
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.php?$args;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ \.php$ {
|
||||||
|
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||||
|
fastcgi_pass php-fpm;
|
||||||
|
fastcgi_index index.php;
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||||
|
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||||
|
fastcgi_param HTTP_X_FORWARDED_PROTO $http_x_forwarded_proto;
|
||||||
|
fastcgi_buffering off;
|
||||||
|
fastcgi_read_timeout 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||||
|
expires max;
|
||||||
|
log_not_found off;
|
||||||
|
access_log off;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /favicon.ico {
|
||||||
|
log_not_found off;
|
||||||
|
access_log off;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /robots.txt {
|
||||||
|
allow all;
|
||||||
|
log_not_found off;
|
||||||
|
access_log off;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ /\. {
|
||||||
|
deny all;
|
||||||
|
access_log off;
|
||||||
|
log_not_found off;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ ~$ {
|
||||||
|
access_log off;
|
||||||
|
log_not_found off;
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}
|
||||||
10
server-php/config/opcache-prod.ini
Normal file
10
server-php/config/opcache-prod.ini
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
[opcache]
|
||||||
|
opcache.enable=1
|
||||||
|
opcache.memory_consumption=256
|
||||||
|
opcache.interned_strings_buffer=32
|
||||||
|
opcache.max_accelerated_files=20000
|
||||||
|
opcache.validate_timestamps=0
|
||||||
|
opcache.save_comments=1
|
||||||
|
opcache.enable_cli=1
|
||||||
|
opcache.jit=1255
|
||||||
|
opcache.jit_buffer_size=128M
|
||||||
11
server-php/config/php-dev.ini
Normal file
11
server-php/config/php-dev.ini
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
; Development PHP settings
|
||||||
|
display_errors = On
|
||||||
|
display_startup_errors = On
|
||||||
|
error_reporting = E_ALL
|
||||||
|
log_errors = On
|
||||||
|
|
||||||
|
memory_limit = 512M
|
||||||
|
max_execution_time = 300
|
||||||
|
max_input_time = 300
|
||||||
|
post_max_size = 128M
|
||||||
|
upload_max_filesize = 128M
|
||||||
17
server-php/config/php-prod.ini
Normal file
17
server-php/config/php-prod.ini
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
; Production PHP settings
|
||||||
|
display_errors = Off
|
||||||
|
display_startup_errors = Off
|
||||||
|
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
|
||||||
|
log_errors = On
|
||||||
|
error_log = /var/log/php/error.log
|
||||||
|
|
||||||
|
memory_limit = 256M
|
||||||
|
max_execution_time = 60
|
||||||
|
max_input_time = 60
|
||||||
|
post_max_size = 64M
|
||||||
|
upload_max_filesize = 64M
|
||||||
|
|
||||||
|
expose_php = Off
|
||||||
|
session.cookie_httponly = 1
|
||||||
|
session.cookie_secure = 1
|
||||||
|
session.use_strict_mode = 1
|
||||||
9
server-php/config/php.ini.template
Normal file
9
server-php/config/php.ini.template
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
[Date]
|
||||||
|
date.timezone="UTC"
|
||||||
|
|
||||||
|
[PHP]
|
||||||
|
expose_php = Off
|
||||||
|
upload_max_filesize = 64M
|
||||||
|
post_max_size = 64M
|
||||||
|
memory_limit = 256M
|
||||||
|
max_execution_time = 300
|
||||||
24
server-php/config/supervisord.conf
Normal file
24
server-php/config/supervisord.conf
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[supervisord]
|
||||||
|
nodaemon=true
|
||||||
|
user=root
|
||||||
|
logfile=/dev/null
|
||||||
|
logfile_maxbytes=0
|
||||||
|
pidfile=/run/supervisord.pid
|
||||||
|
|
||||||
|
[program:php-fpm]
|
||||||
|
command=php-fpm84 -F
|
||||||
|
stdout_logfile=/dev/stdout
|
||||||
|
stdout_logfile_maxbytes=0
|
||||||
|
stderr_logfile=/dev/stderr
|
||||||
|
stderr_logfile_maxbytes=0
|
||||||
|
autorestart=false
|
||||||
|
startretries=0
|
||||||
|
|
||||||
|
[program:nginx]
|
||||||
|
command=nginx -g 'daemon off;'
|
||||||
|
stdout_logfile=/dev/stdout
|
||||||
|
stdout_logfile_maxbytes=0
|
||||||
|
stderr_logfile=/dev/stderr
|
||||||
|
stderr_logfile_maxbytes=0
|
||||||
|
autorestart=false
|
||||||
|
startretries=0
|
||||||
7
server-php/config/xdebug.ini
Normal file
7
server-php/config/xdebug.ini
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
[xdebug]
|
||||||
|
xdebug.mode = develop,debug
|
||||||
|
xdebug.start_with_request = trigger
|
||||||
|
xdebug.client_host = host.docker.internal
|
||||||
|
xdebug.client_port = 9003
|
||||||
|
xdebug.idekey = PHPSTORM
|
||||||
|
xdebug.log_level = 0
|
||||||
20
server-php/patch/README.md
Normal file
20
server-php/patch/README.md
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
# Patch Directory
|
||||||
|
|
||||||
|
This directory contains files that override vendor packages after composer install.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Place files here that should overwrite files in `vendor/` or elsewhere after dependencies are installed. The entire contents of this directory are copied over the application root.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
To patch a vendor file:
|
||||||
|
```
|
||||||
|
patch/vendor/some-package/src/File.php
|
||||||
|
```
|
||||||
|
|
||||||
|
This will overwrite `vendor/some-package/src/File.php` in the built image.
|
||||||
|
|
||||||
|
## Empty by Default
|
||||||
|
|
||||||
|
For the base image, this directory is empty. Applications using this image should mount or copy their own patches.
|
||||||
6
server-php/product/composer.json
Normal file
6
server-php/product/composer.json
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"name": "host-uk/server-php-placeholder",
|
||||||
|
"description": "Placeholder for base image",
|
||||||
|
"type": "project",
|
||||||
|
"require": {}
|
||||||
|
}
|
||||||
8
server-php/product/public/index.php
Normal file
8
server-php/product/public/index.php
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?php
|
||||||
|
// Placeholder for base image
|
||||||
|
// Applications should mount/copy their own code
|
||||||
|
echo json_encode([
|
||||||
|
'status' => 'ok',
|
||||||
|
'message' => 'Host UK Server PHP Base Image',
|
||||||
|
'note' => 'Mount your Laravel app to /var/www/html'
|
||||||
|
]);
|
||||||
20
server-php/scripts/entrypoint.sh
Normal file
20
server-php/scripts/entrypoint.sh
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Install PHP dependencies if vendor directory is empty
|
||||||
|
if [ ! -f "/app/vendor/autoload.php" ]; then
|
||||||
|
echo "Installing PHP dependencies..."
|
||||||
|
composer install --no-interaction --prefer-dist
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install Node dependencies if node_modules is empty
|
||||||
|
if [ ! -d "/app/node_modules" ] || [ -z "$(ls -A /app/node_modules 2>/dev/null)" ]; then
|
||||||
|
echo "Installing Node dependencies..."
|
||||||
|
npm install
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run database migrations (skip errors if DB not ready)
|
||||||
|
php artisan migrate --force 2>/dev/null || echo "Migrations skipped (DB may not be ready)"
|
||||||
|
|
||||||
|
# Execute the main command
|
||||||
|
exec "$@"
|
||||||
Loading…
Add table
Reference in a new issue