Address production-readiness review: clearer errors, safer votes, and ops hardening.
Distinguish auth/lookup failures, make votes idempotent on visible questions, bound shutdown, page admin users, LRU throttle, trusted-proxy CIDRs, avatar cleanup, versioned migrations, and session cleanup logging.
This commit is contained in:
+29
-5
@@ -5,6 +5,7 @@ import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
@@ -56,7 +57,7 @@ func newHandler(db *sql.DB, sessions *store.SessionStore, uploader blob.Uploader
|
||||
srv, err := web.New(store.NewPostgres(db), sessions.Store(), plumber.TemplateFS, plumber.StaticFS, web.Config{
|
||||
AdminSetupSecret: strings.TrimSpace(os.Getenv("ADMIN_SETUP_SECRET")),
|
||||
SecureCookie: secureCookieFromEnv(),
|
||||
TrustProxy: os.Getenv("TRUST_PROXY") == "1",
|
||||
TrustedProxies: parseTrustedProxies(os.Getenv("TRUSTED_PROXY_CIDRS")),
|
||||
Blob: uploader,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -65,6 +66,22 @@ func newHandler(db *sql.DB, sessions *store.SessionStore, uploader blob.Uploader
|
||||
return srv.Handler()
|
||||
}
|
||||
|
||||
func parseTrustedProxies(raw string) []*net.IPNet {
|
||||
var out []*net.IPNet
|
||||
for _, part := range strings.Split(raw, ",") {
|
||||
part = strings.TrimSpace(part)
|
||||
if part == "" {
|
||||
continue
|
||||
}
|
||||
_, n, err := net.ParseCIDR(part)
|
||||
if err != nil {
|
||||
log.Fatalf("TRUSTED_PROXY_CIDRS: bad CIDR %q: %v", part, err)
|
||||
}
|
||||
out = append(out, n)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// secureCookieFromEnv defaults to secure when PORT is set (PaaS/production)
|
||||
// and refuses an explicit disable in that environment.
|
||||
func secureCookieFromEnv() bool {
|
||||
@@ -96,12 +113,19 @@ func run(httpSrv *http.Server) {
|
||||
case sig := <-sigCh:
|
||||
log.Printf("shutdown signal: %v", sig)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
if err := httpSrv.Shutdown(ctx); err != nil {
|
||||
err := httpSrv.Shutdown(ctx)
|
||||
cancel()
|
||||
if err != nil {
|
||||
log.Printf("shutdown: %v", err)
|
||||
_ = httpSrv.Close()
|
||||
}
|
||||
if err := <-errCh; err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
log.Fatal(err)
|
||||
select {
|
||||
case err := <-errCh:
|
||||
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
log.Printf("server exit: %v", err)
|
||||
}
|
||||
case <-time.After(3 * time.Second):
|
||||
log.Printf("server exit: timed out waiting for ListenAndServe")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user