chore: auto-commit 2026-03-16 17:41

This commit is contained in:
cclohmar 2026-03-16 17:41:40 +00:00
parent 2e9a48b088
commit eed80d3b80
5 changed files with 423 additions and 2 deletions

View file

@ -15,8 +15,18 @@ from app.modules.admin.controller import router as admin_router
from app.modules.oauth2 import oauth_router from app.modules.oauth2 import oauth_router
# Convert log level string to logging constant
log_level_map = {
"DEBUG": logging.DEBUG,
"INFO": logging.INFO,
"WARNING": logging.WARNING,
"ERROR": logging.ERROR,
"CRITICAL": logging.CRITICAL,
}
log_level = log_level_map.get(settings.log_level.upper(), logging.INFO)
logging.basicConfig( logging.basicConfig(
level=logging.DEBUG if settings.debug else logging.INFO, level=logging.DEBUG if settings.debug else log_level,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
) )
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View file

@ -12,6 +12,9 @@ class Settings(BaseSettings):
debug: bool = False debug: bool = False
title: str = "Mock API Server" title: str = "Mock API Server"
version: str = "1.0.0" version: str = "1.0.0"
log_level: str = "INFO"
host: str = "0.0.0.0"
port: int = 8000
# Admin authentication # Admin authentication
admin_username: str = "admin" admin_username: str = "admin"
@ -72,7 +75,7 @@ class Settings(BaseSettings):
# Return as-is for other types (should be list) # Return as-is for other types (should be list)
return v return v
model_config = ConfigDict(env_file=".env") model_config = ConfigDict(env_file=".env", extra="allow")
settings = Settings() settings = Settings()

343
fix_vps_complete.sh Normal file
View file

