diff --git a/cmd/verstak-server/server.go b/cmd/verstak-server/server.go index 9d413f5..ac1d208 100644 --- a/cmd/verstak-server/server.go +++ b/cmd/verstak-server/server.go @@ -418,6 +418,7 @@ func (s *Server) routes() *http.ServeMux { mux.HandleFunc("/api/v1/sync/pull", s.handleSyncPull) mux.HandleFunc("/api/v1/blobs/", s.handleBlobs) mux.HandleFunc("/api/client/pair", s.handleClientPair) + mux.HandleFunc("/api/auth/test", s.handleAuthTest) mux.HandleFunc("/api/client/revoke-current", s.handleClientRevoke) mux.HandleFunc("/api/client/me", s.handleClientMe) mux.HandleFunc("/api/client/revoke-device", s.handleClientRevokeDevice) @@ -827,6 +828,46 @@ func (s *Server) handleClientPair(w http.ResponseWriter, r *http.Request) { }) } +func (s *Server) handleAuthTest(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + jsonErr(w, 405, "POST required") + return + } + var req struct { + Username string `json:"username"` + Password string `json:"password"` + } + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + jsonErr(w, 400, "bad json") + return + } + if req.Username == "" || req.Password == "" { + jsonErr(w, 400, "username and password required") + return + } + var hash string + var confirmed, blocked int + err := s.db.QueryRow("SELECT password_hash, confirmed, blocked FROM server_users WHERE username=? OR email=?", + req.Username, strings.ToLower(req.Username)).Scan(&hash, &confirmed, &blocked) + if err != nil { + jsonErr(w, 401, "invalid credentials") + return + } + if blocked != 0 { + jsonErr(w, 403, "account blocked") + return + } + if confirmed == 0 { + jsonErr(w, 403, "email not confirmed") + return + } + if bcrypt.CompareHashAndPassword([]byte(hash), []byte(req.Password)) != nil { + jsonErr(w, 401, "invalid credentials") + return + } + jsonOK(w, map[string]string{"status": "ok"}) +} + func (s *Server) handleClientRevoke(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { jsonErr(w, 405, "POST required") @@ -1698,7 +1739,7 @@ func (s *Server) handleUserWebReset(w http.ResponseWriter, r *http.Request) { return } w.Header().Set("Content-Type", "text/html; charset=utf-8") - html := fmt.Sprintf(resetPasswordHTML, token) + html := strings.ReplaceAll(resetPasswordHTML, "{TOKEN}", token) w.Write([]byte(html)) case "POST": if err := r.ParseForm(); err != nil { @@ -1941,7 +1982,7 @@ func (s *Server) handleAdminDashboard(w http.ResponseWriter, r *http.Request) { smtpSecurity := s.smtpGet("smtp_security") srvURL := s.smtpGet("server_url") - html := fmt.Sprintf(` + html := `
Загрузка...+ _ = smtpURL + _ = smtpUser + _ = smtpFrom + _ = smtpSecurity + _ = smtpHost + _ = smtpPort -`) +