repo.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package repo
  5. import (
  6. "net/http"
  7. "path"
  8. api "github.com/gogs/go-gogs-client"
  9. "github.com/pkg/errors"
  10. log "unknwon.dev/clog/v2"
  11. "github.com/G-Node/gogs/internal/conf"
  12. "github.com/G-Node/gogs/internal/context"
  13. "github.com/G-Node/gogs/internal/db"
  14. "github.com/G-Node/gogs/internal/form"
  15. "github.com/G-Node/gogs/internal/route/api/v1/convert"
  16. )
  17. func Search(c *context.APIContext) {
  18. opts := &db.SearchRepoOptions{
  19. Keyword: path.Base(c.Query("q")),
  20. OwnerID: c.QueryInt64("uid"),
  21. PageSize: convert.ToCorrectPageSize(c.QueryInt("limit")),
  22. Page: c.QueryInt("page"),
  23. }
  24. // GIN specific code
  25. // Workaround for the all query with logged users (?)
  26. if opts.Keyword == "." {
  27. opts.Keyword = ""
  28. }
  29. // Check visibility.
  30. if c.IsLogged && opts.OwnerID > 0 {
  31. if c.User.ID == opts.OwnerID {
  32. opts.Private = true
  33. } else {
  34. u, err := db.GetUserByID(opts.OwnerID)
  35. if err != nil {
  36. c.JSON(http.StatusInternalServerError, map[string]interface{}{
  37. "ok": false,
  38. "error": err.Error(),
  39. })
  40. return
  41. }
  42. if u.IsOrganization() && u.IsOwnedBy(c.User.ID) {
  43. opts.Private = true
  44. }
  45. // FIXME: how about collaborators?
  46. }
  47. }
  48. repos, count, err := db.SearchRepositoryByName(opts)
  49. if err != nil {
  50. c.JSON(http.StatusInternalServerError, map[string]interface{}{
  51. "ok": false,
  52. "error": err.Error(),
  53. })
  54. return
  55. }
  56. if err = db.RepositoryList(repos).LoadAttributes(); err != nil {
  57. c.JSON(http.StatusInternalServerError, map[string]interface{}{
  58. "ok": false,
  59. "error": err.Error(),
  60. })
  61. return
  62. }
  63. // GIN specific code
  64. // 'for' has been modfied to accomodate search in commits as well (?)
  65. results := make([]*api.Repository, 0, len(repos))
  66. for i := range repos {
  67. if !repos[i].IsUnlisted {
  68. rep := repos[i].APIFormat(nil)
  69. rep.Owner.Email = ""
  70. results = append(results, rep)
  71. }
  72. }
  73. c.SetLinkHeader(int(count), opts.PageSize)
  74. c.JSONSuccess(map[string]interface{}{
  75. "ok": true,
  76. "data": results,
  77. })
  78. }
  79. func listUserRepositories(c *context.APIContext, username string) {
  80. user, err := db.GetUserByName(username)
  81. if err != nil {
  82. c.NotFoundOrError(err, "get user by name")
  83. return
  84. }
  85. // Only list public repositories if user requests someone else's repository list,
  86. // or an organization isn't a member of.
  87. var ownRepos []*db.Repository
  88. if user.IsOrganization() {
  89. ownRepos, _, err = user.GetUserRepositories(c.User.ID, 1, user.NumRepos)
  90. } else {
  91. ownRepos, err = db.GetUserRepositories(&db.UserRepoOptions{
  92. UserID: user.ID,
  93. Private: c.User.ID == user.ID,
  94. Page: 1,
  95. PageSize: user.NumRepos,
  96. })
  97. }
  98. if err != nil {
  99. c.Error(err, "get user repositories")
  100. return
  101. }
  102. if err = db.RepositoryList(ownRepos).LoadAttributes(); err != nil {
  103. c.Error(err, "load attributes")
  104. return
  105. }
  106. // Early return for querying other user's repositories
  107. if c.User.ID != user.ID {
  108. repos := make([]*api.Repository, len(ownRepos))
  109. for i := range ownRepos {
  110. repos[i] = ownRepos[i].APIFormat(&api.Permission{Admin: true, Push: true, Pull: true})
  111. }
  112. c.JSONSuccess(&repos)
  113. return
  114. }
  115. accessibleRepos, err := user.GetRepositoryAccesses()
  116. if err != nil {
  117. c.Error(err, "get repositories accesses")
  118. return
  119. }
  120. numOwnRepos := len(ownRepos)
  121. repos := make([]*api.Repository, numOwnRepos+len(accessibleRepos))
  122. for i := range ownRepos {
  123. repos[i] = ownRepos[i].APIFormat(&api.Permission{Admin: true, Push: true, Pull: true})
  124. }
  125. i := numOwnRepos
  126. for repo, access := range accessibleRepos {
  127. repos[i] = repo.APIFormat(&api.Permission{
  128. Admin: access >= db.AccessModeAdmin,
  129. Push: access >= db.AccessModeWrite,
  130. Pull: true,
  131. })
  132. i++
  133. }
  134. c.JSONSuccess(&repos)
  135. }
  136. func ListMyRepos(c *context.APIContext) {
  137. listUserRepositories(c, c.User.Name)
  138. }
  139. func ListUserRepositories(c *context.APIContext) {
  140. listUserRepositories(c, c.Params(":username"))
  141. }
  142. func ListOrgRepositories(c *context.APIContext) {
  143. listUserRepositories(c, c.Params(":org"))
  144. }
  145. func CreateUserRepo(c *context.APIContext, owner *db.User, opt api.CreateRepoOption) {
  146. repo, err := db.CreateRepository(c.User, owner, db.CreateRepoOptions{
  147. Name: opt.Name,
  148. Description: opt.Description,
  149. Gitignores: opt.Gitignores,
  150. License: opt.License,
  151. Readme: opt.Readme,
  152. IsPrivate: opt.Private,
  153. AutoInit: opt.AutoInit,
  154. })
  155. if err != nil {
  156. if db.IsErrRepoAlreadyExist(err) ||
  157. db.IsErrNameNotAllowed(err) {
  158. c.ErrorStatus(http.StatusUnprocessableEntity, err)
  159. } else {
  160. if repo != nil {
  161. if err = db.DeleteRepository(c.User.ID, repo.ID); err != nil {
  162. log.Error("Failed to delete repository: %v", err)
  163. }
  164. }
  165. c.Error(err, "create repository")
  166. }
  167. return
  168. }
  169. c.JSON(201, repo.APIFormat(&api.Permission{Admin: true, Push: true, Pull: true}))
  170. }
  171. func Create(c *context.APIContext, opt api.CreateRepoOption) {
  172. // Shouldn't reach this condition, but just in case.
  173. if c.User.IsOrganization() {
  174. c.ErrorStatus(http.StatusUnprocessableEntity, errors.New("Not allowed to create repository for organization."))
  175. return
  176. }
  177. CreateUserRepo(c, c.User, opt)
  178. }
  179. func CreateOrgRepo(c *context.APIContext, opt api.CreateRepoOption) {
  180. org, err := db.GetOrgByName(c.Params(":org"))
  181. if err != nil {
  182. c.NotFoundOrError(err, "get organization by name")
  183. return
  184. }
  185. if !org.IsOwnedBy(c.User.ID) {
  186. c.ErrorStatus(http.StatusForbidden, errors.New("Given user is not owner of organization."))
  187. return
  188. }
  189. CreateUserRepo(c, org, opt)
  190. }
  191. func Migrate(c *context.APIContext, f form.MigrateRepo) {
  192. ctxUser := c.User
  193. // Not equal means context user is an organization,
  194. // or is another user/organization if current user is admin.
  195. if f.Uid != ctxUser.ID {
  196. org, err := db.GetUserByID(f.Uid)
  197. if err != nil {
  198. if db.IsErrUserNotExist(err) {
  199. c.ErrorStatus(http.StatusUnprocessableEntity, err)
  200. } else {
  201. c.Error(err, "get user by ID")
  202. }
  203. return
  204. } else if !org.IsOrganization() && !c.User.IsAdmin {
  205. c.ErrorStatus(http.StatusForbidden, errors.New("Given user is not an organization."))
  206. return
  207. }
  208. ctxUser = org
  209. }
  210. if c.HasError() {
  211. c.ErrorStatus(http.StatusUnprocessableEntity, errors.New(c.GetErrMsg()))
  212. return
  213. }
  214. if ctxUser.IsOrganization() && !c.User.IsAdmin {
  215. // Check ownership of organization.
  216. if !ctxUser.IsOwnedBy(c.User.ID) {
  217. c.ErrorStatus(http.StatusForbidden, errors.New("Given user is not owner of organization."))
  218. return
  219. }
  220. }
  221. remoteAddr, err := f.ParseRemoteAddr(c.User)
  222. if err != nil {
  223. if db.IsErrInvalidCloneAddr(err) {
  224. addrErr := err.(db.ErrInvalidCloneAddr)
  225. switch {
  226. case addrErr.IsURLError:
  227. c.ErrorStatus(http.StatusUnprocessableEntity, err)
  228. case addrErr.IsPermissionDenied:
  229. c.ErrorStatus(http.StatusUnprocessableEntity, errors.New("You are not allowed to import local repositories."))
  230. case addrErr.IsInvalidPath:
  231. c.ErrorStatus(http.StatusUnprocessableEntity, errors.New("Invalid local path, it does not exist or not a directory."))
  232. default:
  233. c.Error(err, "unexpected error")
  234. }
  235. } else {
  236. c.Error(err, "parse remote address")
  237. }
  238. return
  239. }
  240. repo, err := db.MigrateRepository(c.User, ctxUser, db.MigrateRepoOptions{
  241. Name: f.RepoName,
  242. Description: f.Description,
  243. IsPrivate: f.Private || conf.Repository.ForcePrivate,
  244. IsMirror: f.Mirror,
  245. RemoteAddr: remoteAddr,
  246. })
  247. if err != nil {
  248. if repo != nil {
  249. if errDelete := db.DeleteRepository(ctxUser.ID, repo.ID); errDelete != nil {
  250. log.Error("DeleteRepository: %v", errDelete)
  251. }
  252. }
  253. if db.IsErrReachLimitOfRepo(err) {
  254. c.ErrorStatus(http.StatusUnprocessableEntity, err)
  255. } else {
  256. c.Error(errors.New(db.HandleMirrorCredentials(err.Error(), true)), "migrate repository")
  257. }
  258. return
  259. }
  260. log.Trace("Repository migrated: %s/%s", ctxUser.Name, f.RepoName)
  261. c.JSON(201, repo.APIFormat(&api.Permission{Admin: true, Push: true, Pull: true}))
  262. }
  263. // FIXME: inject in the handler chain
  264. func parseOwnerAndRepo(c *context.APIContext) (*db.User, *db.Repository) {
  265. owner, err := db.GetUserByName(c.Params(":username"))
  266. if err != nil {
  267. if db.IsErrUserNotExist(err) {
  268. c.ErrorStatus(http.StatusUnprocessableEntity, err)
  269. } else {
  270. c.Error(err, "get user by name")
  271. }
  272. return nil, nil
  273. }
  274. repo, err := db.GetRepositoryByName(owner.ID, c.Params(":reponame"))
  275. if err != nil {
  276. c.NotFoundOrError(err, "get repository by name")
  277. return nil, nil
  278. }
  279. return owner, repo
  280. }
  281. func Get(c *context.APIContext) {
  282. _, repo := parseOwnerAndRepo(c)
  283. if c.Written() {
  284. return
  285. }
  286. c.JSONSuccess(repo.APIFormat(&api.Permission{
  287. Admin: c.Repo.IsAdmin(),
  288. Push: c.Repo.IsWriter(),
  289. Pull: true,
  290. }))
  291. }
  292. func Delete(c *context.APIContext) {
  293. owner, repo := parseOwnerAndRepo(c)
  294. if c.Written() {
  295. return
  296. }
  297. if owner.IsOrganization() && !owner.IsOwnedBy(c.User.ID) {
  298. c.ErrorStatus(http.StatusForbidden, errors.New("Given user is not owner of organization."))
  299. return
  300. }
  301. if err := db.DeleteRepository(owner.ID, repo.ID); err != nil {
  302. c.Error(err, "delete repository")
  303. return
  304. }
  305. log.Trace("Repository deleted: %s/%s", owner.Name, repo.Name)
  306. c.NoContent()
  307. }
  308. func ListForks(c *context.APIContext) {
  309. forks, err := c.Repo.Repository.GetForks()
  310. if err != nil {
  311. c.Error(err, "get forks")
  312. return
  313. }
  314. apiForks := make([]*api.Repository, len(forks))
  315. for i := range forks {
  316. if err := forks[i].GetOwner(); err != nil {
  317. c.Error(err, "get owner")
  318. return
  319. }
  320. apiForks[i] = forks[i].APIFormat(&api.Permission{
  321. Admin: c.User.IsAdminOfRepo(forks[i]),
  322. Push: c.User.IsWriterOfRepo(forks[i]),
  323. Pull: true,
  324. })
  325. }
  326. c.JSONSuccess(&apiForks)
  327. }
  328. func IssueTracker(c *context.APIContext, form api.EditIssueTrackerOption) {
  329. _, repo := parseOwnerAndRepo(c)
  330. if c.Written() {
  331. return
  332. }
  333. if form.EnableIssues != nil {
  334. repo.EnableIssues = *form.EnableIssues
  335. }
  336. if form.EnableExternalTracker != nil {
  337. repo.EnableExternalTracker = *form.EnableExternalTracker
  338. }
  339. if form.ExternalTrackerURL != nil {
  340. repo.ExternalTrackerURL = *form.ExternalTrackerURL
  341. }
  342. if form.TrackerURLFormat != nil {
  343. repo.ExternalTrackerFormat = *form.TrackerURLFormat
  344. }
  345. if form.TrackerIssueStyle != nil {
  346. repo.ExternalTrackerStyle = *form.TrackerIssueStyle
  347. }
  348. if err := db.UpdateRepository(repo, false); err != nil {
  349. c.Error(err, "update repository")
  350. return
  351. }
  352. c.NoContent()
  353. }
  354. func MirrorSync(c *context.APIContext) {
  355. _, repo := parseOwnerAndRepo(c)
  356. if c.Written() {
  357. return
  358. } else if !repo.IsMirror {
  359. c.NotFound()
  360. return
  361. }
  362. go db.MirrorQueue.Add(repo.ID)
  363. c.Status(http.StatusAccepted)
  364. }
  365. func Releases(c *context.APIContext) {
  366. _, repo := parseOwnerAndRepo(c)
  367. releases, err := db.GetReleasesByRepoID(repo.ID)
  368. if err != nil {
  369. c.Error(err, "get releases by repository ID")
  370. return
  371. }
  372. apiReleases := make([]*api.Release, 0, len(releases))
  373. for _, r := range releases {
  374. publisher, err := db.GetUserByID(r.PublisherID)
  375. if err != nil {
  376. c.Error(err, "get release publisher")
  377. return
  378. }
  379. r.Publisher = publisher
  380. }
  381. for _, r := range releases {
  382. apiReleases = append(apiReleases, r.APIFormat())
  383. }
  384. c.JSONSuccess(&apiReleases)
  385. }