diff --git a/.gitignore b/.gitignore
index 9b988506..4a6159e4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,7 @@ tmp
# Frontend asset
web/dist
+server/frontend/dist
# build folder
build
diff --git a/server/frontend/frontend.go b/server/frontend/frontend.go
index d1e9b943..81eea94c 100644
--- a/server/frontend/frontend.go
+++ b/server/frontend/frontend.go
@@ -2,6 +2,7 @@ package frontend
import (
"embed"
+ "fmt"
"io/fs"
"net/http"
"strings"
@@ -10,6 +11,8 @@ import (
"github.com/labstack/echo/v4/middleware"
"github.com/usememos/memos/internal/util"
+ "github.com/usememos/memos/server/profile"
+ "github.com/usememos/memos/store"
)
//go:embed dist
@@ -18,7 +21,19 @@ var embeddedFiles embed.FS
//go:embed dist/index.html
var rawIndexHTML string
-func Serve(e *echo.Echo) {
+type FrontendService struct {
+ Profile *profile.Profile
+ Store *store.Store
+}
+
+func NewFrontendService(profile *profile.Profile, store *store.Store) *FrontendService {
+ return &FrontendService{
+ Profile: profile,
+ Store: store,
+ }
+}
+
+func (s *FrontendService) Serve(e *echo.Echo) {
// Use echo static middleware to serve the built dist folder
// refer: https://github.com/labstack/echo/blob/master/middleware/static.go
e.Use(middleware.StaticWithConfig(middleware.StaticConfig{
@@ -44,16 +59,50 @@ func Serve(e *echo.Echo) {
Filesystem: getFileSystem("dist/assets"),
}))
- registerRoutes(e)
+ s.registerRoutes(e)
}
-func registerRoutes(e *echo.Echo) {
+func (s *FrontendService) registerRoutes(e *echo.Echo) {
e.GET("/m/:memoID", func(c echo.Context) error {
- indexHTML := strings.ReplaceAll(rawIndexHTML, "", ""+"\n")
+ ctx := c.Request().Context()
+ memoID, err := util.ConvertStringToInt32(c.Param("memoID"))
+ if err != nil {
+ return echo.NewHTTPError(http.StatusBadRequest, "invalid memo id")
+ }
+
+ memo, err := s.Store.GetMemo(ctx, &store.FindMemo{
+ ID: &memoID,
+ })
+ if err != nil {
+ return echo.NewHTTPError(http.StatusInternalServerError, "failed to retrieve memo")
+ }
+ if memo == nil {
+ return echo.NewHTTPError(http.StatusNotFound, "memo not found")
+ }
+ if memo.Visibility != store.Public {
+ return echo.NewHTTPError(http.StatusForbidden, "memo is not public")
+ }
+ indexHTML := strings.ReplaceAll(rawIndexHTML, "", generateMemoMetadata(memo))
return c.HTML(http.StatusOK, indexHTML)
})
}
+func generateMemoMetadata(memo *store.Memo) string {
+ metadataList := []string{
+ fmt.Sprintf(``, memo.Content),
+ fmt.Sprintf(``, fmt.Sprintf("Memos - %d", memo.ID)),
+ fmt.Sprintf(``, memo.Content),
+ fmt.Sprintf(``, "https://www.usememos.com/logo.png"),
+ ``,
+ // Twitter related metadata.
+ fmt.Sprintf(``, fmt.Sprintf("Memos - %d", memo.ID)),
+ fmt.Sprintf(``, memo.Content),
+ fmt.Sprintf(``, "https://www.usememos.com/logo.png"),
+ ``,
+ }
+ return strings.Join(metadataList, "\n")
+}
+
func getFileSystem(path string) http.FileSystem {
fs, err := fs.Sub(embeddedFiles, path)
if err != nil {
diff --git a/server/server.go b/server/server.go
index 93585fae..aed33630 100644
--- a/server/server.go
+++ b/server/server.go
@@ -33,9 +33,6 @@ type Server struct {
Profile *profile.Profile
Store *store.Store
- // API services.
- apiV2Service *apiv2.APIV2Service
-
// Asynchronous runners.
backupRunner *backup.BackupRunner
telegramBot *telegram.Bot
@@ -84,7 +81,8 @@ func NewServer(ctx context.Context, profile *profile.Profile, store *store.Store
s.ID = serverID
// Serve frontend.
- frontend.Serve(e)
+ frontendService := frontend.NewFrontendService(profile, store)
+ frontendService.Serve(e)
// Serve swagger in dev/demo mode.
if profile.Mode == "dev" || profile.Mode == "demo" {
@@ -110,9 +108,9 @@ func NewServer(ctx context.Context, profile *profile.Profile, store *store.Store
apiV1Service := apiv1.NewAPIV1Service(s.Secret, profile, store, s.telegramBot)
apiV1Service.Register(rootGroup)
- s.apiV2Service = apiv2.NewAPIV2Service(s.Secret, profile, store, s.Profile.Port+1)
+ apiV2Service := apiv2.NewAPIV2Service(s.Secret, profile, store, s.Profile.Port+1)
// Register gRPC gateway as api v2.
- if err := s.apiV2Service.RegisterGateway(ctx, e); err != nil {
+ if err := apiV2Service.RegisterGateway(ctx, e); err != nil {
return nil, errors.Wrap(err, "failed to register gRPC gateway")
}