💾 Archived View for gemini.rmf-dev.com › repo › Vaati › Gemigit › files › 22315ec19b3b27a546c5cc0bef… captured on 2023-01-29 at 16:00:27. Gemini links have been rewritten to link to archived content

View Raw

More Information

➡️ Next capture (2023-03-20)

-=-=-=-=-=-=-

0 package gmi

1

2 import (

3 "bytes"

4 "errors"

5 "fmt"

6 "gemigit/access"

7 "gemigit/config"

8 "gemigit/db"

9 "gemigit/repo"

10 "io"

11 "log"

12 "strconv"

13 "strings"

14 "text/template"

15

16 "github.com/go-git/go-git/v5/plumbing"

17 "github.com/go-git/go-git/v5/plumbing/object"

18 "github.com/go-git/go-git/v5/plumbing/transport"

19 "github.com/pitr/gig"

20 )

21

22 func execT(c gig.Context, template string, data interface{}) error {

23 t := templates.Lookup(template)

24 var b bytes.Buffer

25 err := t.Execute(&b, data)

26 if err != nil {

27 log.Println(err.Error())

28 return c.NoContent(gig.StatusTemporaryFailure, err.Error())

29 }

30 return c.Gemini(b.String())

31 }

32

33 func execTemplate(template string, data interface{}) (string, error) {

34 t := templates.Lookup(template)

35 var b bytes.Buffer

36 err := t.Execute(&b, data)

37 if err != nil {

38 log.Println(err.Error())

39 return "", err

40 }

41 return strings.TrimRight(b.String(), "\n"), nil

42 }

43

44 const (

45 pageLog = iota

46 pageFiles

47 pageRefs

48 pageLicense

49 pageReadme

50 )

51

52 var templates *template.Template

53

54 func LoadTemplate(dir string) error {

55 var err error

56

57 templates = template.New("gmi")

58 template.Must(templates.Funcs(template.FuncMap {

59 "AccessFirst": accessFirstOption,

60 "AccessSecond": accessSecondOption,

61 "AccessPrivilege": privilegeToString,

62 }).ParseFiles(

63 dir + "/index.gmi",

64 dir + "/account.gmi",

65 dir + "/repo.gmi",

66 dir + "/repo_log.gmi",

67 dir + "/repo_files.gmi",

68 dir + "/repo_refs.gmi",

69 dir + "/repo_license.gmi",

70 dir + "/repo_readme.gmi",

71 dir + "/repo_access.gmi",

72 dir + "/public_repo.gmi",

73 dir + "/group_list.gmi",

74 dir + "/group.gmi",

75 dir + "/public_list.gmi",

76 dir + "/public_user.gmi",

77 ))

78 if err != nil {

79 return err

80 }

81 log.Println("Templates loaded")

82 return nil

83 }

84

85 func showRepoFile(user string, reponame string, file string) (string, error) {

86 out, err := repo.GetFile(reponame, user, file)

87 if err != nil {

88 return "", err

89 }

90 reader, err := out.Reader()

91 if err != nil {

92 return "", err

93 }

94 buf, err := io.ReadAll(reader)

95 if err != nil {

96 return "", err

97 }

98 return string(buf), nil

99 }

100

101 func ShowIndex(c gig.Context) (error) {

102 _, connected := db.GetUser(c.CertHash())

103 data := struct {

104 Title string

105 Registration bool

106 Connected bool

107 }{

108 Title: config.Cfg.Title,

109 Registration: config.Cfg.Users.Registration,

110 Connected: connected,

111 }

112 return execT(c, "index.gmi", data)

113 }

114

