gitea/routers/api/v1/explore/code.go
2024-07-09 23:35:42 -04:00

144 lines
3.3 KiB
Go

// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package explore
import (
"net/http"
"slices"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
code_indexer "code.gitea.io/gitea/modules/indexer/code"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
// Code explore code
func Code(ctx *context.APIContext) {
// swagger:operation GET /explore/code explore codeSearch
// ---
// summary: Search for code
// produces:
// - application/json
// parameters:
// - name: q
// in: query
// description: keyword
// type: string
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: fuzzy
// in: query
// description: whether to search fuzzy or strict (defaults to true)
// type: boolean
// responses:
// "200":
// description: "SearchResults of a successful search"
// schema:
// "$ref": "#/definitions/ExploreCodeResult"
if !setting.Indexer.RepoIndexerEnabled {
ctx.NotFound("Indexer not enabled")
return
}
keyword := ctx.FormTrim("q")
isFuzzy := ctx.FormOptionalBool("fuzzy").ValueOrDefault(true)
if keyword == "" {
ctx.JSON(http.StatusOK, api.ExploreCodeResult{
Total: 0,
Results: make([]api.ExploreCodeSearchItem, 0),
})
return
}
page := ctx.FormInt("page")
if page <= 0 {
page = 1
}
var (
repoIDs []int64
err error
isAdmin bool
)
if ctx.Doer != nil {
isAdmin = ctx.Doer.IsAdmin
}
if ctx.Doer == nil || !isAdmin {
repoIDs, err = repo_model.FindUserCodeAccessibleRepoIDs(ctx, ctx.Doer)
if err != nil {
ctx.JSON(http.StatusInternalServerError, api.SearchError{
OK: false,
Error: err.Error(),
})
return
}
}
var (
total int
searchResults []*code_indexer.Result
repoMaps map[int64]*repo_model.Repository
)
if (len(repoIDs) > 0) || isAdmin {
total, searchResults, _, err = code_indexer.PerformSearch(ctx, &code_indexer.SearchOptions{
RepoIDs: repoIDs,
Keyword: keyword,
IsKeywordFuzzy: isFuzzy,
IsHTMLSafe: false,
Paginator: &db.ListOptions{
Page: page,
PageSize: setting.API.DefaultPagingNum,
},
})
if err != nil {
if code_indexer.IsAvailable(ctx) {
ctx.JSON(http.StatusInternalServerError, api.SearchError{
OK: false,
Error: err.Error(),
})
return
}
}
loadRepoIDs := make([]int64, 0, len(searchResults))
for _, result := range searchResults {
if !slices.Contains(loadRepoIDs, result.RepoID) {
loadRepoIDs = append(loadRepoIDs, result.RepoID)
}
}
repoMaps, err = repo_model.GetRepositoriesMapByIDs(ctx, loadRepoIDs)
if err != nil {
ctx.JSON(http.StatusInternalServerError, api.SearchError{
OK: false,
Error: err.Error(),
})
return
}
if len(loadRepoIDs) != len(repoMaps) {
// Remove deleted repos from search results
cleanedSearchResults := make([]*code_indexer.Result, 0, len(repoMaps))
for _, sr := range searchResults {
if _, found := repoMaps[sr.RepoID]; found {
cleanedSearchResults = append(cleanedSearchResults, sr)
}
}
searchResults = cleanedSearchResults
}
}
ctx.JSON(http.StatusOK, convert.ToExploreCodeSearchResults(total, searchResults, repoMaps))
}