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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user