rc2
All checks were successful
release-tag / release-image (push) Successful in 1m46s

This commit is contained in:
2026-01-12 14:29:46 +01:00
parent da56e85edf
commit 15aaba77ef
3 changed files with 66 additions and 39 deletions

View File

@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1 # syntax=docker/dockerfile:1
# Build Go binary # Build Go binary
FROM golang:1.25-alpine AS builder FROM golang:1.22-alpine AS builder
WORKDIR /src WORKDIR /src
RUN apk add --no-cache ca-certificates tzdata RUN apk add --no-cache ca-certificates tzdata
COPY go.mod ./ COPY go.mod ./
@@ -13,7 +13,7 @@ RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /out/ntfywui ./cmd/ntfy
FROM binwiederhier/ntfy:latest AS ntfy FROM binwiederhier/ntfy:latest AS ntfy
# Runtime # Runtime
FROM alpine:3.23 FROM alpine:3.20
RUN apk add --no-cache ca-certificates tzdata \ RUN apk add --no-cache ca-certificates tzdata \
&& addgroup -S ntfywui && adduser -S -G ntfywui -h /home/ntfywui ntfywui && addgroup -S ntfywui && adduser -S -G ntfywui -h /home/ntfywui ntfywui
WORKDIR /app WORKDIR /app

View File

@@ -62,25 +62,25 @@ func main() {
logger := log.New(os.Stdout, "", log.LstdFlags) logger := log.New(os.Stdout, "", log.LstdFlags)
s := app.NewServer(app.Config{ s := app.NewServer(app.Config{
BasePath: strings.TrimRight(*basePath, "/"), BasePath: strings.TrimRight(*basePath, "/"),
DataDir: *dataDir, DataDir: *dataDir,
Secret: secKey, Secret: secKey,
CookieSecure: *cookieSecure, CookieSecure: *cookieSecure,
TrustedProxies: trusted, TrustedProxies: trusted,
NtfyBin: *ntfyBin, NtfyBin: *ntfyBin,
NtfyConfig: *ntfyConfig, NtfyConfig: *ntfyConfig,
NtfyTimeout: *reqTimeout, NtfyTimeout: *reqTimeout,
Logger: logger, Logger: logger,
}) })
httpSrv := &http.Server{ httpSrv := &http.Server{
Addr: *listenAddr, Addr: *listenAddr,
Handler: s.Handler(), Handler: s.Handler(),
ReadTimeout: 15 * time.Second, ReadTimeout: 15 * time.Second,
ReadHeaderTimeout: 10 * time.Second, ReadHeaderTimeout: 10 * time.Second,
WriteTimeout: 30 * time.Second, WriteTimeout: 30 * time.Second,
IdleTimeout: 60 * time.Second, IdleTimeout: 60 * time.Second,
MaxHeaderBytes: 1 << 20, MaxHeaderBytes: 1 << 20,
} }
go func() { go func() {

View File

@@ -5,6 +5,7 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"os"
"os/exec" "os/exec"
"regexp" "regexp"
"strings" "strings"
@@ -45,41 +46,67 @@ func (c *Client) Run(ctx context.Context, args []string, env map[string]string,
if c.Bin == "" { if c.Bin == "" {
return "", "", 0, errors.New("ntfy binary not set") return "", "", 0, errors.New("ntfy binary not set")
} }
// Many (newer) ntfy versions support `--config/-c` for server-side commands
// (serve/user/access/token). Some older builds do not. We try with --config
// first (if configured) and fall back to running without it if the binary
// rejects the flag.
withConfig := args
if c.Config != "" { if c.Config != "" {
args = append([]string{"--config", c.Config}, args...) withConfig = append([]string{"--config", c.Config}, args...)
} }
tctx := ctx tctx := ctx
var cancel context.CancelFunc var cancel context.CancelFunc
if c.Timeout > 0 { if c.Timeout > 0 {
tctx, cancel = context.WithTimeout(ctx, c.Timeout) tctx, cancel = context.WithTimeout(ctx, c.Timeout)
defer cancel() defer cancel()
} }
cmd := exec.CommandContext(tctx, c.Bin, args...) // helper to execute once
if stdin != "" { runOnce := func(a []string) (string, string, int, error) {
cmd.Stdin = strings.NewReader(stdin) cmd := exec.CommandContext(tctx, c.Bin, a...)
if stdin != "" {
cmd.Stdin = strings.NewReader(stdin)
}
var outb, errb bytes.Buffer
cmd.Stdout = &outb
cmd.Stderr = &errb
if env != nil {
// inherit env + add/override provided vars
cmd.Env = append([]string{}, os.Environ()...)
for k, v := range env {
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", k, v))
}
}
err := cmd.Run()
exit := 0
if err != nil {
var ee *exec.ExitError
if errors.As(err, &ee) {
exit = ee.ExitCode()
} else if errors.Is(err, context.DeadlineExceeded) {
return outb.String(), errb.String(), -1, fmt.Errorf("ntfy timeout")
} else {
return outb.String(), errb.String(), -1, err
}
}
return outb.String(), errb.String(), exit, nil
} }
var outb, errb bytes.Buffer
cmd.Stdout = &outb // first attempt (maybe with --config)
cmd.Stderr = &errb out, errOut, exit, err := runOnce(withConfig)
if env != nil { if c.Config == "" {
// inherit env automatically return out, errOut, exit, err
for k, v := range env { }
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", k, v)) // fallback for older ntfy binaries that don't know --config
if exit != 0 {
errTrim := strings.TrimSpace(errOut)
if strings.Contains(errTrim, "flag provided but not defined: -config") ||
strings.Contains(errTrim, "unknown flag") && strings.Contains(errTrim, "config") {
return runOnce(args)
} }
} }
err := cmd.Run() return out, errOut, exit, err
exit := 0
if err != nil {
var ee *exec.ExitError
if errors.As(err, &ee) {
exit = ee.ExitCode()
} else if errors.Is(err, context.DeadlineExceeded) {
return outb.String(), errb.String(), -1, fmt.Errorf("ntfy timeout")
} else {
return outb.String(), errb.String(), -1, err
}
}
return outb.String(), errb.String(), exit, nil
} }
func (c *Client) ListUsers(ctx context.Context) ([]User, error) { func (c *Client) ListUsers(ctx context.Context) ([]User, error) {