115 func ShowAccount(c gig.Context) (error) {

116 user, exist := db.GetUser(c.CertHash())

117 if !exist {

118 return c.NoContent(gig.StatusBadRequest,

119 "Invalid username")

120 }

121 repoNames := []string{}

122 repos, err := user.GetRepos(false)

123 if err != nil {

124 repoNames = []string{"Failed to load repositories"}

125 log.Println(err)

126 } else {

127 for _, repo := range repos {

128 repoNames = append(repoNames, repo.Name)

129 }

130 }

131 accessRepos, err := db.HasReadAccessTo(user.ID)

132 data := struct {

133 Username string

134 Description string

135 Repositories []string

136 RepositoriesAccess []db.Repo

137 }{

138 Username: user.Name,

139 Description: user.Description,

140 Repositories: repoNames,

141 RepositoriesAccess: accessRepos,

142 }

143 return execT(c, "account.gmi", data)

144 }

145

146 func ShowGroups(c gig.Context) (error) {

147 user, exist := db.GetUser(c.CertHash())

148 if !exist {

149 return c.NoContent(gig.StatusBadRequest,

150 "Invalid username")

151 }

152 groups, err := user.GetGroups()

153 if err != nil {

154 log.Println(err.Error())

155 return c.NoContent(gig.StatusTemporaryFailure,

156 "Failed to fetch groups")

157 }

158 data := struct {

159 Groups []db.Group

160 }{

161 Groups: groups,

162 }

163 return execT(c, "group_list.gmi", data)

164 }

165

166 func ShowMembers(c gig.Context) (error) {

167 user, exist := db.GetUser(c.CertHash())

168 if !exist {

169 return c.NoContent(gig.StatusBadRequest,

170 "Invalid username")

171 }

172 group := c.Param("group")

173 isOwner, err := user.IsInGroup(group)

174 if err != nil {

175 return c.NoContent(gig.StatusTemporaryFailure,

176 "Group not found")

177 }

178

179 members, err := user.GetMembers(group)

180 if err != nil {

181 log.Println(err.Error())

182 return c.NoContent(gig.StatusTemporaryFailure,

183 "Failed to fetch group members")

184 }

185 desc, err := db.GetGroupDesc(group)

186 if err != nil {

187 log.Println(err.Error())

188 return c.NoContent(gig.StatusTemporaryFailure,

189 "Failed to fetch group description")

190 }

191

192 owner := ""

193 if isOwner {

194 owner = user.Name

195 } else {

196 m, err := db.GetGroupOwner(group)

197 if err != nil {

198 log.Println(err.Error())

199 return c.NoContent(gig.StatusTemporaryFailure,

200 "Failed to fetch group owner")

201 }

202 owner = m.Name

203 }

204

205 data := struct {

206 Members []db.Member

207 MembersCount int

208 IsOwner bool

209 Owner string

210 Group string

211 Description string

212 }{

213 Members: members,

214 MembersCount: len(members),

215 IsOwner: isOwner,

216 Owner: owner,

217 Group: group,

218 Description: desc,

219 }

220 return execT(c, "group.gmi", data)

221 }

222

223 func getRepo(c gig.Context, owner bool) (string, string, error) {

224 username := ""

225 if owner {

226 user, exist := db.GetUser(c.CertHash())

227 if !exist {

228 return "", "", c.NoContent(gig.StatusBadRequest,

229 "Invalid username")

230 }

231 username = user.Name

232 } else {

233 username = c.Param("user")

234 ret, err := db.IsRepoPublic(c.Param("repo"), c.Param("user"))

235 if !ret {

236 user, exist := db.GetUser(c.CertHash())

237 if exist {

238 err := access.HasReadAccess(c.Param("repo"),

239 c.Param("user"),

240 user.Name)

241 ret = err == nil

242 }

243 }

244 if !ret || err != nil {

245 return "", "", c.NoContent(gig.StatusBadRequest,

246 "No repository called " + c.Param("repo") +

247 " by user " + c.Param("user"))

248 }

249 }

250 return username, c.Param("repo"), nil

251 }

252

253 func hasFile(name string, author string, file string) bool {

254 ret, err := repo.GetFile(name, author, file)

255 if ret != nil && err == nil {

256 return true

257 }

258 return false

259 }

260

