Proto-to-Prod
This commit is contained in:
parent
82a8a6a21a
commit
5cabc31d37
63 changed files with 3783 additions and 602 deletions
|
|
@ -16,7 +16,6 @@ import (
|
|||
type server struct {
|
||||
staticDir string
|
||||
password string
|
||||
qrKey string
|
||||
secret []byte
|
||||
secure bool
|
||||
loginLimiter *rateLimiter
|
||||
|
|
@ -26,20 +25,13 @@ func main() {
|
|||
addr := flag.String("addr", ":8181", "listen address")
|
||||
staticDir := flag.String("static", "../web/build", "static asset directory")
|
||||
insecure := flag.Bool("insecure", false, "drop Secure flag from cookie (dev only)")
|
||||
strict := flag.Bool("strict", false, "production mode: refuse to start without DOCENT_COOKIE_SECRET and DOCENT_QR_KEY")
|
||||
strict := flag.Bool("strict", false, "production mode: refuse to start without DOCENT_COOKIE_SECRET")
|
||||
flag.Parse()
|
||||
|
||||
password := os.Getenv("DOCENT_PASSWORD")
|
||||
if password == "" {
|
||||
log.Fatal("DOCENT_PASSWORD must be set")
|
||||
}
|
||||
qrKey := os.Getenv("DOCENT_QR_KEY")
|
||||
if qrKey == "" {
|
||||
if *strict {
|
||||
log.Fatal("DOCENT_QR_KEY must be set in -strict mode")
|
||||
}
|
||||
log.Println("warning: DOCENT_QR_KEY not set; QR-code auto-login disabled")
|
||||
}
|
||||
|
||||
cookieSecret := os.Getenv("DOCENT_COOKIE_SECRET")
|
||||
if *strict && cookieSecret == "" {
|
||||
|
|
@ -61,7 +53,6 @@ func main() {
|
|||
s := &server{
|
||||
staticDir: abs,
|
||||
password: password,
|
||||
qrKey: qrKey,
|
||||
secret: secret,
|
||||
secure: !*insecure,
|
||||
loginLimiter: newRateLimiter(8, time.Minute),
|
||||
|
|
@ -69,7 +60,6 @@ func main() {
|
|||
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("POST /api/login", s.handleLogin)
|
||||
mux.HandleFunc("POST /api/key", s.handleKey)
|
||||
mux.HandleFunc("POST /api/logout", s.handleLogout)
|
||||
mux.HandleFunc("GET /api/me", s.handleMe)
|
||||
mux.Handle("GET /audio/", s.requireAuth(http.HandlerFunc(s.serveStatic)))
|
||||
|
|
@ -126,26 +116,6 @@ func (s *server) handleLogin(w http.ResponseWriter, r *http.Request) {
|
|||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func (s *server) handleKey(w http.ResponseWriter, r *http.Request) {
|
||||
if !s.loginLimiter.allow(clientIP(r)) {
|
||||
w.Header().Set("Retry-After", "60")
|
||||
http.Error(w, "too many attempts", http.StatusTooManyRequests)
|
||||
return
|
||||
}
|
||||
var body struct {
|
||||
Key string `json:"key"`
|
||||
}
|
||||
err := json.NewDecoder(r.Body).Decode(&body)
|
||||
// An empty qrKey means the QR auto-login is disabled — never accept any value.
|
||||
if err != nil || s.qrKey == "" || !constantTimeEqualString(body.Key, s.qrKey) {
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
s.setSessionCookie(w)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func (s *server) handleLogout(w http.ResponseWriter, r *http.Request) {
|
||||
s.clearSessionCookie(w)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
|
|
@ -207,7 +177,6 @@ func (s *server) serveSPA(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
// Unknown path → SPA fallback.
|
||||
w.Header().Set("Cache-Control", "no-cache")
|
||||
http.ServeFile(w, r, filepath.Join(s.staticDir, "index.html"))
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue