mirror of
https://github.com/kevinveenbirkenbach/homepage.veen.world.git
synced 2026-04-07 05:12:19 +00:00
- Replace requirements.txt with pyproject.toml for modern Python packaging - Add unit, integration, lint and security test suites under tests/ - Add utils/export_runtime_requirements.py and utils/check_hadolint_sarif.py - Split monolithic CI into reusable lint.yml, security.yml and tests.yml - Refactor ci.yml to orchestrate reusable workflows; publish on semver tag only - Modernize Dockerfile: pin python:3.12-slim, install via pyproject.toml - Expand Makefile with lint, security, test and CI targets - Add test-e2e via act with portfolio container stop/start around run - Fix navbar_logo_visibility.spec.js: win.fullscreen() → win.enterFullscreen() - Set use_reloader=False in app.run() to prevent double-start in CI - Add app/core.* and build artifacts to .gitignore - Fix apt-get → sudo apt-get in tests.yml e2e job - Fix pip install --ignore-installed to handle stale act cache Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
48 lines
1.5 KiB
Python
48 lines
1.5 KiB
Python
import hashlib
|
|
import mimetypes
|
|
import os
|
|
|
|
import requests
|
|
|
|
|
|
class CacheManager:
|
|
def __init__(self, cache_dir="static/cache"):
|
|
self.cache_dir = cache_dir
|
|
self._ensure_cache_dir_exists()
|
|
|
|
def _ensure_cache_dir_exists(self):
|
|
os.makedirs(self.cache_dir, exist_ok=True)
|
|
|
|
def clear_cache(self):
|
|
if os.path.exists(self.cache_dir):
|
|
for filename in os.listdir(self.cache_dir):
|
|
path = os.path.join(self.cache_dir, filename)
|
|
if os.path.isfile(path):
|
|
os.remove(path)
|
|
|
|
def cache_file(self, file_url):
|
|
hash_suffix = hashlib.blake2s(
|
|
file_url.encode("utf-8"),
|
|
digest_size=8,
|
|
).hexdigest()
|
|
parts = file_url.rstrip("/").split("/")
|
|
base = parts[-2] if parts[-1] == "download" else parts[-1]
|
|
|
|
try:
|
|
resp = requests.get(file_url, stream=True, timeout=5)
|
|
resp.raise_for_status()
|
|
except requests.RequestException:
|
|
return None
|
|
|
|
content_type = resp.headers.get("Content-Type", "")
|
|
ext = mimetypes.guess_extension(content_type.split(";")[0].strip()) or ".png"
|
|
filename = f"{base}_{hash_suffix}{ext}"
|
|
full_path = os.path.join(self.cache_dir, filename)
|
|
|
|
if not os.path.exists(full_path):
|
|
with open(full_path, "wb") as f:
|
|
for chunk in resp.iter_content(1024):
|
|
f.write(chunk)
|
|
|
|
return f"cache/{filename}"
|