95 lines
4.3 KiB
Python
95 lines
4.3 KiB
Python
import os
|
|
from dotenv import load_dotenv
|
|
|
|
# Use an absolute path so .env is found regardless of the process working
|
|
# directory — under systemd the CWD may not be the project root.
|
|
_here = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
load_dotenv(os.path.join(_here, '.env'))
|
|
|
|
class Config:
|
|
SECRET_KEY = os.environ.get('SECRET_KEY') or 'dev-secret-key-change-in-production'
|
|
SQLALCHEMY_DATABASE_URI = (
|
|
f"mysql+pymysql://{os.environ.get('DB_USER', 'root')}:"
|
|
f"{os.environ.get('DB_PASSWORD', '')}@"
|
|
f"{os.environ.get('DB_HOST', 'localhost')}:"
|
|
f"{os.environ.get('DB_PORT', '3306')}/"
|
|
f"{os.environ.get('DB_NAME', 'it_tickets')}"
|
|
)
|
|
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
|
SQLALCHEMY_ENGINE_OPTIONS = {
|
|
'pool_recycle': 300,
|
|
'pool_pre_ping': True,
|
|
}
|
|
|
|
# Mail
|
|
MAIL_SERVER = os.environ.get('MAIL_SERVER', 'localhost')
|
|
MAIL_PORT = int(os.environ.get('MAIL_PORT', 587))
|
|
MAIL_USE_TLS = os.environ.get('MAIL_USE_TLS', 'True').lower() == 'true'
|
|
MAIL_USE_SSL = os.environ.get('MAIL_USE_SSL', 'False').lower() == 'true'
|
|
MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
|
|
MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
|
|
MAIL_DEFAULT_SENDER = os.environ.get('MAIL_DEFAULT_SENDER', 'IT Helpdesk <noreply@yourdomain.com>')
|
|
|
|
# IT Department
|
|
IT_DEPT_EMAIL = os.environ.get('IT_DEPT_EMAIL', 'it@yourdomain.com')
|
|
APP_BASE_URL = os.environ.get('APP_BASE_URL', 'http://localhost:5000')
|
|
|
|
# Uploads
|
|
# UPLOAD_FOLDER must be an absolute path so that save (os.path.join) and
|
|
# serve (send_from_directory) always resolve to the same directory regardless
|
|
# of the process CWD. The default anchors to the project root via __file__.
|
|
_project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
_raw_upload = os.environ.get('UPLOAD_FOLDER', '')
|
|
UPLOAD_FOLDER = (
|
|
_raw_upload if (_raw_upload and os.path.isabs(_raw_upload))
|
|
else os.path.join(_project_root, 'app', 'static', 'uploads')
|
|
)
|
|
MAX_CONTENT_LENGTH = int(os.environ.get('MAX_CONTENT_LENGTH', 16 * 1024 * 1024))
|
|
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'pdf', 'doc', 'docx', 'txt', 'zip', 'log'}
|
|
|
|
# AI Chatbot — Groq (free tier: 14,400 req/day, no credit card, no region restrictions)
|
|
# Get a key at: https://console.groq.com → API Keys
|
|
GROQ_API_KEY = os.environ.get('GROQ_API_KEY', '')
|
|
GROQ_MODEL = os.environ.get('GROQ_MODEL', 'llama-3.3-70b-versatile')
|
|
|
|
# Rate limiting storage.
|
|
# The `limits` library (used by Flask-Limiter) does not support MySQL as a
|
|
# storage backend — its only supported schemes are memory://, redis://, and
|
|
# memcached://. Since gunicorn.conf.py enforces a single worker process
|
|
# (workers = 1, worker_class = eventlet), in-process memory is both correct
|
|
# and sufficient — there is no other worker to share counters with, and a
|
|
# counter reset on restart is an acceptable trade-off versus adding Redis.
|
|
# To switch to Redis when it becomes available: set RATELIMIT_STORAGE_URI=redis://...
|
|
RATELIMIT_STORAGE_URI = os.environ.get('RATELIMIT_STORAGE_URI', 'memory://')
|
|
|
|
# SLA — hours until a ticket is considered overdue, by priority.
|
|
# Override via environment variables if needed (e.g. SLA_CRITICAL_HOURS=2).
|
|
# These are also seeded into SystemSetting so admins can tune them in-app.
|
|
SLA_CRITICAL_HOURS = int(os.environ.get('SLA_CRITICAL_HOURS', 4))
|
|
SLA_HIGH_HOURS = int(os.environ.get('SLA_HIGH_HOURS', 8))
|
|
SLA_MEDIUM_HOURS = int(os.environ.get('SLA_MEDIUM_HOURS', 48))
|
|
SLA_LOW_HOURS = int(os.environ.get('SLA_LOW_HOURS', 120))
|
|
|
|
# Admin
|
|
ADMIN_EMAIL = os.environ.get('ADMIN_EMAIL', 'admin@yourdomain.com')
|
|
ADMIN_PASSWORD = os.environ.get('ADMIN_PASSWORD', 'Admin@123!')
|
|
|
|
|
|
class DevelopmentConfig(Config):
|
|
DEBUG = True
|
|
SQLALCHEMY_ECHO = False
|
|
|
|
|
|
class ProductionConfig(Config):
|
|
DEBUG = False
|
|
# Session cookie hardening — enforce HTTPS-only, no JS access, strict same-site.
|
|
SESSION_COOKIE_SECURE = True # only sent over HTTPS
|
|
SESSION_COOKIE_HTTPONLY = True # not accessible via document.cookie
|
|
SESSION_COOKIE_SAMESITE = 'Lax' # blocks cross-site POST forgery; 'Strict' would break OAuth flows
|
|
|
|
|
|
config = {
|
|
'development': DevelopmentConfig,
|
|
'production': ProductionConfig,
|
|
'default': ProductionConfig
|
|
} |