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);