gitcat
1package server
2
3import (
4 "fmt"
5 "net/http"
6 "strings"
7
8 "gitcat/internal/gitx"
9 "gitcat/internal/store"
10)
11
12var reservedRepoNames = map[string]bool{
13 "admin": true, "login": true, "logout": true, "install": true, "new": true,
14 "settings": true, "static": true, "media": true, "favicon.ico": true, "api": true,
15 "avatars": true, "assets": true, "help": true, "about": true,
16}
17
18func (s *Server) handleHome(w http.ResponseWriter, r *http.Request) {
19 q := strings.TrimSpace(r.URL.Query().Get("q"))
20 repos, err := s.st.ListRepos(q)
21 if err != nil {
22 s.renderError(w, r, http.StatusInternalServerError, "读取项目列表失败")
23 return
24 }
25 pushes, _ := s.st.RecentPushes(8)
26 users, repoCount, pushCount, _ := s.st.Counts()
27
28 p := s.page(r, "项目")
29 p.Active = "home"
30 p.Data["Repos"] = repos
31 p.Data["Query"] = q
32 p.Data["RecentPushes"] = pushes
33 p.Data["PushActors"] = s.pushActors(pushes)
34 p.Data["StatRepos"] = repoCount
35 p.Data["StatUsers"] = users
36 p.Data["StatPushes"] = pushCount
37 p.Data["HasRepos"] = len(repos) > 0
38 p.Data["Announcement"] = s.st.GetSetting("announcement")
39 s.render(w, r, "home", p)
40}
41
42func (s *Server) handleNewProjectPage(w http.ResponseWriter, r *http.Request) {
43 u := s.requireLogin(w, r)
44 if u == nil {
45 return
46 }
47 if !s.st.Installed() {
48 http.Redirect(w, r, "/install", http.StatusFound)
49 return
50 }
51 p := s.page(r, "新建项目")
52 p.Active = "new"
53 s.render(w, r, "new_project", p)
54}
55
56func (s *Server) handleNewProjectSubmit(w http.ResponseWriter, r *http.Request) {
57 u := s.requireLogin(w, r)
58 if u == nil {
59 return
60 }
61 if !s.checkCSRF(r) {
62 s.renderError(w, r, http.StatusForbidden, "表单已过期,请返回重试")
63 return
64 }
65 name := strings.TrimSpace(r.FormValue("name"))
66 desc := strings.TrimSpace(r.FormValue("description"))
67 name = strings.TrimSuffix(name, ".git")
68
69 fail := func(msg string) {
70 p := s.page(r, "新建项目")
71 p.Active = "new"
72 p.Error = msg
73 p.Data["Name"] = name
74 p.Data["Description"] = desc
75 w.WriteHeader(http.StatusBadRequest)
76 s.render(w, r, "new_project", p)
77 }
78
79 switch {
80 case name == "":
81 fail("请填写项目名称")
82 return
83 case !gitx.ValidRepoName(name):
84 fail("项目名称只能包含字母、数字、点、下划线、短横线,且需以字母或数字开头")
85 return
86 case reservedRepoNames[strings.ToLower(name)]:
87 fail("该名称为系统保留名,请换一个")
88 return
89 case len(desc) > 300:
90 fail("项目描述不能超过 300 个字符")
91 return
92 }
93 if _, err := s.st.RepoByName(name); err == nil {
94 fail("项目 " + name + " 已存在")
95 return
96 }
97
98 repo, err := s.st.CreateRepo(name, desc, u.ID)
99 if err != nil {
100 fail(err.Error())
101 return
102 }
103 if err := gitx.InitBare(s.repoPath(repo.Name), repo.DefaultBranch); err != nil {
104 _ = s.st.DeleteRepo(repo.ID)
105 fail("初始化 git 仓库失败:" + err.Error())
106 return
107 }
108 setFlash(w, fmt.Sprintf("项目 %s 创建成功,现在可以把代码推上来了", repo.Name))
109 http.Redirect(w, r, "/"+repo.Name, http.StatusFound)
110}
111
112// canManageRepo 判断当前用户能否管理该仓库(创建者或管理员)。
113func canManageRepo(u *store.User, repo *store.Repo) bool {
114 if u == nil {
115 return false
116 }
117 if u.IsAdmin {
118 return true
119 }
120 return repo.OwnerID != nil && *repo.OwnerID == u.ID
121}