add source code and readme
This commit is contained in:
36
build/piefed/piefed-web/Dockerfile
Normal file
36
build/piefed/piefed-web/Dockerfile
Normal file
@@ -0,0 +1,36 @@
|
||||
FROM piefed-base AS piefed-web
|
||||
|
||||
# No additional Alpine packages needed - uWSGI installed via pip in base image
|
||||
|
||||
# Web-specific Python configuration for Flask
|
||||
RUN echo 'import os' > /app/uwsgi_config.py && \
|
||||
echo 'os.environ.setdefault("FLASK_APP", "pyfedi.py")' >> /app/uwsgi_config.py
|
||||
|
||||
# Copy web-specific configuration files
|
||||
COPY nginx.conf /etc/nginx/nginx.conf
|
||||
COPY uwsgi.ini /app/uwsgi.ini
|
||||
COPY supervisord-web.conf /etc/supervisor/conf.d/supervisord.conf
|
||||
COPY entrypoint-web.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
# Create nginx directories and set permissions
|
||||
RUN mkdir -p /var/log/nginx /var/log/supervisor /var/log/uwsgi \
|
||||
&& chown -R nginx:nginx /var/log/nginx \
|
||||
&& chown -R piefed:piefed /var/log/uwsgi \
|
||||
&& mkdir -p /var/cache/nginx \
|
||||
&& chown -R nginx:nginx /var/cache/nginx \
|
||||
&& chown -R piefed:piefed /app/logs \
|
||||
&& chmod -R 755 /app/logs
|
||||
|
||||
# Health check optimized for web container
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
|
||||
CMD curl -f http://localhost:80/api/health || curl -f http://localhost:80/ || exit 1
|
||||
|
||||
# Expose HTTP port
|
||||
EXPOSE 80
|
||||
|
||||
# Run as root to manage nginx and uwsgi
|
||||
USER root
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
CMD ["supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
|
||||
73
build/piefed/piefed-web/entrypoint-web.sh
Normal file
73
build/piefed/piefed-web/entrypoint-web.sh
Normal file
@@ -0,0 +1,73 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
# Source common functions
|
||||
. /usr/local/bin/entrypoint-common.sh
|
||||
|
||||
log "Starting PieFed web container..."
|
||||
|
||||
# Run common startup sequence
|
||||
common_startup
|
||||
|
||||
# Web-specific initialization
|
||||
log "Initializing web container..."
|
||||
|
||||
# Apply dual logging configuration (file + stdout for OpenObserve)
|
||||
log "Configuring dual logging for OpenObserve..."
|
||||
|
||||
# Pre-create log file with correct ownership to prevent permission issues
|
||||
log "Pre-creating log file with proper ownership..."
|
||||
touch /app/logs/pyfedi.log
|
||||
chown piefed:piefed /app/logs/pyfedi.log
|
||||
chmod 664 /app/logs/pyfedi.log
|
||||
|
||||
# Setup dual logging (file + stdout) directly
|
||||
python -c "
|
||||
import logging
|
||||
import sys
|
||||
|
||||
def setup_dual_logging():
|
||||
'''Add stdout handlers to existing loggers without disrupting file logging'''
|
||||
# Create a shared console handler
|
||||
console_handler = logging.StreamHandler(sys.stdout)
|
||||
console_handler.setLevel(logging.INFO)
|
||||
console_handler.setFormatter(logging.Formatter(
|
||||
'%(asctime)s [%(name)s] %(levelname)s: %(message)s'
|
||||
))
|
||||
|
||||
# Add console handler to key loggers (in addition to their existing file handlers)
|
||||
loggers_to_enhance = [
|
||||
'flask.app', # Flask application logger
|
||||
'werkzeug', # Web server logger
|
||||
'celery', # Celery worker logger
|
||||
'celery.task', # Celery task logger
|
||||
'celery.worker', # Celery worker logger
|
||||
'' # Root logger
|
||||
]
|
||||
|
||||
for logger_name in loggers_to_enhance:
|
||||
logger = logging.getLogger(logger_name)
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
# Check if this logger already has a stdout handler
|
||||
has_stdout_handler = any(
|
||||
isinstance(h, logging.StreamHandler) and h.stream == sys.stdout
|
||||
for h in logger.handlers
|
||||
)
|
||||
|
||||
if not has_stdout_handler:
|
||||
logger.addHandler(console_handler)
|
||||
|
||||
print('Dual logging configured: file + stdout for OpenObserve')
|
||||
|
||||
# Call the function
|
||||
setup_dual_logging()
|
||||
"
|
||||
|
||||
# Test nginx configuration
|
||||
log "Testing nginx configuration..."
|
||||
nginx -t
|
||||
|
||||
# Start services via supervisor
|
||||
log "Starting web services (nginx + uwsgi)..."
|
||||
exec "$@"
|
||||
178
build/piefed/piefed-web/nginx.conf
Normal file
178
build/piefed/piefed-web/nginx.conf
Normal file
@@ -0,0 +1,178 @@
|
||||
# No user directive needed for non-root containers
|
||||
worker_processes auto;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
use epoll;
|
||||
multi_accept on;
|
||||
}
|
||||
|
||||
http {
|
||||
# Basic Settings
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
keepalive_timeout 65;
|
||||
types_hash_max_size 2048;
|
||||
client_max_body_size 100M;
|
||||
server_tokens off;
|
||||
|
||||
# MIME Types
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
# Logging - Output to stdout/stderr for container log collection
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
log_format timed '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for" '
|
||||
'rt=$request_time uct=$upstream_connect_time uht=$upstream_header_time urt=$upstream_response_time';
|
||||
|
||||
access_log /dev/stdout timed;
|
||||
error_log /dev/stderr warn;
|
||||
|
||||
# Gzip compression
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1024;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_types
|
||||
text/plain
|
||||
text/css
|
||||
text/xml
|
||||
text/javascript
|
||||
application/json
|
||||
application/javascript
|
||||
application/xml+rss
|
||||
application/atom+xml
|
||||
application/activity+json
|
||||
application/ld+json
|
||||
image/svg+xml;
|
||||
|
||||
# Rate limiting removed - handled at ingress level for better client IP detection
|
||||
|
||||
# Upstream for uWSGI
|
||||
upstream piefed_app {
|
||||
server 127.0.0.1:8000;
|
||||
keepalive 2;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
# 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;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
|
||||
# HTTPS enforcement and mixed content prevention
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
add_header Content-Security-Policy "upgrade-insecure-requests" always;
|
||||
|
||||
# Real IP forwarding (for Kubernetes ingress)
|
||||
real_ip_header X-Forwarded-For;
|
||||
set_real_ip_from 10.0.0.0/8;
|
||||
set_real_ip_from 172.16.0.0/12;
|
||||
set_real_ip_from 192.168.0.0/16;
|
||||
|
||||
# Serve static files directly with nginx (following PieFed official recommendation)
|
||||
location /static/ {
|
||||
alias /app/app/static/;
|
||||
expires max;
|
||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||
add_header Vary "Accept-Encoding";
|
||||
|
||||
# Security headers for static assets
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
add_header Content-Security-Policy "upgrade-insecure-requests" always;
|
||||
|
||||
# Handle trailing slashes gracefully
|
||||
try_files $uri $uri/ =404;
|
||||
}
|
||||
|
||||
# Media files (user uploads) - long cache since they don't change
|
||||
location /media/ {
|
||||
alias /app/media/;
|
||||
expires 1d;
|
||||
add_header Cache-Control "public, max-age=31536000";
|
||||
}
|
||||
|
||||
# Health check endpoint
|
||||
location /health {
|
||||
access_log off;
|
||||
return 200 "healthy\n";
|
||||
add_header Content-Type text/plain;
|
||||
}
|
||||
|
||||
# NodeInfo endpoints - no override needed, PieFed already sets application/json correctly
|
||||
location ~ ^/nodeinfo/ {
|
||||
proxy_pass http://piefed_app;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
}
|
||||
|
||||
# Webfinger endpoint - ensure correct Content-Type per WebFinger spec
|
||||
location ~ ^/\.well-known/webfinger {
|
||||
proxy_pass http://piefed_app;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
# Force application/jrd+json Content-Type for webfinger (per WebFinger spec)
|
||||
proxy_hide_header Content-Type;
|
||||
add_header Content-Type "application/jrd+json" always;
|
||||
# Ensure CORS headers are present for federation discovery
|
||||
add_header Access-Control-Allow-Origin "*" always;
|
||||
add_header Access-Control-Allow-Methods "GET, OPTIONS" always;
|
||||
add_header Access-Control-Allow-Headers "Content-Type, Authorization, Accept, User-Agent" always;
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
}
|
||||
|
||||
# API and federation endpoints
|
||||
location ~ ^/(api|\.well-known|inbox) {
|
||||
proxy_pass http://piefed_app;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto https; # Force HTTPS scheme
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
}
|
||||
|
||||
# All other requests
|
||||
location / {
|
||||
proxy_pass http://piefed_app;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto https; # Force HTTPS scheme
|
||||
proxy_connect_timeout 30s;
|
||||
proxy_send_timeout 30s;
|
||||
proxy_read_timeout 30s;
|
||||
}
|
||||
|
||||
# Error pages
|
||||
error_page 404 /404.html;
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
}
|
||||
}
|
||||
38
build/piefed/piefed-web/supervisord-web.conf
Normal file
38
build/piefed/piefed-web/supervisord-web.conf
Normal file
@@ -0,0 +1,38 @@
|
||||
[supervisord]
|
||||
nodaemon=true
|
||||
user=root
|
||||
logfile=/dev/stdout
|
||||
logfile_maxbytes=0
|
||||
pidfile=/var/run/supervisord.pid
|
||||
silent=false
|
||||
|
||||
[program:uwsgi]
|
||||
command=uwsgi --ini /app/uwsgi.ini
|
||||
user=piefed
|
||||
directory=/app
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
autorestart=true
|
||||
priority=100
|
||||
startsecs=10
|
||||
stopasgroup=true
|
||||
killasgroup=true
|
||||
|
||||
[program:nginx]
|
||||
command=nginx -g "daemon off;"
|
||||
user=root
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
autorestart=true
|
||||
priority=200
|
||||
startsecs=5
|
||||
stopasgroup=true
|
||||
killasgroup=true
|
||||
|
||||
[group:piefed-web]
|
||||
programs=uwsgi,nginx
|
||||
priority=999
|
||||
47
build/piefed/piefed-web/uwsgi.ini
Normal file
47
build/piefed/piefed-web/uwsgi.ini
Normal file
@@ -0,0 +1,47 @@
|
||||
[uwsgi]
|
||||
# Application configuration
|
||||
module = pyfedi:app
|
||||
pythonpath = /app
|
||||
virtualenv = /app/venv
|
||||
chdir = /app
|
||||
|
||||
# Process configuration
|
||||
master = true
|
||||
processes = 6
|
||||
threads = 4
|
||||
enable-threads = true
|
||||
thunder-lock = true
|
||||
vacuum = true
|
||||
|
||||
# Socket configuration
|
||||
http-socket = 127.0.0.1:8000
|
||||
uid = piefed
|
||||
gid = piefed
|
||||
|
||||
# Performance settings
|
||||
buffer-size = 32768
|
||||
post-buffering = 8192
|
||||
max-requests = 1000
|
||||
max-requests-delta = 100
|
||||
harakiri = 60
|
||||
harakiri-verbose = true
|
||||
|
||||
# Memory optimization
|
||||
reload-on-rss = 512
|
||||
evil-reload-on-rss = 1024
|
||||
|
||||
# Logging - Minimal configuration, let supervisor handle log redirection
|
||||
# Disable uWSGI's own logging to avoid permission issues, logs will go through supervisor
|
||||
disable-logging = true
|
||||
|
||||
# Process management
|
||||
die-on-term = true
|
||||
lazy-apps = true
|
||||
|
||||
# Static file serving (fallback if nginx doesn't handle)
|
||||
static-map = /static=/app/static
|
||||
static-map = /media=/app/media
|
||||
|
||||
# Environment variables for Flask
|
||||
env = FLASK_APP=pyfedi.py
|
||||
env = FLASK_ENV=production
|
||||
Reference in New Issue
Block a user