Major updates: LinkedIn auto-posting, timezone fixes, and Docker improvements

Features:
- Add LinkedIn OAuth integration and auto-posting functionality
- Add scheduler service for automated post publishing
- Add metadata field to generated_posts for LinkedIn URLs
- Add privacy policy page for LinkedIn API compliance
- Add company management features and employee accounts
- Add license key system for company registrations

Fixes:
- Fix timezone issues (use UTC consistently across app)
- Fix datetime serialization errors in database operations
- Fix scheduling timezone conversion (local time to UTC)
- Fix import errors (get_database -> db)

Infrastructure:
- Update Docker setup to use port 8001 (avoid conflicts)
- Add SSL support with nginx-proxy and Let's Encrypt
- Add LinkedIn setup documentation
- Add migration scripts for schema updates

Services:
- Add linkedin_service.py for LinkedIn API integration
- Add scheduler_service.py for background job processing
- Add storage_service.py for Supabase Storage
- Add email_service.py improvements
- Add encryption utilities for token storage

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-11 11:30:20 +01:00
parent b50594dbfa
commit f14515e9cf
94 changed files with 21601 additions and 5111 deletions

View File

@@ -6,7 +6,6 @@ from pathlib import Path
class Settings(BaseSettings):
"""Application settings loaded from environment variables."""
# API Keys
openai_api_key: str
perplexity_api_key: str
@@ -15,6 +14,7 @@ class Settings(BaseSettings):
# Supabase
supabase_url: str
supabase_key: str
supabase_service_role_key: str = "" # Required for admin operations like deleting users
# Apify
apify_actor_id: str = "apimaestro~linkedin-profile-posts"
@@ -46,6 +46,14 @@ class Settings(BaseSettings):
user_frontend_enabled: bool = True # Enable user frontend with LinkedIn OAuth
supabase_redirect_url: str = "" # OAuth Callback URL (e.g., https://linkedin.onyva.dev/auth/callback)
# LinkedIn API (Custom OAuth for auto-posting)
linkedin_client_id: str = ""
linkedin_client_secret: str = ""
linkedin_redirect_uri: str = "" # e.g., https://yourdomain.com/settings/linkedin/callback
# Token Encryption
encryption_key: str = "" # Generate with: python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
@@ -55,3 +63,18 @@ class Settings(BaseSettings):
# Global settings instance
settings = Settings()
# API pricing per 1M tokens (input, output)
API_PRICING = {
"gpt-4o": {"input": 2.50, "output": 10.00},
"gpt-4o-mini": {"input": 0.15, "output": 0.60},
"sonar": {"input": 1.00, "output": 1.00},
}
def estimate_cost(model: str, prompt_tokens: int, completion_tokens: int) -> float:
"""Estimate cost in USD for an API call."""
pricing = API_PRICING.get(model, {"input": 1.00, "output": 1.00})
input_cost = (prompt_tokens / 1_000_000) * pricing["input"]
output_cost = (completion_tokens / 1_000_000) * pricing["output"]
return input_cost + output_cost