mirror of
https://github.com/thomiceli/opengist.git
synced 2025-06-13 05:47:12 +02:00
Add Gist code search (#194)
This commit is contained in:
@ -46,6 +46,7 @@ func adminIndex(ctx echo.Context) error {
|
||||
setData(ctx, "gitGcRepos", actions.IsRunning(actions.GitGcRepos))
|
||||
setData(ctx, "syncGistPreviews", actions.IsRunning(actions.SyncGistPreviews))
|
||||
setData(ctx, "resetHooks", actions.IsRunning(actions.ResetHooks))
|
||||
setData(ctx, "indexGists", actions.IsRunning(actions.IndexGists))
|
||||
return html(ctx, "admin_index.html")
|
||||
}
|
||||
|
||||
@ -116,6 +117,8 @@ func adminGistDelete(ctx echo.Context) error {
|
||||
return errorRes(500, "Cannot delete this gist", err)
|
||||
}
|
||||
|
||||
gist.RemoveFromIndex()
|
||||
|
||||
addFlash(ctx, "Gist has been deleted", "success")
|
||||
return redirect(ctx, "/admin-panel/gists")
|
||||
}
|
||||
@ -150,6 +153,12 @@ func adminResetHooks(ctx echo.Context) error {
|
||||
return redirect(ctx, "/admin-panel")
|
||||
}
|
||||
|
||||
func adminIndexGists(ctx echo.Context) error {
|
||||
addFlash(ctx, "Indexing all gists...", "success")
|
||||
go actions.Run(actions.IndexGists)
|
||||
return redirect(ctx, "/admin-panel")
|
||||
}
|
||||
|
||||
func adminConfig(ctx echo.Context) error {
|
||||
setData(ctx, "title", "Configuration")
|
||||
setData(ctx, "htmlTitle", "Configuration - Admin panel")
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/thomiceli/opengist/internal/git"
|
||||
"github.com/thomiceli/opengist/internal/index"
|
||||
"github.com/thomiceli/opengist/internal/render"
|
||||
"html/template"
|
||||
"net/url"
|
||||
@ -252,20 +253,20 @@ func allGists(ctx echo.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
renderedFiles := make([]*render.RenderedGist, 0, len(gists))
|
||||
renderedGists := make([]*render.RenderedGist, 0, len(gists))
|
||||
for _, gist := range gists {
|
||||
rendered, err := render.HighlightGistPreview(gist)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("Error rendering gist preview for " + gist.Identifier() + " - " + gist.PreviewFilename)
|
||||
log.Error().Err(err).Msg("Error rendering gist preview for " + gist.Identifier() + " - " + gist.PreviewFilename)
|
||||
}
|
||||
renderedFiles = append(renderedFiles, &rendered)
|
||||
renderedGists = append(renderedGists, &rendered)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return errorRes(500, "Error fetching gists", err)
|
||||
}
|
||||
|
||||
if err = paginate(ctx, renderedFiles, pageInt, 10, "gists", fromUserStr, 2, "&sort="+sort+"&order="+order); err != nil {
|
||||
if err = paginate(ctx, renderedGists, pageInt, 10, "gists", fromUserStr, 2, "&sort="+sort+"&order="+order); err != nil {
|
||||
return errorRes(404, "Page not found", nil)
|
||||
}
|
||||
|
||||
@ -273,6 +274,69 @@ func allGists(ctx echo.Context) error {
|
||||
return html(ctx, "all.html")
|
||||
}
|
||||
|
||||
func search(ctx echo.Context) error {
|
||||
var err error
|
||||
|
||||
content, meta := parseSearchQueryStr(ctx.QueryParam("q"))
|
||||
pageInt := getPage(ctx)
|
||||
|
||||
var currentUserId uint
|
||||
userLogged := getUserLogged(ctx)
|
||||
if userLogged != nil {
|
||||
currentUserId = userLogged.ID
|
||||
} else {
|
||||
currentUserId = 0
|
||||
}
|
||||
|
||||
var visibleGistsIds []uint
|
||||
visibleGistsIds, err = db.GetAllGistsVisibleByUser(currentUserId)
|
||||
if err != nil {
|
||||
return errorRes(500, "Error fetching gists", err)
|
||||
}
|
||||
|
||||
gistsIds, nbHits, langs, err := index.SearchGists(content, index.SearchGistMetadata{
|
||||
Username: meta["user"],
|
||||
Title: meta["title"],
|
||||
Filename: meta["filename"],
|
||||
Extension: meta["extension"],
|
||||
Language: meta["language"],
|
||||
}, visibleGistsIds, pageInt)
|
||||
if err != nil {
|
||||
return errorRes(500, "Error searching gists", err)
|
||||
}
|
||||
|
||||
gists, err := db.GetAllGistsByIds(gistsIds)
|
||||
if err != nil {
|
||||
return errorRes(500, "Error fetching gists", err)
|
||||
}
|
||||
|
||||
renderedGists := make([]*render.RenderedGist, 0, len(gists))
|
||||
for _, gist := range gists {
|
||||
rendered, err := render.HighlightGistPreview(gist)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Error rendering gist preview for " + gist.Identifier() + " - " + gist.PreviewFilename)
|
||||
}
|
||||
renderedGists = append(renderedGists, &rendered)
|
||||
}
|
||||
|
||||
if pageInt > 1 && len(renderedGists) != 0 {
|
||||
setData(ctx, "prevPage", pageInt-1)
|
||||
}
|
||||
if 10*pageInt < int(nbHits) {
|
||||
setData(ctx, "nextPage", pageInt+1)
|
||||
}
|
||||
setData(ctx, "prevLabel", tr(ctx, "pagination.previous"))
|
||||
setData(ctx, "nextLabel", tr(ctx, "pagination.next"))
|
||||
setData(ctx, "urlPage", "search")
|
||||
setData(ctx, "urlParams", template.URL("&q="+ctx.QueryParam("q")))
|
||||
setData(ctx, "htmlTitle", "Search results")
|
||||
setData(ctx, "nbHits", nbHits)
|
||||
setData(ctx, "gists", renderedGists)
|
||||
setData(ctx, "langs", langs)
|
||||
setData(ctx, "searchQuery", ctx.QueryParam("q"))
|
||||
return html(ctx, "search.html")
|
||||
}
|
||||
|
||||
func gistIndex(ctx echo.Context) error {
|
||||
if getData(ctx, "gistpage") == "js" {
|
||||
return gistJs(ctx)
|
||||
@ -545,6 +609,8 @@ func processCreate(ctx echo.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
gist.AddInIndex()
|
||||
|
||||
return redirect(ctx, "/"+user.Username+"/"+gist.Identifier())
|
||||
}
|
||||
|
||||
@ -566,6 +632,7 @@ func deleteGist(ctx echo.Context) error {
|
||||
if err := gist.Delete(); err != nil {
|
||||
return errorRes(500, "Error deleting this gist", err)
|
||||
}
|
||||
gist.RemoveFromIndex()
|
||||
|
||||
addFlash(ctx, "Gist has been deleted", "success")
|
||||
return redirect(ctx, "/")
|
||||
|
@ -218,6 +218,7 @@ func pack(ctx echo.Context, serviceType string) error {
|
||||
|
||||
_ = gist.SetLastActiveNow()
|
||||
_ = gist.UpdatePreviewAndCount(false)
|
||||
gist.AddInIndex()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -3,7 +3,9 @@ package web
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/thomiceli/opengist/internal/index"
|
||||
htmlpkg "html"
|
||||
"html/template"
|
||||
"io"
|
||||
@ -115,6 +117,22 @@ var (
|
||||
"safe": func(s string) template.HTML {
|
||||
return template.HTML(s)
|
||||
},
|
||||
"dict": func(values ...interface{}) (map[string]interface{}, error) {
|
||||
if len(values)%2 != 0 {
|
||||
return nil, errors.New("invalid dict call")
|
||||
}
|
||||
dict := make(map[string]interface{})
|
||||
for i := 0; i < len(values); i += 2 {
|
||||
key, ok := values[i].(string)
|
||||
if !ok {
|
||||
return nil, errors.New("dict keys must be strings")
|
||||
}
|
||||
dict[key] = values[i+1]
|
||||
}
|
||||
return dict, nil
|
||||
},
|
||||
"addMetadataToSearchQuery": addMetadataToSearchQuery,
|
||||
"indexEnabled": index.Enabled,
|
||||
}
|
||||
)
|
||||
|
||||
@ -223,7 +241,6 @@ func NewServer(isDev bool) *Server {
|
||||
g1.DELETE("/settings/ssh-keys/:id", sshKeysDelete, logged)
|
||||
g1.PUT("/settings/password", passwordProcess, logged)
|
||||
g1.PUT("/settings/username", usernameProcess, logged)
|
||||
|
||||
g2 := g1.Group("/admin-panel")
|
||||
{
|
||||
g2.Use(adminPermission)
|
||||
@ -237,6 +254,7 @@ func NewServer(isDev bool) *Server {
|
||||
g2.POST("/gc-repos", adminGcRepos)
|
||||
g2.POST("/sync-previews", adminSyncGistPreviews)
|
||||
g2.POST("/reset-hooks", adminResetHooks)
|
||||
g2.POST("/index-gists", adminIndexGists)
|
||||
g2.GET("/configuration", adminConfig)
|
||||
g2.PUT("/set-config", adminSetConfig)
|
||||
}
|
||||
@ -246,7 +264,13 @@ func NewServer(isDev bool) *Server {
|
||||
}
|
||||
|
||||
g1.GET("/all", allGists, checkRequireLogin)
|
||||
g1.GET("/search", allGists, checkRequireLogin)
|
||||
|
||||
if index.Enabled() {
|
||||
g1.GET("/search", search, checkRequireLogin)
|
||||
} else {
|
||||
g1.GET("/search", allGists, checkRequireLogin)
|
||||
}
|
||||
|
||||
g1.GET("/:user", allGists, checkRequireLogin)
|
||||
g1.GET("/:user/liked", allGists, checkRequireLogin)
|
||||
g1.GET("/:user/forked", allGists, checkRequireLogin)
|
||||
|
@ -1,11 +1,10 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/thomiceli/opengist/internal/db"
|
||||
"github.com/thomiceli/opengist/internal/git"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGists(t *testing.T) {
|
||||
|
@ -133,14 +133,13 @@ func setup(t *testing.T) {
|
||||
|
||||
git.ReposDirectory = path.Join("tests")
|
||||
|
||||
config.C.IndexEnabled = false
|
||||
config.C.LogLevel = "debug"
|
||||
config.InitLog()
|
||||
|
||||
homePath := config.GetHomeDir()
|
||||
log.Info().Msg("Data directory: " + homePath)
|
||||
|
||||
err = os.MkdirAll(filepath.Join(homePath, "repos"), 0755)
|
||||
require.NoError(t, err, "Could not create repos directory")
|
||||
|
||||
err = os.MkdirAll(filepath.Join(homePath, "tmp", "repos"), 0755)
|
||||
require.NoError(t, err, "Could not create tmp repos directory")
|
||||
|
||||
@ -149,6 +148,9 @@ func setup(t *testing.T) {
|
||||
|
||||
err = memdb.Setup()
|
||||
require.NoError(t, err, "Could not initialize in memory database")
|
||||
|
||||
// err = index.Open(filepath.Join(homePath, "testsindex", "opengist.index"))
|
||||
// require.NoError(t, err, "Could not open index")
|
||||
}
|
||||
|
||||
func teardown(t *testing.T, s *testServer) {
|
||||
@ -159,4 +161,10 @@ func teardown(t *testing.T, s *testServer) {
|
||||
|
||||
err = os.RemoveAll(path.Join(config.C.OpengistHome, "tests"))
|
||||
require.NoError(t, err, "Could not remove repos directory")
|
||||
|
||||
// err = os.RemoveAll(path.Join(config.C.OpengistHome, "testsindex"))
|
||||
// require.NoError(t, err, "Could not remove repos directory")
|
||||
|
||||
// err = index.Close()
|
||||
// require.NoError(t, err, "Could not close index")
|
||||
}
|
||||
|
@ -248,6 +248,46 @@ func tr(ctx echo.Context, key string) template.HTML {
|
||||
return l.Tr(key)
|
||||
}
|
||||
|
||||
func parseSearchQueryStr(query string) (string, map[string]string) {
|
||||
words := strings.Fields(query)
|
||||
metadata := make(map[string]string)
|
||||
var contentBuilder strings.Builder
|
||||
|
||||
for _, word := range words {
|
||||
if strings.Contains(word, ":") {
|
||||
keyValue := strings.SplitN(word, ":", 2)
|
||||
if len(keyValue) == 2 {
|
||||
key := keyValue[0]
|
||||
value := keyValue[1]
|
||||
metadata[key] = value
|
||||
}
|
||||
} else {
|
||||
contentBuilder.WriteString(word + " ")
|
||||
}
|
||||
}
|
||||
|
||||
content := strings.TrimSpace(contentBuilder.String())
|
||||
return content, metadata
|
||||
}
|
||||
|
||||
func addMetadataToSearchQuery(input, key, value string) string {
|
||||
content, metadata := parseSearchQueryStr(input)
|
||||
|
||||
metadata[key] = value
|
||||
|
||||
var resultBuilder strings.Builder
|
||||
resultBuilder.WriteString(content)
|
||||
|
||||
for k, v := range metadata {
|
||||
resultBuilder.WriteString(" ")
|
||||
resultBuilder.WriteString(k)
|
||||
resultBuilder.WriteString(":")
|
||||
resultBuilder.WriteString(v)
|
||||
}
|
||||
|
||||
return strings.TrimSpace(resultBuilder.String())
|
||||
}
|
||||
|
||||
type Argon2ID struct {
|
||||
format string
|
||||
version int
|
||||
|
Reference in New Issue
Block a user