From 18d33ed141217da4764e994a9d35ffa1b24c1a69 Mon Sep 17 00:00:00 2001 From: lgcosta Date: Thu, 5 Nov 2026 08:04:27 -0300 Subject: [PATCH] =?UTF-8?q?feat(pwa):=20app=20instal=C3=A1vel=20com=20serv?= =?UTF-8?q?ice=20worker=20e=20shell=20offline?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - manifest.webmanifest: standalone, theme/background color, ícones 192/512 any+maskable+monochrome (PNGs em web/icons) e atalho 'Nova nota' (?new=1) - sw.js: cache versionado (notas-shell-v1); navegação network-first com fallback ao shell (offline), estáticos stale-while-revalidate, /api/* e SSE sempre passthrough; activate limpa caches e notifica janelas - app.js: registro do SW, beforeinstallprompt com item 'Instalar Aplicativo…' no menu Ajuda (escondido em standalone/após instalar), toast de atualização, aviso offline/online, deep-link ?new=1 - index.html: meta tags theme-color/mobile-web-app/apple-touch-icon/manifest - server: staticHandler com MIME application/manifest+json e Cache-Control no-cache para /sw.js; embed inclui manifest, sw e ícones - README: seção PWA + orientação de serving (Go atual e equivalente Rust) --- README.md | 53 +++++++++++++++++++++++ internal/api/server.go | 21 +++++++++- web/embed.go | 4 +- web/icons/icon-192.png | Bin 0 -> 2950 bytes web/icons/icon-512.png | Bin 0 -> 10818 bytes web/icons/maskable-192.png | Bin 0 -> 2495 bytes web/icons/maskable-512.png | Bin 0 -> 9869 bytes web/icons/monochrome-512.png | Bin 0 -> 9422 bytes web/index.html | 9 ++++ web/js/app.js | 49 ++++++++++++++++++++++ web/manifest.webmanifest | 31 ++++++++++++++ web/sw.js | 79 +++++++++++++++++++++++++++++++++++ 12 files changed, 243 insertions(+), 3 deletions(-) create mode 100644 web/icons/icon-192.png create mode 100644 web/icons/icon-512.png create mode 100644 web/icons/maskable-192.png create mode 100644 web/icons/maskable-512.png create mode 100644 web/icons/monochrome-512.png create mode 100644 web/manifest.webmanifest create mode 100644 web/sw.js diff --git a/README.md b/README.md index bae53c0..52bc3aa 100644 --- a/README.md +++ b/README.md @@ -223,6 +223,59 @@ curl -X POST -H "Authorization: Bearer $T" -H 'Content-Type: application/json' \ # (clientes que ignoram o evento podem sobrescrever a própria edição sem querer) ``` +## PWA — instalável e offline + +O app é um **PWA completo**: instalável no Linux/Windows/macOS/Android (menu **Ajuda → +Instalar Aplicativo…**, ou o ícone da barra de endereço), com janela `standalone` sem chrome +do navegador e atalho de área de trabalho "Nova nota" (`/?new=1`). + +- **`web/manifest.webmanifest`** — `name/short_name/description/start_url/scope/theme_color/ + background_color`, `display: standalone` (+ `display_override` com window-controls-overlay), + ícones **192/512 em `any`, `maskable` e `monochrome`** (PNGs reais em `web/icons/`) e atalho. +- **`web/sw.js`** — Service Worker com cache versionado (`notas-shell-v1`): + - `install` pré-cacheia o shell e `skipWaiting`; `activate` expurga caches antigos, + `clients.claim()` e avisa as janelas (`notas:updated` → toast "App atualizado"); + - navegação: **network-first com fallback ao shell** (abre offline); + - estáticos: **stale-while-revalidate** (carregamento instantâneo); + - **`/api/*` nunca é interceptado** — dados sempre frescos e o SSE de tempo real não quebra. + - **Ao publicar mudanças de assets, bump na constante `CACHE`** do `sw.js`. +- **Meta tags** em `index.html`: `theme-color`, `mobile-web-app-capable`, + `apple-mobile-web-app-*`, `apple-touch-icon` e ``. +- **Offline UX**: shell + interface funcionam sem rede; ações que precisam do servidor mostram + toast claro ("Sem conexão — o app abre offline; salvar precisa do servidor"). + +### Servindo os arquivos (backend Go atual) + +Já implementado em `internal/api/server.go` (`staticHandler`): + +- `sw.js` → `Cache-Control: no-cache, no-store, must-revalidate` + `Content-Type: + text/javascript` (a detecção de update do SW é por bytes — nunca cacheie!); +- `manifest.webmanifest` → `Content-Type: application/manifest+json; charset=utf-8` + (o Go não conhece a extensão `.webmanifest` por padrão); +- tudo vem do `go:embed` (`web/embed.go` inclui `manifest.webmanifest sw.js icons`). +- O Service Worker só registra em origem segura: **HTTPS ou `localhost`/`127.0.0.1`** — em IP + de LAN puro (`http://192.168.x.x`) o navegador bloqueia SW; use um túnel/reverse proxy TLS + para testar instalação real fora de localhost. + +### Equivalente em Rust (axum, caso o backend migre) + +```rust +// MIME do manifest + no-cache no SW +async fn manifest() -> impl IntoResponse { + ([(header::CONTENT_TYPE, "application/manifest+json")], include_str!("../web/manifest.webmanifest")) +} +async fn sw() -> impl IntoResponse { + [ + (header::CONTENT_TYPE, "text/javascript"), + (header::CACHE_CONTROL, "no-cache, no-store, must-revalidate"), + ] +} +``` +No actix-web, o `fs::Files` já define MIME por extensão, mas registre antes: +`mime::BABFILE` não — use `mime_guess::init::detect_EXTENSION("webmanifest", +"application/manifest+json")` e um `wrap_fn` que sobrepõe `Cache-Control` quando o caminho for +`/sw.js`. (No `tower-http` `SetResponseHeaderLayer` com `Match` por path é o caminho limpo.) + ## Como funciona o versionamento - O conteúdo é guardado em `data/blobs//` (content-addressed, como objetos do diff --git a/internal/api/server.go b/internal/api/server.go index 0b6c852..5bcb6ed 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -78,11 +78,30 @@ func (s *Server) Handler() http.Handler { mux.HandleFunc("POST /api/presence", s.auth(s.handlePresence)) mux.HandleFunc("GET /{$}", s.serveIndex) - mux.Handle("GET /", http.FileServerFS(s.webFS)) + mux.Handle("GET /", s.staticHandler()) return s.wrap(mux) } +// staticHandler serve o FS embutido com os cabeçalhos que PWA exige: +// sw.js nunca pode ser cacheado pelo browser (a detecção de nova versão é por +// bytes) e o manifest ganha o MIME application/manifest+json. +func (s *Server) staticHandler() http.Handler { + files := http.FileServerFS(s.webFS) + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case "/sw.js": + w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") + w.Header().Set("Content-Type", "text/javascript; charset=utf-8") + files.ServeHTTP(w, r) + return + case "/manifest.webmanifest": + w.Header().Set("Content-Type", "application/manifest+json; charset=utf-8") + } + files.ServeHTTP(w, r) + }) +} + // wrap aplica middlewares globais: recuperação, headers de segurança e log. func (s *Server) wrap(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/web/embed.go b/web/embed.go index f1e72d7..4472076 100644 --- a/web/embed.go +++ b/web/embed.go @@ -3,7 +3,7 @@ package web import "embed" -// FS contém index.html, css/ e js/ na raiz. +// FS contém index.html, css/, js/, icons/, sw.js e manifest.webmanifest na raiz. // -//go:embed index.html css js +//go:embed index.html manifest.webmanifest sw.js css js icons var FS embed.FS diff --git a/web/icons/icon-192.png b/web/icons/icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..5aa11d3b3b24f7c7f523da67c44bfb1a3d8f5fa0 GIT binary patch literal 2950 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 PFOZ2L-r$*@TkQV;qV^J- literal 0 HcmV?d00001 diff --git a/web/icons/icon-512.png b/web/icons/icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..ec8850838eb9f1a4632892a0c3957d3ae407ff59 GIT binary patch literal 10818 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 literal 0 HcmV?d00001 diff --git a/web/icons/maskable-192.png b/web/icons/maskable-192.png new file mode 100644 index 0000000000000000000000000000000000000000..9dd863069d0c3448f668189711f8aa89368d60b6 GIT binary patch literal 2495 zcmZ`*dmt417oQoEmbhluBmAQE-jwoAq+M+6W|J*)CE3U$ByAEAW4XoV8IjjT7b_Lw z%Jn8G-4Y@4E=nSz1K98@TY7iUnzUpCym^G?zW>8j@IFK%^ZDMGnA>yc4aYQg)L%X zzL#VK`Y<`|k%ss_*>Oo(-&?#CiiGOpHa%~aFPR`5yW~V1=#t5|C1}+f2@TGuo$-9| zJj?V$C{?dJdxq7q(lENdHgLeBYFu&TTw$7m@UgxoqqkH7MIDFuErJU~lp|dP?u*9- z!L10k4_%gyHptC_sBp?IXl6zQUzE@ zD+uONj5-+*tY2u;u?Ha}S~N!pBNicaJdYk!!iSI|ex}D&674^ixH4?_{y>F}2${yR zJwBlUqjP!=HGC!nyYj|#semJ=y33x05HQJUQP`dOVcK$p)-Pyw*Sm;UBL zFxr`@YZ?!{u%x$$Y$edY;Sf8@=(7O;G(M4WP5J*U6E@5l@6ND}h7eWs`pF?x9P*p6 zd?iAp;ZZc**h~~_Y9A^xRsvCBj5}BIZoWn_vZ3bAg(L`BzNu;23Nm+}eJ(EnY69#i_=BW1+TU42@E5hm-%b!|Q0Z8HdMF6E;n80s+Yv>P zH}*UA-GB$ONV|R@i3XXR&(pL3fS6a|{bUGvoxaIl4)}kCCNLI{Oh|{2SUClkNf7d! z(*8@bdN3-hXWY=a5t-Uhl%-=P)3JTgudyPaPGVZ`o1&fQ+?%1;9=rDIXI<_jMmo(V zO7wOt_>DT4I?1FQVqJ!%FY%nRU2Z_h>M~EK@A#!5hDSl3akGq)pj z4tH-q7XDpsOBI16rhdyDZ`S%MCbHTGAqy3@7oyPJ>f%D%y^m?XXP~L7bDSuAD_7wk!I7OOzHOhg&?u!>wy)q2 zf@1Q6HN1qdGjW@1zk5>({4g?9TU2heJlXdkpds9}APGZIyU$uVl>en3+7!R``e9fv zb?gxJbRiFNtZ{M}QFp07thBXW+d8l`=i{dzt-V|k7tH0T`Jeq>R+s&wyNWKWa=G4x z44IJ|NmnPNO=`aeQdYD!plu189YQQ2c<-i^z%ME|v@$>D-9aGZo)KL(VW5bqCw{BW za6AGmxAWXNn}KE1Hny8Luv{~&3*nCc?%}w)=qOn73^aIJ(vp_n(m0q|&Q$vchv1IF z2Y$id-UP4QetT)|`OH0?Nq3iF_m#Dv+QB&4VnrC~)aVEh-uitY{#C;F{W^w8j9M9# zK=9nVW&&qB*PD6zvH=ZQYQ!{s{?k2+wDugSfX6cXAs8`#Uoh_eWJN0vVl={r1TAcB4NC`RRc(n?n{_u;m3-3~PTC9>Eb!Z$_?dZ!&p0KVfIB7^y zksx549|a$4qN=csS2qm*@SeF|KkB^GOMF1x3MbD~GiHJnnmXbZdq}zSVHP-f?7C}+ zHY?Lk$}az+-f*?))6Ch>rcmqI7i|mM0&|0=B5n(5j%%Mm0`q?lOe2n8rNWDK`c9z1 z+R9g2%Zqf~LR~8T`E^Uj)At6izl-+XlMUi)>$*!(*fn@G_3syzdIg2vsq^341%hY% zkRYP9;V*6y1df=2WIvdWzO3IeVu@u8$jwxbh;qSqxXKfXLou4$)It_W&>pERUSJ5y z9}#@m0$}qfZ{Q=FV{uXVh0DjJ)vtBV+#!3J@)}J0T1fq~zS|gCY_jV7kh>f=y28h` zz!Dd-8Y~c`?T(Ejkxwo36HQB*wkl1xE3yweJqekx62Wv=Uq&`2W3N0%gAI@<@xIXW z%&-p*d01;%$nrtuQ~IBtrtEW^JZUe|`AVgHw#B2ruF*a|XEx=R5sP);gzABCDZOEf14lDqD)!p|5ynGtF&sVdxM9uR8kYC(_^zwv5cwd;eJ{qPjqnSX~}F zr-A$_DWA0Q0WD*|wq+>&To>sH3_!JIP$>BrisfQG>D}@OfpbvGSoHoqYqU&@IHhv{ zf@8-#bO*tEXJTv<*uXBeJGme9sIPU@rC5?S1%KIaQw+He0C%vCzuOK5yn*8?A2f#R z$uVUYP`0H{yBu}^E!@dN)6VQ~qaaSI)Fd8J8lHPI*AfC~e^58+p&D`f(dV!Sq z{4xKB_Fydy8kD@%UN=-Dp9u@j=^X+U0XN$4hZqpyCM@JkVC>_rO3G+IfU?rWha#(44@)4-Ha_D zI4g#4-c81#)3)v`PY|mfyP93(W3P<+#IVoa0tLs;5}S~RQGYF+se>tih@aei9HhKN z&9Xj*#IVKb)2`YCStT5@)Ygl+2Pk__6+I(>9_FPpeSt?0)GeGXL5@f_P TSl}t$1D-(4{WkOxa}MucbNm_f literal 0 HcmV?d00001 diff --git a/web/icons/maskable-512.png b/web/icons/maskable-512.png new file mode 100644 index 0000000000000000000000000000000000000000..7afade6f5f2aa7bdd52c5635e7282616a26329dd GIT binary patch literal 9869 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 Notas — editor estilo Mousepad + + + + + + + + @@ -171,6 +179,7 @@