pre remote host
This commit is contained in:
parent
9569f90aba
commit
86d66717c1
6 changed files with 90 additions and 18 deletions
16
.env.example
Normal file
16
.env.example
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# API Keys Configuration
|
||||||
|
# Copy this file to .env and replace with your actual API keys
|
||||||
|
|
||||||
|
# INPUT API Keys (comma-separated, for POST operations)
|
||||||
|
# These keys allow writing/posting data to the API
|
||||||
|
INPUT_API_KEYS=9d207bf0-10f5-4d8f-a479-22ff5aeff8d1,f47d4a2c-24cf-4745-937e-620a5963c0b8,b7061546-75e8-444b-a2c4-f19655d07eb8
|
||||||
|
|
||||||
|
# READ API Keys (comma-separated, for GET operations)
|
||||||
|
# These keys allow reading data from the API
|
||||||
|
READ_API_KEYS=read_key_1,read_key_2,read_key_3
|
||||||
|
|
||||||
|
# Security Notes:
|
||||||
|
# - Use strong, randomly generated UUIDs for production
|
||||||
|
# - Store the .env file securely and never commit it to version control
|
||||||
|
# - Rotate keys regularly for enhanced security
|
||||||
|
# - Use different keys for different environments (dev, staging, prod)
|
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -1,3 +1,6 @@
|
||||||
|
# .env file
|
||||||
|
.env
|
||||||
|
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
|
@ -150,4 +153,4 @@ input/
|
||||||
*.temp
|
*.temp
|
||||||
|
|
||||||
# Container runtime
|
# Container runtime
|
||||||
.container_id
|
.container_id
|
||||||
|
|
|
@ -21,6 +21,9 @@ RUN pip install --no-cache-dir --upgrade pip && \
|
||||||
# Copy application code
|
# Copy application code
|
||||||
COPY main.py .
|
COPY main.py .
|
||||||
|
|
||||||
|
# Copy .env.example for reference (actual .env will be mounted at runtime)
|
||||||
|
COPY .env.example .
|
||||||
|
|
||||||
# Create input directory and set permissions
|
# Create input directory and set permissions
|
||||||
RUN mkdir -p /app/input && \
|
RUN mkdir -p /app/input && \
|
||||||
chown -R appuser:appuser /app
|
chown -R appuser:appuser /app
|
||||||
|
|
31
main.py
31
main.py
|
@ -8,6 +8,11 @@ from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import re
|
import re
|
||||||
import bleach
|
import bleach
|
||||||
|
import os
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
# Load environment variables
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
# Initialize FastAPI application
|
# Initialize FastAPI application
|
||||||
app = FastAPI(
|
app = FastAPI(
|
||||||
|
@ -20,17 +25,29 @@ app = FastAPI(
|
||||||
INPUT_DIR = Path("input")
|
INPUT_DIR = Path("input")
|
||||||
INPUT_DIR.mkdir(exist_ok=True) # Create directory if it doesn't exist
|
INPUT_DIR.mkdir(exist_ok=True) # Create directory if it doesn't exist
|
||||||
|
|
||||||
# Authentication tokens
|
# Load API keys from environment variables (with fallback for backward compatibility)
|
||||||
INPUT_TOKEN = "input_token_123" # Token for POST endpoints
|
INPUT_API_KEYS = [
|
||||||
READ_TOKEN = "read_token_456" # Token for GET endpoints
|
key.strip() for key in os.getenv("INPUT_API_KEYS", "input_token_123").split(",")
|
||||||
|
if key.strip()
|
||||||
|
]
|
||||||
|
READ_API_KEYS = [
|
||||||
|
key.strip() for key in os.getenv("READ_API_KEYS", "read_token_456").split(",")
|
||||||
|
if key.strip()
|
||||||
|
]
|
||||||
|
|
||||||
|
# Validate that we have at least one key for each operation
|
||||||
|
if not INPUT_API_KEYS:
|
||||||
|
raise ValueError("At least one INPUT_API_KEY must be configured")
|
||||||
|
if not READ_API_KEYS:
|
||||||
|
raise ValueError("At least one READ_API_KEY must be configured")
|
||||||
|
|
||||||
# Security schemes
|
# Security schemes
|
||||||
input_security = HTTPBearer()
|
input_security = HTTPBearer()
|
||||||
read_security = HTTPBearer()
|
read_security = HTTPBearer()
|
||||||
|
|
||||||
def verify_input_token(credentials: HTTPAuthorizationCredentials = Depends(input_security)):
|
def verify_input_token(credentials: HTTPAuthorizationCredentials = Depends(input_security)):
|
||||||
"""Verify bearer token for input operations"""
|
"""Verify bearer token for input operations (supports multiple API keys)"""
|
||||||
if credentials.credentials != INPUT_TOKEN:
|
if credentials.credentials not in INPUT_API_KEYS:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=401,
|
status_code=401,
|
||||||
detail="Invalid authentication token for input operations"
|
detail="Invalid authentication token for input operations"
|
||||||
|
@ -38,8 +55,8 @@ def verify_input_token(credentials: HTTPAuthorizationCredentials = Depends(input
|
||||||
return credentials
|
return credentials
|
||||||
|
|
||||||
def verify_read_token(credentials: HTTPAuthorizationCredentials = Depends(read_security)):
|
def verify_read_token(credentials: HTTPAuthorizationCredentials = Depends(read_security)):
|
||||||
"""Verify bearer token for read operations"""
|
"""Verify bearer token for read operations (supports multiple API keys)"""
|
||||||
if credentials.credentials != READ_TOKEN:
|
if credentials.credentials not in READ_API_KEYS:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=401,
|
status_code=401,
|
||||||
detail="Invalid authentication token for read operations"
|
detail="Invalid authentication token for read operations"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
fastapi==0.104.1
|
fastapi==0.104.1
|
||||||
uvicorn[standard]==0.24.0
|
uvicorn[standard]==0.24.0
|
||||||
pydantic==2.5.0
|
pydantic==2.5.0
|
||||||
bleach==6.1.0
|
bleach==6.1.0
|
||||||
|
python-dotenv==1.0.0
|
|
@ -88,18 +88,48 @@ setup_data_directory() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Check for .env file
|
||||||
|
check_env_file() {
|
||||||
|
if [ ! -f ".env" ]; then
|
||||||
|
print_warning ".env file not found"
|
||||||
|
print_status "Creating .env file from .env.example..."
|
||||||
|
|
||||||
|
if [ -f ".env.example" ]; then
|
||||||
|
cp .env.example .env
|
||||||
|
print_warning "Please edit .env file with your actual API keys before running the container"
|
||||||
|
print_status "You can edit .env now or the container will use example keys"
|
||||||
|
|
||||||
|
# Ask user if they want to edit now
|
||||||
|
read -p "Do you want to edit .env file now? (y/N): " -n 1 -r
|
||||||
|
echo
|
||||||
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
${EDITOR:-nano} .env
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
print_error ".env.example file not found. Cannot create .env file."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
print_status ".env file found"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Run container
|
# Run container
|
||||||
run_container() {
|
run_container() {
|
||||||
print_status "Starting container: ${CONTAINER_NAME}"
|
print_status "Starting container: ${CONTAINER_NAME}"
|
||||||
|
|
||||||
# Convert relative path to absolute path
|
# Convert relative path to absolute path
|
||||||
#DATA_DIR_ABS=$(realpath "${DATA_DIR}")
|
DATA_DIR_ABS=$(realpath "${DATA_DIR}")
|
||||||
DATA_DIR_ABS="/home/user/Documents/Dom.Decimal/30-Projects/30-Misc/30-00-ZeroOne/30-00-002-Work-FastAPI-REST-Endpoint/input"
|
ENV_FILE_ABS=$(realpath ".env")
|
||||||
|
|
||||||
|
print_status "Mounting data directory: ${DATA_DIR_ABS}"
|
||||||
|
print_status "Mounting .env file: ${ENV_FILE_ABS}"
|
||||||
|
|
||||||
$CONTAINER_CMD run -d \
|
$CONTAINER_CMD run -d \
|
||||||
--name "${CONTAINER_NAME}" \
|
--name "${CONTAINER_NAME}" \
|
||||||
--publish "${HOST_PORT}:${CONTAINER_PORT}" \
|
--publish "${HOST_PORT}:${CONTAINER_PORT}" \
|
||||||
--volume "${DATA_DIR_ABS}:/app/input:Z" \
|
--volume "${DATA_DIR_ABS}:/app/input:Z" \
|
||||||
|
--volume "${ENV_FILE_ABS}:/app/.env:ro,Z" \
|
||||||
--restart unless-stopped \
|
--restart unless-stopped \
|
||||||
--security-opt no-new-privileges \
|
--security-opt no-new-privileges \
|
||||||
--cap-drop ALL \
|
--cap-drop ALL \
|
||||||
|
@ -157,18 +187,19 @@ show_usage_info() {
|
||||||
print_status "API Documentation: http://localhost:${HOST_PORT}/docs"
|
print_status "API Documentation: http://localhost:${HOST_PORT}/docs"
|
||||||
print_status "Health Check: http://localhost:${HOST_PORT}/health"
|
print_status "Health Check: http://localhost:${HOST_PORT}/health"
|
||||||
echo
|
echo
|
||||||
print_status "Authentication Tokens:"
|
print_status "API Key Configuration:"
|
||||||
echo " - Input Token: input_token_123"
|
echo " - Keys are loaded from .env file"
|
||||||
echo " - Read Token: read_token_456"
|
echo " - Separate keys for INPUT and READ operations"
|
||||||
|
echo " - Multiple keys supported per operation type"
|
||||||
echo
|
echo
|
||||||
print_status "Example Usage:"
|
print_status "Example Usage:"
|
||||||
echo " # Send data:"
|
echo " # Send data (use any key from INPUT_API_KEYS):"
|
||||||
echo ' curl -X POST http://localhost:8000/api/run1/ \'
|
echo ' curl -X POST http://localhost:8000/api/run1/ \'
|
||||||
echo ' -H "Authorization: Bearer input_token_123" \'
|
echo ' -H "Authorization: Bearer <YOUR_INPUT_API_KEY>" \'
|
||||||
echo ' -d "host_a,is ok"'
|
echo ' -d "host_a,is ok"'
|
||||||
echo
|
echo
|
||||||
echo " # Read results:"
|
echo " # Read results (use any key from READ_API_KEYS):"
|
||||||
echo ' curl -H "Authorization: Bearer read_token_456" \'
|
echo ' curl -H "Authorization: Bearer <YOUR_READ_API_KEY>" \'
|
||||||
echo ' http://localhost:8000/results/run1/'
|
echo ' http://localhost:8000/results/run1/'
|
||||||
echo
|
echo
|
||||||
print_status "Data Directory: ${DATA_DIR_ABS}"
|
print_status "Data Directory: ${DATA_DIR_ABS}"
|
||||||
|
@ -191,6 +222,7 @@ main() {
|
||||||
cleanup_existing_container
|
cleanup_existing_container
|
||||||
build_image
|
build_image
|
||||||
setup_data_directory
|
setup_data_directory
|
||||||
|
check_env_file
|
||||||
run_container
|
run_container
|
||||||
verify_container
|
verify_container
|
||||||
show_usage_info
|
show_usage_info
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue