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
|
# Talk2Me Environment Configuration
|
||||||
# Copy this file to .env and update with your actual values
|
# Copy this file to .env and fill in your values
|
||||||
|
|
||||||
# Flask Configuration
|
# 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
|
# Server Configuration
|
||||||
UPLOAD_FOLDER=/path/to/secure/upload/folder
|
HOST=0.0.0.0
|
||||||
|
PORT=5005
|
||||||
|
|
||||||
# TTS Server Configuration
|
# Database Configuration
|
||||||
TTS_SERVER_URL=http://localhost:5050/v1/audio/speech
|
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
|
TTS_API_KEY=your-tts-api-key-here
|
||||||
|
|
||||||
# CORS Configuration (for production)
|
# Security Configuration
|
||||||
CORS_ORIGINS=https://yourdomain.com,https://app.yourdomain.com
|
JWT_SECRET_KEY=your-jwt-secret-key-here
|
||||||
ADMIN_CORS_ORIGINS=https://admin.yourdomain.com
|
JWT_ACCESS_TOKEN_EXPIRES=3600
|
||||||
|
JWT_REFRESH_TOKEN_EXPIRES=2592000
|
||||||
|
|
||||||
# Admin Token (for admin endpoints)
|
# Admin Configuration
|
||||||
ADMIN_TOKEN=your-secure-admin-token-here
|
ADMIN_USERNAME=admin
|
||||||
|
ADMIN_PASSWORD=change-this-password
|
||||||
|
ADMIN_EMAIL=admin@example.com
|
||||||
|
|
||||||
# Optional: GPU Configuration
|
# Rate Limiting
|
||||||
# CUDA_VISIBLE_DEVICES=0
|
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
|
#### Using Environment Variables
|
||||||
|
|
||||||
Create a `.env` file:
|
Create a `.env` file by copying `.env.example`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
Key environment variables:
|
||||||
|
|
||||||
```env
|
```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_API_KEY=your-api-key-here
|
||||||
TTS_SERVER_URL=http://localhost:5050/v1/audio/speech
|
TTS_SERVER_URL=http://localhost:5050/v1/audio/speech
|
||||||
ADMIN_TOKEN=your-secure-admin-token
|
ADMIN_TOKEN=your-secure-admin-token
|
||||||
@ -169,6 +176,12 @@ GPU_MEMORY_THRESHOLD_MB=2048
|
|||||||
MEMORY_CLEANUP_INTERVAL=30
|
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
|
### Advanced Configuration
|
||||||
|
|
||||||
#### CORS Settings
|
#### 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
|
from functools import wraps
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
@ -483,14 +483,26 @@ def check_system_health():
|
|||||||
health['overall'] = 'degraded'
|
health['overall'] = 'degraded'
|
||||||
|
|
||||||
# Check TTS Server
|
# 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:
|
if tts_server_url:
|
||||||
try:
|
try:
|
||||||
import requests
|
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:
|
if response.status_code == 200:
|
||||||
health['tts'] = 'healthy'
|
health['tts'] = 'healthy'
|
||||||
health['tts_details'] = response.json() if response.headers.get('content-type') == 'application/json' else {}
|
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:
|
else:
|
||||||
health['tts'] = 'unhealthy'
|
health['tts'] = 'unhealthy'
|
||||||
health['overall'] = 'degraded'
|
health['overall'] = 'degraded'
|
||||||
@ -522,8 +534,8 @@ def get_tts_status():
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Check configuration
|
# Check configuration
|
||||||
tts_server_url = app.config.get('TTS_SERVER_URL')
|
tts_server_url = current_app.config.get('TTS_SERVER_URL')
|
||||||
tts_api_key = app.config.get('TTS_API_KEY')
|
tts_api_key = current_app.config.get('TTS_API_KEY')
|
||||||
|
|
||||||
if tts_server_url:
|
if tts_server_url:
|
||||||
tts_info['configured'] = True
|
tts_info['configured'] = True
|
||||||
@ -538,7 +550,11 @@ def get_tts_status():
|
|||||||
headers['Authorization'] = f'Bearer {tts_api_key}'
|
headers['Authorization'] = f'Bearer {tts_api_key}'
|
||||||
|
|
||||||
# Check health endpoint
|
# 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:
|
if response.status_code == 200:
|
||||||
tts_info['status'] = 'healthy'
|
tts_info['status'] = 'healthy'
|
||||||
if response.headers.get('content-type') == 'application/json':
|
if response.headers.get('content-type') == 'application/json':
|
||||||
@ -549,11 +565,16 @@ def get_tts_status():
|
|||||||
|
|
||||||
# Try to get voice list
|
# Try to get voice list
|
||||||
try:
|
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':
|
if voices_response.status_code == 200 and voices_response.headers.get('content-type') == 'application/json':
|
||||||
voices_data = voices_response.json()
|
voices_data = voices_response.json()
|
||||||
tts_info['details']['available_voices'] = voices_data.get('voices', [])
|
tts_info['details']['available_voices'] = voices_data.get('voices', [])
|
||||||
tts_info['details']['voice_count'] = len(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:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -636,7 +657,7 @@ def stream_updates():
|
|||||||
|
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
return app.response_class(
|
return current_app.response_class(
|
||||||
generate(),
|
generate(),
|
||||||
mimetype='text/event-stream',
|
mimetype='text/event-stream',
|
||||||
headers={
|
headers={
|
||||||
|
@ -253,6 +253,11 @@ def init_db(app):
|
|||||||
$$ language 'plpgsql';
|
$$ 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
|
# Create trigger for user_preferences
|
||||||
db.session.execute(text("""
|
db.session.execute(text("""
|
||||||
CREATE TRIGGER update_user_preferences_updated_at
|
CREATE TRIGGER update_user_preferences_updated_at
|
||||||
@ -263,6 +268,6 @@ def init_db(app):
|
|||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# Triggers might already exist
|
# Log error but don't fail - database might not support triggers
|
||||||
db.session.rollback()
|
db.session.rollback()
|
||||||
app.logger.debug(f"Database initialization note: {e}")
|
app.logger.debug(f"Database initialization note: {e}")
|
@ -178,6 +178,11 @@ def upgrade():
|
|||||||
$$ language 'plpgsql';
|
$$ 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
|
# Create trigger for users table
|
||||||
op.execute("""
|
op.execute("""
|
||||||
CREATE TRIGGER update_users_updated_at
|
CREATE TRIGGER update_users_updated_at
|
||||||
|
Loading…
Reference in New Issue
Block a user