@ -0,0 +1,343 @@
#!/bin/bash
# Complete fix script for MockAPI VPS deployment issues
# Run this on the VPS as the same user who installed MockAPI
set -e
echo "=== MockAPI VPS Complete Fix Script ==="
echo
APP_DIR="/opt/mockapi"
ENV_FILE="${APP_DIR}/.env"
CONFIG_FILE="${APP_DIR}/app/core/config.py"
APP_FILE="${APP_DIR}/app/core/app.py"
# Function to print colored output
print_status() {
echo -e "\033[0;32m[+]\033[0m $1"
}
print_warning() {
echo -e "\033[1;33m[!]\033[0m $1"
}
print_error() {
echo -e "\033[0;31m[!]\033[0m $1"
}
# Step 0: Check we're in the right place
print_status "Step 0: Checking environment"
if [[ ! -d "$APP_DIR" ]]; then
print_error "Application directory not found: $APP_DIR"
exit 1
fi
if [[ ! -f "$CONFIG_FILE" ]]; then
print_error "Config file not found: $CONFIG_FILE"
exit 1
fi
cd "$APP_DIR"
# Step 1: Backup original files
print_status "Step 1: Backing up original files"
BACKUP_DIR="${APP_DIR}/backup_$(date +%s)"
mkdir -p "$BACKUP_DIR"
cp "$CONFIG_FILE" "$BACKUP_DIR/config.py.backup"
cp "$APP_FILE" "$BACKUP_DIR/app.py.backup"
if [[ -f "$ENV_FILE" ]]; then
cp "$ENV_FILE" "$BACKUP_DIR/env.backup"
fi
print_status "Backups created in: $BACKUP_DIR"
# Step 2: Fix config.py - add missing fields and allow extra fields
print_status "Step 2: Fixing config.py"
# Check if log_level field already exists
if ! grep -q "log_level:" "$CONFIG_FILE"; then
print_status "Adding log_level, host, and port fields to config.py"
# Insert after the version field line
sed -i '/version: str = "1.0.0"/a\ log_level: str = "INFO"\n host: str = "0.0.0.0"\n port: int = 8000' "$CONFIG_FILE"
else
print_status "log_level field already exists"
fi
# Ensure model_config allows extra fields
if grep -q 'model_config = ConfigDict(env_file=".env")' "$CONFIG_FILE"; then
print_status "Updating model_config to allow extra fields"
sed -i 's/model_config = ConfigDict(env_file=".env")/model_config = ConfigDict(env_file=".env", extra="allow")/' "$CONFIG_FILE"
elif grep -q 'extra="allow"' "$CONFIG_FILE"; then
print_status "model_config already allows extra fields"
else
print_warning "Could not find model_config line to update"
fi
# Step 3: Fix app.py - update logging configuration
print_status "Step 3: Fixing app.py logging configuration"
# Check if log_level_map already exists
if ! grep -q "log_level_map" "$APP_FILE"; then
print_status "Updating logging configuration in app.py"
# Create a temporary file with the new logging config
cat > /tmp/new_logging_config.py << 'EOF'
# Convert log level string to logging constant
log_level_map = {
"DEBUG": logging.DEBUG,
"INFO": logging.INFO,
"WARNING": logging.WARNING,
"ERROR": logging.ERROR,
"CRITICAL": logging.CRITICAL,
}
log_level = log_level_map.get(settings.log_level.upper(), logging.INFO)
logging.basicConfig(
level=logging.DEBUG if settings.debug else log_level,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
logger = logging.getLogger(__name__)
EOF
# Replace the old logging.basicConfig block
# Find the line numbers of the old block
START_LINE=$(grep -n "logging.basicConfig" "$APP_FILE" | head -1 | cut -d: -f1)
if [[ -n "$START_LINE" ]]; then
# Find the next line after the closing parenthesis
END_LINE=$((START_LINE + 3))
# Check if we have the right pattern
if sed -n "${START_LINE},${END_LINE}p" "$APP_FILE" | grep -q "logger = logging.getLogger"; then
# Replace lines START_LINE to END_LINE with new config
sed -i "${START_LINE},${END_LINE}d" "$APP_FILE"
sed -i "${START_LINE}r /tmp/new_logging_config.py" "$APP_FILE"
print_status "Updated logging configuration"
else
print_warning "Could not find exact logging block pattern, attempting manual replacement"
# Fallback: replace the whole block we can identify
sed -i '/logging.basicConfig/,/^logger = logging.getLogger/d' "$APP_FILE"
sed -i "/import logging/a\\
# Convert log level string to logging constant\\
log_level_map = {\\
\"DEBUG\": logging.DEBUG,\\
\"INFO\": logging.INFO,\\
\"WARNING\": logging.WARNING,\\
\"ERROR\": logging.ERROR,\\
\"CRITICAL\": logging.CRITICAL,\\
}\\
log_level = log_level_map.get(settings.log_level.upper(), logging.INFO)\\
\\
logging.basicConfig(\\
level=logging.DEBUG if settings.debug else log_level,\\
format=\"%(asctime)s - %(name)s - %(levelname)s - %(message)s\",\\
)\\
logger = logging.getLogger(__name__)" "$APP_FILE"
fi
else
print_error "Could not find logging.basicConfig in app.py"
fi
rm /tmp/new_logging_config.py
else
print_status "Logging configuration already updated"
fi
# Step 4: Fix .env file format issues
print_status "Step 4: Fixing .env file format issues"
if [[ -f "$ENV_FILE" ]]; then
# Fix OAUTH2_SUPPORTED_SCOPES to proper JSON format
SCOPES_LINE=$(grep -i "^OAUTH2_SUPPORTED_SCOPES=" "$ENV_FILE" | head -1)
if [[ -n "$SCOPES_LINE" ]]; then
SCOPES_VALUE=$(echo "$SCOPES_LINE" | cut -d'=' -f2-)
# Check if value is valid JSON array
if ! python3 -c "import json; json.loads('$SCOPES_VALUE')" 2>/dev/null; then
print_warning "OAUTH2_SUPPORTED_SCOPES value is not valid JSON: $SCOPES_VALUE"
print_status "Converting to JSON array format"
# Convert space-separated or comma-separated to JSON array
CLEAN_VALUE=$(echo "$SCOPES_VALUE" | sed 's/"//g' | sed "s/'//g")
JSON_ARRAY=$(echo "$CLEAN_VALUE" | python3 -c "
import sys, json
value = sys.stdin.read().strip()
if ',' in value:
items = [item.strip() for item in value.split(',')]
else:
items = [item.strip() for item in value.split()]
print(json.dumps([item for item in items if item]))
")
# Update .env file
sed -i "s/^OAUTH2_SUPPORTED_SCOPES=.*$/OAUTH2_SUPPORTED_SCOPES=$JSON_ARRAY/" "$ENV_FILE"
print_status "Updated OAUTH2_SUPPORTED_SCOPES to: $JSON_ARRAY"
else
print_status "OAUTH2_SUPPORTED_SCOPES already in valid JSON format"
fi
fi
# Fix OAUTH2_SUPPORTED_GRANT_TYPES to proper JSON format
GRANT_LINE=$(grep -i "^OAUTH2_SUPPORTED_GRANT_TYPES=" "$ENV_FILE" | head -1)
if [[ -n "$GRANT_LINE" ]]; then
GRANT_VALUE=$(echo "$GRANT_LINE" | cut -d'=' -f2-)
# Check if value is valid JSON array
if ! python3 -c "import json; json.loads('$GRANT_VALUE')" 2>/dev/null; then
print_warning "OAUTH2_SUPPORTED_GRANT_TYPES value is not valid JSON: $GRANT_VALUE"
print_status "Converting to JSON array format"
# Convert space-separated or comma-separated to JSON array
CLEAN_VALUE=$(echo "$GRANT_VALUE" | sed 's/"//g' | sed "s/'//g")
JSON_ARRAY=$(echo "$CLEAN_VALUE" | python3 -c "
import sys, json
value = sys.stdin.read().strip()
if ',' in value:
items = [item.strip() for item in value.split(',')]
else:
items = [item.strip() for item in value.split()]
print(json.dumps([item for item in items if item]))
")
# Update .env file
sed -i "s/^OAUTH2_SUPPORTED_GRANT_TYPES=.*$/OAUTH2_SUPPORTED_GRANT_TYPES=$JSON_ARRAY/" "$ENV_FILE"
print_status "Updated OAUTH2_SUPPORTED_GRANT_TYPES to: $JSON_ARRAY"
else
print_status "OAUTH2_SUPPORTED_GRANT_TYPES already in valid JSON format"
fi
fi
# Remove duplicate entries
print_status "Checking for duplicate entries in .env"
for FIELD in OAUTH2_SUPPORTED_SCOPES OAUTH2_SUPPORTED_GRANT_TYPES; do
COUNT=$(grep -i "^${FIELD}=" "$ENV_FILE" | wc -l)
if [[ $COUNT -gt 1 ]]; then
print_warning "Found $COUNT entries for $FIELD, keeping only the first"
FIRST_LINE=$(grep -i -n "^${FIELD}=" "$ENV_FILE" | head -1 | cut -d: -f1)
# Create temp file without any of this field
grep -v -i "^${FIELD}=" "$ENV_FILE" > "${ENV_FILE}.tmp"
# Add back the first occurrence
grep -i "^${FIELD}=" "$ENV_FILE" | head -1 >> "${ENV_FILE}.tmp"
mv "${ENV_FILE}.tmp" "$ENV_FILE"
fi
done
# Ensure required fields exist
print_status "Ensuring all required fields exist in .env"
if ! grep -q "^LOG_LEVEL=" "$ENV_FILE"; then
echo "LOG_LEVEL=INFO" >> "$ENV_FILE"
print_status "Added LOG_LEVEL=INFO"
fi
if ! grep -q "^HOST=" "$ENV_FILE"; then
echo "HOST=0.0.0.0" >> "$ENV_FILE"
print_status "Added HOST=0.0.0.0"
fi
if ! grep -q "^PORT=" "$ENV_FILE"; then
echo "PORT=8000" >> "$ENV_FILE"
print_status "Added PORT=8000"
fi
if ! grep -q "^OAUTH2_AUTHORIZATION_CODE_EXPIRE_MINUTES=" "$ENV_FILE"; then
echo "OAUTH2_AUTHORIZATION_CODE_EXPIRE_MINUTES=10" >> "$ENV_FILE"
print_status "Added OAUTH2_AUTHORIZATION_CODE_EXPIRE_MINUTES=10"
fi
if ! grep -q "^OAUTH2_PKCE_REQUIRED=" "$ENV_FILE"; then
echo "OAUTH2_PKCE_REQUIRED=false" >> "$ENV_FILE"
print_status "Added OAUTH2_PKCE_REQUIRED=false"
fi
# Update OAUTH2_ISSUER if port has changed
CURRENT_PORT=$(grep "^PORT=" "$ENV_FILE" | cut -d'=' -f2)
CURRENT_ISSUER=$(grep "^OAUTH2_ISSUER=" "$ENV_FILE" | cut -d'=' -f2)
EXPECTED_ISSUER="http://localhost:${CURRENT_PORT}"
if [[ "$CURRENT_ISSUER" != "$EXPECTED_ISSUER" ]]; then
print_status "Updating OAUTH2_ISSUER to match port"
sed -i "s|^OAUTH2_ISSUER=.*|OAUTH2_ISSUER=$EXPECTED_ISSUER|" "$ENV_FILE"
fi
else
print_warning ".env file not found, skipping .env fixes"
fi
# Step 5: Test the configuration
print_status "Step 5: Testing configuration import"
cat > test_config.py << 'EOF'
import os
import sys
import json
# Add app directory to path
sys.path.insert(0, os.getcwd())
try:
print("Attempting to import config...")
from app.core.config import settings
print("✓ Config import successful!")
print(f" DEBUG: {settings.debug}")
print(f" LOG_LEVEL: {settings.log_level}")
print(f" HOST: {settings.host}")
print(f" PORT: {settings.port}")
print(f" OAUTH2_SUPPORTED_SCOPES: {settings.oauth2_supported_scopes}")
print(f" OAUTH2_SUPPORTED_GRANT_TYPES: {settings.oauth2_supported_grant_types}")
print(f" Type of scopes: {type(settings.oauth2_supported_scopes)}")
print(f" Type of grant types: {type(settings.oauth2_supported_grant_types)}")
# Test JSON serialization
print(f" Scopes as JSON: {json.dumps(settings.oauth2_supported_scopes)}")
print("✓ All configuration tests passed!")
except Exception as e:
print(f"✗ Config import failed: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
EOF
if python3 test_config.py; then
print_status "Configuration test passed!"
rm test_config.py
else
print_error "Configuration test failed"
print_warning "Check the error above. The .env file may still have issues."
exit 1
fi
# Step 6: Restart the service
print_status "Step 6: Restarting MockAPI service"
sudo systemctl restart mockapi
sleep 5
if sudo systemctl is-active --quiet mockapi; then
print_status "✓ Service is running successfully!"
echo
echo "Service status:"
sudo systemctl status mockapi --no-pager -l
echo
print_status "Testing API endpoints..."
if curl -f -s http://localhost:8000/health > /dev/null 2>&1; then
print_status "✓ Health check passed: http://localhost:8000/health"
echo
echo "Access your MockAPI:"
echo " Admin UI: http://localhost:8000/admin/login"
echo " API Docs: http://localhost:8000/docs"
echo " Health check: http://localhost:8000/health"
else
print_warning "Health check failed. Checking logs..."
sudo journalctl -u mockapi -n 10 --no-pager
fi
else
print_error "Service failed to start"
echo
print_warning "Checking service logs..."
sudo journalctl -u mockapi -n 30 --no-pager
echo
print_warning "If the service still fails, check:"
print_warning "1. The .env file format: ${ENV_FILE}"
print_warning "2. File permissions in ${APP_DIR}"
print_warning "3. Database file permissions"
exit 1
fi
echo
print_status "=== Fix completed successfully! ==="
echo
echo "Backup files are in: $BACKUP_DIR"
echo "If you encounter issues, you can restore from backup."
echo

29
test_fix/env.test.txt Normal file
View file

@ -0,0 +1,29 @@
# Mock API Configuration
# Generated on test
# Database Configuration
DATABASE_URL=sqlite+aiosqlite:///./mockapi.db
# Admin Authentication
ADMIN_USERNAME=admin
ADMIN_PASSWORD=admin123
# Security
SECRET_KEY=testsecretkey
# Application Settings
DEBUG=False
LOG_LEVEL=INFO
# OAuth2 Settings
OAUTH2_ISSUER=http://localhost:8000
OAUTH2_ACCESS_TOKEN_EXPIRE_MINUTES=30
OAUTH2_REFRESH_TOKEN_EXPIRE_DAYS=7
OAUTH2_AUTHORIZATION_CODE_EXPIRE_MINUTES=10
OAUTH2_SUPPORTED_GRANT_TYPES=["authorization_code", "client_credentials", "refresh_token"]
OAUTH2_SUPPORTED_SCOPES=["openid", "profile", "email", "api:read", "api:write"]
OAUTH2_PKCE_REQUIRED=false
# Server Settings
HOST=0.0.0.0
PORT=8000

36
test_fix/test_config.py Normal file
View file

@ -0,0 +1,36 @@
import os
import sys
import json
# Add parent directory to path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# Set environment variable to use our test .env
os.environ['ENV_FILE'] = os.path.join(os.path.dirname(__file__), 'env.test.txt')
print(f"Using env file: {os.environ['ENV_FILE']}")
try:
print("Attempting to import config...")
from app.core.config import settings
print("✓ Config import successful!")
print(f" DEBUG: {settings.debug}")
print(f" LOG_LEVEL: {settings.log_level}")
print(f" HOST: {settings.host}")
print(f" PORT: {settings.port}")
print(f" OAUTH2_SUPPORTED_SCOPES: {settings.oauth2_supported_scopes}")
print(f" OAUTH2_SUPPORTED_GRANT_TYPES: {settings.oauth2_supported_grant_types}")
print(f" Type of scopes: {type(settings.oauth2_supported_scopes)}")
print(f" Type of grant types: {type(settings.oauth2_supported_grant_types)}")
# Test JSON serialization
print(f" Scopes as JSON: {json.dumps(settings.oauth2_supported_scopes)}")
print("✓ All configuration tests passed!")
except Exception as e:
print(f"✗ Config import failed: {e}")
import traceback
traceback.print_exc()
sys.exit(1)