Skip to content

Commit

Permalink
feat: hot-reload configuration file
Browse files Browse the repository at this point in the history
  • Loading branch information
d3vv3 committed Apr 7, 2024
1 parent 3612168 commit db25d6d
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 16 deletions.
22 changes: 17 additions & 5 deletions app/main.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
"""FastAPI module to serve the backend API and all frontend sites."""

from contextlib import asynccontextmanager
from fastapi import FastAPI, Depends, Request, Path as FasAPIPath
from fastapi.staticfiles import StaticFiles
from fastapi.responses import Response
from pydantic import EmailStr, Field, BaseModel
from os import getenv
from datetime import datetime
from os import getenv
from pathlib import Path

from fastapi import Depends, FastAPI
from fastapi import Path as FasAPIPath
from fastapi import Request
from fastapi.responses import Response
from fastapi.staticfiles import StaticFiles
from modules.config_handler import ConfigHandler
from pydantic import BaseModel, EmailStr, Field
from watchdog.observers import Observer

API_PREFIX: str = getenv("API_PREFIX", "/api")
DATA_PATH: Path = Path(getenv("DATA_PATH", "./data"))
CSV_HEADER: str = "email,timestamp\n"
ADMIN_SECRET: str = getenv("ADMIN_SECRET", "admin")
STATIC_PAGES_DIR: Path = Path(getenv("STATIC_PAGES_DIR", "./static_pages"))

CONF_PATH: Path = Path("./conf/config.yaml")


@asynccontextmanager
async def lifespan(app: FastAPI):
Expand Down Expand Up @@ -99,3 +106,8 @@ async def get_emails(


app.mount("/", StaticFiles(directory=STATIC_PAGES_DIR, html=True), name="static")

observer = Observer()
config_handler = ConfigHandler()
observer.schedule(config_handler, path=CONF_PATH, recursive=False)
observer.start()
18 changes: 18 additions & 0 deletions app/modules/config_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from pathlib import Path

from modules.site_renderer import SiteRenderer
from watchdog.events import FileSystemEventHandler

TEMPLATE_SRC: Path = Path("./template/src/")
CONF_PATH: Path = Path("./conf/config.yaml")
STATIC_PAGES_DIR: Path = Path("./static_pages")


class ConfigHandler(FileSystemEventHandler):

def __init__(self):
self.site_renderer = SiteRenderer(CONF_PATH, TEMPLATE_SRC, STATIC_PAGES_DIR)

def on_modified(self, event):
if event.src_path == str(CONF_PATH):
self.site_renderer.render_sites()
34 changes: 26 additions & 8 deletions app/scripts/render_sites.py → app/modules/site_renderer.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,33 @@
"""Module to render the template for the configured sites."""

import yaml
from pathlib import Path
import os
import shutil
from pathlib import Path

import yaml
from jinja2 import Environment, FileSystemLoader
from loguru import logger

TEMPLATE_SRC: Path = Path("./template/src/")
CONF_PATH: Path = Path("./conf/config.yaml")
STATIC_PAGES_DIR: Path = Path("./static_pages")

if __name__ == "__main__":
with open(CONF_PATH, "r") as conf_file:
conf = yaml.safe_load(conf_file)

for site in conf["sites"]:
class SiteRenderer:
def __init__(self, conf_path: Path, template_src: Path, static_pages_dir: Path):
self.conf_path = conf_path
self.template_src = template_src
self.static_pages_dir = static_pages_dir

def render_site(self, site: dict) -> None:
page_identifier = site["page_identifier"]
logger.info(f"Rendering site: {page_identifier}")
site_path: Path = STATIC_PAGES_DIR / page_identifier
site_path: Path = self.static_pages_dir / page_identifier

# recursive copy of template files to site directory
shutil.copytree(TEMPLATE_SRC, site_path)
if os.path.exists(site_path):
shutil.rmtree(site_path)
shutil.copytree(self.template_src, site_path)

# replace jinja variables in index.html
env = Environment(loader=FileSystemLoader(site_path))
Expand All @@ -29,3 +36,14 @@

with open(site_path / "index.html", "w") as f:
f.write(rendered)

def render_sites(self) -> None:
with open(self.conf_path, "r") as conf_file:
conf = yaml.safe_load(conf_file)

for site in conf["sites"]:
self.render_site(site)


if __name__ == "__main__":
SiteRenderer(CONF_PATH, TEMPLATE_SRC, STATIC_PAGES_DIR).render_sites()
4 changes: 2 additions & 2 deletions app/prestart.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#! /usr/bin/env bash

cd /app
python scripts/render_sites.py
cd /app || exit
python modules/site_renderer.py
1 change: 1 addition & 0 deletions requirements.in
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ loguru
pydantic
pyyaml
uvicorn
watchdog
4 changes: 3 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# This file is autogenerated by pip-compile with Python 3.11
# This file is autogenerated by pip-compile with Python 3.12
# by the following command:
#
# pip-compile
Expand Down Expand Up @@ -51,3 +51,5 @@ typing-extensions==4.11.0
# pydantic-core
uvicorn==0.29.0
# via -r requirements.in
watchdog==4.0.0
# via -r requirements.in

0 comments on commit db25d6d

Please sign in to comment.