commit 68314d3ba6a0e677161b98d99fc414970f3bec9f
parent cd177267820be381098cf18c8c71917c10a86010
Author: Amit Dutta <mail@amit.is-a.dev>
Date: Fri, 4 Sep 2026 07:14:26 +0530
Merge pull request #9 from notamitgamer/feat/favicon-logo-pwa-manifest
Add favicon, logo, and installable PWA manifest (no-cache)
Diffstat:
14 files changed, 169 insertions(+), 2 deletions(-)
diff --git a/app/main.py b/app/main.py
@@ -3,8 +3,9 @@ import shutil
import uuid
import mimetypes
import httpx
+from pathlib import Path
from fastapi import FastAPI, Request, File, UploadFile, Depends, HTTPException
-from fastapi.responses import StreamingResponse, HTMLResponse, PlainTextResponse, RedirectResponse
+from fastapi.responses import StreamingResponse, HTMLResponse, PlainTextResponse, RedirectResponse, FileResponse
from fastapi.templating import Jinja2Templates
from .storage import is_file, list_directory, upload_temp_file, HF_REPO_ID
@@ -14,6 +15,42 @@ app = FastAPI()
templates = Jinja2Templates(directory="app/templates")
+STATIC_DIR = Path(__file__).parent / "static"
+
+# manifest.json and the service worker must always be fetched fresh —
+# the app can change at any time and we never want a stale PWA shell
+# or a stale install prompt. Icons are safe to cache normally.
+_NO_STORE_FILES = {"manifest.json", "sw.js"}
+
+@app.get("/static/{filename}")
+async def static_no_cache_root(filename: str):
+ if filename not in _NO_STORE_FILES:
+ raise HTTPException(status_code=404)
+ file_path = STATIC_DIR / filename
+ if not file_path.is_file():
+ raise HTTPException(status_code=404)
+ return FileResponse(
+ file_path,
+ headers={"Cache-Control": "no-store, no-cache, must-revalidate, max-age=0"},
+ )
+
+@app.get("/static/icons/{filename}")
+async def static_icons(filename: str):
+ file_path = STATIC_DIR / "icons" / filename
+ if not file_path.is_file():
+ raise HTTPException(status_code=404)
+ return FileResponse(
+ file_path,
+ headers={"Cache-Control": "public, max-age=86400"},
+ )
+
+@app.get("/favicon.ico")
+async def favicon():
+ return FileResponse(
+ STATIC_DIR / "favicon.ico",
+ headers={"Cache-Control": "public, max-age=86400"},
+ )
+
CDN_BASE_URL = os.getenv("CDN_BASE_URL", "https://cdn-zt7p.onrender.com")
RAW_DOMAIN = os.getenv("RAW_DOMAIN", "raw.cdn.amit.is-a.dev")
RAW_BASE_URL = os.getenv("RAW_BASE_URL", f"https://{RAW_DOMAIN}")
diff --git a/app/static/favicon.ico b/app/static/favicon.ico
Binary files differ.
diff --git a/app/static/icons/apple-touch-icon.png b/app/static/icons/apple-touch-icon.png
Binary files differ.
diff --git a/app/static/icons/favicon-16x16.png b/app/static/icons/favicon-16x16.png
Binary files differ.
diff --git a/app/static/icons/favicon-32x32.png b/app/static/icons/favicon-32x32.png
Binary files differ.
diff --git a/app/static/icons/icon-192.png b/app/static/icons/icon-192.png
Binary files differ.
diff --git a/app/static/icons/icon-512.png b/app/static/icons/icon-512.png
Binary files differ.
diff --git a/app/static/icons/icon-maskable-192.png b/app/static/icons/icon-maskable-192.png
Binary files differ.
diff --git a/app/static/icons/icon-maskable-512.png b/app/static/icons/icon-maskable-512.png
Binary files differ.
diff --git a/app/static/icons/logo.svg b/app/static/icons/logo.svg
@@ -0,0 +1,20 @@
+<svg width="512" height="512" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
+ <rect width="512" height="512" rx="112" fill="#050505"/>
+ <rect x="30" y="30" width="452" height="452" rx="92" fill="none" stroke="#21262d" stroke-width="2"/>
+
+ <!-- Terminal caret / bracket mark echoing the monospace theme -->
+ <g fill="none" stroke-linecap="round" stroke-linejoin="round">
+ <path d="M180 150 L110 256 L180 362" stroke="#5eead4" stroke-width="34"/>
+ <path d="M332 150 L402 256 L332 362" stroke="#fca5a5" stroke-width="34"/>
+ </g>
+
+ <!-- center slash / cursor, echoing directory listing dash -->
+ <rect x="243" y="150" width="26" height="212" rx="8" fill="#ffffff" transform="rotate(14 256 256)"/>
+
+ <!-- three-color accent dash at the bottom, matches site's .top-accent gradient -->
+ <g>
+ <rect x="176" y="410" width="52" height="10" rx="5" fill="#5eead4"/>
+ <rect x="230" y="410" width="52" height="10" rx="5" fill="#fca5a5"/>
+ <rect x="284" y="410" width="52" height="10" rx="5" fill="#ffffff"/>
+ </g>
+</svg>
diff --git a/app/static/icons/og-logo.png b/app/static/icons/og-logo.png
Binary files differ.
diff --git a/app/static/manifest.json b/app/static/manifest.json
@@ -0,0 +1,48 @@
+{
+ "name": "amit's CDN",
+ "short_name": "CDN",
+ "description": "Personal CDN for browsing, uploading, and linking hosted assets.",
+ "start_url": "/",
+ "scope": "/",
+ "display": "standalone",
+ "background_color": "#050505",
+ "theme_color": "#050505",
+ "orientation": "any",
+ "lang": "en",
+ "icons": [
+ {
+ "src": "/static/icons/favicon-16x16.png",
+ "sizes": "16x16",
+ "type": "image/png"
+ },
+ {
+ "src": "/static/icons/favicon-32x32.png",
+ "sizes": "32x32",
+ "type": "image/png"
+ },
+ {
+ "src": "/static/icons/icon-192.png",
+ "sizes": "192x192",
+ "type": "image/png",
+ "purpose": "any"
+ },
+ {
+ "src": "/static/icons/icon-512.png",
+ "sizes": "512x512",
+ "type": "image/png",
+ "purpose": "any"
+ },
+ {
+ "src": "/static/icons/icon-maskable-192.png",
+ "sizes": "192x192",
+ "type": "image/png",
+ "purpose": "maskable"
+ },
+ {
+ "src": "/static/icons/icon-maskable-512.png",
+ "sizes": "512x512",
+ "type": "image/png",
+ "purpose": "maskable"
+ }
+ ]
+}
diff --git a/app/static/sw.js b/app/static/sw.js
@@ -0,0 +1,27 @@
+// This service worker intentionally does NOT cache anything.
+// It exists only so the site satisfies PWA "installability" requirements
+// (Add to Home Screen / desktop install) while every request still goes
+// straight to the network. Directory listings and files here can change
+// at any time, so nothing is ever served from a cache.
+
+self.addEventListener("install", (event) => {
+ // Activate the new worker immediately, don't wait for old tabs to close.
+ self.skipWaiting();
+});
+
+self.addEventListener("activate", (event) => {
+ event.waitUntil(
+ (async () => {
+ // Defensive cleanup: if a cache was ever created by a previous
+ // version of this worker, delete it so nothing stale lingers.
+ const keys = await caches.keys();
+ await Promise.all(keys.map((key) => caches.delete(key)));
+ await self.clients.claim();
+ })()
+ );
+});
+
+self.addEventListener("fetch", (event) => {
+ // Always hit the network. No cache.match, no cache.put — ever.
+ event.respondWith(fetch(event.request));
+});
diff --git a/app/templates/index.html b/app/templates/index.html
@@ -4,7 +4,30 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CDN | {{ page | capitalize }}</title>
-
+ <meta name="description" content="Personal CDN for browsing, uploading, and linking hosted assets.">
+
+ <!-- Favicon -->
+ <link rel="icon" href="/favicon.ico" sizes="any">
+ <link rel="icon" type="image/png" sizes="32x32" href="/static/icons/favicon-32x32.png">
+ <link rel="icon" type="image/png" sizes="16x16" href="/static/icons/favicon-16x16.png">
+ <link rel="icon" type="image/svg+xml" href="/static/icons/logo.svg">
+ <link rel="apple-touch-icon" sizes="180x180" href="/static/icons/apple-touch-icon.png">
+
+ <!-- PWA manifest (fetched with no-store, see /static/manifest.json route) -->
+ <link rel="manifest" href="/static/manifest.json">
+ <meta name="theme-color" content="#050505">
+ <meta name="color-scheme" content="dark">
+ <meta name="apple-mobile-web-app-capable" content="yes">
+ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
+ <meta name="apple-mobile-web-app-title" content="CDN">
+
+ <!-- Social preview -->
+ <meta property="og:title" content="CDN | {{ page | capitalize }}">
+ <meta property="og:description" content="Personal CDN for browsing, uploading, and linking hosted assets.">
+ <meta property="og:image" content="/static/icons/og-logo.png">
+ <meta property="og:type" content="website">
+ <meta name="twitter:card" content="summary">
+
<!-- ADD THIS: Turbo to make the site a Single Page App -->
<script type="module" src="https://cdn.jsdelivr.net/npm/@hotwired/turbo@8.0.4/dist/turbo.es2017-esm.js"></script>
@@ -475,6 +498,18 @@
console.error('failed to copy: ', err);
});
}
+
+ // Registers a service worker so the site is installable as a PWA.
+ // The worker (see /static/sw.js) does NOT cache anything — it only
+ // passes requests straight through to the network, since this CDN's
+ // listings and files can change at any time and must never be stale.
+ if ('serviceWorker' in navigator) {
+ window.addEventListener('load', () => {
+ navigator.serviceWorker.register('/static/sw.js').catch(err => {
+ console.error('service worker registration failed:', err);
+ });
+ });
+ }
</script>
</body>
</html>