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>
115 lines
4.0 KiB
Python
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()
|