Upload 3 files
Browse files- Dockerfile +216 -0
- app_utils.py +136 -0
- docker-compose.yml +44 -0
Dockerfile
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Base image
|
| 2 |
+
FROM python:3.9-slim
|
| 3 |
+
|
| 4 |
+
# Install system dependencies, build tools, and libraries
|
| 5 |
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
| 6 |
+
ca-certificates \
|
| 7 |
+
wget \
|
| 8 |
+
tar \
|
| 9 |
+
xz-utils \
|
| 10 |
+
fonts-liberation \
|
| 11 |
+
fontconfig \
|
| 12 |
+
build-essential \
|
| 13 |
+
yasm \
|
| 14 |
+
cmake \
|
| 15 |
+
meson \
|
| 16 |
+
ninja-build \
|
| 17 |
+
nasm \
|
| 18 |
+
libssl-dev \
|
| 19 |
+
libvpx-dev \
|
| 20 |
+
libx264-dev \
|
| 21 |
+
libx265-dev \
|
| 22 |
+
libnuma-dev \
|
| 23 |
+
libmp3lame-dev \
|
| 24 |
+
libopus-dev \
|
| 25 |
+
libvorbis-dev \
|
| 26 |
+
libtheora-dev \
|
| 27 |
+
libspeex-dev \
|
| 28 |
+
libfreetype6-dev \
|
| 29 |
+
libfontconfig1-dev \
|
| 30 |
+
libgnutls28-dev \
|
| 31 |
+
libaom-dev \
|
| 32 |
+
libdav1d-dev \
|
| 33 |
+
librav1e-dev \
|
| 34 |
+
libsvtav1-dev \
|
| 35 |
+
libzimg-dev \
|
| 36 |
+
libwebp-dev \
|
| 37 |
+
git \
|
| 38 |
+
pkg-config \
|
| 39 |
+
autoconf \
|
| 40 |
+
automake \
|
| 41 |
+
libtool \
|
| 42 |
+
libfribidi-dev \
|
| 43 |
+
libharfbuzz-dev \
|
| 44 |
+
libnss3 \
|
| 45 |
+
libatk1.0-0 \
|
| 46 |
+
libatk-bridge2.0-0 \
|
| 47 |
+
libcups2 \
|
| 48 |
+
libxcomposite1 \
|
| 49 |
+
libxrandr2 \
|
| 50 |
+
libxdamage1 \
|
| 51 |
+
libgbm1 \
|
| 52 |
+
libasound2 \
|
| 53 |
+
libpangocairo-1.0-0 \
|
| 54 |
+
libpangoft2-1.0-0 \
|
| 55 |
+
libgtk-3-0 \
|
| 56 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 57 |
+
|
| 58 |
+
# Install SRT from source (latest version using cmake)
|
| 59 |
+
RUN git clone https://github.com/Haivision/srt.git && \
|
| 60 |
+
cd srt && \
|
| 61 |
+
mkdir build && cd build && \
|
| 62 |
+
cmake .. && \
|
| 63 |
+
make -j$(nproc) && \
|
| 64 |
+
make install && \
|
| 65 |
+
cd ../.. && rm -rf srt
|
| 66 |
+
|
| 67 |
+
# Install SVT-AV1 from source
|
| 68 |
+
RUN git clone https://gitlab.com/AOMediaCodec/SVT-AV1.git && \
|
| 69 |
+
cd SVT-AV1 && \
|
| 70 |
+
git checkout v0.9.0 && \
|
| 71 |
+
cd Build && \
|
| 72 |
+
cmake .. && \
|
| 73 |
+
make -j$(nproc) && \
|
| 74 |
+
make install && \
|
| 75 |
+
cd ../.. && rm -rf SVT-AV1
|
| 76 |
+
|
| 77 |
+
# Install libvmaf from source
|
| 78 |
+
RUN git clone https://github.com/Netflix/vmaf.git && \
|
| 79 |
+
cd vmaf/libvmaf && \
|
| 80 |
+
meson build --buildtype release && \
|
| 81 |
+
ninja -C build && \
|
| 82 |
+
ninja -C build install && \
|
| 83 |
+
cd ../.. && rm -rf vmaf && \
|
| 84 |
+
ldconfig # Update the dynamic linker cache
|
| 85 |
+
|
| 86 |
+
# Manually build and install fdk-aac (since it is not available via apt-get)
|
| 87 |
+
RUN git clone https://github.com/mstorsjo/fdk-aac && \
|
| 88 |
+
cd fdk-aac && \
|
| 89 |
+
autoreconf -fiv && \
|
| 90 |
+
./configure && \
|
| 91 |
+
make -j$(nproc) && \
|
| 92 |
+
make install && \
|
| 93 |
+
cd .. && rm -rf fdk-aac
|
| 94 |
+
|
| 95 |
+
# Install libunibreak (required for ASS_FEATURE_WRAP_UNICODE)
|
| 96 |
+
RUN git clone https://github.com/adah1972/libunibreak.git && \
|
| 97 |
+
cd libunibreak && \
|
| 98 |
+
./autogen.sh && \
|
| 99 |
+
./configure && \
|
| 100 |
+
make -j$(nproc) && \
|
| 101 |
+
make install && \
|
| 102 |
+
ldconfig && \
|
| 103 |
+
cd .. && rm -rf libunibreak
|
| 104 |
+
|
| 105 |
+
# Build and install libass with libunibreak support and ASS_FEATURE_WRAP_UNICODE enabled
|
| 106 |
+
RUN git clone https://github.com/libass/libass.git && \
|
| 107 |
+
cd libass && \
|
| 108 |
+
autoreconf -i && \
|
| 109 |
+
./configure --enable-libunibreak || { cat config.log; exit 1; } && \
|
| 110 |
+
mkdir -p /app && echo "Config log located at: /app/config.log" && cp config.log /app/config.log && \
|
| 111 |
+
make -j$(nproc) || { echo "Libass build failed"; exit 1; } && \
|
| 112 |
+
make install && \
|
| 113 |
+
ldconfig && \
|
| 114 |
+
cd .. && rm -rf libass
|
| 115 |
+
|
| 116 |
+
# Build and install FFmpeg with all required features
|
| 117 |
+
RUN git clone https://git.ffmpeg.org/ffmpeg.git ffmpeg && \
|
| 118 |
+
cd ffmpeg && \
|
| 119 |
+
git checkout n7.0.2 && \
|
| 120 |
+
PKG_CONFIG_PATH="/usr/lib/x86_64-linux-gnu/pkgconfig:/usr/local/lib/pkgconfig" \
|
| 121 |
+
CFLAGS="-I/usr/include/freetype2" \
|
| 122 |
+
LDFLAGS="-L/usr/lib/x86_64-linux-gnu" \
|
| 123 |
+
./configure --prefix=/usr/local \
|
| 124 |
+
--enable-gpl \
|
| 125 |
+
--enable-pthreads \
|
| 126 |
+
--enable-neon \
|
| 127 |
+
--enable-libaom \
|
| 128 |
+
--enable-libdav1d \
|
| 129 |
+
--enable-librav1e \
|
| 130 |
+
--enable-libsvtav1 \
|
| 131 |
+
--enable-libvmaf \
|
| 132 |
+
--enable-libzimg \
|
| 133 |
+
--enable-libx264 \
|
| 134 |
+
--enable-libx265 \
|
| 135 |
+
--enable-libvpx \
|
| 136 |
+
--enable-libwebp \
|
| 137 |
+
--enable-libmp3lame \
|
| 138 |
+
--enable-libopus \
|
| 139 |
+
--enable-libvorbis \
|
| 140 |
+
--enable-libtheora \
|
| 141 |
+
--enable-libspeex \
|
| 142 |
+
--enable-libass \
|
| 143 |
+
--enable-libfreetype \
|
| 144 |
+
--enable-libharfbuzz \
|
| 145 |
+
--enable-fontconfig \
|
| 146 |
+
--enable-libsrt \
|
| 147 |
+
--enable-filter=drawtext \
|
| 148 |
+
--extra-cflags="-I/usr/include/freetype2 -I/usr/include/libpng16 -I/usr/include" \
|
| 149 |
+
--extra-ldflags="-L/usr/lib/x86_64-linux-gnu -lfreetype -lfontconfig" \
|
| 150 |
+
--enable-gnutls \
|
| 151 |
+
&& make -j$(nproc) && \
|
| 152 |
+
make install && \
|
| 153 |
+
cd .. && rm -rf ffmpeg
|
| 154 |
+
|
| 155 |
+
# Add /usr/local/bin to PATH (if not already included)
|
| 156 |
+
ENV PATH="/usr/local/bin:${PATH}"
|
| 157 |
+
|
| 158 |
+
# Copy fonts into the custom fonts directory
|
| 159 |
+
COPY ./fonts /usr/share/fonts/custom
|
| 160 |
+
|
| 161 |
+
# Rebuild the font cache so that fontconfig can see the custom fonts
|
| 162 |
+
RUN fc-cache -f -v
|
| 163 |
+
|
| 164 |
+
# Set work directory
|
| 165 |
+
WORKDIR /app
|
| 166 |
+
|
| 167 |
+
# Set environment variable for Whisper cache
|
| 168 |
+
ENV WHISPER_CACHE_DIR="/app/whisper_cache"
|
| 169 |
+
|
| 170 |
+
# Create cache directory (no need for chown here yet)
|
| 171 |
+
RUN mkdir -p ${WHISPER_CACHE_DIR}
|
| 172 |
+
|
| 173 |
+
# Copy the requirements file first to optimize caching
|
| 174 |
+
COPY requirements.txt .
|
| 175 |
+
|
| 176 |
+
# Install Python dependencies, upgrade pip
|
| 177 |
+
RUN pip install --no-cache-dir --upgrade pip && \
|
| 178 |
+
pip install --no-cache-dir -r requirements.txt && \
|
| 179 |
+
pip install openai-whisper && \
|
| 180 |
+
pip install playwright && \
|
| 181 |
+
pip install jsonschema
|
| 182 |
+
|
| 183 |
+
# Create the appuser
|
| 184 |
+
RUN useradd -m appuser
|
| 185 |
+
|
| 186 |
+
# Give appuser ownership of the /app directory (including whisper_cache)
|
| 187 |
+
RUN chown appuser:appuser /app
|
| 188 |
+
|
| 189 |
+
# Important: Switch to the appuser before downloading the model
|
| 190 |
+
USER appuser
|
| 191 |
+
|
| 192 |
+
RUN python -c "import os; print(os.environ.get('WHISPER_CACHE_DIR')); import whisper; whisper.load_model('base')"
|
| 193 |
+
|
| 194 |
+
# Install Playwright Chromium browser as appuser
|
| 195 |
+
RUN playwright install chromium
|
| 196 |
+
|
| 197 |
+
# Copy the rest of the application code
|
| 198 |
+
COPY . .
|
| 199 |
+
|
| 200 |
+
# Expose the port the app runs on
|
| 201 |
+
EXPOSE 8080
|
| 202 |
+
|
| 203 |
+
# Set environment variables
|
| 204 |
+
ENV PYTHONUNBUFFERED=1
|
| 205 |
+
|
| 206 |
+
RUN echo '#!/bin/bash\n\
|
| 207 |
+
gunicorn --bind 0.0.0.0:8080 \
|
| 208 |
+
--workers ${GUNICORN_WORKERS:-2} \
|
| 209 |
+
--timeout ${GUNICORN_TIMEOUT:-300} \
|
| 210 |
+
--worker-class sync \
|
| 211 |
+
--keep-alive 80 \
|
| 212 |
+
app:app' > /app/run_gunicorn.sh && \
|
| 213 |
+
chmod +x /app/run_gunicorn.sh
|
| 214 |
+
|
| 215 |
+
# Run the shell script
|
| 216 |
+
CMD ["/app/run_gunicorn.sh"]
|
app_utils.py
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2025 Stephen G. Pope
|
| 2 |
+
#
|
| 3 |
+
# This program is free software; you can redistribute it and/or modify
|
| 4 |
+
# it under the terms of the GNU General Public License as published by
|
| 5 |
+
# the Free Software Foundation; either version 2 of the License, or
|
| 6 |
+
# (at your option) any later version.
|
| 7 |
+
#
|
| 8 |
+
# This program is distributed in the hope that it will be useful,
|
| 9 |
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 10 |
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
| 11 |
+
# GNU General Public License for more details.
|
| 12 |
+
#
|
| 13 |
+
# You should have received a copy of the GNU General Public License along
|
| 14 |
+
# with this program; if not, write to the Free Software Foundation, Inc.,
|
| 15 |
+
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
from flask import request, jsonify, current_app
|
| 20 |
+
from functools import wraps
|
| 21 |
+
import jsonschema
|
| 22 |
+
import os
|
| 23 |
+
import json
|
| 24 |
+
import time
|
| 25 |
+
from config import LOCAL_STORAGE_PATH
|
| 26 |
+
|
| 27 |
+
def validate_payload(schema):
|
| 28 |
+
def decorator(f):
|
| 29 |
+
@wraps(f)
|
| 30 |
+
def decorated_function(*args, **kwargs):
|
| 31 |
+
if not request.json:
|
| 32 |
+
return jsonify({"message": "Missing JSON in request"}), 400
|
| 33 |
+
try:
|
| 34 |
+
jsonschema.validate(instance=request.json, schema=schema)
|
| 35 |
+
except jsonschema.exceptions.ValidationError as validation_error:
|
| 36 |
+
return jsonify({"message": f"Invalid payload: {validation_error.message}"}), 400
|
| 37 |
+
|
| 38 |
+
return f(*args, **kwargs)
|
| 39 |
+
return decorated_function
|
| 40 |
+
return decorator
|
| 41 |
+
|
| 42 |
+
def log_job_status(job_id, data):
|
| 43 |
+
"""
|
| 44 |
+
Log job status to a file in the STORAGE_PATH/jobs folder
|
| 45 |
+
|
| 46 |
+
Args:
|
| 47 |
+
job_id (str): The unique job ID
|
| 48 |
+
data (dict): Data to write to the log file
|
| 49 |
+
"""
|
| 50 |
+
jobs_dir = os.path.join(LOCAL_STORAGE_PATH, 'jobs')
|
| 51 |
+
|
| 52 |
+
# Create jobs directory if it doesn't exist
|
| 53 |
+
if not os.path.exists(jobs_dir):
|
| 54 |
+
os.makedirs(jobs_dir, exist_ok=True)
|
| 55 |
+
|
| 56 |
+
# Create or update the job log file
|
| 57 |
+
job_file = os.path.join(jobs_dir, f"{job_id}.json")
|
| 58 |
+
|
| 59 |
+
# Write data directly to file
|
| 60 |
+
with open(job_file, 'w') as f:
|
| 61 |
+
json.dump(data, f, indent=2)
|
| 62 |
+
|
| 63 |
+
def queue_task_wrapper(bypass_queue=False):
|
| 64 |
+
def decorator(f):
|
| 65 |
+
def wrapper(*args, **kwargs):
|
| 66 |
+
return current_app.queue_task(bypass_queue=bypass_queue)(f)(*args, **kwargs)
|
| 67 |
+
return wrapper
|
| 68 |
+
return decorator
|
| 69 |
+
|
| 70 |
+
def discover_and_register_blueprints(app, base_dir='routes'):
|
| 71 |
+
"""
|
| 72 |
+
Dynamically discovers and registers all Flask blueprints in the routes directory.
|
| 73 |
+
Recursively searches all subdirectories for Python modules containing Blueprint instances.
|
| 74 |
+
|
| 75 |
+
Args:
|
| 76 |
+
app (Flask): The Flask application instance
|
| 77 |
+
base_dir (str): Base directory to start searching for blueprints (default: 'routes')
|
| 78 |
+
"""
|
| 79 |
+
import importlib
|
| 80 |
+
import pkgutil
|
| 81 |
+
import inspect
|
| 82 |
+
import sys
|
| 83 |
+
import os
|
| 84 |
+
from flask import Blueprint
|
| 85 |
+
import logging
|
| 86 |
+
import glob
|
| 87 |
+
|
| 88 |
+
logger = logging.getLogger(__name__)
|
| 89 |
+
logger.info(f"Discovering blueprints in {base_dir}")
|
| 90 |
+
|
| 91 |
+
# Add the current working directory to sys.path if it's not already there
|
| 92 |
+
cwd = os.getcwd()
|
| 93 |
+
if cwd not in sys.path:
|
| 94 |
+
sys.path.insert(0, cwd)
|
| 95 |
+
|
| 96 |
+
# Get the absolute path to the base directory
|
| 97 |
+
if not os.path.isabs(base_dir):
|
| 98 |
+
base_dir = os.path.join(cwd, base_dir)
|
| 99 |
+
|
| 100 |
+
registered_blueprints = set()
|
| 101 |
+
|
| 102 |
+
# Find all Python files in the routes directory, including subdirectories
|
| 103 |
+
python_files = glob.glob(os.path.join(base_dir, '**', '*.py'), recursive=True)
|
| 104 |
+
logger.info(f"Found {len(python_files)} Python files in {base_dir}")
|
| 105 |
+
|
| 106 |
+
for file_path in python_files:
|
| 107 |
+
try:
|
| 108 |
+
# Convert file path to import path
|
| 109 |
+
rel_path = os.path.relpath(file_path, cwd)
|
| 110 |
+
# Remove .py extension
|
| 111 |
+
module_path = os.path.splitext(rel_path)[0]
|
| 112 |
+
# Convert path separators to dots for import
|
| 113 |
+
module_path = module_path.replace(os.path.sep, '.')
|
| 114 |
+
|
| 115 |
+
# Skip __init__.py files
|
| 116 |
+
if module_path.endswith('__init__'):
|
| 117 |
+
continue
|
| 118 |
+
|
| 119 |
+
#logger.info(f"Attempting to import module: {module_path}")
|
| 120 |
+
|
| 121 |
+
# Import the module
|
| 122 |
+
module = importlib.import_module(module_path)
|
| 123 |
+
|
| 124 |
+
# Find all Blueprint instances in the module
|
| 125 |
+
for name, obj in inspect.getmembers(module):
|
| 126 |
+
if isinstance(obj, Blueprint) and obj not in registered_blueprints:
|
| 127 |
+
pid = os.getpid()
|
| 128 |
+
logger.info(f"PID {pid} Registering: {module_path}")
|
| 129 |
+
app.register_blueprint(obj)
|
| 130 |
+
registered_blueprints.add(obj)
|
| 131 |
+
|
| 132 |
+
except Exception as e:
|
| 133 |
+
logger.error(f"Error importing module {module_path}: {str(e)}")
|
| 134 |
+
|
| 135 |
+
logger.info(f"PID {pid} Registered {len(registered_blueprints)} blueprints")
|
| 136 |
+
return registered_blueprints
|
docker-compose.yml
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
services:
|
| 2 |
+
traefik:
|
| 3 |
+
image: "traefik"
|
| 4 |
+
restart: unless-stopped
|
| 5 |
+
command:
|
| 6 |
+
- "--api=true"
|
| 7 |
+
- "--api.insecure=true"
|
| 8 |
+
- "--providers.docker=true"
|
| 9 |
+
- "--providers.docker.exposedbydefault=false"
|
| 10 |
+
- "--entrypoints.web.address=:80"
|
| 11 |
+
- "--entrypoints.web.http.redirections.entryPoint.to=websecure"
|
| 12 |
+
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
|
| 13 |
+
- "--entrypoints.websecure.address=:443"
|
| 14 |
+
- "--certificatesresolvers.mytlschallenge.acme.tlschallenge=true"
|
| 15 |
+
- "--certificatesresolvers.mytlschallenge.acme.email=${SSL_EMAIL}"
|
| 16 |
+
- "--certificatesresolvers.mytlschallenge.acme.storage=/letsencrypt/acme.json"
|
| 17 |
+
ports:
|
| 18 |
+
- "80:80"
|
| 19 |
+
- "443:443"
|
| 20 |
+
volumes:
|
| 21 |
+
- traefik_data:/letsencrypt
|
| 22 |
+
- /var/run/docker.sock:/var/run/docker.sock:ro
|
| 23 |
+
ncat:
|
| 24 |
+
image: stephengpope/no-code-architects-toolkit:latest
|
| 25 |
+
env_file:
|
| 26 |
+
- .env
|
| 27 |
+
labels:
|
| 28 |
+
- traefik.enable=true
|
| 29 |
+
- traefik.http.routers.ncat.rule=Host(`${APP_DOMAIN}`)
|
| 30 |
+
- traefik.http.routers.ncat.tls=true
|
| 31 |
+
- traefik.http.routers.ncat.entrypoints=web,websecure
|
| 32 |
+
- traefik.http.routers.ncat.tls.certresolver=mytlschallenge
|
| 33 |
+
volumes:
|
| 34 |
+
- storage:/var/www/html/storage/app
|
| 35 |
+
- logs:/var/www/html/storage/logs
|
| 36 |
+
restart: unless-stopped
|
| 37 |
+
|
| 38 |
+
volumes:
|
| 39 |
+
traefik_data:
|
| 40 |
+
driver: local
|
| 41 |
+
storage:
|
| 42 |
+
driver: local
|
| 43 |
+
logs:
|
| 44 |
+
driver: local
|