Fix TTS server status errors and startup warnings
- Fixed 'app is not defined' errors by using current_app - Improved TTS health check to handle missing /health endpoint - Fixed database trigger creation to be idempotent - Added .env.example with all configuration options - Updated README with security configuration instructions
This commit is contained in:
parent
c97d025acb
commit
e5a274d191
79
.env.example
79
.env.example
@ -1,22 +1,73 @@
|
||||
# Example environment configuration for Talk2Me
|
||||
# Copy this file to .env and update with your actual values
|
||||
# Talk2Me Environment Configuration
|
||||
# Copy this file to .env and fill in your values
|
||||
|
||||
# Flask Configuration
|
||||
SECRET_KEY=your-secret-key-here-change-this
|
||||
FLASK_ENV=development
|
||||
FLASK_SECRET_KEY=your-secret-key-here-change-in-production
|
||||
FLASK_DEBUG=False
|
||||
|
||||
# Upload Configuration
|
||||
UPLOAD_FOLDER=/path/to/secure/upload/folder
|
||||
# Server Configuration
|
||||
HOST=0.0.0.0
|
||||
PORT=5005
|
||||
|
||||
# TTS Server Configuration
|
||||
TTS_SERVER_URL=http://localhost:5050/v1/audio/speech
|
||||
# Database Configuration
|
||||
DATABASE_URL=postgresql://user:password@localhost:5432/talk2me
|
||||
REDIS_URL=redis://localhost:6379/0
|
||||
|
||||
# Ollama Configuration
|
||||
OLLAMA_BASE_URL=http://localhost:11434
|
||||
OLLAMA_MODEL=gemma2:9b
|
||||
OLLAMA_LARGE_MODEL=gemma3:27b
|
||||
|
||||
# TTS Configuration
|
||||
TTS_SERVER_URL=http://localhost:8000
|
||||
TTS_API_KEY=your-tts-api-key-here
|
||||
|
||||
# CORS Configuration (for production)
|
||||
CORS_ORIGINS=https://yourdomain.com,https://app.yourdomain.com
|
||||
ADMIN_CORS_ORIGINS=https://admin.yourdomain.com
|
||||
# Security Configuration
|
||||
JWT_SECRET_KEY=your-jwt-secret-key-here
|
||||
JWT_ACCESS_TOKEN_EXPIRES=3600
|
||||
JWT_REFRESH_TOKEN_EXPIRES=2592000
|
||||
|
||||
# Admin Token (for admin endpoints)
|
||||
ADMIN_TOKEN=your-secure-admin-token-here
|
||||
# Admin Configuration
|
||||
ADMIN_USERNAME=admin
|
||||
ADMIN_PASSWORD=change-this-password
|
||||
ADMIN_EMAIL=admin@example.com
|
||||
|
||||
# Optional: GPU Configuration
|
||||
# CUDA_VISIBLE_DEVICES=0
|
||||
# Rate Limiting
|
||||
RATE_LIMIT_PER_MINUTE=60
|
||||
RATE_LIMIT_PER_HOUR=1000
|
||||
|
||||
# Session Configuration
|
||||
SESSION_LIFETIME=86400
|
||||
SESSION_CLEANUP_INTERVAL=3600
|
||||
|
||||
# Logging
|
||||
LOG_LEVEL=INFO
|
||||
LOG_FORMAT=json
|
||||
|
||||
# CORS Configuration
|
||||
CORS_ORIGINS=http://localhost:3000,http://localhost:5005
|
||||
|
||||
# Feature Flags
|
||||
ENABLE_ANALYTICS=true
|
||||
ENABLE_RATE_LIMITING=true
|
||||
ENABLE_SESSION_MANAGEMENT=true
|
||||
ENABLE_ERROR_TRACKING=true
|
||||
|
||||
# Performance Settings
|
||||
MAX_CONTENT_LENGTH=16777216
|
||||
REQUEST_TIMEOUT=300
|
||||
WHISPER_MODEL=base
|
||||
WHISPER_DEVICE=auto
|
||||
|
||||
# Email Configuration (Optional)
|
||||
SMTP_HOST=smtp.gmail.com
|
||||
SMTP_PORT=587
|
||||
SMTP_USER=your-email@example.com
|
||||
SMTP_PASSWORD=your-email-password
|
||||
SMTP_FROM=noreply@example.com
|
||||
|
||||
# External Services (Optional)
|
||||
SENTRY_DSN=
|
||||
DATADOG_API_KEY=
|
||||
NEWRELIC_LICENSE_KEY=
|
17
README.md
17
README.md
@ -145,10 +145,17 @@ python manage_secrets.py rotate
|
||||
|
||||
#### Using Environment Variables
|
||||
|
||||
Create a `.env` file:
|
||||
Create a `.env` file by copying `.env.example`:
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
Key environment variables:
|
||||
|
||||
```env
|
||||
# Core Configuration
|
||||
# Core Configuration (REQUIRED for production)
|
||||
FLASK_SECRET_KEY=your-secret-key-here-change-in-production # IMPORTANT: Set this for production!
|
||||
TTS_API_KEY=your-api-key-here
|
||||
TTS_SERVER_URL=http://localhost:5050/v1/audio/speech
|
||||
ADMIN_TOKEN=your-secure-admin-token
|
||||
@ -169,6 +176,12 @@ GPU_MEMORY_THRESHOLD_MB=2048
|
||||
MEMORY_CLEANUP_INTERVAL=30
|
||||
```
|
||||
|
||||
**Important**: Always set `FLASK_SECRET_KEY` to a secure, random value in production. You can generate one using:
|
||||
|
||||
```bash
|
||||
python -c "import secrets; print(secrets.token_hex(32))"
|
||||
```
|
||||
|
||||
### Advanced Configuration
|
||||
|
||||
#### CORS Settings
|
||||
|
@ -1,4 +1,4 @@
|
||||
from flask import Blueprint, request, jsonify, render_template, redirect, url_for, session
|
||||
from flask import Blueprint, request, jsonify, render_template, redirect, url_for, session, current_app
|
||||
from functools import wraps
|
||||
import os
|
||||
import logging
|
||||
@ -483,14 +483,26 @@ def check_system_health():
|
||||
health['overall'] = 'degraded'
|
||||
|
||||
# Check TTS Server
|
||||
tts_server_url = app.config.get('TTS_SERVER_URL')
|
||||
tts_server_url = current_app.config.get('TTS_SERVER_URL')
|
||||
if tts_server_url:
|
||||
try:
|
||||
import requests
|
||||
response = requests.get(f"{tts_server_url}/health", timeout=2)
|
||||
# Extract base URL from the speech endpoint
|
||||
base_url = tts_server_url.rsplit('/v1/audio/speech', 1)[0] if '/v1/audio/speech' in tts_server_url else tts_server_url
|
||||
health_url = f"{base_url}/health" if not tts_server_url.endswith('/health') else tts_server_url
|
||||
response = requests.get(health_url, timeout=2)
|
||||
if response.status_code == 200:
|
||||
health['tts'] = 'healthy'
|
||||
health['tts_details'] = response.json() if response.headers.get('content-type') == 'application/json' else {}
|
||||
elif response.status_code == 404:
|
||||
# Try voices endpoint as fallback
|
||||
voices_url = f"{base_url}/voices" if base_url else f"{tts_server_url.rsplit('/speech', 1)[0]}/voices"
|
||||
voices_response = requests.get(voices_url, timeout=2)
|
||||
if voices_response.status_code == 200:
|
||||
health['tts'] = 'healthy'
|
||||
else:
|
||||
health['tts'] = 'unhealthy'
|
||||
health['overall'] = 'degraded'
|
||||
else:
|
||||
health['tts'] = 'unhealthy'
|
||||
health['overall'] = 'degraded'
|
||||
@ -522,8 +534,8 @@ def get_tts_status():
|
||||
}
|
||||
|
||||
# Check configuration
|
||||
tts_server_url = app.config.get('TTS_SERVER_URL')
|
||||
tts_api_key = app.config.get('TTS_API_KEY')
|
||||
tts_server_url = current_app.config.get('TTS_SERVER_URL')
|
||||
tts_api_key = current_app.config.get('TTS_API_KEY')
|
||||
|
||||
if tts_server_url:
|
||||
tts_info['configured'] = True
|
||||
@ -538,7 +550,11 @@ def get_tts_status():
|
||||
headers['Authorization'] = f'Bearer {tts_api_key}'
|
||||
|
||||
# Check health endpoint
|
||||
response = requests.get(f"{tts_server_url}/health", headers=headers, timeout=3)
|
||||
# Extract base URL from the speech endpoint
|
||||
base_url = tts_server_url.rsplit('/v1/audio/speech', 1)[0] if '/v1/audio/speech' in tts_server_url else tts_server_url
|
||||
health_url = f"{base_url}/health" if not tts_server_url.endswith('/health') else tts_server_url
|
||||
|
||||
response = requests.get(health_url, headers=headers, timeout=3)
|
||||
if response.status_code == 200:
|
||||
tts_info['status'] = 'healthy'
|
||||
if response.headers.get('content-type') == 'application/json':
|
||||
@ -549,11 +565,16 @@ def get_tts_status():
|
||||
|
||||
# Try to get voice list
|
||||
try:
|
||||
voices_response = requests.get(f"{tts_server_url}/voices", headers=headers, timeout=3)
|
||||
voices_url = f"{base_url}/voices" if base_url else f"{tts_server_url.rsplit('/speech', 1)[0]}/voices"
|
||||
voices_response = requests.get(voices_url, headers=headers, timeout=3)
|
||||
if voices_response.status_code == 200 and voices_response.headers.get('content-type') == 'application/json':
|
||||
voices_data = voices_response.json()
|
||||
tts_info['details']['available_voices'] = voices_data.get('voices', [])
|
||||
tts_info['details']['voice_count'] = len(voices_data.get('voices', []))
|
||||
# If we can get voices, consider the server healthy even if health endpoint doesn't exist
|
||||
if tts_info['status'] == 'unhealthy' and response.status_code == 404:
|
||||
tts_info['status'] = 'healthy'
|
||||
tts_info['details'].pop('error', None)
|
||||
except:
|
||||
pass
|
||||
|
||||
@ -636,7 +657,7 @@ def stream_updates():
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
return app.response_class(
|
||||
return current_app.response_class(
|
||||
generate(),
|
||||
mimetype='text/event-stream',
|
||||
headers={
|
||||
|
@ -253,6 +253,11 @@ def init_db(app):
|
||||
$$ language 'plpgsql';
|
||||
"""))
|
||||
|
||||
# Drop existing trigger if it exists and recreate it
|
||||
db.session.execute(text("""
|
||||
DROP TRIGGER IF EXISTS update_user_preferences_updated_at ON user_preferences;
|
||||
"""))
|
||||
|
||||
# Create trigger for user_preferences
|
||||
db.session.execute(text("""
|
||||
CREATE TRIGGER update_user_preferences_updated_at
|
||||
@ -263,6 +268,6 @@ def init_db(app):
|
||||
|
||||
db.session.commit()
|
||||
except Exception as e:
|
||||
# Triggers might already exist
|
||||
# Log error but don't fail - database might not support triggers
|
||||
db.session.rollback()
|
||||
app.logger.debug(f"Database initialization note: {e}")
|
@ -178,6 +178,11 @@ def upgrade():
|
||||
$$ language 'plpgsql';
|
||||
""")
|
||||
|
||||
# Drop existing trigger if it exists and recreate it
|
||||
op.execute("""
|
||||
DROP TRIGGER IF EXISTS update_users_updated_at ON users;
|
||||
""")
|
||||
|
||||
# Create trigger for users table
|
||||
op.execute("""
|
||||
CREATE TRIGGER update_users_updated_at
|
||||
|
Loading…
Reference in New Issue
Block a user