Skip to content

Commit 376c97f

Browse files
authored
Merge pull request #87 from markrai/fix/vapid-docker-discovery
fix: expose pushConfigured on auth status and wire VAPID env through docker
2 parents fc31106 + 8bf0394 commit 376c97f

31 files changed

Lines changed: 783 additions & 1971 deletions

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ coverage.out
4141
*.test
4242

4343
# Development related
44-
/docs/draft/*
44+
/docs/draft/
4545
f.bat
4646
a.bat
4747
*.pem

API.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ When the server is configured with OIDC environment variables (`SCRUMBOY_OIDC_IS
252252

253253
These are browser-redirect endpoints, not JSON APIs. After successful OIDC login, the user receives a standard `scrumboy_session` cookie. MCP and REST access work identically to password-based sessions.
254254

255-
`GET /api/auth/status` includes `oidcEnabled` (bool) and `localAuthEnabled` (bool) when OIDC is configured.
255+
`GET /api/auth/status` includes `oidcEnabled` (bool) and `localAuthEnabled` (bool) when OIDC is configured, plus `pushConfigured` (bool) to indicate whether Web Push VAPID is fully configured on the server.
256256

257257
### API access tokens (REST)
258258

CHANGELOG.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,27 @@
22

33
> **Upgrades:** No breaking changes in **3.7.x** / **3.8.x** / **3.9.x** / **3.10.x** / **3.11.x** / **3.12.x** / **3.13.x** / **3.14.x** / **3.15.x** / **3.16.x** / **3.17.x** unless noted below.
44
5+
## [3.17.1] - 2026-05-28
6+
7+
### Fixed
8+
9+
- **Web Push / VAPID discovery** - Exposes **`pushConfigured`** on **`GET /api/auth/status`** so the SPA can gate auto-subscribe and Settings without probing **`/api/push/vapid-public-key`**. Push is enabled only in full mode with both VAPID keys set; partial or anonymous-mode key pairs are ignored.
10+
11+
- **Docker deployments** - **`docker-compose.yml`** forwards **`SCRUMBOY_VAPID_*`** and **`SCRUMBOY_DEBUG_PUSH`** into the container environment.
12+
13+
### Improvements
14+
15+
- **Startup logging** - Server logs whether Web Push is enabled, disabled, partially configured, or blocked by anonymous mode.
16+
17+
### Tests
18+
19+
- **Auth status / push config** - Coverage for **`pushConfigured`** across full, partial, and anonymous VAPID setups.
20+
- **Router / Settings** - Auto-subscribe gating and Settings push UI use auth status instead of live VAPID endpoint probes.
21+
22+
### Documentation
23+
24+
- **`docs/pwa.md`**, **`README.md`**, **`API.md`**, and **`scrumboy.env.example`** - Docker verification steps and VAPID env wiring.
25+
526
## [3.17.0] - 2026-05-28
627

728
### Features

FAQ.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,12 +137,14 @@ Do not confuse them: turning on desktop notifications does **not** replace VAPID
137137
- `SCRUMBOY_VAPID_PUBLIC_KEY`
138138
- `SCRUMBOY_VAPID_PRIVATE_KEY`
139139

140-
(URL-safe base64 from a VAPID generator; see [`docs/pwa.md`](docs/pwa.md).) When both are set, signed-in clients may try to subscribe automatically; each user must still **allow notifications** in the browser. **Settings → Customization → Web Push** can turn push off or back on per device.
140+
(URL-safe base64 from a VAPID generator.) When both are set in **full mode**, signed-in clients may try to subscribe automatically; each user must still **allow notifications** in the browser. **Settings → Customization → Web Push** can turn push off or back on per device.
141141

142142
Optional: `SCRUMBOY_VAPID_SUBSCRIBER` is a **contact hint for push providers** (plain email or `mailto:` / `https:` URL). It does **not** control who can sign in and does not need to match OIDC or user emails.
143143

144144
**Not telemetry:** VAPID identifies **your** Scrumboy server to the push network so assignment events can be delivered. It is not product analytics and does not send board data to Scrumboy’s project maintainers.
145145

146+
For what VAPID is, how it fits this project, key generation, and verification, see [`docs/vapid.md`](docs/vapid.md). For PWA install, Docker wiring, and auto-subscribe behavior, see [`docs/pwa.md`](docs/pwa.md).
147+
146148
# Auditing
147149

148150
## How does auditing work, and where can I see it?

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<p align="center">
22
<img width="372" src="internal/httpapi/web/githublogo.png" alt="scrumboy logo" />
33
<br />
4-
<img src="https://img.shields.io/badge/version-v3.17.0-blue" alt="version" />
4+
<img src="https://img.shields.io/badge/version-v3.17.1-blue" alt="version" />
55
<a href="LICENSE">
66
<img src="https://img.shields.io/badge/license-AGPL--v3-orange" alt="license" />
77
</a>
@@ -133,7 +133,7 @@ See [`docs/oidc.md`](docs/oidc.md) for full setup details, constraints, and trou
133133

134134
### PWA / Web Push (optional)
135135

136-
Install the app from the browser for a standalone window and better mobile UX. **Background assignment alerts** use the **Web Push API** with **VAPID** keys on the server. When both keys are set, signed-in clients attempt to subscribe automatically (browser permission may be prompted). Details and subscriber contact semantics: **[`docs/pwa.md`](docs/pwa.md)**.
136+
Install the app from the browser for a standalone window and better mobile UX. **Background assignment alerts** use the **Web Push API** with **VAPID** keys on the server. When both keys are set, signed-in clients attempt to subscribe automatically (browser permission may be prompted). Docker users must pass the VAPID variables into the container environment and recreate the container after changes. Details, Docker verification steps, and subscriber contact semantics: **[`docs/pwa.md`](docs/pwa.md)**.
137137

138138
### Frontend build note
139139

cmd/scrumboy/main.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"net/http"
99
"os"
1010
"os/signal"
11+
"strings"
1112
"syscall"
1213
"time"
1314

@@ -88,6 +89,7 @@ func main() {
8889
})
8990
logger.Printf("OIDC enabled (issuer: %s)", cfg.OIDCIssuerCanonical)
9091
}
92+
logWebPushConfiguration(logger, cfg.ScrumboyMode, cfg.VAPIDPublicKey, cfg.VAPIDPrivateKey)
9193

9294
maxB := cfg.MaxRequestBodyBytes
9395
if maxB <= 0 {
@@ -223,3 +225,18 @@ func main() {
223225
}
224226
srv.Close()
225227
}
228+
229+
func logWebPushConfiguration(logger *log.Logger, mode, publicKey, privateKey string) {
230+
pub := strings.TrimSpace(publicKey)
231+
priv := strings.TrimSpace(privateKey)
232+
switch {
233+
case httpapi.PushConfigured(mode, publicKey, privateKey):
234+
logger.Printf("web push: enabled")
235+
case strings.TrimSpace(mode) == "anonymous" && pub != "" && priv != "":
236+
logger.Printf("web push: disabled (anonymous mode)")
237+
case pub != "" || priv != "":
238+
logger.Printf("web push: partial config ignored")
239+
default:
240+
logger.Printf("web push: disabled (set SCRUMBOY_VAPID_PUBLIC_KEY and SCRUMBOY_VAPID_PRIVATE_KEY)")
241+
}
242+
}

cmd/scrumboy/main_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"log"
6+
"strings"
7+
"testing"
8+
)
9+
10+
func TestLogWebPushConfiguration(t *testing.T) {
11+
cases := []struct {
12+
name string
13+
mode string
14+
pub string
15+
priv string
16+
want string
17+
}{
18+
{name: "enabled", mode: "full", pub: "pub", priv: "priv", want: "web push: enabled"},
19+
{name: "disabled", mode: "full", pub: "", priv: "", want: "web push: disabled (set SCRUMBOY_VAPID_PUBLIC_KEY and SCRUMBOY_VAPID_PRIVATE_KEY)"},
20+
{name: "partial public only", mode: "full", pub: "pub", priv: "", want: "web push: partial config ignored"},
21+
{name: "partial private only", mode: "full", pub: "", priv: "priv", want: "web push: partial config ignored"},
22+
{name: "trimmed disabled", mode: "full", pub: " ", priv: "\t", want: "web push: disabled (set SCRUMBOY_VAPID_PUBLIC_KEY and SCRUMBOY_VAPID_PRIVATE_KEY)"},
23+
{name: "anonymous with keys", mode: "anonymous", pub: "pub", priv: "priv", want: "web push: disabled (anonymous mode)"},
24+
}
25+
26+
for _, tc := range cases {
27+
t.Run(tc.name, func(t *testing.T) {
28+
var buf bytes.Buffer
29+
logger := log.New(&buf, "", 0)
30+
logWebPushConfiguration(logger, tc.mode, tc.pub, tc.priv)
31+
if got := strings.TrimSpace(buf.String()); got != tc.want {
32+
t.Fatalf("log output = %q, want %q", got, tc.want)
33+
}
34+
})
35+
}
36+
}

docker-compose.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ services:
1313
- SQLITE_JOURNAL_MODE=WAL
1414
- SQLITE_SYNCHRONOUS=FULL
1515
- MAX_REQUEST_BODY_BYTES=1048576
16+
- SCRUMBOY_VAPID_PUBLIC_KEY=${SCRUMBOY_VAPID_PUBLIC_KEY:-}
17+
- SCRUMBOY_VAPID_PRIVATE_KEY=${SCRUMBOY_VAPID_PRIVATE_KEY:-}
18+
- SCRUMBOY_VAPID_SUBSCRIBER=${SCRUMBOY_VAPID_SUBSCRIBER:-}
19+
- SCRUMBOY_DEBUG_PUSH=${SCRUMBOY_DEBUG_PUSH:-}
1620
volumes:
1721
- ./data:/data
1822
restart: unless-stopped

0 commit comments

Comments
 (0)