package server import ( "net/http" "task/internal/sqlite" ) func (s *Server) handleListTokens(w http.ResponseWriter, r *http.Request) { id := r.PathValue("id") if _, ok, code := s.requireProjectRole(id, sqlite.RoleAdmin, r); !ok { writeError(w, code, "forbidden") return } if _, tok := s.currentTokenAuth(r); tok { writeError(w, http.StatusForbidden, "session required") return } list, err := s.DB.ListProjectTokens(id) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } writeJSON(w, http.StatusOK, list) } func (s *Server) handleCreateToken(w http.ResponseWriter, r *http.Request) { id := r.PathValue("id") u, ok, code := s.requireProjectRole(id, sqlite.RoleAdmin, r) if !ok { writeError(w, code, "forbidden") return } if _, tok := s.currentTokenAuth(r); tok { writeError(w, http.StatusForbidden, "session required") return } var body struct { Name string `json:"name"` Scope string `json:"scope"` } if err := readJSON(r, &body); err != nil { writeError(w, http.StatusBadRequest, "invalid json") return } if body.Scope == "" { body.Scope = sqlite.TokenScopeWrite } t, err := s.DB.CreateProjectToken(id, body.Name, body.Scope, u.ID) if err != nil { writeError(w, http.StatusBadRequest, err.Error()) return } writeJSON(w, http.StatusCreated, t) } func (s *Server) handleRevokeToken(w http.ResponseWriter, r *http.Request) { id := r.PathValue("id") tid := r.PathValue("tid") if _, ok, code := s.requireProjectRole(id, sqlite.RoleAdmin, r); !ok { writeError(w, code, "forbidden") return } if _, tok := s.currentTokenAuth(r); tok { writeError(w, http.StatusForbidden, "session required") return } if err := s.DB.RevokeProjectToken(id, tid); err != nil { writeError(w, http.StatusBadRequest, err.Error()) return } writeJSON(w, http.StatusOK, map[string]string{"status": "ok"}) } func (s *Server) handlePatchUser(w http.ResponseWriter, r *http.Request) { id := r.PathValue("id") me, _ := s.currentUser(r) var body struct { IsAdmin *bool `json:"isAdmin"` } if err := readJSON(r, &body); err != nil { writeError(w, http.StatusBadRequest, "invalid json") return } if body.IsAdmin == nil { writeError(w, http.StatusBadRequest, "isAdmin required") return } if id == me.ID && !*body.IsAdmin { writeError(w, http.StatusBadRequest, "cannot remove your own admin flag") return } if err := s.DB.SetUserAdmin(id, *body.IsAdmin); err != nil { writeError(w, http.StatusBadRequest, err.Error()) return } u, err := s.DB.GetUserByID(id) if err != nil { writeError(w, http.StatusNotFound, err.Error()) return } u.PasswordHash = "" writeJSON(w, http.StatusOK, u) } func (s *Server) handleDeleteUser(w http.ResponseWriter, r *http.Request) { id := r.PathValue("id") me, _ := s.currentUser(r) if id == me.ID { writeError(w, http.StatusBadRequest, "cannot delete yourself") return } if err := s.DB.DeleteUser(id); err != nil { writeError(w, http.StatusBadRequest, err.Error()) return } writeJSON(w, http.StatusOK, map[string]string{"status": "ok"}) } // handleDashboard returns a lightweight overview for the front page. func (s *Server) handleDashboard(w http.ResponseWriter, r *http.Request) { u, _ := s.currentUser(r) if _, tok := s.currentTokenAuth(r); tok { writeError(w, http.StatusForbidden, "session required") return } projects, err := s.Projects.ListIDsForUser(u.ID, u.IsAdmin) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } type openTicket struct { ID string `json:"id"` Title string `json:"title"` Status string `json:"status"` ProjectID string `json:"projectId"` Project string `json:"projectName"` } type projSum struct { ID string `json:"id"` Name string `json:"name"` OpenCount int `json:"openCount"` DurationNs int64 `json:"durationNs"` } open := make([]openTicket, 0) sums := make([]projSum, 0, len(projects)) for _, p := range projects { tickets, err := s.Tickets.List(p.ID) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } openCount := 0 for _, t := range tickets { if string(t.Status) != "done" { openCount++ if len(open) < 20 { open = append(open, openTicket{ ID: t.ID, Title: t.Title, Status: string(t.Status), ProjectID: p.ID, Project: p.Name, }) } } } entries, _ := s.Times.ListByProject(p.ID) var dur int64 for _, e := range entries { dur += int64(e.Duration) } sums = append(sums, projSum{ID: p.ID, Name: p.Name, OpenCount: openCount, DurationNs: dur}) } writeJSON(w, http.StatusOK, map[string]any{ "projects": sums, "openTickets": open, }) }