Phase 2-6: Git sync, sharing, 2FA, AI integration

- 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
This commit is contained in:
2026-05-22 19:53:24 +02:00
parent 0c1047d390
commit 4df87cbf9a
8 changed files with 830 additions and 3 deletions
+24 -2
View File
@@ -9,6 +9,7 @@ import (
"markdownhub/internal/auth"
"markdownhub/internal/files"
"markdownhub/internal/git"
)
func writeJSON(w http.ResponseWriter, status int, v interface{}) {
@@ -28,6 +29,7 @@ func (s *Server) handleLogin(w http.ResponseWriter, r *http.Request) {
var req struct {
Email string `json:"email"`
Password string `json:"password"`
TOTPCode string `json:"totp_code"`
}
if err := decodeBody(r, &req); err != nil {
writeJSON(w, 400, map[string]string{"error": "invalid request"})
@@ -36,14 +38,27 @@ func (s *Server) handleLogin(w http.ResponseWriter, r *http.Request) {
var id, hash string
var isAdmin bool
var totpSecret *string
err := s.db.QueryRow(
"SELECT id, password_hash, is_admin FROM users WHERE email = ?", req.Email,
).Scan(&id, &hash, &isAdmin)
"SELECT id, password_hash, is_admin, totp_secret FROM users WHERE email = ?", req.Email,
).Scan(&id, &hash, &isAdmin, &totpSecret)
if err != nil || !auth.CheckPassword(hash, req.Password) {
writeJSON(w, 401, map[string]string{"error": "invalid credentials"})
return
}
// Check TOTP if enabled
if totpSecret != nil && *totpSecret != "" {
if req.TOTPCode == "" {
writeJSON(w, 401, map[string]string{"error": "totp_required"})
return
}
if !auth.ValidateTOTP(*totpSecret, req.TOTPCode) {
writeJSON(w, 401, map[string]string{"error": "invalid TOTP code"})
return
}
}
token, err := auth.CreateToken(id, isAdmin, s.secret)
if err != nil {
writeJSON(w, 500, map[string]string{"error": "token creation failed"})
@@ -186,6 +201,13 @@ func (s *Server) handleWriteFile(w http.ResponseWriter, r *http.Request) {
writeJSON(w, 500, map[string]string{"error": "write failed"})
return
}
// Auto-commit on save
go func() {
git.InitRepo(s.dataDir, userID)
git.AutoCommit(s.dataDir, userID, req.Path)
}()
writeJSON(w, 200, map[string]string{"status": "ok", "path": req.Path})
}