package server import ( "fmt" "net/http" "strings" "gitcat/internal/gitx" "gitcat/internal/store" ) var reservedRepoNames = map[string]bool{ "admin": true, "login": true, "logout": true, "install": true, "new": true, "settings": true, "static": true, "media": true, "favicon.ico": true, "api": true, "avatars": true, "assets": true, "help": true, "about": true, } func (s *Server) handleHome(w http.ResponseWriter, r *http.Request) { q := strings.TrimSpace(r.URL.Query().Get("q")) repos, err := s.st.ListRepos(q) if err != nil { s.renderError(w, r, http.StatusInternalServerError, "读取项目列表失败") return } pushes, _ := s.st.RecentPushes(8) users, repoCount, pushCount, _ := s.st.Counts() p := s.page(r, "项目") p.Active = "home" p.Data["Repos"] = repos p.Data["Query"] = q p.Data["RecentPushes"] = pushes p.Data["PushActors"] = s.pushActors(pushes) p.Data["StatRepos"] = repoCount p.Data["StatUsers"] = users p.Data["StatPushes"] = pushCount p.Data["HasRepos"] = len(repos) > 0 p.Data["Announcement"] = s.st.GetSetting("announcement") s.render(w, r, "home", p) } func (s *Server) handleNewProjectPage(w http.ResponseWriter, r *http.Request) { u := s.requireLogin(w, r) if u == nil { return } if !s.st.Installed() { http.Redirect(w, r, "/install", http.StatusFound) return } p := s.page(r, "新建项目") p.Active = "new" s.render(w, r, "new_project", p) } func (s *Server) handleNewProjectSubmit(w http.ResponseWriter, r *http.Request) { u := s.requireLogin(w, r) if u == nil { return } if !s.checkCSRF(r) { s.renderError(w, r, http.StatusForbidden, "表单已过期,请返回重试") return } name := strings.TrimSpace(r.FormValue("name")) desc := strings.TrimSpace(r.FormValue("description")) name = strings.TrimSuffix(name, ".git") fail := func(msg string) { p := s.page(r, "新建项目") p.Active = "new" p.Error = msg p.Data["Name"] = name p.Data["Description"] = desc w.WriteHeader(http.StatusBadRequest) s.render(w, r, "new_project", p) } switch { case name == "": fail("请填写项目名称") return case !gitx.ValidRepoName(name): fail("项目名称只能包含字母、数字、点、下划线、短横线,且需以字母或数字开头") return case reservedRepoNames[strings.ToLower(name)]: fail("该名称为系统保留名,请换一个") return case len(desc) > 300: fail("项目描述不能超过 300 个字符") return } if _, err := s.st.RepoByName(name); err == nil { fail("项目 " + name + " 已存在") return } repo, err := s.st.CreateRepo(name, desc, u.ID) if err != nil { fail(err.Error()) return } if err := gitx.InitBare(s.repoPath(repo.Name), repo.DefaultBranch); err != nil { _ = s.st.DeleteRepo(repo.ID) fail("初始化 git 仓库失败:" + err.Error()) return } setFlash(w, fmt.Sprintf("项目 %s 创建成功,现在可以把代码推上来了", repo.Name)) http.Redirect(w, r, "/"+repo.Name, http.StatusFound) } // canManageRepo 判断当前用户能否管理该仓库(创建者或管理员)。 func canManageRepo(u *store.User, repo *store.Repo) bool { if u == nil { return false } if u.IsAdmin { return true } return repo.OwnerID != nil && *repo.OwnerID == u.ID }