0 package repo
1
2 import (
3 "errors"
4 "gemigit/config"
5 "gemigit/db"
6 io "gemigit/util"
7 "os"
8 "time"
9
10 "github.com/go-git/go-git/v5"
11 "github.com/go-git/go-git/v5/plumbing"
12 "github.com/go-git/go-git/v5/plumbing/object"
13 "github.com/go-git/go-git/v5/plumbing/storer"
14 "github.com/go-git/go-git/v5/storage"
15 "github.com/go-git/go-git/v5/storage/memory"
16 "github.com/go-git/go-git/v5/plumbing/transport/http"
17 )
18
19 var rootPath string
20 type repo struct {
21 memory storage.Storer
22 repository *git.Repository
23 update time.Time
24 }
25 var repositories = make(map[string]repo)
26
27 func Init(path string) error {
28 if config.Cfg.Git.Remote.Enabled {
29 return nil
30 }
31 rootPath = path
32 return os.MkdirAll(path, 0700)
33 }
34
35 func InitRepo(name string, username string) error {
36 if config.Cfg.Git.Remote.Enabled {
37 err := request("api/" + config.Cfg.Git.Remote.Key + "/init/" +
38 username + "/" + name)
39 return err
40 }
41 _, err := git.PlainInit(rootPath+"/"+username+"/"+name, true)
42 return err
43 }
44
45 func RemoveRepo(name string, username string) error {
46 if config.Cfg.Git.Remote.Enabled {
47 err := request("api/" + config.Cfg.Git.Remote.Key + "/rm/" +
48 username + "/" + name)
49 return err
50 }
51 return os.RemoveAll(rootPath + "/" + username + "/" + name)
52 }
53
54 func getRepo(name string, username string) (*git.Repository, error) {
55 var repository *git.Repository
56 var err error
57 url := username + "/" + name
58 if !config.Cfg.Git.Remote.Enabled {
59 repository, err = git.PlainOpen(rootPath + "/" + url)
60 return repository, err
61 }
62 var exist bool
63 r, exist := repositories[url]
64 if !exist {
65 r = repo{}
66 }
67 if exist && time.Now().Sub(r.update).Seconds() < 15 {
68 return r.repository, nil
69 }
70 r.update = time.Now()
71 repositories[url] = r
72 if r.repository == nil {
73 r.memory = memory.NewStorage()
74 r.repository, err = git.Clone(r.memory, nil,
75 &git.CloneOptions {
76 URL: config.Cfg.Git.Remote.Url + "/" + url,
77 Auth: &http.BasicAuth {
78 Username: "root#",
79 Password: config.Cfg.Git.Remote.Key,
80 },
81 })
82 if err != nil {
83 r.memory = nil
84 r.repository = nil
85 }
86 repositories[url] = r
87 return r.repository, err
88 }
89 err = r.repository.Fetch(&git.FetchOptions {
90 Auth: &http.BasicAuth {
91 Username: "root#",
92 Password: config.Cfg.Git.Remote.Key,
93 },
94 })
95 if err != nil {
96 return nil, err
97 }
98 repositories[url] = r
99 return r.repository, nil
100 }
101
102 func GetCommit(name string, username string,
103 hash plumbing.Hash) (*object.Commit, error) {
104 repo, err := getRepo(name, username)
105 if err != nil {
106 return nil, err
107 }
108 obj, err := repo.CommitObject(hash)
109 if err != nil {
110 return nil, err
111 }
112 return obj, nil
113 }
114
115 func GetCommits(name string, username string) (object.CommitIter, error) {
116 repo, err := getRepo(name, username)
117 if repo == nil || err != nil {
118 return nil, err
119 }
120 ref, err := repo.Head()
121 if err != nil {
122 return nil, nil // Empty repo
123 }
124 cIter, err := repo.Log(&git.LogOptions{From: ref.Hash()})
125 if err != nil {
126 return nil, err
127 }
128 return cIter, nil
129 }
130
131 func GetRefs(name string, username string) (storer.ReferenceIter, error) {
132 repo, err := getRepo(name, username)
133 if repo == nil || err != nil {
134 return nil, err
135 }
136 _, err = repo.Head()
137 if err != nil {
138 return nil, nil // Empty repo
139 }
140 refs, err := repo.References()
141 if err != nil {
142 return nil, err
143 }
144 return refs, nil
145 }
146
147 func getTree(name string, username string) (*object.Tree, error) {
148 repo, err := getRepo(name, username)
149 if repo == nil || err != nil {
150 return nil, err
151 }
152 ref, err := repo.Head()
153 if err != nil {
154 return nil, nil // Empty repo
155 }
156 last, err := repo.CommitObject(ref.Hash())
157 if err != nil {
158 return nil, err
159 }
160 tree, err := repo.TreeObject(last.TreeHash)
161 if err != nil {
162 return nil, err
163 }
164 return tree, nil
165 }
166
167 func GetFiles(name string, username string) (*object.FileIter, error) {
168 tree, err := getTree(name, username)
169 if err != nil {
170 return nil, err
171 }
172 if tree == nil {
173 return nil, nil
174 }
175 return tree.Files(), nil
176 }
177
178 func GetFile(name string, username string, file string) (*object.File, error) {
179 tree, err := getTree(name, username)
180 if err != nil {
181 return nil, err
182 }
183 if tree == nil {
184 return nil, nil
185 }
186 out, err := tree.File(file)
187 if err != nil {
188 return nil, err
189 }
190 return out, nil
191 }
192
193 func GetPublicFile(name string, username string, hash string) (string, error) {
194 public, err := db.IsRepoPublic(name, username)
195 if err != nil {
196 return "", err
197 }
198 if !public {
199 return "", errors.New("repository is private")
200 }
201 repo, err := git.PlainOpen(rootPath + "/" + username + "/" + name)
202 if err != nil {
203 return "", err
204 }
205 file, err := repo.BlobObject(plumbing.NewHash(hash))
206 if err != nil {
207 return "", err
208 }
209 reader, err := file.Reader()
210 if err != nil {
211 return "", err
212 }
213 buf, err := io.ReadAll(reader)
214 if err != nil {
215 return "", err
216 }
217 return string(buf), nil
218 }
219
220 func GetPrivateFile(name string, username string,
221 hash string, sig string) (string, error) {
222 user, b := db.GetUser(sig)
223 if !b || username != user.Name {
224 return "", errors.New("invalid signature")
225 }
226 repo, err := git.PlainOpen(rootPath + "/" + username + "/" + name)
227 if err != nil {
228 return "", err
229 }
230 file, err := repo.BlobObject(plumbing.NewHash(hash))
231 if err != nil {
232 return "", err
233 }
234 reader, err := file.Reader()
235 if err != nil {
236 return "", err
237 }
238 buf, err := io.ReadAll(reader)
239 if err != nil {
240 return "", err
241 }
242 return string(buf), nil
243 }
244
245 func ChangeRepoDir(name string, username string, newname string) error {
246 if config.Cfg.Git.Remote.Enabled {
247 err := request("api/" + config.Cfg.Git.Remote.Key + "/mv/" +
248 username + "/" + name + "/" + newname)
249 return err
250 }
251 return os.Rename(rootPath + "/" + username + "/" + name,
252 rootPath + "/" + username + "/" + newname)
253 }
254