Files
orion/app/modules/hosting/services/template_service.py
Samir Boulahtit 2e043260eb feat(hosting): add industry template infrastructure (Workstream 3B)
5 industry templates as JSON presets, each with theme + multi-page content:
- generic: clean minimal (homepage, about, contact)
- restaurant: warm tones, Playfair Display (homepage, about, menu, contact)
- construction: amber/earth tones, Montserrat (homepage, services, projects, contact)
- auto-parts: red/bold, parts-focused (homepage, catalog, contact)
- professional-services: navy/blue, Merriweather (homepage, services, team, contact)

Each template has:
- meta.json (name, description, tags, languages)
- theme.json (colors, fonts, layout, header style)
- pages/*.json (section-based homepage + content pages with i18n)
- {{placeholder}} variables for prospect data injection

TemplateService loads from templates_library/ directory with caching.
GET /admin/hosting/sites/templates endpoint to list available templates.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 22:41:33 +02:00

115 lines
4.0 KiB
Python

# app/modules/hosting/services/template_service.py
"""
Template service for the hosting module.
Loads and manages industry templates from the templates_library directory.
Templates are JSON files that define page content, themes, and sections
for different business types (restaurant, construction, etc.).
"""
import json
import logging
from pathlib import Path
logger = logging.getLogger(__name__)
TEMPLATES_DIR = Path(__file__).parent.parent / "templates_library"
class TemplateService:
"""Manages industry templates for POC site generation."""
def __init__(self):
self._manifest = None
self._cache: dict[str, dict] = {}
def _load_manifest(self) -> dict:
"""Load the manifest.json file."""
if self._manifest is None:
manifest_path = TEMPLATES_DIR / "manifest.json"
self._manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
return self._manifest
def list_templates(self) -> list[dict]:
"""List all available templates with metadata."""
manifest = self._load_manifest()
templates = []
for entry in manifest.get("templates", []):
template_id = entry["id"]
meta = self._load_meta(template_id)
templates.append({
"id": template_id,
"name": meta.get("name", entry.get("name", template_id)),
"description": meta.get("description", entry.get("description", "")),
"tags": meta.get("tags", entry.get("tags", [])),
"languages": meta.get("languages", []),
"pages": entry.get("pages", []),
})
return templates
def get_template(self, template_id: str) -> dict | None:
"""Load a complete template with meta, theme, and all pages."""
if template_id in self._cache:
return self._cache[template_id]
template_dir = TEMPLATES_DIR / template_id
if not template_dir.is_dir():
return None
meta = self._load_meta(template_id)
theme = self._load_json(template_dir / "theme.json")
pages = self._load_pages(template_dir)
template = {
"id": template_id,
"meta": meta,
"theme": theme,
"pages": pages,
}
self._cache[template_id] = template
return template
def get_theme(self, template_id: str) -> dict | None:
"""Load just the theme configuration for a template."""
template_dir = TEMPLATES_DIR / template_id
return self._load_json(template_dir / "theme.json")
def get_page(self, template_id: str, page_slug: str) -> dict | None:
"""Load a single page definition from a template."""
page_path = TEMPLATES_DIR / template_id / "pages" / f"{page_slug}.json"
return self._load_json(page_path)
def template_exists(self, template_id: str) -> bool:
"""Check if a template exists."""
return (TEMPLATES_DIR / template_id / "meta.json").is_file()
def _load_meta(self, template_id: str) -> dict:
"""Load meta.json for a template."""
return self._load_json(TEMPLATES_DIR / template_id / "meta.json") or {}
def _load_pages(self, template_dir: Path) -> list[dict]:
"""Load all page JSONs from a template's pages/ directory."""
pages_dir = template_dir / "pages"
if not pages_dir.is_dir():
return []
pages = []
for page_file in sorted(pages_dir.glob("*.json")):
page_data = self._load_json(page_file)
if page_data:
pages.append(page_data)
return pages
@staticmethod
def _load_json(path: Path) -> dict | None:
"""Safely load a JSON file."""
if not path.is_file():
return None
try:
return json.loads(path.read_text(encoding="utf-8"))
except (json.JSONDecodeError, OSError) as e:
logger.warning("Failed to load template file %s: %s", path, e)
return None
template_service = TemplateService()