💾 Archived View for gemini.rmf-dev.com › repo › Vaati › Gemigit › files › 6987bf0be2d3ff7b02c6c5c946… captured on 2023-11-14 at 08:20:43. Gemini links have been rewritten to link to archived content

View Raw

More Information

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

Go Back

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 "gemigit/util"

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 dirlen := len(dir)

58 if dirlen > 1 && dir[dirlen - 1] == '/' {

59 dir = dir[:dirlen - 1]

60 }

61

62 templates = template.New("gmi")

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

64 "AccessFirst": accessFirstOption,

65 "AccessSecond": accessSecondOption,

66 "AccessPrivilege": privilegeToString,

67 }).ParseFiles(

68 dir + "/index.gmi",

69 dir + "/account.gmi",

70 dir + "/repo.gmi",

71 dir + "/repo_log.gmi",

72 dir + "/repo_files.gmi",

73 dir + "/repo_refs.gmi",

74 dir + "/repo_license.gmi",

75 dir + "/repo_readme.gmi",

76 dir + "/repo_access.gmi",

77 dir + "/register_success.gmi",

78 dir + "/public_repo.gmi",

79 dir + "/group_list.gmi",

80 dir + "/group.gmi",

81 dir + "/public_list.gmi",

82 dir + "/public_user.gmi",

83 dir + "/otp.gmi",

84 dir + "/token.gmi",

85 dir + "/token_new.gmi",

86 ))

87 if err != nil {

88 return err

89 }

90 log.Println("Templates loaded")

91 return nil

92 }

93

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

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

96 if err != nil {

97 return "", err

98 }

99 reader, err := out.Reader()

100 if err != nil {

101 return "", err

102 }

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

104 if err != nil {

105 return "", err

106 }

107 return string(buf), nil

108 }

109

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

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

112 data := struct {

113 Title string

114 Registration bool

115 Connected bool

116 Public bool

117 }{

118 Title: config.Cfg.Title,

119 Registration: config.Cfg.Users.Registration,

120 Connected: connected,

121 Public: connected || config.Cfg.Git.Public,

122 }

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

124 }

125

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

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

128 if !exist {

129 return c.NoContent(gig.StatusBadRequest, "Invalid username")

130 }

131 repoNames := []string{}

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

133 if err != nil {

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

135 log.Println(err)

136 } else {

137 for _, repo := range repos {

138 repoNames = append(repoNames, repo.Name)

139 }

140 }

141 accessRepos, err := user.HasReadAccessTo()

142 sessions, err := user.GetSessionsCount()

143 if err != nil {

144 log.Println(err)

145 return c.NoContent(gig.StatusBadRequest, "Unexpected error")

146 }

147 if sessions == 1 {

148 sessions = 0

149 }

150 data := struct {

151 Username string

152 Description string

153 Repositories []string

154 RepositoriesAccess []db.Repo

155 Sessions int

156 }{

157 Username: user.Name,

158 Description: user.Description,

159 Repositories: repoNames,

160 RepositoriesAccess: accessRepos,

161 Sessions: sessions,

162 }

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

164 }

165

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

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

168 if !exist {

169 return c.NoContent(gig.StatusBadRequest, "Invalid username")

170 }

171 groups, err := user.GetGroups()

172 if err != nil {

173 log.Println(err.Error())

174 return c.NoContent(gig.StatusTemporaryFailure,

175 "Failed to fetch groups")

176 }

177 data := struct {

178 Groups []db.Group

179 }{

180 Groups: groups,

181 }

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

183 }

184

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

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

187 if !exist {

188 return c.NoContent(gig.StatusBadRequest, "Invalid username")

189 }

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

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

192 if err != nil {

193 return c.NoContent(gig.StatusTemporaryFailure,

194 "Group not found")

195 }

196

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

198 if err != nil {

199 log.Println(err.Error())

200 return c.NoContent(gig.StatusTemporaryFailure,

201 "Failed to fetch group members")

202 }

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

204 if err != nil {

205 log.Println(err.Error())

206 return c.NoContent(gig.StatusTemporaryFailure,

207 "Failed to fetch group description")

208 }

209

210 owner := ""

211 if isOwner {

212 owner = user.Name

213 } else {

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

215 if err != nil {

216 log.Println(err.Error())

217 return c.NoContent(gig.StatusTemporaryFailure,

218 "Failed to fetch group owner")

219 }

220 owner = m.Name

221 }

222

223 data := struct {

224 Members []db.Member

225 MembersCount int

226 IsOwner bool

227 Owner string

228 Group string

229 Description string

230 }{

231 Members: members,

232 MembersCount: len(members),

233 IsOwner: isOwner,

234 Owner: owner,

235 Group: group,

236 Description: desc,

237 }

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

239 }

240

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

242 username := ""

243 if owner {

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

245 if !exist {

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

247 "Invalid username")

248 }

249 username = user.Name

250 } else {

251 username = c.Param("user")

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

253 if !ret {

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

255 if exist {

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

257 c.Param("user"),

258 user.Name)

259 ret = err == nil

260 }

261 }

262 if !ret || err != nil {

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

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

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

266 }

