mirror of
https://github.com/Dictionarry-Hub/profilarr.git
synced 2026-01-22 10:51:02 +01:00
feat: add production Dockerfile and docker-compose configuration for backend setup
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -8,8 +8,12 @@ __pycache__/
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
.env.prod
|
||||
.env.1
|
||||
.env.2
|
||||
|
||||
# OS files
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
|
||||
# build files
|
||||
backend/app/static/
|
||||
17
backend/Dockerfile.prod
Normal file
17
backend/Dockerfile.prod
Normal file
@@ -0,0 +1,17 @@
|
||||
# backend/Dockerfile.prod
|
||||
# Build frontend
|
||||
FROM node:18 AS frontend-builder
|
||||
WORKDIR /frontend
|
||||
COPY frontend/package*.json ./
|
||||
RUN npm install
|
||||
COPY frontend/ ./
|
||||
RUN npm run build
|
||||
|
||||
# Backend
|
||||
FROM python:3.9
|
||||
WORKDIR /app
|
||||
COPY backend/requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
COPY backend/ .
|
||||
COPY --from=frontend-builder /frontend/dist ./app/static
|
||||
CMD ["python", "-m", "app.main"]
|
||||
@@ -23,9 +23,19 @@ def create_app():
|
||||
logger = setup_logging()
|
||||
|
||||
logger.info("Creating Flask application")
|
||||
app = Flask(__name__)
|
||||
app = Flask(__name__, static_folder='static')
|
||||
CORS(app, resources={r"/*": {"origins": "*"}})
|
||||
|
||||
# Serve static files
|
||||
@app.route('/', defaults={'path': ''})
|
||||
@app.route('/<path:path>')
|
||||
def serve_static(path):
|
||||
if path.startswith('api/'):
|
||||
return # Let API routes handle these
|
||||
if path and os.path.exists(os.path.join(app.static_folder, path)):
|
||||
return send_from_directory(app.static_folder, path)
|
||||
return send_from_directory(app.static_folder, 'index.html')
|
||||
|
||||
# Initialize directories and database
|
||||
logger.info("Ensuring required directories exist")
|
||||
config.ensure_directories()
|
||||
@@ -58,7 +68,7 @@ def create_app():
|
||||
app.register_blueprint(logs_bp, url_prefix='/api/logs')
|
||||
app.register_blueprint(git_bp, url_prefix='/api/git')
|
||||
app.register_blueprint(data_bp, url_prefix='/api/data')
|
||||
app.register_blueprint(importarr_bp, url_prefix='/api/importarr')
|
||||
app.register_blueprint(importarr_bp, url_prefix='/api/import')
|
||||
app.register_blueprint(arr_bp, url_prefix='/api/arr')
|
||||
app.register_blueprint(tasks_bp, url_prefix='/api/tasks')
|
||||
|
||||
@@ -67,7 +77,7 @@ def create_app():
|
||||
init_middleware(app)
|
||||
|
||||
# Add settings route
|
||||
@app.route('/settings', methods=['GET'])
|
||||
@app.route('/api/settings', methods=['GET'])
|
||||
def handle_settings():
|
||||
settings = get_settings()
|
||||
return jsonify(settings), 200
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# backend/app/middleware.py
|
||||
|
||||
from functools import wraps
|
||||
from flask import request, session, jsonify, current_app
|
||||
from flask import request, session, jsonify, send_from_directory
|
||||
from .db import get_db
|
||||
import logging
|
||||
|
||||
@@ -13,41 +12,49 @@ def init_middleware(app):
|
||||
|
||||
@app.before_request
|
||||
def authenticate_request():
|
||||
# Skip authentication for auth blueprint routes
|
||||
if request.blueprint == 'auth':
|
||||
return
|
||||
|
||||
# Skip authentication for OPTIONS requests (CORS preflight)
|
||||
if request.method == 'OPTIONS':
|
||||
return
|
||||
|
||||
# List of paths that don't require authentication
|
||||
PUBLIC_PATHS = ['/auth/setup', '/auth/authenticate']
|
||||
|
||||
if request.path in PUBLIC_PATHS:
|
||||
# Always allow auth endpoints
|
||||
if request.path.startswith('/api/auth/'):
|
||||
return
|
||||
|
||||
# Check session authentication (for web users)
|
||||
if session.get('authenticated'):
|
||||
db = get_db()
|
||||
user = db.execute('SELECT session_id FROM auth').fetchone()
|
||||
if user and session.get('session_id') == user['session_id']:
|
||||
return
|
||||
# Allow static assets needed for auth pages
|
||||
if request.path.startswith(
|
||||
('/assets/',
|
||||
'/static/')) or request.path in ['/', '/regex.svg', '/clone.svg']:
|
||||
return
|
||||
|
||||
# Check API key authentication (for API users)
|
||||
api_key = request.headers.get('X-Api-Key')
|
||||
if api_key:
|
||||
db = get_db()
|
||||
try:
|
||||
user = db.execute('SELECT 1 FROM auth WHERE api_key = ?',
|
||||
(api_key, )).fetchone()
|
||||
if user:
|
||||
# For API routes, require auth
|
||||
if request.path.startswith('/api/'):
|
||||
# Check session authentication (for web users)
|
||||
if session.get('authenticated'):
|
||||
db = get_db()
|
||||
user = db.execute('SELECT session_id FROM auth').fetchone()
|
||||
if user and session.get('session_id') == user['session_id']:
|
||||
return
|
||||
logger.warning(f'Invalid API key attempt: {api_key[:10]}...')
|
||||
except Exception as e:
|
||||
logger.error(f'Database error during API key check: {str(e)}')
|
||||
return jsonify({'error': 'Internal server error'}), 500
|
||||
|
||||
# If no valid authentication is found, return 401
|
||||
logger.warning(f'Unauthorized access attempt to {request.path}')
|
||||
return jsonify({'error': 'Unauthorized'}), 401
|
||||
# Check API key authentication (for API users)
|
||||
api_key = request.headers.get('X-Api-Key')
|
||||
if api_key:
|
||||
db = get_db()
|
||||
try:
|
||||
user = db.execute('SELECT 1 FROM auth WHERE api_key = ?',
|
||||
(api_key, )).fetchone()
|
||||
if user:
|
||||
return
|
||||
logger.warning(
|
||||
f'Invalid API key attempt: {api_key[:10]}...')
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f'Database error during API key check: {str(e)}')
|
||||
return jsonify({'error': 'Internal server error'}), 500
|
||||
|
||||
# If no valid authentication is found, return 401
|
||||
logger.warning(f'Unauthorized access attempt to {request.path}')
|
||||
return jsonify({'error': 'Unauthorized'}), 401
|
||||
|
||||
# For all other routes (frontend routes), serve index.html
|
||||
# This lets React handle auth and routing
|
||||
return send_from_directory(app.static_folder, 'index.html')
|
||||
|
||||
21
docker-compose.prod.yml
Normal file
21
docker-compose.prod.yml
Normal file
@@ -0,0 +1,21 @@
|
||||
# docker-compose.prod.yml
|
||||
version: '3.8'
|
||||
services:
|
||||
backend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: backend/Dockerfile.prod
|
||||
ports:
|
||||
- '5000:5000'
|
||||
volumes:
|
||||
- profilarr_data:/config
|
||||
environment:
|
||||
- FLASK_ENV=production
|
||||
- TZ=Australia/Adelaide
|
||||
env_file:
|
||||
- .env.prod
|
||||
restart: always
|
||||
|
||||
volumes:
|
||||
profilarr_data:
|
||||
name: profilarr_data
|
||||
Reference in New Issue
Block a user