diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..11420c3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +# Environment variables +.env + +# Python +__pycache__/ +*.pyc + +# BMad Configuration - Do not commit local changes +.bmad-core/core-config.yaml + +# Database +*.db +*.sqlite3 + +# IDE specific +.vscode/ +.idea/ \ No newline at end of file diff --git a/env.example b/env.example new file mode 100644 index 0000000..7db6ab7 --- /dev/null +++ b/env.example @@ -0,0 +1,48 @@ +# Database Configuration +DATABASE_URL=sqlite:///./content_automation.db + +# AI Service Configuration +AI_API_KEY=your_ai_service_api_key_here +AI_API_BASE_URL=https://api.openai.com/v1 +AI_MODEL=gpt-4 + +# AWS S3 Configuration +AWS_ACCESS_KEY_ID=your_aws_access_key_here +AWS_SECRET_ACCESS_KEY=your_aws_secret_key_here +AWS_REGION=us-east-1 + +# Azure Blob Storage Configuration +AZURE_STORAGE_ACCOUNT_NAME=your_azure_account_name_here +AZURE_STORAGE_ACCOUNT_KEY=your_azure_account_key_here + +# Bunny.net Configuration +BUNNY_API_KEY=your_bunny_api_key_here +BUNNY_STORAGE_ZONE=your_bunny_zone_here + +# Digital Ocean Spaces Configuration +DO_SPACES_ACCESS_KEY=your_do_spaces_key_here +DO_SPACES_SECRET_KEY=your_do_secret_key_here +DO_SPACES_REGION=nyc3 +DO_SPACES_ENDPOINT=https://nyc3.digitaloceanspaces.com + +# Linode Object Storage Configuration +LINODE_ACCESS_KEY=your_linode_access_key_here +LINODE_SECRET_KEY=your_linode_secret_key_here +LINODE_REGION=us-east-1 + +# Backblaze B2 Configuration +B2_APPLICATION_KEY_ID=your_b2_key_id_here +B2_APPLICATION_KEY=your_b2_application_key_here +B2_BUCKET_NAME=your_b2_bucket_name_here + +# Cloudflare Pages Configuration +CLOUDFLARE_API_TOKEN=your_cloudflare_token_here +CLOUDFLARE_ACCOUNT_ID=your_cloudflare_account_id_here + +# Link Building Machine API +LINK_BUILDER_API_URL=http://localhost:8001/api +LINK_BUILDER_API_KEY=your_link_builder_api_key_here + +# Application Configuration +LOG_LEVEL=INFO +ENVIRONMENT=development diff --git a/main.py b/main.py new file mode 100644 index 0000000..79a3762 --- /dev/null +++ b/main.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 +""" +Content Automation & Syndication Platform +Main CLI entry point +""" + +import sys +from pathlib import Path + +# Add src to Python path +src_path = Path(__file__).parent / "src" +sys.path.insert(0, str(src_path)) + +from cli.commands import app + +if __name__ == "__main__": + app() diff --git a/master.config.json b/master.config.json new file mode 100644 index 0000000..fed356d --- /dev/null +++ b/master.config.json @@ -0,0 +1,85 @@ +{ + "application": { + "name": "Content Automation & Syndication Platform", + "version": "1.0.0", + "environment": "development" + }, + "database": { + "url": "sqlite:///./content_automation.db", + "echo": false, + "pool_size": 5, + "max_overflow": 10 + }, + "ai_service": { + "provider": "openai", + "model": "gpt-4", + "max_tokens": 4000, + "temperature": 0.7, + "timeout": 30 + }, + "content_rules": { + "h1_keyword_required": true, + "h2_keyword_count": 1, + "h3_keyword_count": 1, + "faq_section_required": true, + "image_alt_text_required": true, + "min_content_length": 1000, + "max_content_length": 5000 + }, + "templates": { + "default": "basic", + "mappings": { + "aws-s3-bucket-1": "modern", + "bunny-bucket-1": "classic", + "azure-bucket-1": "minimal" + } + }, + "deployment": { + "providers": { + "aws": { + "enabled": true, + "default_region": "us-east-1" + }, + "azure": { + "enabled": true, + "default_region": "eastus" + }, + "bunny": { + "enabled": true + }, + "digitalocean": { + "enabled": true, + "default_region": "nyc3" + }, + "linode": { + "enabled": true, + "default_region": "us-east-1" + }, + "backblaze": { + "enabled": true + }, + "cloudflare": { + "enabled": true + } + } + }, + "interlinking": { + "wheel_links": true, + "home_page_link": true, + "random_article_link": true, + "max_links_per_article": 5 + }, + "logging": { + "level": "INFO", + "format": "json", + "file": "logs/app.log", + "max_size": "10MB", + "backup_count": 5 + }, + "api": { + "host": "0.0.0.0", + "port": 8000, + "reload": true, + "workers": 1 + } +} diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..367abf2 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,42 @@ +# Core Framework +fastapi==0.104.1 +uvicorn[standard]==0.24.0 + +# CLI Framework +click==8.1.7 +typer==0.9.0 + +# Database +sqlalchemy==2.0.23 +alembic==1.12.1 + +# Authentication +passlib[bcrypt]==1.7.4 +python-jose[cryptography]==3.3.0 + +# Configuration +pydantic==2.5.0 +python-dotenv==1.0.0 + +# Cloud Providers +boto3==1.34.0 +azure-storage-blob==12.19.0 +requests==2.31.0 + +# Data Processing +pandas==2.1.4 +openpyxl==3.1.2 + +# AI/ML (placeholder - to be specified based on chosen AI service) +openai==1.3.7 + +# Testing +pytest==7.4.3 +pytest-asyncio==0.21.1 +pytest-mock==3.12.0 +moto==4.2.14 + +# Development +black==23.11.0 +flake8==6.1.0 +mypy==1.7.1 diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..da1f86d --- /dev/null +++ b/src/__init__.py @@ -0,0 +1 @@ +# Content Automation & Syndication Platform diff --git a/src/api/__init__.py b/src/api/__init__.py new file mode 100644 index 0000000..99cf069 --- /dev/null +++ b/src/api/__init__.py @@ -0,0 +1 @@ +# API module for internal REST API diff --git a/src/api/main.py b/src/api/main.py new file mode 100644 index 0000000..e9dbbb9 --- /dev/null +++ b/src/api/main.py @@ -0,0 +1 @@ +# FastAPI app instance diff --git a/src/api/routes.py b/src/api/routes.py new file mode 100644 index 0000000..09a8f87 --- /dev/null +++ b/src/api/routes.py @@ -0,0 +1 @@ +# API endpoint definitions diff --git a/src/api/schemas.py b/src/api/schemas.py new file mode 100644 index 0000000..f4f4da6 --- /dev/null +++ b/src/api/schemas.py @@ -0,0 +1 @@ +# Pydantic models for API diff --git a/src/auth/__init__.py b/src/auth/__init__.py new file mode 100644 index 0000000..2c97749 --- /dev/null +++ b/src/auth/__init__.py @@ -0,0 +1 @@ +# Authentication module diff --git a/src/auth/service.py b/src/auth/service.py new file mode 100644 index 0000000..ac78ef7 --- /dev/null +++ b/src/auth/service.py @@ -0,0 +1 @@ +# Hashing, validation, role checks diff --git a/src/cli/__init__.py b/src/cli/__init__.py new file mode 100644 index 0000000..1e184fe --- /dev/null +++ b/src/cli/__init__.py @@ -0,0 +1 @@ +# CLI module diff --git a/src/cli/commands.py b/src/cli/commands.py new file mode 100644 index 0000000..6954cfc --- /dev/null +++ b/src/cli/commands.py @@ -0,0 +1,44 @@ +""" +CLI command definitions using Click +""" + +import click +from core.config import get_config + + +@click.group() +@click.version_option(version="1.0.0") +def app(): + """Content Automation & Syndication Platform CLI""" + pass + + +@app.command() +def config(): + """Show current configuration""" + try: + config = get_config() + click.echo("Current Configuration:") + click.echo(f"Application: {config.application.name} v{config.application.version}") + click.echo(f"Environment: {config.application.environment}") + click.echo(f"Database: {config.database.url}") + click.echo(f"AI Model: {config.ai_service.model}") + click.echo(f"Log Level: {config.logging.level}") + except Exception as e: + click.echo(f"Error loading configuration: {e}", err=True) + + +@app.command() +def health(): + """Check system health""" + try: + config = get_config() + click.echo("✅ Configuration loaded successfully") + click.echo("✅ System is healthy") + except Exception as e: + click.echo(f"❌ System health check failed: {e}", err=True) + raise click.Abort() + + +if __name__ == "__main__": + app() diff --git a/src/core/__init__.py b/src/core/__init__.py new file mode 100644 index 0000000..a5759d5 --- /dev/null +++ b/src/core/__init__.py @@ -0,0 +1 @@ +# Core application module diff --git a/src/core/config.py b/src/core/config.py new file mode 100644 index 0000000..d5f0dae --- /dev/null +++ b/src/core/config.py @@ -0,0 +1,157 @@ +""" +Configuration loading and validation module +""" + +import json +import os +from pathlib import Path +from typing import Dict, Any, Optional +from pydantic import BaseModel, Field +from dotenv import load_dotenv + + +class DatabaseConfig(BaseModel): + url: str + echo: bool = False + pool_size: int = 5 + max_overflow: int = 10 + + +class AIServiceConfig(BaseModel): + provider: str = "openai" + model: str = "gpt-4" + max_tokens: int = 4000 + temperature: float = 0.7 + timeout: int = 30 + + +class ContentRulesConfig(BaseModel): + h1_keyword_required: bool = True + h2_keyword_count: int = 1 + h3_keyword_count: int = 1 + faq_section_required: bool = True + image_alt_text_required: bool = True + min_content_length: int = 1000 + max_content_length: int = 5000 + + +class TemplateConfig(BaseModel): + default: str = "basic" + mappings: Dict[str, str] = Field(default_factory=dict) + + +class DeploymentConfig(BaseModel): + providers: Dict[str, Dict[str, Any]] = Field(default_factory=dict) + + +class InterlinkingConfig(BaseModel): + wheel_links: bool = True + home_page_link: bool = True + random_article_link: bool = True + max_links_per_article: int = 5 + + +class LoggingConfig(BaseModel): + level: str = "INFO" + format: str = "json" + file: str = "logs/app.log" + max_size: str = "10MB" + backup_count: int = 5 + + +class APIConfig(BaseModel): + host: str = "0.0.0.0" + port: int = 8000 + reload: bool = True + workers: int = 1 + + +class ApplicationConfig(BaseModel): + name: str + version: str + environment: str + + +class Config(BaseModel): + application: ApplicationConfig + database: DatabaseConfig + ai_service: AIServiceConfig + content_rules: ContentRulesConfig + templates: TemplateConfig + deployment: DeploymentConfig + interlinking: InterlinkingConfig + logging: LoggingConfig + api: APIConfig + + +class ConfigManager: + """Manages application configuration loading and validation""" + + def __init__(self, config_path: Optional[str] = None): + self.config_path = config_path or "master.config.json" + self._config: Optional[Config] = None + + def load_config(self) -> Config: + """Load and validate configuration from JSON file""" + if self._config is None: + # Load environment variables first + load_dotenv() + + # Load JSON configuration + config_path = Path(self.config_path) + if not config_path.exists(): + raise FileNotFoundError(f"Configuration file not found: {config_path}") + + with open(config_path, 'r') as f: + config_data = json.load(f) + + # Override with environment variables where applicable + self._override_with_env(config_data) + + # Validate configuration + self._config = Config(**config_data) + + return self._config + + def _override_with_env(self, config_data: Dict[str, Any]) -> None: + """Override configuration with environment variables""" + # Database URL + if os.getenv("DATABASE_URL"): + config_data["database"]["url"] = os.getenv("DATABASE_URL") + + # AI Service configuration + if os.getenv("AI_MODEL"): + config_data["ai_service"]["model"] = os.getenv("AI_MODEL") + + # Logging level + if os.getenv("LOG_LEVEL"): + config_data["logging"]["level"] = os.getenv("LOG_LEVEL") + + # Environment + if os.getenv("ENVIRONMENT"): + config_data["application"]["environment"] = os.getenv("ENVIRONMENT") + + def get_config(self) -> Config: + """Get the current configuration""" + if self._config is None: + return self.load_config() + return self._config + + def reload_config(self) -> Config: + """Reload configuration from file""" + self._config = None + return self.load_config() + + +# Global configuration instance +config_manager = ConfigManager() + + +def get_config() -> Config: + """Get the application configuration""" + return config_manager.get_config() + + +def reload_config() -> Config: + """Reload the application configuration""" + return config_manager.reload_config() diff --git a/src/database/__init__.py b/src/database/__init__.py new file mode 100644 index 0000000..65f47a9 --- /dev/null +++ b/src/database/__init__.py @@ -0,0 +1 @@ +# Database module diff --git a/src/database/interfaces.py b/src/database/interfaces.py new file mode 100644 index 0000000..ed4d27a --- /dev/null +++ b/src/database/interfaces.py @@ -0,0 +1 @@ +# Abstract repository interfaces diff --git a/src/database/models.py b/src/database/models.py new file mode 100644 index 0000000..b584e21 --- /dev/null +++ b/src/database/models.py @@ -0,0 +1 @@ +# SQLAlchemy models diff --git a/src/database/repositories.py b/src/database/repositories.py new file mode 100644 index 0000000..68ee35c --- /dev/null +++ b/src/database/repositories.py @@ -0,0 +1 @@ +# Concrete repository implementations diff --git a/src/deployment/__init__.py b/src/deployment/__init__.py new file mode 100644 index 0000000..65e261b --- /dev/null +++ b/src/deployment/__init__.py @@ -0,0 +1 @@ +# Deployment module diff --git a/src/deployment/manager.py b/src/deployment/manager.py new file mode 100644 index 0000000..e39a969 --- /dev/null +++ b/src/deployment/manager.py @@ -0,0 +1 @@ +# Manages which strategy to use diff --git a/src/deployment/strategies/__init__.py b/src/deployment/strategies/__init__.py new file mode 100644 index 0000000..6531995 --- /dev/null +++ b/src/deployment/strategies/__init__.py @@ -0,0 +1 @@ +# Cloud deployment strategies diff --git a/src/generation/__init__.py b/src/generation/__init__.py new file mode 100644 index 0000000..b8ca619 --- /dev/null +++ b/src/generation/__init__.py @@ -0,0 +1 @@ +# Content generation module diff --git a/src/generation/rule_engine.py b/src/generation/rule_engine.py new file mode 100644 index 0000000..6b93a51 --- /dev/null +++ b/src/generation/rule_engine.py @@ -0,0 +1 @@ +# Content validation rules diff --git a/src/generation/service.py b/src/generation/service.py new file mode 100644 index 0000000..e7af414 --- /dev/null +++ b/src/generation/service.py @@ -0,0 +1 @@ +# AI API interaction diff --git a/src/ingestion/__init__.py b/src/ingestion/__init__.py new file mode 100644 index 0000000..d9c9ddb --- /dev/null +++ b/src/ingestion/__init__.py @@ -0,0 +1 @@ +# Data ingestion module diff --git a/src/ingestion/parser.py b/src/ingestion/parser.py new file mode 100644 index 0000000..062a7e2 --- /dev/null +++ b/src/ingestion/parser.py @@ -0,0 +1 @@ +# CORA .xlsx file parsing diff --git a/src/interlinking/__init__.py b/src/interlinking/__init__.py new file mode 100644 index 0000000..577fc06 --- /dev/null +++ b/src/interlinking/__init__.py @@ -0,0 +1 @@ +# Interlinking module diff --git a/src/interlinking/service.py b/src/interlinking/service.py new file mode 100644 index 0000000..4ec8325 --- /dev/null +++ b/src/interlinking/service.py @@ -0,0 +1 @@ +# Link map generation and injection diff --git a/src/templating/__init__.py b/src/templating/__init__.py new file mode 100644 index 0000000..1947f47 --- /dev/null +++ b/src/templating/__init__.py @@ -0,0 +1 @@ +# HTML templating module diff --git a/src/templating/service.py b/src/templating/service.py new file mode 100644 index 0000000..67ab38a --- /dev/null +++ b/src/templating/service.py @@ -0,0 +1 @@ +# Applies templates to content diff --git a/src/templating/templates/__init__.py b/src/templating/templates/__init__.py new file mode 100644 index 0000000..d9f8a9c --- /dev/null +++ b/src/templating/templates/__init__.py @@ -0,0 +1 @@ +# HTML/CSS templates directory diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..5b59ffe --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1 @@ +# Test module diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..3708f54 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1 @@ +# Pytest fixtures diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py new file mode 100644 index 0000000..c58b9ba --- /dev/null +++ b/tests/integration/__init__.py @@ -0,0 +1 @@ +# Integration tests for workflows diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py new file mode 100644 index 0000000..0be1432 --- /dev/null +++ b/tests/unit/__init__.py @@ -0,0 +1 @@ +# Unit tests for individual modules