4df87cbf9a
- Git: init, commit, log, diff, restore, remotes, push/pull - Auto-commit on every file save - Sharing: share/unshare files with other users (ro/rw) - Shared documents view in sidebar - 2FA: TOTP setup/verify/disable, enforced at login - AI: verify spec endpoint (LiteLLM), generate (summarize/prompt/expand) - Light/dark theme with CSS variables - File delete (recursive for folders) - Admin panel + preferences panel - File creation timestamp display
76 lines
1.9 KiB
Go
76 lines
1.9 KiB
Go
package api
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"markdownhub/internal/auth"
|
|
)
|
|
|
|
func (s *Server) handleTOTPSetup(w http.ResponseWriter, r *http.Request) {
|
|
userID := getUserID(r)
|
|
|
|
// Generate secret
|
|
secret := auth.GenerateTOTPSecret()
|
|
|
|
// Get user email for URI
|
|
var email string
|
|
s.db.QueryRow("SELECT email FROM users WHERE id = ?", userID).Scan(&email)
|
|
|
|
uri := auth.TOTPUri(secret, email, "MarkdownHub")
|
|
|
|
// Store secret (not yet verified)
|
|
s.db.Exec("UPDATE users SET totp_secret = ? WHERE id = ?", secret, userID)
|
|
|
|
writeJSON(w, 200, map[string]string{
|
|
"secret": secret,
|
|
"uri": uri,
|
|
})
|
|
}
|
|
|
|
func (s *Server) handleTOTPVerify(w http.ResponseWriter, r *http.Request) {
|
|
var req struct {
|
|
Code string `json:"code"`
|
|
}
|
|
if err := decodeBody(r, &req); err != nil || req.Code == "" {
|
|
writeJSON(w, 400, map[string]string{"error": "code required"})
|
|
return
|
|
}
|
|
|
|
userID := getUserID(r)
|
|
var secret string
|
|
s.db.QueryRow("SELECT totp_secret FROM users WHERE id = ?", userID).Scan(&secret)
|
|
if secret == "" {
|
|
writeJSON(w, 400, map[string]string{"error": "TOTP not set up"})
|
|
return
|
|
}
|
|
|
|
if !auth.ValidateTOTP(secret, req.Code) {
|
|
writeJSON(w, 401, map[string]string{"error": "invalid code"})
|
|
return
|
|
}
|
|
|
|
writeJSON(w, 200, map[string]string{"status": "verified"})
|
|
}
|
|
|
|
func (s *Server) handleTOTPDisable(w http.ResponseWriter, r *http.Request) {
|
|
var req struct {
|
|
Code string `json:"code"`
|
|
}
|
|
if err := decodeBody(r, &req); err != nil || req.Code == "" {
|
|
writeJSON(w, 400, map[string]string{"error": "code required"})
|
|
return
|
|
}
|
|
|
|
userID := getUserID(r)
|
|
var secret string
|
|
s.db.QueryRow("SELECT totp_secret FROM users WHERE id = ?", userID).Scan(&secret)
|
|
|
|
if !auth.ValidateTOTP(secret, req.Code) {
|
|
writeJSON(w, 401, map[string]string{"error": "invalid code"})
|
|
return
|
|
}
|
|
|
|
s.db.Exec("UPDATE users SET totp_secret = NULL WHERE id = ?", userID)
|
|
writeJSON(w, 200, map[string]string{"status": "disabled"})
|
|
}
|