added app
This commit is contained in:
parent
6114ae76b2
commit
2912e41374
|
|
@ -3,7 +3,7 @@ NoteDiscovery - Self-Hosted Markdown Knowledge Base
|
|||
Main FastAPI application
|
||||
"""
|
||||
|
||||
from fastapi import FastAPI, HTTPException, UploadFile, File, Request, Form, Depends, APIRouter
|
||||
from fastapi import FastAPI, HTTPException, UploadFile, File, Request, Form, Depends, APIRouter, Response
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.responses import HTMLResponse, JSONResponse, FileResponse, RedirectResponse
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
|
@ -171,6 +171,21 @@ plugin_manager.run_hook('on_app_startup')
|
|||
static_path = Path(__file__).parent.parent / "frontend"
|
||||
app.mount("/static", StaticFiles(directory=static_path), name="static")
|
||||
|
||||
# PWA Service Worker - must be served from root for proper scope
|
||||
@app.get("/sw.js")
|
||||
@limiter.limit("30/minute")
|
||||
async def service_worker(request: Request):
|
||||
"""Serve the PWA service worker from root path for proper scope.
|
||||
Injects the app version from VERSION file for cache invalidation."""
|
||||
sw_path = static_path / "sw.js"
|
||||
if sw_path.exists():
|
||||
async with aiofiles.open(sw_path, 'r', encoding='utf-8') as f:
|
||||
content = await f.read()
|
||||
# Inject app version into cache name
|
||||
content = content.replace('__APP_VERSION__', version)
|
||||
return Response(content=content, media_type="application/javascript")
|
||||
raise HTTPException(status_code=404, detail="Service worker not found")
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Custom Exception Handlers
|
||||
|
|
|
|||
|
|
@ -875,6 +875,15 @@ function noteApp() {
|
|||
if (this.showGraph) {
|
||||
setTimeout(() => this.initGraph(), 300);
|
||||
}
|
||||
|
||||
// Update PWA theme-color meta tag to match current theme
|
||||
const themeColorMeta = document.querySelector('meta[name="theme-color"]');
|
||||
if (themeColorMeta) {
|
||||
// Get the accent color from CSS variables
|
||||
const accentColor = getComputedStyle(document.documentElement)
|
||||
.getPropertyValue('--accent-primary').trim() || '#667eea';
|
||||
themeColorMeta.setAttribute('content', accentColor);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load theme:', error);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,20 @@
|
|||
<link rel="icon" type="image/png" sizes="16x16" href="/static/favicon.svg">
|
||||
<link rel="apple-touch-icon" href="/static/logo.svg">
|
||||
|
||||
<!-- PWA Manifest -->
|
||||
<link rel="manifest" href="/static/manifest.json">
|
||||
|
||||
<!-- PWA Meta Tags -->
|
||||
<meta name="application-name" content="NoteDiscovery">
|
||||
<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="NoteDiscovery">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<!-- Theme color: initial value, updated dynamically by app.js when theme changes -->
|
||||
<meta name="theme-color" content="#667eea">
|
||||
<meta name="msapplication-TileColor" content="#667eea">
|
||||
<meta name="msapplication-tap-highlight" content="no">
|
||||
|
||||
<!-- Tailwind CSS from CDN (v3.4.17) -->
|
||||
<script src="https://cdn.tailwindcss.com/3.4.17"></script>
|
||||
|
||||
|
|
@ -2816,6 +2830,21 @@
|
|||
</nav>
|
||||
|
||||
<script src="/static/app.js"></script>
|
||||
|
||||
<!-- PWA Service Worker Registration -->
|
||||
<script>
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', () => {
|
||||
navigator.serviceWorker.register('/sw.js')
|
||||
.then((registration) => {
|
||||
console.log('SW registered:', registration.scope);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log('SW registration failed:', error);
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,13 @@
|
|||
<title>Login - NoteDiscovery</title>
|
||||
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
|
||||
|
||||
<!-- PWA Manifest -->
|
||||
<link rel="manifest" href="/static/manifest.json">
|
||||
<meta name="theme-color" content="#667eea">
|
||||
<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="NoteDiscovery">
|
||||
|
||||
<!-- Preload translations (same approach as index.html) -->
|
||||
<script>
|
||||
(function() {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"name": "NoteDiscovery",
|
||||
"short_name": "Notes",
|
||||
"description": "A beautiful, self-hosted markdown notes application",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#0f172a",
|
||||
"theme_color": "#667eea",
|
||||
"orientation": "any",
|
||||
"scope": "/",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/static/logo.svg",
|
||||
"sizes": "any",
|
||||
"type": "image/svg+xml",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/static/logo.svg",
|
||||
"sizes": "192x192",
|
||||
"type": "image/svg+xml",
|
||||
"purpose": "any maskable"
|
||||
},
|
||||
{
|
||||
"src": "/static/logo.svg",
|
||||
"sizes": "512x512",
|
||||
"type": "image/svg+xml",
|
||||
"purpose": "any maskable"
|
||||
}
|
||||
],
|
||||
"categories": ["productivity", "utilities"]
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
// NoteDiscovery Service Worker
|
||||
// Minimal service worker for PWA install support
|
||||
|
||||
// Cache version - automatically uses app version from VERSION file
|
||||
// Cache is invalidated when app version changes (e.g., 0.10.4 -> 0.10.5)
|
||||
// This forces users to download fresh files when you release a new version.
|
||||
const CACHE_NAME = 'notediscovery-__APP_VERSION__';
|
||||
|
||||
// Assets to cache for faster repeat visits
|
||||
const PRECACHE_ASSETS = [
|
||||
'/static/logo.svg',
|
||||
'/static/favicon.svg',
|
||||
'/static/app.js'
|
||||
];
|
||||
|
||||
// Install event - cache essential assets
|
||||
self.addEventListener('install', (event) => {
|
||||
event.waitUntil(
|
||||
caches.open(CACHE_NAME)
|
||||
.then((cache) => cache.addAll(PRECACHE_ASSETS))
|
||||
.then(() => self.skipWaiting())
|
||||
);
|
||||
});
|
||||
|
||||
// Activate event - clean up old caches
|
||||
self.addEventListener('activate', (event) => {
|
||||
event.waitUntil(
|
||||
caches.keys().then((cacheNames) => {
|
||||
return Promise.all(
|
||||
cacheNames
|
||||
.filter((name) => name !== CACHE_NAME)
|
||||
.map((name) => caches.delete(name))
|
||||
);
|
||||
}).then(() => self.clients.claim())
|
||||
);
|
||||
});
|
||||
|
||||
// Fetch event - network first, fallback to cache for assets
|
||||
self.addEventListener('fetch', (event) => {
|
||||
const url = new URL(event.request.url);
|
||||
|
||||
// Only handle same-origin requests
|
||||
if (url.origin !== location.origin) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For API calls, always go to network
|
||||
if (url.pathname.startsWith('/api/')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For static assets, try cache first then network
|
||||
if (url.pathname.startsWith('/static/')) {
|
||||
event.respondWith(
|
||||
caches.match(event.request)
|
||||
.then((cached) => cached || fetch(event.request))
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// For everything else, network first
|
||||
event.respondWith(
|
||||
fetch(event.request)
|
||||
.catch(() => caches.match(event.request))
|
||||
);
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue