💾 Archived View for gemini.rmf-dev.com › repo › Vaati › Gemigit › files › 8c8a7c29a2e49afb6bdeb504de… captured on 2023-09-08 at 16:30:38. 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 err = ret.ForEach(func(c *object.Commit) error {

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

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

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

308 Message: c.Message})

309 return nil

310 })

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

312 }

313

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

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

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

317 return "", nil

318 }

319 if err != nil {

320 log.Println(err.Error())

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

322 }

323 files := []file{}

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

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

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

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

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

329 return nil

330 })

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

332 }

333

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

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

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

337 return "", nil

338 }

339 if err != nil {

340 log.Println(err)

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

342 }

343 branches := []branch{}

344 tags := []branch{}

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

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

347 c.Name().IsRemote() {

348 return nil

349 }

350 var b branch

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

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

353 b.Info = "last commit on "

354

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

356 if err != nil {

357 b.Info = "failed to fetch commit"

358 } else {

359 when := commit.Author.When

360 str := fmt.Sprintf(

361 "%d-%02d-%02d %02d:%02d:%02d",

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

363 when.Day(), when.Hour(),

364 when.Minute(), when.Second())

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

366 }

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

368 branches = append(branches, b)

369 } else {

370 tags = append(tags, b)

371 }

372 return nil

373 })

374 refs.Close()

375 data := struct {

376 Branches []branch

377 Tags []branch

378 }{

379 branches,

380 tags,

381 }

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

383 }

384

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

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

387 if err != nil {

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

389 }

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

391 }

392

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

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

395 if err != nil {

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

397 }

398 if err != nil {

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

400 if err == nil {

401 content = fromMarkdownToGmi(content)

402 }

403 }

404 if err != nil {

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

406 }

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

408 }

409

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

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

412 if err != nil {

413 log.Println(err.Error())

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

415 }

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

417 if err != nil {

418 log.Println(err.Error())

419 return c.NoContent(gig.StatusTemporaryFailure,

420 "Repository not found")

421 }

422 protocol := "http"

423 if config.Cfg.Git.Https {

424 protocol = "https"

425 }

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

427 if err != nil {

428 log.Println(err.Error())

429 return c.NoContent(gig.StatusTemporaryFailure,

430 "Repository not found")

431 }

432

433 content := ""

434 switch page {

435 case pageLog:

436 content, err = showRepoLogs(name, author)

437 case pageFiles:

438 content, err = showRepoFiles(name, author)

439 case pageRefs:

440 content, err = showRepoRefs(name, author)

441 case pageLicense:

442 content, err = showRepoLicense(name, author)

443 case pageReadme:

444 content, err = showRepoReadme(name, author)

445 }

446 if err != nil {

447 return c.NoContent(gig.StatusBadRequest,

448 "Invalid repository")

449 }

450

451 data := struct {

452 Protocol string

453 Domain string

454 User string

455 Description string

456 Repo string

457 Public bool

458 HasReadme bool

459 HasLicense bool

460 Content string

461 }{

462 Protocol: protocol,

463 Domain: config.Cfg.Git.Domain,

464 User: author,

465 Description: desc,

466 Repo: name,

467 Public: public,

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

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

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

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

472 Content: content,

473 }

474 if owner {

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

476 }

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

478 }

479

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

481 repos, err := db.GetPublicRepo()

482 if err != nil {

483 log.Println(err.Error())

484 return c.NoContent(gig.StatusTemporaryFailure,

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

486 }

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

488 }

489

490 func PublicAccount(c gig.Context) error {

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

492 if err != nil {

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

494 }

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

496 if err != nil {

497 return c.NoContent(gig.StatusTemporaryFailure,

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

499 }

500 data := struct {

501 Name string

502 Description string

503 Repositories []db.Repo

504 }{

505 user.Name,

506 user.Description,

507 repos,

508 }

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

510 }

511

512 func ShowAccess(c gig.Context) error {

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

514 if !exist {

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

516 }

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

518 if err != nil {

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

520 }

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

522 if err != nil {

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

524 }

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

526 if err != nil {

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

528 }

529 data := struct {

530 Repo string

531 Collaborators []db.Access

532 Groups []db.Access

533 Owner bool

534 }{

535 Repo: repo.Name,

536 Collaborators: access,

537 Groups: groups,

538 Owner: true,

539 }

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

541 }

542