-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconfig.py
More file actions
159 lines (125 loc) · 5.55 KB
/
Copy pathconfig.py
File metadata and controls
159 lines (125 loc) · 5.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
"""
Configuration management for URL Shortener
Supports multiple environments: development, testing, production
"""
import os
from typing import Optional
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
class Config:
"""Base configuration"""
# Application
APP_NAME = "url-shortener"
ENV = os.getenv("FLASK_ENV", "development")
DEBUG = os.getenv("DEBUG", "False").lower() == "true"
SECRET_KEY = os.getenv("SECRET_KEY", "dev-secret-key-change-in-production")
# Server
HOST = os.getenv("HOST", "0.0.0.0")
PORT = int(os.getenv("PORT", "5000"))
# Redis
REDIS_HOST = os.getenv("REDIS_HOST", "redis")
REDIS_PORT = int(os.getenv("REDIS_PORT", "6379"))
REDIS_DB = int(os.getenv("REDIS_DB", "0"))
REDIS_PASSWORD = os.getenv("REDIS_PASSWORD")
REDIS_MAX_CONNECTIONS = int(os.getenv("REDIS_MAX_CONNECTIONS", "50"))
# MySQL/Database
DB_HOST = os.getenv("MYSQL_HOST", "db")
DB_PORT = int(os.getenv("MYSQL_PORT", "3306"))
DB_USER = os.getenv("MYSQL_USER", "root")
DB_PASSWORD = os.getenv("MYSQL_PASSWORD", "example")
DB_DATABASE = os.getenv("MYSQL_DB", "urlshortener")
DB_POOL_SIZE = int(os.getenv("MYSQL_POOL_SIZE", "10"))
DB_POOL_RECYCLE = int(os.getenv("MYSQL_POOL_RECYCLE", "3600"))
DB_POOL_TIMEOUT = int(os.getenv("MYSQL_POOL_TIMEOUT", "30"))
# Celery
CELERY_BROKER_URL = f"redis://{REDIS_HOST}:{REDIS_PORT}/0"
CELERY_RESULT_BACKEND = f"redis://{REDIS_HOST}:{REDIS_PORT}/0"
CELERY_TASK_SERIALIZER = "json"
CELERY_RESULT_SERIALIZER = "json"
CELERY_ACCEPT_CONTENT = ["json"]
CELERY_TIMEZONE = "UTC"
CELERY_ENABLE_UTC = True
CELERY_TASK_TRACK_STARTED = True
CELERY_TASK_TIME_LIMIT = 30 * 60 # 30 minutes
CELERY_TASK_SOFT_TIME_LIMIT = 25 * 60 # 25 minutes
# JWT
JWT_PRIVATE_KEY_PATH = os.getenv("JWT_PRIVATE_KEY", "private.pem")
JWT_PUBLIC_KEY_PATH = os.getenv("JWT_PUBLIC_KEY", "public.pem")
JWT_ALGORITHM = os.getenv("JWT_ALGORITHM", "RS256")
JWT_ACCESS_TOKEN_EXPIRES = int(os.getenv("JWT_ACCESS_TOKEN_EXPIRES", "900")) # 15 minutes
JWT_REFRESH_TOKEN_EXPIRES = int(os.getenv("JWT_REFRESH_TOKEN_EXPIRES", "2592000")) # 30 days
JWT_ISSUER = os.getenv("JWT_ISSUER", "url-shortener.com")
JWT_AUDIENCE = os.getenv("JWT_AUDIENCE", "url-shortener.com")
# Rate Limiting
RATE_LIMIT_PER_MINUTE = int(os.getenv("RATE_LIMIT_PER_MINUTE", "60"))
RATE_LIMIT_PER_HOUR = int(os.getenv("RATE_LIMIT_PER_HOUR", "1000"))
RATE_LIMIT_STORAGE_URL = f"redis://{REDIS_HOST}:{REDIS_PORT}/1"
# Fraud Detection
MAX_CLICKS_PER_MINUTE_PER_IP = int(os.getenv("MAX_CLICKS_PER_MINUTE_PER_IP", "10"))
MAX_CLICKS_PER_MINUTE_PER_IP_URL = int(os.getenv("MAX_CLICKS_PER_MINUTE_PER_IP_URL", "5"))
VELOCITY_THRESHOLD = float(os.getenv("VELOCITY_THRESHOLD", "1.0"))
MAX_CLICKS_PER_WINDOW = int(os.getenv("MAX_CLICKS_PER_WINDOW", "5"))
WINDOW_SECONDS = int(os.getenv("WINDOW_SECONDS", "10"))
MAX_SEQUENCE_LENGTH = int(os.getenv("MAX_SEQUENCE_LENGTH", "5"))
# URL Settings
SHORT_CODE_LENGTH = int(os.getenv("SHORT_CODE_LENGTH", "8"))
MAX_URL_LENGTH = int(os.getenv("MAX_URL_LENGTH", "2048"))
URL_EXPIRY_DAYS = int(os.getenv("URL_EXPIRY_DAYS", "0")) # 0 = never expire
# Cache Settings
CACHE_DEFAULT_TIMEOUT = int(os.getenv("CACHE_DEFAULT_TIMEOUT", "86400")) # 1 day
CACHE_URL_TIMEOUT = int(os.getenv("CACHE_URL_TIMEOUT", "86400")) # 1 day
# Logging
LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")
LOG_FORMAT = os.getenv("LOG_FORMAT", "json") # json or text
# CORS
CORS_ORIGINS = os.getenv("CORS_ORIGINS", "*").split(",")
# Monitoring
PROMETHEUS_MULTIPROC_DIR = os.getenv("PROMETHEUS_MULTIPROC_DIR", "/tmp")
ENABLE_METRICS = os.getenv("ENABLE_METRICS", "True").lower() == "true"
# GeoIP
GEOIP_DB_PATH = os.getenv("GEOIP_DB_PATH", "data/GeoLite2-City.mmdb")
class DevelopmentConfig(Config):
"""Development configuration"""
DEBUG = True
LOG_LEVEL = "DEBUG"
class TestingConfig(Config):
"""Testing configuration"""
TESTING = True
DEBUG = True
# Use separate databases for testing
DB_DATABASE = "urlshortener_test"
REDIS_DB = 10
# Shorter timeouts for tests
JWT_ACCESS_TOKEN_EXPIRES = 60 # 1 minute
JWT_REFRESH_TOKEN_EXPIRES = 300 # 5 minutes
class ProductionConfig(Config):
"""Production configuration"""
DEBUG = False
# Stricter settings for production
RATE_LIMIT_PER_MINUTE = 30
RATE_LIMIT_PER_HOUR = 500
# Better pool settings
DB_POOL_SIZE = 20
REDIS_MAX_CONNECTIONS = 100
# Ensure secret key is set
SECRET_KEY = os.getenv("SECRET_KEY")
if not SECRET_KEY or SECRET_KEY == "dev-secret-key-change-in-production":
raise ValueError("SECRET_KEY must be set in production!")
# Ensure JWT keys are properly configured
if not os.path.exists(Config.JWT_PRIVATE_KEY_PATH):
raise FileNotFoundError(f"JWT private key not found at {Config.JWT_PRIVATE_KEY_PATH}")
if not os.path.exists(Config.JWT_PUBLIC_KEY_PATH):
raise FileNotFoundError(f"JWT public key not found at {Config.JWT_PUBLIC_KEY_PATH}")
# Config dictionary
config = {
"development": DevelopmentConfig,
"testing": TestingConfig,
"production": ProductionConfig,
"default": DevelopmentConfig
}
def get_config(env: Optional[str] = None) -> Config:
"""Get configuration based on environment"""
if env is None:
env = os.getenv("FLASK_ENV", "development")
return config.get(env, config["default"])()