From 639af6371e8867cc974a1f0de888081c88f864d1 Mon Sep 17 00:00:00 2001 From: Agent Date: Sun, 14 Jun 2026 10:15:20 -0400 Subject: [PATCH] feat: add PWA support - manifest, service worker, offline page, icons --- public/assets/img/icon-192.png | Bin 0 -> 7712 bytes public/assets/img/icon-512.png | Bin 0 -> 23067 bytes public/manifest.json | 26 ++++++++++++ public/sw.js | 71 +++++++++++++++++++++++++++++++++ routes/web.php | 5 +++ views/errors/offline.php | 21 ++++++++++ views/layouts/auth.php | 2 + views/layouts/main.php | 10 +++++ 8 files changed, 135 insertions(+) create mode 100644 public/assets/img/icon-192.png create mode 100644 public/assets/img/icon-512.png create mode 100644 public/manifest.json create mode 100644 public/sw.js create mode 100644 views/errors/offline.php diff --git a/public/assets/img/icon-192.png b/public/assets/img/icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..60d823888b23d45e1b7065ed19dd27c8506799c2 GIT binary patch literal 7712 zcmai(2T)U6x5p2lU_l^&(p!+;t8@@UQIIB8dKD01#ML~+8cL*XVAiYTy z2uO#7qCkj*_QJjI-kCS^ee?F@WUtwowa-lEoVEVH|Bltu(YSGq@frXCH|}df32<+=tvX z^q<*A1sED?d?x$lA!QCx$^Uknu|Q@?n%f-hwoSU4Pm#nw4OACdJ zb-MDpSPuF+XOUy3-d(83E_@EX{PGr{9_U|Ekq4vYC8?@xFm{KeWLTK-eby-d0Jz&kEY&-op1g4QEQ&46gF>#`ms&HE-5K#_&`^;am<_F zYPvaR?vL!q`Oa!|zWl{x3_drCg*OZMbx@qyTy z*>Dg@mf^QFMGxCC?!3E^4%{DCtlMa!EKLbu(p&tE6+}YM@fcq=J}{Vv%e#yCx2;{i zRppfR_-yDp$y>7U^-?%VJ!*Zh!|Frt!P+2;v;hP%v_@?3YNX-WsX^xC9HxHC4fSb| zxE*=_ZFUsrQb7iN3?(fnrItTNjoGS-ou7-pu&l~a%UtSdZOrxD7}whBt<`i#0Tru7 zk245F+CC)Ksi1;kK1XtM-rDs%sn3bXMmCPhUmXH#^itW@JRP{Ek{};I-M?xO(C2{2 zD-{5h<92bu=2;8@};Q*+}*ylyHKk~o%yI++wM`xC@!waXB^_J zVlFu~^xGcQnBpnZ{k0vmJ@Zn!_=q7Kdr5ee=KNY_w3^=zECBxdtGM@vBazIHpBufs z+hg-!tl?A!S*hZKLf59sX;=j3YaGUTBfr45rKI}eTr-|@vEnS@>8h%A(#5A7o@tsL zWj}2kN$iaHozR~%6y<{)4mB5`F_fjkF|ce6->I#NGmo8xL=Ge^FZ@d~EH((_{sY|CwZC`Uf3q*6ncVlk#p(lP2&#QZM`^6{C3HGnGY zD-dwH7-N+95?jt{bV`$gNr`o5g5-YDxCN8< z7v|=!(OxJL1mD5E*F{Yk=ZP|o@f7L0NmSPuV4>9dW8GZrOw~FkMsW0}IHMrkH4=7- zM0N7768@^V_GQ5}8^OmK5X0`N8v)Knckiye=($XINzbmzfIuYe>WBwVi;N;ae6XQo zf8wfKvUtuA%~kWxTDkfw=Z{)7-v%^>E59h2=v@jlk*$A~M*P_~@)kemH)7t~ov~vk z-#(aRGs_ubu{8Q)UBHMYj?%66HWT^d^uSP`Hi0hjOdCnW=TNXKk{)1;-Eroz->zEO z7g$Mm<~-l{6B^qSAPUr>!McmH;edvp0Njq9wBDs)3WKG?&d(!aB4Ampq~XSoI<@sH zcX<2E8?$u76jC*8sYgA!fe}|Rir$?hWd=>*FZ#4rtw^7Bv<=73oW3GCWc)xBkbG^u z_(D_Jvwie5j0v8v470r6e7r`$bvI{OmHC#G~E59vr$ z3T_lU*LPUy6#&uiI_*zTJ*5tZq%ETtV%SNlo!CS39Kw98Ro$SRw- z_?!WiJHrVud$+L%Ia9@Q0GXIlhYUp#**wAJfdFoHVG4lix32OpS#2P^5HLPj$tyxs zhrpGWpxLLchL$SM&dw!oa&r-Q9`F=RI$OF(VS0KE z@!>mqZVXv5rA`pw)^%UIqRdc=;_UbYXHXH2ESCauOYyFs46-$Dc}1DQv0*zwB{p zytxrspS14CTvS-39!7Kea1&!e4F=D~UXwWB9b^+zRf;h$d2nCfGTBXN9lW`rU%u_! zMslU22wn#g1)^kVf-nV{pvjt&s^󈦪WJv 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 Z)rWE;)X*i2nIeA>26{%iB{~j&{ug3wLazV- literal 0 HcmV?d00001 diff --git a/public/manifest.json b/public/manifest.json new file mode 100644 index 0000000..2ff7373 --- /dev/null +++ b/public/manifest.json @@ -0,0 +1,26 @@ +{ + "name": "ServerManager", + "short_name": "SM", + "description": "Linux Server Management Platform", + "start_url": "/dashboard", + "display": "standalone", + "background_color": "#0f1117", + "theme_color": "#6366f1", + "categories": ["productivity", "utilities"], + "lang": "en", + "orientation": "any", + "icons": [ + { + "src": "/assets/img/icon-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "any maskable" + }, + { + "src": "/assets/img/icon-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "any maskable" + } + ] +} diff --git a/public/sw.js b/public/sw.js new file mode 100644 index 0000000..f6fda07 --- /dev/null +++ b/public/sw.js @@ -0,0 +1,71 @@ +const CACHE = 'sm-v1'; +const STATIC_ASSETS = [ + '/assets/css/style.css', + '/assets/js/app.js', + '/assets/js/main.js', + '/assets/img/server.svg', + '/assets/img/icon-192.png', + '/assets/img/icon-512.png', + '/manifest.json', + '/offline', +]; + +self.addEventListener('install', function(e) { + e.waitUntil( + caches.open(CACHE).then(function(cache) { + return cache.addAll(STATIC_ASSETS); + }).then(function() { + return self.skipWaiting(); + }) + ); +}); + +self.addEventListener('activate', function(e) { + e.waitUntil( + caches.keys().then(function(keys) { + return Promise.all( + keys.filter(function(k) { return k !== CACHE; }).map(function(k) { return caches.delete(k); }) + ); + }).then(function() { + return self.clients.claim(); + }) + ); +}); + +self.addEventListener('fetch', function(e) { + var url = new URL(e.request.url); + + if (url.pathname.startsWith('/api/')) { + return; + } + + if ( + url.pathname.startsWith('/assets/') || + url.pathname === '/manifest.json' + ) { + e.respondWith( + caches.open(CACHE).then(function(cache) { + return cache.match(e.request).then(function(cached) { + var fetchPromise = fetch(e.request).then(function(response) { + cache.put(e.request, response.clone()); + return response; + }); + return cached || fetchPromise; + }); + }) + ); + return; + } + + e.respondWith( + fetch(e.request).catch(function() { + return caches.match(e.request).then(function(cached) { + if (cached) return cached; + if (e.request.mode === 'navigate') { + return caches.match('/offline'); + } + return new Response('Offline', { status: 503 }); + }); + }) + ); +}); diff --git a/routes/web.php b/routes/web.php index c813489..ce1921d 100755 --- a/routes/web.php +++ b/routes/web.php @@ -27,6 +27,11 @@ $router->get('/notifications', [DashboardController::class, 'notifications'], [A $router->get('/privacy', [DashboardController::class, 'privacy']); $router->get('/terms', [DashboardController::class, 'terms']); +$router->get('/offline', function () { + $view = \ServerManager\Core\App::getInstance()->getView(); + $view->display('errors.offline', ['title' => 'Offline']); +}); + $router->get('/login', [AuthController::class, 'loginForm']); $router->post('/login', [AuthController::class, 'login'], [CSRFMiddleware::class, RateLimitMiddleware::class]); $router->get('/register', [AuthController::class, 'registerForm']); diff --git a/views/errors/offline.php b/views/errors/offline.php new file mode 100644 index 0000000..a555f8f --- /dev/null +++ b/views/errors/offline.php @@ -0,0 +1,21 @@ + + + + + + Offline — ServerManager + + + +
+
📡
+

You're Offline

+

ServerManager needs an internet connection to manage your servers.
Check your connection and try again.

+ +
+ + diff --git a/views/layouts/auth.php b/views/layouts/auth.php index d14013b..9b92c52 100755 --- a/views/layouts/auth.php +++ b/views/layouts/auth.php @@ -5,6 +5,8 @@ <?= htmlspecialchars($title ?? 'ServerManager') ?> + + diff --git a/views/layouts/main.php b/views/layouts/main.php index ceb4611..c62d367 100755 --- a/views/layouts/main.php +++ b/views/layouts/main.php @@ -21,6 +21,11 @@ if (!isset($user) || $user === null) { + + + + + @@ -225,5 +230,10 @@ if (!isset($user) || $user === null) { +