261 type commit struct {

262 Message string

263 Info string

264 }

265

266 type file struct {

267 Hash string

268 Info string

269 }

270

271 type branch struct {

272 Name string

273 Info string

274 }

275

276 func showRepoLogs(name string, author string) (string, error) {

277 ret, err := repo.GetCommits(name, author)

278 if ret == nil || err == transport.ErrEmptyRemoteRepository {

279 return "", nil

280 }

281 if err != nil {

282 log.Println(err.Error())

283 return "", errors.New("Corrupted repository")

284 }

285 commits := []commit{}

286 err = ret.ForEach(func(c *object.Commit) error {

287 info := c.Hash.String() + ", by " + c.Author.Name + " on " +

288 c.Author.When.Format("2006-01-02 15:04:05")

289 commits = append(commits, commit{Info: info,

290 Message: c.Message})

291 return nil

292 })

293 return execTemplate("repo_log.gmi", commits)

294 }

295

296 func showRepoFiles(name string, author string) (string, error) {

297 ret, err := repo.GetFiles(name, author)

298 if ret == nil || err == transport.ErrEmptyRemoteRepository {

299 return "", nil

300 }

301 if err != nil {

302 log.Println(err.Error())

303 return "", errors.New("Corrupted repository")

304 }

305 files := []file{}

306 err = ret.ForEach(func(f *object.File) error {

307 info := f.Mode.String() + " " + f.Name +

308 " " + strconv.Itoa(int(f.Size))

309 files = append(files, file{Info: info,

310 Hash: f.Blob.Hash.String()})

311 return nil

312 })

313 return execTemplate("repo_files.gmi", files)

314 }

315

316 func showRepoRefs(name string, author string) (string, error) {

317 refs, err := repo.GetRefs(name, author)

318 if refs == nil || err == transport.ErrEmptyRemoteRepository {

319 return "", nil

320 }

321 if err != nil {

322 log.Println(err)

323 return "", errors.New("Corrupted repository")

324 }

325 branches := []branch{}

326 tags := []branch{}

327 err = refs.ForEach(func(c *plumbing.Reference) error {

328 if c.Type().String() != "hash-reference" ||

329 c.Name().IsRemote() {

330 return nil

331 }

332 var b branch

333 b.Name = c.Name().String()

334 b.Name = b.Name[strings.LastIndex(b.Name, "/") + 1:]

335 b.Info = "last commit on "

336

337 commit, err := repo.GetCommit(name, author, c.Hash())

338 if err != nil {

339 b.Info = "failed to fetch commit"

340 } else {

341 when := commit.Author.When

342 str := fmt.Sprintf(

343 "%d-%02d-%02d %02d:%02d:%02d",

344 when.Year(), int(when.Month()),

345 when.Day(), when.Hour(),

346 when.Minute(), when.Second())

347 b.Info += str + " by " + commit.Author.Name

348 }

349 if c.Name().IsBranch() {

350 branches = append(branches, b)

351 } else {

352 tags = append(tags, b)

353 }

354 return nil

355 })

356 refs.Close()

357 data := struct {

358 Branches []branch

359 Tags []branch

360 }{

361 branches,

362 tags,

363 }

364 return execTemplate("repo_refs.gmi", data)

365 }

366

367 func showRepoLicense(name string, author string) (string, error) {

368 content, err := showRepoFile(author, name, "LICENSE")

369 if err != nil {

370 return "", errors.New("No license found")

371 }

372 return execTemplate("repo_license.gmi", content)

373 }

374

375 func showRepoReadme(name string, author string) (string, error) {

376 content, err := showRepoFile(author, name, "README.gmi")

377 if err != nil {

378 content, err = showRepoFile(author, name, "README")

379 }

380 if err != nil {

381 return "", errors.New("No readme found")

382 }

383 return execTemplate("repo_readme.gmi", content)

384 }

385

