package app import ( "net/http" "strconv" "strings" ) // ---------------- HTTP routing ---------------- func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { if strings.HasPrefix(r.URL.Path, "/api/") { s.handleAPI(w, r) return } if strings.Contains(r.URL.Path, ".git") { s.handleGitHTTP(w, r) return } s.handleWeb(w, r) } func (s *Server) handleAPI(w http.ResponseWriter, r *http.Request) { path := strings.TrimPrefix(r.URL.Path, "/api") switch { case r.Method == http.MethodPost && path == "/register": s.handleRegister(w, r) case r.Method == http.MethodPost && path == "/login": s.handleLogin(w, r) case r.Method == http.MethodPost && path == "/logout": s.handleLogout(w, r) case r.Method == http.MethodGet && path == "/me": s.handleMe(w, r) case strings.HasPrefix(path, "/tokens"): s.handleTokenRoutes(w, r, strings.TrimPrefix(path, "/tokens")) case strings.HasPrefix(path, "/admin"): s.handleAdminRoutes(w, r, strings.TrimPrefix(path, "/admin")) case r.Method == http.MethodPost && path == "/repos": s.handleRepoCreate(w, r) case r.Method == http.MethodGet && path == "/repos/search": s.handleRepoSearch(w, r) case strings.HasPrefix(path, "/repos/"): s.handleRepoSubroute(w, r, strings.TrimPrefix(path, "/repos/")) default: writeError(w, http.StatusNotFound, "not found") } } func (s *Server) handleRepoSubroute(w http.ResponseWriter, r *http.Request, rest string) { parts := strings.Split(rest, "/") if len(parts) < 2 { writeError(w, http.StatusNotFound, "not found") return } owner, repo := parts[0], parts[1] if len(parts) == 2 { switch r.Method { case http.MethodGet: s.handleRepoGet(w, r, owner, repo) case http.MethodPatch: s.handleRepoUpdate(w, r, owner, repo) case http.MethodDelete: s.handleRepoDelete(w, r, owner, repo) default: writeError(w, http.StatusMethodNotAllowed, "method not allowed") } return } if len(parts) == 3 && parts[2] == "fork" && r.Method == http.MethodPost { s.handleRepoFork(w, r, owner, repo) return } if len(parts) == 3 && parts[2] == "branches" && r.Method == http.MethodGet { s.handleRepoBranches(w, r, owner, repo) return } if len(parts) == 3 && parts[2] == "tags" && r.Method == http.MethodGet { s.handleRepoTags(w, r, owner, repo) return } if len(parts) >= 3 && parts[2] == "collaborators" { s.handleCollaborators(w, r, owner, repo, parts[3:]) return } if len(parts) >= 3 && parts[2] == "pulls" { if len(parts) == 3 { switch r.Method { case http.MethodGet: s.handlePRList(w, r, owner, repo) case http.MethodPost: s.handlePRCreate(w, r, owner, repo) default: writeError(w, http.StatusMethodNotAllowed, "method not allowed") } return } if len(parts) >= 4 { n, err := strconv.Atoi(parts[3]) if err != nil { writeError(w, http.StatusBadRequest, "invalid pull request number") return } if len(parts) == 4 && r.Method == http.MethodGet { s.handlePRView(w, r, owner, repo, n) return } if len(parts) == 5 && r.Method == http.MethodGet && parts[4] == "diff" { s.handlePRDiff(w, r, owner, repo, n) return } if len(parts) == 5 && parts[4] == "comments" { if r.Method == http.MethodGet { s.handlePRCommentsList(w, r, owner, repo, n) } else if r.Method == http.MethodPost { s.handlePRCommentCreate(w, r, owner, repo, n) } else { writeError(w, http.StatusMethodNotAllowed, "method not allowed") } return } if len(parts) == 5 && r.Method == http.MethodPost && parts[4] == "close" { s.handlePRClose(w, r, owner, repo, n) return } if len(parts) == 5 && r.Method == http.MethodPost && parts[4] == "merge" { s.handlePRMerge(w, r, owner, repo, n) return } } } writeError(w, http.StatusNotFound, "not found") }