forked from retoor/devplacepy
feat: add push notification infrastructure with VAPID key generation and PWA manifest support
- Introduce push notification module with VAPID certificate generation, Web Push encryption, and subscription management - Add push registration table index and database schema for storing user subscriptions - Integrate push notification dispatch into existing notification creation flow via async background tasks - Include PWA manifest, service worker, and install prompt UI elements in base template - Register new push router and ensure certificate initialization on application startup - Add configuration variables for VAPID private/public key file paths and subscription contact email - Update JavaScript application entry point with PushManager and PwaInstaller modules - Extend top navigation bar with hidden install and push enable buttons for authenticated users
This commit is contained in:
@@ -38,9 +38,10 @@ devplacepy/
|
||||
database.py # dataset connection, index creation
|
||||
templating.py # Shared Jinja2 environment + globals
|
||||
avatar.py # Multiavatar generation, URL builder
|
||||
utils.py # Password hashing, session mgmt, time_ago
|
||||
utils.py # Password hashing, session mgmt, time_ago, notification hook
|
||||
models.py # Pydantic schemas
|
||||
routers/ # One file per domain (auth, feed, posts, ...)
|
||||
push.py # Web push crypto, VAPID keys, encrypt/send/register
|
||||
routers/ # One file per domain (auth, feed, posts, push, ...)
|
||||
templates/ # Jinja2 HTML templates
|
||||
static/css/ # Page-specific CSS files
|
||||
static/js/ # Application.js (ES6 module)
|
||||
@@ -67,6 +68,7 @@ devplacepy/
|
||||
| `/services` | Background service monitoring (status, logs) |
|
||||
| `/admin` | Admin panel (user management, news curation, settings) |
|
||||
| `(none)` | `/robots.txt`, `/sitemap.xml` (SEO) |
|
||||
| `(none)` | `/push.json` (VAPID key + subscribe), `/service-worker.js`, `/manifest.json` (push + PWA) |
|
||||
|
||||
## Configuration
|
||||
|
||||
@@ -74,6 +76,7 @@ devplacepy/
|
||||
|---------|---------|---------|
|
||||
| `DEVPLACE_DATABASE_URL` | `sqlite:///devplace.db` | Database connection string |
|
||||
| `SECRET_KEY` | hardcoded fallback | Session signing key |
|
||||
| `DEVPLACE_VAPID_SUB` | `mailto:retoor@molodetz.nl` | Contact address in the VAPID JWT `sub` claim |
|
||||
|
||||
## Background Services
|
||||
|
||||
@@ -110,6 +113,66 @@ News articles have detail pages at `/news/{slug}` with full comment support (sam
|
||||
|
||||
CLI: `devplace news clear` - delete all news from local database.
|
||||
|
||||
## Push notifications & PWA
|
||||
|
||||
Authenticated users can receive native web push notifications, and the site is an
|
||||
installable Progressive Web App. Push uses only standard libraries (`cryptography`,
|
||||
`PyJWT`, `httpx`) against the Web Push Protocol — no third-party push wrapper.
|
||||
|
||||
### Events
|
||||
|
||||
Every event that already produces an in-app notification also sends a web push,
|
||||
because both share a single funnel — `create_notification()` in `utils.py`:
|
||||
|
||||
| Event | Recipient |
|
||||
|-------|-----------|
|
||||
| Direct message received | receiver |
|
||||
| Comment on your post | post author |
|
||||
| Reply to your comment | comment author |
|
||||
| `@mention` in any content | mentioned user |
|
||||
| Upvote on your content | content owner |
|
||||
| New follower | followed user |
|
||||
|
||||
`create_notification` schedules delivery as a fire-and-forget async task, so a dead
|
||||
subscription or push-service error never blocks the triggering request. Delivery
|
||||
(`push.notify_user`) iterates a user's subscriptions, encrypts the payload
|
||||
(legacy `aesgcm` content encoding), and POSTs to each endpoint; subscriptions that
|
||||
return `404`/`410` are soft-deleted.
|
||||
|
||||
### VAPID keys
|
||||
|
||||
The server identity is three PEM files generated once at startup in the repository
|
||||
root: `notification-private.pem`, `notification-private.pkcs8.pem`,
|
||||
`notification-public.pem`. They are git-ignored.
|
||||
|
||||
**These keys are the application's identity to the push services. If they are lost or
|
||||
regenerated, every existing subscription becomes permanently undeliverable.** Persist
|
||||
them across deployments and back them up; do not regenerate them.
|
||||
|
||||
### Opt-in
|
||||
|
||||
The browser requires the first permission prompt to originate from a user gesture, so
|
||||
opt-in is exposed as a button in both the top navigation (bell-with-slash icon) and on
|
||||
the `/notifications` page. After opt-in, the subscription is refreshed silently on
|
||||
every page load. `PushManager.js` owns registration, subscription, and the opt-in UI.
|
||||
|
||||
### PWA
|
||||
|
||||
`manifest.json` (192/512 and maskable icons), `service-worker.js`, and an install
|
||||
button (`PwaInstaller.js`) make the app installable. The service worker uses a
|
||||
network-first strategy for navigations and falls back to `static/offline.html` when
|
||||
offline. Installation requires a secure origin (HTTPS, or `localhost` for development).
|
||||
|
||||
| File | Role |
|
||||
|------|------|
|
||||
| `devplacepy/push.py` | VAPID keys, payload encryption, send, register |
|
||||
| `devplacepy/routers/push.py` | `/push.json`, `/service-worker.js`, `/manifest.json` |
|
||||
| `static/js/PushManager.js` | Service-worker registration + subscribe + opt-in UI |
|
||||
| `static/js/PwaInstaller.js` | `beforeinstallprompt` capture + install button |
|
||||
| `static/service-worker.js` | Receives push, shows notification, offline fallback |
|
||||
| `static/manifest.json` | PWA manifest (icons, display, theme) |
|
||||
| `static/offline.html` | Offline fallback page |
|
||||
|
||||
## Database
|
||||
|
||||
SQLite via `dataset` with production-oriented pragmas set on every connection:
|
||||
@@ -127,7 +190,7 @@ All indexes are created via `CREATE INDEX IF NOT EXISTS` wrapped in try/except -
|
||||
|
||||
## Testing
|
||||
|
||||
- **148 tests** across 14 files: Playwright integration + unit tests
|
||||
- **274 tests** across 23 files: Playwright integration + unit tests
|
||||
- Playwright (NOT pytest-playwright plugin - conflicts, uninstall it)
|
||||
- Server starts as subprocess on port 10501 with isolated temp database
|
||||
- Test users `alice_test` / `bob_test` seeded via HTTP at session start
|
||||
|
||||
Reference in New Issue
Block a user