aprhodite/config.py

107 lines
3.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
config.py Shared configuration and utilities for SexyChat.
Centralises constants, config loading, AES-GCM helpers, and JWT helpers
so that app.py and routes.py share a single source of truth.
"""
import os
import json
import uuid
import base64
from datetime import datetime, timezone, timedelta
import jwt as pyjwt
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
# ---------------------------------------------------------------------------
# Configuration Loader
# ---------------------------------------------------------------------------
def load_config():
conf = {}
config_path = os.path.join(os.path.dirname(__file__), "config.json")
if os.path.exists(config_path):
try:
with open(config_path, "r") as f:
conf = json.load(f)
except Exception as e:
print(f"⚠️ Warning: Failed to load config.json: {e}")
return conf
_CONFIG = load_config()
def get_conf(key, default=None):
"""Resolve a config value: Env Var → config.json → default."""
return os.environ.get(key, _CONFIG.get(key, default))
# ---------------------------------------------------------------------------
# Shared Constants
# ---------------------------------------------------------------------------
SECRET_KEY = get_conf("SECRET_KEY", uuid.uuid4().hex)
JWT_SECRET = get_conf("JWT_SECRET", uuid.uuid4().hex)
ADMIN_PASSWORD = get_conf("ADMIN_PASSWORD", "admin1234")
DATABASE_URL = get_conf("DATABASE_URL", "sqlite:///sexchat.db")
PAYMENT_SECRET = get_conf("PAYMENT_SECRET", "change-me-payment-secret")
CORS_ORIGINS = get_conf("CORS_ORIGINS", None)
MAX_MSG_LEN = 500
LOBBY = "lobby"
AI_FREE_LIMIT = int(get_conf("AI_FREE_LIMIT", 3))
AI_BOT_NAME = "Violet"
JWT_EXPIRY_DAYS = 7
MAX_HISTORY = 500
# Ollama
OLLAMA_URL = get_conf("OLLAMA_URL", "http://localhost:11434")
VIOLET_MODEL = get_conf("VIOLET_MODEL", "sam860/dolphin3-llama3.2:3b")
VIOLET_SYSTEM = (
"You are Violet, a flirtatious and sophisticated nightclub hostess at "
"an exclusive, dimly-lit members-only club. You are charming, witty, "
"and seductive — never crude or offensive. You speak with elegance, "
"mystery, and a hint of playful danger. Keep every reply to 13 "
"sentences maximum. You are in a private conversation with a special "
"guest who has caught your eye."
)
# ---------------------------------------------------------------------------
# AES-GCM Helpers
# ---------------------------------------------------------------------------
def aesgcm_encrypt(key_b64: str, plaintext: str) -> tuple:
"""Encrypt plaintext with AES-GCM. Returns (ciphertext_b64, nonce_b64)."""
key = base64.b64decode(key_b64)
nonce = os.urandom(12)
ct = AESGCM(key).encrypt(nonce, plaintext.encode("utf-8"), None)
return base64.b64encode(ct).decode(), base64.b64encode(nonce).decode()
def aesgcm_decrypt(key_b64: str, ciphertext_b64: str, nonce_b64: str) -> str:
"""Decrypt AES-GCM ciphertext. Raises on authentication failure."""
key = base64.b64decode(key_b64)
ct = base64.b64decode(ciphertext_b64)
nonce = base64.b64decode(nonce_b64)
return AESGCM(key).decrypt(nonce, ct, None).decode("utf-8")
# ---------------------------------------------------------------------------
# JWT Helpers
# ---------------------------------------------------------------------------
def issue_jwt(user_id: int, username: str) -> str:
"""Issue a signed JWT with user_id and username claims."""
payload = {
"user_id": user_id,
"username": username,
"exp": datetime.now(timezone.utc) + timedelta(days=JWT_EXPIRY_DAYS),
}
return pyjwt.encode(payload, JWT_SECRET, algorithm="HS256")
def verify_jwt(token: str):
"""Decode and verify a JWT. Returns payload dict or None."""
try:
return pyjwt.decode(token, JWT_SECRET, algorithms=["HS256"])
except pyjwt.PyJWTError:
return None