386 func showRepo(c gig.Context, page int, owner bool) (error) {

387 author, name, err := getRepo(c, owner)

388 if err != nil {

389 log.Println(err.Error())

390 return c.NoContent(gig.StatusTemporaryFailure, err.Error())

391 }

392 desc, err := db.GetRepoDesc(name, author)

393 if err != nil {

394 log.Println(err.Error())

395 return c.NoContent(gig.StatusTemporaryFailure,

396 "Repository not found")

397 }

398 protocol := "http"

399 if config.Cfg.Git.Https {

400 protocol = "https"

401 }

402 public, err := db.IsRepoPublic(name, author)

403 if err != nil {

404 log.Println(err.Error())

405 return c.NoContent(gig.StatusTemporaryFailure,

406 "Repository not found")

407 }

408

409 content := ""

410 switch page {

411 case pageLog:

412 content, err = showRepoLogs(name, author)

413 case pageFiles:

414 content, err = showRepoFiles(name, author)

415 case pageRefs:

416 content, err = showRepoRefs(name, author)

417 case pageLicense:

418 content, err = showRepoLicense(name, author)

419 case pageReadme:

420 content, err = showRepoReadme(name, author)

421 }

422 if err != nil {

423 return c.NoContent(gig.StatusBadRequest,

424 "Invalid repository")

425 }

426

427 data := struct {

428 Protocol string

429 Domain string

430 User string

431 Description string

432 Repo string

433 Public bool

434 HasReadme bool

435 HasLicense bool

436 Content string

437 }{

438 Protocol: protocol,

439 Domain: config.Cfg.Git.Domain,

440 User: author,

441 Description: desc,

442 Repo: name,

443 Public: public,

444 HasReadme: hasFile(name, author, "README.gmi") ||

445 hasFile(name, author, "README"),

446 HasLicense: hasFile(name, author, "LICENSE"),

447 Content: content,

448 }

449 if owner {

450 return execT(c, "repo.gmi", data)

451 }

452 return execT(c, "public_repo.gmi", data)

453 }

454

455 func PublicList(c gig.Context) (error) {

456 repos, err := db.GetPublicRepo()

457 if err != nil {

458 log.Println(err.Error())

459 return c.NoContent(gig.StatusTemporaryFailure,

460 "Internal error, "+err.Error())

461 }

462 return execT(c, "public_list.gmi", repos)

463 }

464

465 func PublicAccount(c gig.Context) error {

466 user, err := db.GetPublicUser(c.Param("user"))

467 if err != nil {

468 return c.NoContent(gig.StatusBadRequest, err.Error())

469 }

470 repos, err := user.GetRepos(true)

471 if err != nil {

472 return c.NoContent(gig.StatusTemporaryFailure,

473 "Invalid account, " + err.Error())

474 }

475 data := struct {

476 Name string

477 Description string

478 Repositories []db.Repo

479 }{

480 user.Name,

481 user.Description,

482 repos,

483 }

484 return execT(c, "public_user.gmi", data)

485 }

486

487 func ShowAccess(c gig.Context) error {

488 user, exist := db.GetUser(c.CertHash())

489 if !exist {

490 return c.NoContent(gig.StatusBadRequest,

491 "Invalid username")

492 }

493 repo, err := user.GetRepo(c.Param("repo"))

494 if err != nil {

495 return c.NoContent(gig.StatusBadRequest, err.Error())

496 }

497 access, err := db.GetRepoAccess(repo.RepoID)

498 if err != nil {

499 return c.NoContent(gig.StatusBadRequest, err.Error())

500 }

501 groups, err := db.GetRepoGroupAccess(repo.RepoID)

502 if err != nil {

503 return c.NoContent(gig.StatusBadRequest, err.Error())

504 }

505 data := struct {

506 Repo string

507 Collaborators []db.Access

508 Groups []db.Access

509 Owner bool

510 }{

511 Repo: repo.Name,

512 Collaborators: access,

513 Groups: groups,

514 Owner: true,

515 }

516 return execT(c, "repo_access.gmi", data)

517 }

518