💾 Archived View for gemini.rmf-dev.com › repo › Vaati › Gemigit › files › ebc7a25c896f1e460b88029786… captured on 2023-12-28 at 15:34:45. 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 loggedAs := ""

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

419 if exist {

420 loggedAs = user.Name + "@"

421 }

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

423 if err != nil {

424 log.Println(err.Error())

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

426 }

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

428 if err != nil {

429 log.Println(err.Error())

430 return c.NoContent(gig.StatusTemporaryFailure,

431 "Repository not found")

432 }

433 protocol := "http"

434 if config.Cfg.Git.Http.Https {

435 protocol = "https"

436 }

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

438 if err != nil {

439 log.Println(err.Error())

440 return c.NoContent(gig.StatusTemporaryFailure,

441 "Repository not found")

442 }

443 if public && config.Cfg.Git.Public {

444 loggedAs = "anon@"

445 }

446

447 content := ""

448 switch page {

449 case pageLog:

450 content, err = showRepoLogs(name, author)

451 case pageFiles:

452 content, err = showRepoFiles(name, author)

453 case pageRefs:

454 content, err = showRepoRefs(name, author)

455 case pageLicense:

456 content, err = showRepoLicense(name, author)

457 case pageReadme:

458 content, err = showRepoReadme(name, author)

459 }

460 if err != nil {

461 return c.NoContent(gig.StatusBadRequest,

462 "Invalid repository")

463 }

464

465 data := struct {

466 HasHTTP bool

467 HttpProtocol string

468 HttpDomain string

469 HasSSH bool

470 SshDomain string

471 LoggedAs string

472 User string

473 Description string

474 Repo string

475 Public bool

476 HasReadme bool

477 HasLicense bool

478 Content string

479 }{

480 HasHTTP: config.Cfg.Git.Http.Enabled,

481 HttpProtocol: protocol,

482 HttpDomain: config.Cfg.Git.Http.Domain,

483 HasSSH: config.Cfg.Git.SSH.Enabled,

484 SshDomain: config.Cfg.Git.SSH.Domain,

485 LoggedAs: loggedAs,

486 User: author,

487 Description: desc,

488 Repo: name,

489 Public: public,

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

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

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

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

494 Content: content,

495 }

496 if owner {

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

498 }

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

500 }

501

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

503 repos, err := db.GetPublicRepo()

504 if err != nil {

505 log.Println(err.Error())

506 return c.NoContent(gig.StatusTemporaryFailure,

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

508 }

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

510 }

511

512 func PublicAccount(c gig.Context) error {

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

514 if err != nil {

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

516 }

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

518 if err != nil {

519 return c.NoContent(gig.StatusTemporaryFailure,

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

521 }

522 data := struct {

523 Name string

524 Description string

525 Repositories []db.Repo

526 }{

527 user.Name,

528 user.Description,

529 repos,

530 }

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

532 }

533

534 func ShowAccess(c gig.Context) error {

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

536 if !exist {

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

538 }

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

540 if err != nil {

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

542 }

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

544 if err != nil {

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

546 }

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

548 if err != nil {

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

550 }

551 data := struct {

552 Repo string

553 Collaborators []db.Access

554 Groups []db.Access

555 Owner bool

556 }{

557 Repo: repo.Name,

558 Collaborators: access,

559 Groups: groups,

560 Owner: true,

561 }

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

563 }

564