Skip to content
2 changes: 2 additions & 0 deletions modules/indexer/code/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type Result struct {
Color string
LineNumbers []int
FormattedLines template.HTML
ContentLines []string
}

type SearchResultLanguages = internal.SearchResultLanguages
Expand Down Expand Up @@ -108,6 +109,7 @@ func searchResult(result *internal.SearchResult, startIndex, endIndex int) (*Res
Color: result.Color,
LineNumbers: lineNumbers,
FormattedLines: highlighted,
ContentLines: contentLines,
}, nil
}

Expand Down
28 changes: 28 additions & 0 deletions modules/structs/indexer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package structs

import (
"time"
)

// IndexerResult a search result to display
type IndexerResult struct {
RepoID int64 `json:"repo_id"`
Filename string `json:"filename"`
CommitID string `json:"commit_id"`
Updated time.Time `json:"updated"`
Language string `json:"language"`
Color string `json:"color"`
LineNumbers []int `json:"line_numbers"`
FormattedLines string `json:"formated_lines"`
ContentLines []string `json:"content_lines"`
}

// IndexerSearchResultLanguages result of top languages count in search results
type IndexerSearchResultLanguages struct {
Language string `json:"language"`
Color string `json:"color"`
Count int `json:"count"`
}
7 changes: 7 additions & 0 deletions modules/structs/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,13 @@ type RepoTransfer struct {
Teams []*Team `json:"teams"`
}

// RepoCodeSearchAPIResponse represents the resposne of the code_search API endpoint
type RepoCodeSearchAPIResponse struct {
Total int `json:"total"`
SearchResults []*IndexerResult `json:"search_results"`
SearchResultLanguages []*IndexerSearchResultLanguages `json:"search_results_language"`
}

// NewIssuePinsAllowed represents an API response that says if new Issue Pins are allowed
type NewIssuePinsAllowed struct {
Issues bool `json:"issues"`
Expand Down
4 changes: 4 additions & 0 deletions routers/api/v1/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -876,6 +876,7 @@ func Routes() *web.Route {
m.Get("/licenses/{name}", misc.GetLicenseTemplateInfo)
m.Get("/label/templates", misc.ListLabelTemplates)
m.Get("/label/templates/{name}", misc.GetLabelTemplate)
m.Get("/code_search", misc.CodeSearch)

m.Group("/settings", func() {
m.Get("/ui", settings.GetGeneralUISettings)
Expand Down Expand Up @@ -915,6 +916,7 @@ func Routes() *web.Route {
}, reqSelfOrAdmin(), reqBasicOrRevProxyAuth())

m.Get("/activities/feeds", user.ListUserActivityFeeds)
m.Get("/code_search", tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository), user.CodeSearch)
}, context_service.UserAssignmentAPI(), individualPermsChecker)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser))

