diff --git a/.gitignore b/.gitignore index 0e0dcf2..715357a 100644 --- a/.gitignore +++ b/.gitignore @@ -196,6 +196,9 @@ Thumbs.db !uploads/.gitkeep !local_uploads/.gitkeep +# Generated PWA Files +/public/*manifest.json + # Misc *.log .env.* diff --git a/nodemon.json b/nodemon.json new file mode 100644 index 0000000..68e70ad --- /dev/null +++ b/nodemon.json @@ -0,0 +1,3 @@ +{ + "ignore": ["asset-manifest.json", "manifest.json"] +} \ No newline at end of file diff --git a/icon.png b/public/assets/icon.png similarity index 100% rename from icon.png rename to public/assets/icon.png diff --git a/icon.svg b/public/assets/icon.svg similarity index 100% rename from icon.svg rename to public/assets/icon.svg diff --git a/public/index.html b/public/index.html index 885122c..f7097af 100644 --- a/public/index.html +++ b/public/index.html @@ -7,6 +7,7 @@ +
diff --git a/public/service-worker.js b/public/service-worker.js new file mode 100644 index 0000000..5f6c206 --- /dev/null +++ b/public/service-worker.js @@ -0,0 +1,32 @@ +const CACHE_NAME = "DUMBDROP_PWA_CACHE_V1"; +const ASSETS_TO_CACHE = []; + +const preload = async () => { + console.log("Installing web app"); + return await caches.open(CACHE_NAME) + .then(async (cache) => { + console.log("caching index and important routes"); + const response = await fetch("/asset-manifest.json"); + const assets = await response.json(); + ASSETS_TO_CACHE.push(...assets); + console.log("Assets Cached:", ASSETS_TO_CACHE); + return cache.addAll(ASSETS_TO_CACHE); + }); +} + +// Fetch asset manifest dynamically +globalThis.addEventListener("install", (event) => { + event.waitUntil(preload()); +}); + +globalThis.addEventListener("activate", (event) => { + event.waitUntil(clients.claim()); +}); + +globalThis.addEventListener("fetch", (event) => { + event.respondWith( + caches.match(event.request).then((cachedResponse) => { + return cachedResponse || fetch(event.request); + }) + ); +}); \ No newline at end of file diff --git a/src/scripts/pwa-manifest-generator.js b/src/scripts/pwa-manifest-generator.js new file mode 100644 index 0000000..9f5939a --- /dev/null +++ b/src/scripts/pwa-manifest-generator.js @@ -0,0 +1,60 @@ +const fs = require("fs"); +const path = require("path"); +const PUBLIC_DIR = path.join(__dirname, "..", "..", "public"); + +function getFiles(dir, basePath = "/") { + let fileList = []; + const files = fs.readdirSync(dir); + + files.forEach((file) => { + const filePath = path.join(dir, file); + const fileUrl = path.join(basePath, file).replace(/\\/g, "/"); + + if (fs.statSync(filePath).isDirectory()) { + fileList = fileList.concat(getFiles(filePath, fileUrl)); + } else { + fileList.push(fileUrl); + } + }); + + return fileList; +} + +function generateAssetManifest() { + const assets = getFiles(PUBLIC_DIR); + fs.writeFileSync(path.join(PUBLIC_DIR, "asset-manifest.json"), JSON.stringify(assets, null, 2)); + console.log("Asset manifest generated!", assets); +} + +function generatePWAManifest() { + generateAssetManifest(); // fetched later in service-worker + + const siteTitle = process.env.DUMBDROP_TITLE || process.env.SITE_TITLE || "DumbDrop"; + const pwaManifest = { + name: siteTitle, + short_name: siteTitle, + description: "A simple file upload application", + start_url: "/", + display: "standalone", + background_color: "#ffffff", + theme_color: "#000000", + icons: [ + { + src: "/assets/icon.png", + type: "image/png", + sizes: "192x192" + }, + { + src: "/assets/icon.png", + type: "image/png", + sizes: "512x512" + } + ], + orientation: "any" + }; + + fs.writeFileSync(path.join(PUBLIC_DIR, "manifest.json"), JSON.stringify(pwaManifest, null, 2)); + console.log("PWA manifest generated!", pwaManifest); +} + +module.exports = { generatePWAManifest }; \ No newline at end of file diff --git a/src/server.js b/src/server.js index b680128..6390251 100644 --- a/src/server.js +++ b/src/server.js @@ -8,6 +8,7 @@ const { app, initialize, config } = require('./app'); const logger = require('./utils/logger'); const fs = require('fs'); const { executeCleanup } = require('./utils/cleanup'); +const { generatePWAManifest } = require('./scripts/pwa-manifest-generator') // Track open connections const connections = new Set(); @@ -40,6 +41,9 @@ async function startServer() { } }); + // Dynamically generate PWA manifest into public folder + generatePWAManifest(); + // Track new connections server.on('connection', (connection) => { connections.add(connection);