feat: Complete Story 1.1 - Project Initialization & Configuration

- Create monorepo directory structure as defined in architecture
- Initialize Python project with core dependencies (FastAPI, SQLAlchemy, Click, Pydantic)
- Add requirements.txt with all necessary libraries
- Create env.example with placeholders for all cloud provider secrets
- Implement master.config.json with comprehensive configuration
- Add Pydantic-based configuration loading and validation
- Create basic CLI framework with health check and config commands
- Set up main.py as CLI entry point
- Organize all modules according to source tree specification

This completes the foundational infrastructure for Epic 1: Foundation & Core Services.
main
PeninsulaInd 2025-10-17 23:42:16 -05:00
parent 31b958029b
commit 70b9de20b4
37 changed files with 440 additions and 0 deletions

17
.gitignore vendored 100644
View File

@ -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/

48
env.example 100644
View File

@ -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

17
main.py 100644
View File

@ -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()

85
master.config.json 100644
View File

@ -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
}
}

42
requirements.txt 100644
View File

@ -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

1
src/__init__.py 100644
View File

@ -0,0 +1 @@
# Content Automation & Syndication Platform

View File

@ -0,0 +1 @@
# API module for internal REST API

1
src/api/main.py 100644
View File

@ -0,0 +1 @@
# FastAPI app instance

View File

@ -0,0 +1 @@
# API endpoint definitions

View File

@ -0,0 +1 @@
# Pydantic models for API

View File

@ -0,0 +1 @@
# Authentication module

View File

@ -0,0 +1 @@
# Hashing, validation, role checks

View File

@ -0,0 +1 @@
# CLI module

View File

@ -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()

View File

@ -0,0 +1 @@
# Core application module

157
src/core/config.py 100644
View File

@ -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()

View File

@ -0,0 +1 @@
# Database module

View File

@ -0,0 +1 @@
# Abstract repository interfaces

View File

@ -0,0 +1 @@
# SQLAlchemy models

View File

@ -0,0 +1 @@
# Concrete repository implementations

View File

@ -0,0 +1 @@
# Deployment module

View File

@ -0,0 +1 @@
# Manages which strategy to use

View File

@ -0,0 +1 @@
# Cloud deployment strategies

View File

@ -0,0 +1 @@
# Content generation module

View File

@ -0,0 +1 @@
# Content validation rules

View File

@ -0,0 +1 @@
# AI API interaction

View File

@ -0,0 +1 @@
# Data ingestion module

View File

@ -0,0 +1 @@
# CORA .xlsx file parsing

View File

@ -0,0 +1 @@
# Interlinking module

View File

@ -0,0 +1 @@
# Link map generation and injection

View File

@ -0,0 +1 @@
# HTML templating module

View File

@ -0,0 +1 @@
# Applies templates to content

View File

@ -0,0 +1 @@
# HTML/CSS templates directory

View File

@ -0,0 +1 @@
# Test module

View File

@ -0,0 +1 @@
# Pytest fixtures

View File

@ -0,0 +1 @@
# Integration tests for workflows

View File

@ -0,0 +1 @@
# Unit tests for individual modules