mirror of
https://github.com/aykhans/slash-e.git
synced 2025-04-16 04:13:12 +00:00
fix: shortcut view activity
This commit is contained in:
parent
2ddb47f4df
commit
93bb880e8e
6
server/common/common.go
Normal file
6
server/common/common.go
Normal file
@ -0,0 +1,6 @@
|
||||
package common
|
||||
|
||||
const (
|
||||
// BotID is the id of bot.
|
||||
BotID = 0
|
||||
)
|
@ -10,8 +10,6 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/exp/slices"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/peer"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
@ -110,11 +108,6 @@ func (s *APIV1Service) GetShortcutByName(ctx context.Context, request *v1pb.GetS
|
||||
}
|
||||
}
|
||||
|
||||
// Create shortcut view activity.
|
||||
if err := s.createShortcutViewActivity(ctx, shortcut); err != nil {
|
||||
fmt.Printf("failed to create activity, err: %v", err)
|
||||
}
|
||||
|
||||
composedShortcut, err := s.convertShortcutFromStorepb(ctx, shortcut)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to convert shortcut, err: %v", err)
|
||||
@ -352,35 +345,6 @@ func mapToAnalyticsSlice(m map[string]int32) []*v1pb.GetShortcutAnalyticsRespons
|
||||
return analyticsSlice
|
||||
}
|
||||
|
||||
func (s *APIV1Service) createShortcutViewActivity(ctx context.Context, shortcut *storepb.Shortcut) error {
|
||||
p, _ := peer.FromContext(ctx)
|
||||
headers, ok := metadata.FromIncomingContext(ctx)
|
||||
if !ok {
|
||||
return errors.New("Failed to get metadata from context")
|
||||
}
|
||||
payload := &storepb.ActivityShorcutViewPayload{
|
||||
ShortcutId: shortcut.Id,
|
||||
Ip: p.Addr.String(),
|
||||
Referer: headers.Get("referer")[0],
|
||||
UserAgent: headers.Get("user-agent")[0],
|
||||
}
|
||||
payloadStr, err := protojson.Marshal(payload)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Failed to marshal activity payload")
|
||||
}
|
||||
activity := &store.Activity{
|
||||
CreatorID: BotID,
|
||||
Type: store.ActivityShortcutView,
|
||||
Level: store.ActivityInfo,
|
||||
Payload: string(payloadStr),
|
||||
}
|
||||
_, err = s.Store.CreateActivity(ctx, activity)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Failed to create activity")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *APIV1Service) createShortcutCreateActivity(ctx context.Context, shortcut *storepb.Shortcut) error {
|
||||
payload := &storepb.ActivityShorcutCreatePayload{
|
||||
ShortcutId: shortcut.Id,
|
||||
|
@ -18,11 +18,6 @@ import (
|
||||
"github.com/yourselfhosted/slash/store"
|
||||
)
|
||||
|
||||
const (
|
||||
// BotID is the id of bot.
|
||||
BotID = 0
|
||||
)
|
||||
|
||||
func (s *APIV1Service) ListUsers(ctx context.Context, _ *v1pb.ListUsersRequest) (*v1pb.ListUsersResponse, error) {
|
||||
users, err := s.Store.ListUsers(ctx, &store.FindUser{})
|
||||
if err != nil {
|
||||
|
@ -5,14 +5,18 @@ import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
"github.com/pkg/errors"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
|
||||
"github.com/yourselfhosted/slash/internal/util"
|
||||
storepb "github.com/yourselfhosted/slash/proto/gen/store"
|
||||
"github.com/yourselfhosted/slash/server/common"
|
||||
"github.com/yourselfhosted/slash/server/metric"
|
||||
"github.com/yourselfhosted/slash/server/profile"
|
||||
"github.com/yourselfhosted/slash/store"
|
||||
@ -44,35 +48,34 @@ func (s *FrontendService) Serve(ctx context.Context, e *echo.Echo) {
|
||||
HTML5: true,
|
||||
Filesystem: getFileSystem("dist"),
|
||||
Skipper: func(c echo.Context) bool {
|
||||
return util.HasPrefixes(c.Path(), "/api", "/slash.api.v1", "/robots.txt", "/sitemap.xml", "/s/:shortcutName", "/c/:collectionName")
|
||||
return util.HasPrefixes(c.Path(), "/api", "/slash.api.v1", "/s/:shortcutName", "/c/:collectionName")
|
||||
},
|
||||
}))
|
||||
|
||||
g := e.Group("assets")
|
||||
assetsGroup := e.Group("assets")
|
||||
// Use echo gzip middleware to compress the response.
|
||||
// Reference: https://echo.labstack.com/docs/middleware/gzip
|
||||
g.Use(middleware.GzipWithConfig(middleware.GzipConfig{
|
||||
assetsGroup.Use(middleware.GzipWithConfig(middleware.GzipConfig{
|
||||
Skipper: func(c echo.Context) bool {
|
||||
return util.HasPrefixes(c.Path(), "/api", "/slash.api.v1", "/robots.txt", "/sitemap.xml", "/s/:shortcutName", "/c/:collectionName")
|
||||
return util.HasPrefixes(c.Path(), "/api", "/slash.api.v1", "/s/:shortcutName", "/c/:collectionName")
|
||||
},
|
||||
Level: 5,
|
||||
}))
|
||||
g.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
assetsGroup.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
c.Response().Header().Set(echo.HeaderCacheControl, "max-age=31536000, immutable")
|
||||
return next(c)
|
||||
}
|
||||
})
|
||||
g.Use(middleware.StaticWithConfig(middleware.StaticConfig{
|
||||
assetsGroup.Use(middleware.StaticWithConfig(middleware.StaticConfig{
|
||||
HTML5: true,
|
||||
Filesystem: getFileSystem("dist/assets"),
|
||||
Skipper: func(c echo.Context) bool {
|
||||
return util.HasPrefixes(c.Path(), "/api", "/slash.api.v1", "/robots.txt", "/sitemap.xml", "/s/:shortcutName", "/c/:collectionName")
|
||||
return util.HasPrefixes(c.Path(), "/api", "/slash.api.v1", "/s/:shortcutName", "/c/:collectionName")
|
||||
},
|
||||
}))
|
||||
|
||||
s.registerRoutes(e)
|
||||
s.registerFileRoutes(ctx, e)
|
||||
}
|
||||
|
||||
func (s *FrontendService) registerRoutes(e *echo.Echo) {
|
||||
@ -84,21 +87,20 @@ func (s *FrontendService) registerRoutes(e *echo.Echo) {
|
||||
shortcut, err := s.Store.GetShortcut(ctx, &store.FindShortcut{
|
||||
Name: &shortcutName,
|
||||
})
|
||||
if err != nil {
|
||||
return c.HTML(http.StatusOK, rawIndexHTML)
|
||||
}
|
||||
if shortcut == nil {
|
||||
// If any error occurs or the shortcut is not found, return the raw `index.html`.
|
||||
if err != nil || shortcut == nil {
|
||||
return c.HTML(http.StatusOK, rawIndexHTML)
|
||||
}
|
||||
|
||||
metric.Enqueue("shortcut view")
|
||||
// Only set the `Location` header if the link is a valid URI.
|
||||
if util.ValidateURI(shortcut.Link) {
|
||||
c.Response().Header().Set("Location", shortcut.Link)
|
||||
// Create shortcut view activity.
|
||||
if err := s.createShortcutViewActivity(ctx, c.Request(), shortcut); err != nil {
|
||||
slog.Warn("failed to create shortcut view activity", slog.String("error", err.Error()))
|
||||
}
|
||||
metric.Enqueue("shortcut view")
|
||||
|
||||
// Inject shortcut metadata into `index.html`.
|
||||
indexHTML := strings.ReplaceAll(rawIndexHTML, headerMetadataPlaceholder, generateShortcutMetadata(shortcut).String())
|
||||
return c.HTML(http.StatusPermanentRedirect, indexHTML)
|
||||
return c.HTML(http.StatusOK, indexHTML)
|
||||
})
|
||||
|
||||
e.GET("/c/:collectionName", func(c echo.Context) error {
|
||||
@ -107,10 +109,8 @@ func (s *FrontendService) registerRoutes(e *echo.Echo) {
|
||||
collection, err := s.Store.GetCollection(ctx, &store.FindCollection{
|
||||
Name: &collectionName,
|
||||
})
|
||||
if err != nil {
|
||||
return c.HTML(http.StatusOK, rawIndexHTML)
|
||||
}
|
||||
if collection == nil {
|
||||
// If any error occurs or the collection is not found, return the raw `index.html`.
|
||||
if err != nil || collection == nil {
|
||||
return c.HTML(http.StatusOK, rawIndexHTML)
|
||||
}
|
||||
|
||||
@ -121,50 +121,42 @@ func (s *FrontendService) registerRoutes(e *echo.Echo) {
|
||||
})
|
||||
}
|
||||
|
||||
func (s *FrontendService) registerFileRoutes(ctx context.Context, e *echo.Echo) {
|
||||
workspaceGeneralSetting, err := s.Store.GetWorkspaceGeneralSetting(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
func (s *FrontendService) createShortcutViewActivity(ctx context.Context, request *http.Request, shortcut *storepb.Shortcut) error {
|
||||
ip := getReadUserIP(request)
|
||||
referer := request.Header.Get("Referer")
|
||||
userAgent := request.Header.Get("User-Agent")
|
||||
payload := &storepb.ActivityShorcutViewPayload{
|
||||
ShortcutId: shortcut.Id,
|
||||
Ip: ip,
|
||||
Referer: referer,
|
||||
UserAgent: userAgent,
|
||||
}
|
||||
instanceURL := workspaceGeneralSetting.InstanceUrl
|
||||
if instanceURL == "" {
|
||||
return
|
||||
payloadStr, err := protojson.Marshal(payload)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Failed to marshal activity payload")
|
||||
}
|
||||
activity := &store.Activity{
|
||||
CreatorID: common.BotID,
|
||||
Type: store.ActivityShortcutView,
|
||||
Level: store.ActivityInfo,
|
||||
Payload: string(payloadStr),
|
||||
}
|
||||
_, err = s.Store.CreateActivity(ctx, activity)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Failed to create activity")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
e.GET("/robots.txt", func(c echo.Context) error {
|
||||
robotsTxt := fmt.Sprintf(`User-agent: *
|
||||
Allow: /
|
||||
Host: %s
|
||||
Sitemap: %s/sitemap.xml`, instanceURL, instanceURL)
|
||||
return c.String(http.StatusOK, robotsTxt)
|
||||
})
|
||||
|
||||
e.GET("/sitemap.xml", func(c echo.Context) error {
|
||||
urlsets := []string{}
|
||||
// Append shortcut list.
|
||||
shortcuts, err := s.Store.ListShortcuts(ctx, &store.FindShortcut{
|
||||
VisibilityList: []store.Visibility{store.VisibilityPublic},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
func getReadUserIP(r *http.Request) string {
|
||||
IPAddress := r.Header.Get("X-Real-Ip")
|
||||
if IPAddress == "" {
|
||||
IPAddress = r.Header.Get("X-Forwarded-For")
|
||||
}
|
||||
for _, shortcut := range shortcuts {
|
||||
urlsets = append(urlsets, fmt.Sprintf(`<url><loc>%s/s/%s</loc></url>`, instanceURL, shortcut.Name))
|
||||
if IPAddress == "" {
|
||||
IPAddress = r.RemoteAddr
|
||||
}
|
||||
// Append collection list.
|
||||
collections, err := s.Store.ListCollections(ctx, &store.FindCollection{
|
||||
VisibilityList: []store.Visibility{store.VisibilityPublic},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, collection := range collections {
|
||||
urlsets = append(urlsets, fmt.Sprintf(`<url><loc>%s/c/%s</loc></url>`, instanceURL, collection.Name))
|
||||
}
|
||||
|
||||
sitemap := fmt.Sprintf(`<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1">%s</urlset>`, strings.Join(urlsets, "\n"))
|
||||
return c.XMLBlob(http.StatusOK, []byte(sitemap))
|
||||
})
|
||||
return IPAddress
|
||||
}
|
||||
|
||||
func getFileSystem(path string) http.FileSystem {
|
||||
|
Loading…
x
Reference in New Issue
Block a user