Expand Down Expand Up @@ -1273,6 +1275,7 @@ func Routes() *web.Route {
m.Get("/issue_config", context.ReferencesGitRepo(), repo.GetIssueConfig)
m.Get("/issue_config/validate", context.ReferencesGitRepo(), repo.ValidateIssueConfig)
m.Get("/languages", reqRepoReader(unit.TypeCode), repo.GetLanguages)
m.Get("/code_search", repo.CodeSearch)
m.Get("/activities/feeds", repo.ListRepoActivityFeeds)
m.Get("/new_pin_allowed", repo.AreNewIssuePinsAllowed)
m.Group("/avatar", func() {
Expand Down Expand Up @@ -1476,6 +1479,7 @@ func Routes() *web.Route {
m.Delete("", org.DeleteAvatar)
}, reqToken(), reqOrgOwnership())
m.Get("/activities/feeds", org.ListOrgActivityFeeds)
m.Get("/code_search", org.CodeSearch)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), orgAssignment(true))
m.Group("/teams/{teamid}", func() {
m.Combo("").Get(reqToken(), org.GetTeam).
Expand Down
57 changes: 57 additions & 0 deletions routers/api/v1/misc/search.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package misc

import (
"net/http"

repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/routers/api/v1/utils"
)

// CodeSearch Performs a code search on all Repos
func CodeSearch(ctx *context.APIContext) {
// swagger:operation GET /code_search miscellaneous globalCodeSearch
// ---
// summary: Performs a code search on all Repos
// produces:
// - application/json
// parameters:
// - name: keyword
// in: query
// description: the keyword the search for
// type: string
// required: true
// - name: language
// in: query
// description: filter results by language
// type: string
// - name: match
// in: query
// description: only exact match (defaults to false)
// type: boolean
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results
// type: string
// responses:
// "200":
// "$ref": "#/responses/UserHeatmapData"
// "422":
// description: "The keyword is empty"
// "501":
// description: "The repo indexer is disabled for this instance"
repos, err := repo_model.FindUserCodeAccessibleRepoIDs(ctx, ctx.Doer)
if err != nil {
ctx.Error(http.StatusInternalServerError, "FindUserCodeAccessibleRepoIDs", err)
return
}

utils.PerformCodeSearch(ctx, repos)
}
53 changes: 53 additions & 0 deletions routers/api/v1/org/org.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/models/perm"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
Expand Down Expand Up @@ -452,3 +453,55 @@ func ListOrgActivityFeeds(ctx *context.APIContext) {

ctx.JSON(http.StatusOK, convert.ToActivities(ctx, feeds, ctx.Doer))
}

// CodeSearch Performs a code search on a Organization
func CodeSearch(ctx *context.APIContext) {
// swagger:operation GET /orgs/{org}/code_search organization orgCodeSearch
// ---
// summary: Performs a code search on a Organization
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the org
// type: string
// required: true
// - name: keyword
// in: query
// description: the keyword the search for
// type: string
// required: true
// - name: language
// in: query
// description: filter results by language
// type: string
// - name: match
// in: query
// description: only exact match (defaults to false)
// type: boolean
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results
// type: string
// responses:
// "200":
// "$ref": "#/responses/UserHeatmapData"
// "404":
// "$ref": "#/responses/notFound"
// "422":
// description: "The keyword is empty"
// "501":
// description: "The repo indexer is disabled for this instance"
repos, err := repo_model.FindUserCodeAccessibleOwnerRepoIDs(ctx, ctx.ContextUser.ID, ctx.Doer)
if err != nil {
ctx.Error(http.StatusInternalServerError, "FindUserCodeAccessibleOwnerRepoIDs", err)
return
}

utils.PerformCodeSearch(ctx, repos)
}
51 changes: 51 additions & 0 deletions routers/api/v1/repo/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -1282,3 +1282,54 @@ func ListRepoActivityFeeds(ctx *context.APIContext) {

ctx.JSON(http.StatusOK, convert.ToActivities(ctx, feeds, ctx.Doer))
}

// CodeSearch Performs a code search on a Repo
func CodeSearch(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/code_search repository repoCodeSearch
// ---
// summary: Performs a code search on a Repo
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// - name: keyword
// in: query
// description: the keyword the search for
// type: string
// required: true
// - name: language
// in: query
// description: filter results by language
// type: string
// - name: match
// in: query
// description: only exact match (defaults to false)
// type: boolean
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results
// type: string
// responses:
// "200":
// "$ref": "#/responses/RepoCodeSearch"
// "404":
// "$ref": "#/responses/notFound"
// "422":
// description: "The keyword is empty"
// "501":
// description: "The repo indexer is disabled for this instance"
utils.PerformCodeSearch(ctx, []int64{ctx.Repo.Repository.ID})
}
7 changes: 7 additions & 0 deletions routers/api/v1/swagger/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,13 @@ type swaggerRepoCollaboratorPermission struct {
Body api.RepoCollaboratorPermission `json:"body"`
}

// RepoCodeSearch
// swagger:response RepoCodeSearch
type swaggerRepoCodeSearch struct {
// in:body
Body api.RepoCodeSearchAPIResponse `json:"body"`
}

// RepoIssueConfig
// swagger:response RepoIssueConfig
type swaggerRepoIssueConfig struct {
Expand Down
53 changes: 53 additions & 0 deletions routers/api/v1/user/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"net/http"

activities_model "code.gitea.io/gitea/models/activities"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/routers/api/v1/utils"
Expand Down Expand Up @@ -216,3 +217,55 @@ func ListUserActivityFeeds(ctx *context.APIContext) {

ctx.JSON(http.StatusOK, convert.ToActivities(ctx, feeds, ctx.Doer))
}

// CodeSearch Performs a code search on a User
func CodeSearch(ctx *context.APIContext) {
// swagger:operation GET /users/{username}/code_search user userCodeSearch
// ---
// summary: Performs a code search on a User
// produces:
// - application/json
// parameters:
// - name: username
// in: path
// description: username of user to get
// type: string
// required: true
// - name: keyword
// in: query
// description: the keyword the search for
// type: string
// required: true
// - name: language
// in: query
// description: filter results by language
// type: string
// - name: match
// in: query
// description: only exact match (defaults to false)
// type: boolean
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results
// type: string
// responses:
// "200":
// "$ref": "#/responses/UserHeatmapData"
// "404":
// "$ref": "#/responses/notFound"
// "422":
// description: "The keyword is empty"
// "501":
// description: "The repo indexer is disabled for this instance"
repos, err := repo_model.FindUserCodeAccessibleOwnerRepoIDs(ctx, ctx.ContextUser.ID, ctx.Doer)
if err != nil {
ctx.Error(http.StatusInternalServerError, "FindUserCodeAccessibleOwnerRepoIDs", err)
return
}

utils.PerformCodeSearch(ctx, repos)
}
Loading