267 }

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

269 }

270

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

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

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

274 return true

275 }

276 return false

277 }

278

279 type commit struct {

280 Message string

281 Info string

282 }

283

284 type file struct {

285 Hash string

286 Info string

287 }

288

289 type branch struct {

290 Name string

291 Info string

292 }

293

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

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

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

297 return "", nil

298 }

299 if err != nil {

300 log.Println(err.Error())

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

302 }

303 commits := []commit{}

304 maximum := config.Cfg.Git.MaximumCommits

305 for i := 0; maximum == 0 || i < maximum; i++ {

306 c, err := ret.Next()

307 if err != nil {

308 if err.Error() == "EOF" { break }

309 log.Println(err.Error())

310 return "", err

311 }

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

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

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

315 Message: c.Message})

316 }

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

318 }

319

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

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

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

323 return "", nil

324 }

325 if err != nil {

326 log.Println(err.Error())

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

328 }

329 files := []file{}

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

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

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

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

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

335 return nil

336 })

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

338 }

339

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

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

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

343 return "", nil

344 }

345 if err != nil {

346 log.Println(err)

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

348 }

349 branches := []branch{}

350 tags := []branch{}

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

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

353 c.Name().IsRemote() {

354 return nil

355 }

356 var b branch

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

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

359 b.Info = "last commit on "

360

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

362 if err != nil {

363 b.Info = "failed to fetch commit"

364 } else {

365 when := commit.Author.When

366 str := fmt.Sprintf(

367 "%d-%02d-%02d %02d:%02d:%02d",

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

369 when.Day(), when.Hour(),

370 when.Minute(), when.Second())

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

372 }

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

374 branches = append(branches, b)

375 } else {

376 tags = append(tags, b)

377 }

378 return nil

379 })

380 refs.Close()

381 data := struct {

382 Branches []branch

383 Tags []branch

384 }{

385 branches,

386 tags,

387 }

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

389 }

390

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

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

393 if err != nil {

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

395 }

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

397 }

398

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

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

401 if err != nil {

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

403 }

404 if err != nil {

405 content, err = showRepoFile(author, name, "README.md")

406 if err == nil {

407 content = fromMarkdownToGmi(content)

408 }

409 }

410 if err != nil {

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

412 }

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

414 }

415

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

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

418 if err != nil {

419 log.Println(err.Error())

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

421 }

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

423 if err != nil {

424 log.Println(err.Error())

425 return c.NoContent(gig.StatusTemporaryFailure,

426 "Repository not found")

427 }

428 protocol := "http"

429 if config.Cfg.Git.Https {

430 protocol = "https"

431 }

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

433 if err != nil {

434 log.Println(err.Error())

435 return c.NoContent(gig.StatusTemporaryFailure,

436 "Repository not found")

437 }

438

439 content := ""

440 switch page {

441 case pageLog:

442 content, err = showRepoLogs(name, author)

443 case pageFiles:

444 content, err = showRepoFiles(name, author)

445 case pageRefs:

446 content, err = showRepoRefs(name, author)

447 case pageLicense:

448 content, err = showRepoLicense(name, author)

449 case pageReadme:

450 content, err = showRepoReadme(name, author)

451 }

452 if err != nil {

453 return c.NoContent(gig.StatusBadRequest,

454 "Invalid repository")

455 }

456

457 data := struct {

458 Protocol string

459 Domain string

460 User string

461 Description string

462 Repo string

463 Public bool

464 HasReadme bool

465 HasLicense bool

466 Content string

467 }{

468 Protocol: protocol,

469 Domain: config.Cfg.Git.Domain,

470 User: author,

471 Description: desc,

472 Repo: name,

473 Public: public,

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

475 hasFile(name, author, "README.md") ||

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

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

478 Content: content,

479 }

480 if owner {

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

482 }

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

484 }

485

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

487 repos, err := db.GetPublicRepo()

488 if err != nil {

489 log.Println(err.Error())

490 return c.NoContent(gig.StatusTemporaryFailure,

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

492 }

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

494 }

495

496 func PublicAccount(c gig.Context) error {

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

498 if err != nil {

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

500 }

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

502 if err != nil {

503 return c.NoContent(gig.StatusTemporaryFailure,

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

505 }

506 data := struct {

507 Name string

508 Description string

509 Repositories []db.Repo

510 }{

511 user.Name,

512 user.Description,

513 repos,

514 }

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

516 }

517

518 func ShowAccess(c gig.Context) error {

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

520 if !exist {

521 return c.NoContent(gig.StatusBadRequest, "Invalid username")

522 }

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

524 if err != nil {

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

526 }

527 access, err := db.GetRepoUserAccess(repo.ID)

528 if err != nil {

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

530 }

531 groups, err := db.GetRepoGroupAccess(repo.ID)

532 if err != nil {

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

534 }

535 data := struct {

536 Repo string

537 Collaborators []db.Access

538 Groups []db.Access

539 Owner bool

540 }{

541 Repo: repo.Name,

542 Collaborators: access,

543 Groups: groups,

544 Owner: true,

545 }

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

547 }

548