mirror of
https://github.com/usememos/memos.git
synced 2025-02-25 13:54:32 +08:00
chore: implement memo route
This commit is contained in:
parent
6cf7192d6a
commit
52743017a3
3 changed files with 58 additions and 10 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -6,6 +6,7 @@ tmp
|
||||||
|
|
||||||
# Frontend asset
|
# Frontend asset
|
||||||
web/dist
|
web/dist
|
||||||
|
server/frontend/dist
|
||||||
|
|
||||||
# build folder
|
# build folder
|
||||||
build
|
build
|
||||||
|
|
|
@ -2,6 +2,7 @@ package frontend
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -10,6 +11,8 @@ import (
|
||||||
"github.com/labstack/echo/v4/middleware"
|
"github.com/labstack/echo/v4/middleware"
|
||||||
|
|
||||||
"github.com/usememos/memos/internal/util"
|
"github.com/usememos/memos/internal/util"
|
||||||
|
"github.com/usememos/memos/server/profile"
|
||||||
|
"github.com/usememos/memos/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed dist
|
//go:embed dist
|
||||||
|
@ -18,7 +21,19 @@ var embeddedFiles embed.FS
|
||||||
//go:embed dist/index.html
|
//go:embed dist/index.html
|
||||||
var rawIndexHTML string
|
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
|
// Use echo static middleware to serve the built dist folder
|
||||||
// refer: https://github.com/labstack/echo/blob/master/middleware/static.go
|
// refer: https://github.com/labstack/echo/blob/master/middleware/static.go
|
||||||
e.Use(middleware.StaticWithConfig(middleware.StaticConfig{
|
e.Use(middleware.StaticWithConfig(middleware.StaticConfig{
|
||||||
|
@ -44,16 +59,50 @@ func Serve(e *echo.Echo) {
|
||||||
Filesystem: getFileSystem("dist/assets"),
|
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 {
|
e.GET("/m/:memoID", func(c echo.Context) error {
|
||||||
indexHTML := strings.ReplaceAll(rawIndexHTML, "<!-- memos.metadata -->", "<meta name=\"memos-memo-id\" content=\""+c.Param("memoID")+"\">"+"\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, "<!-- memos.metadata -->", generateMemoMetadata(memo))
|
||||||
return c.HTML(http.StatusOK, indexHTML)
|
return c.HTML(http.StatusOK, indexHTML)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func generateMemoMetadata(memo *store.Memo) string {
|
||||||
|
metadataList := []string{
|
||||||
|
fmt.Sprintf(`<meta name="description" content="%s" />`, memo.Content),
|
||||||
|
fmt.Sprintf(`<meta property="og:title" content="%s" />`, fmt.Sprintf("Memos - %d", memo.ID)),
|
||||||
|
fmt.Sprintf(`<meta property="og:description" content="%s" />`, memo.Content),
|
||||||
|
fmt.Sprintf(`<meta property="og:image" content="%s" />`, "https://www.usememos.com/logo.png"),
|
||||||
|
`<meta property="og:type" content="website" />`,
|
||||||
|
// Twitter related metadata.
|
||||||
|
fmt.Sprintf(`<meta name="twitter:title" content="%s" />`, fmt.Sprintf("Memos - %d", memo.ID)),
|
||||||
|
fmt.Sprintf(`<meta name="twitter:description" content="%s" />`, memo.Content),
|
||||||
|
fmt.Sprintf(`<meta name="twitter:image" content="%s" />`, "https://www.usememos.com/logo.png"),
|
||||||
|
`<meta name="twitter:card" content="summary" />`,
|
||||||
|
}
|
||||||
|
return strings.Join(metadataList, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
func getFileSystem(path string) http.FileSystem {
|
func getFileSystem(path string) http.FileSystem {
|
||||||
fs, err := fs.Sub(embeddedFiles, path)
|
fs, err := fs.Sub(embeddedFiles, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -33,9 +33,6 @@ type Server struct {
|
||||||
Profile *profile.Profile
|
Profile *profile.Profile
|
||||||
Store *store.Store
|
Store *store.Store
|
||||||
|
|
||||||
// API services.
|
|
||||||
apiV2Service *apiv2.APIV2Service
|
|
||||||
|
|
||||||
// Asynchronous runners.
|
// Asynchronous runners.
|
||||||
backupRunner *backup.BackupRunner
|
backupRunner *backup.BackupRunner
|
||||||
telegramBot *telegram.Bot
|
telegramBot *telegram.Bot
|
||||||
|
@ -84,7 +81,8 @@ func NewServer(ctx context.Context, profile *profile.Profile, store *store.Store
|
||||||
s.ID = serverID
|
s.ID = serverID
|
||||||
|
|
||||||
// Serve frontend.
|
// Serve frontend.
|
||||||
frontend.Serve(e)
|
frontendService := frontend.NewFrontendService(profile, store)
|
||||||
|
frontendService.Serve(e)
|
||||||
|
|
||||||
// Serve swagger in dev/demo mode.
|
// Serve swagger in dev/demo mode.
|
||||||
if profile.Mode == "dev" || profile.Mode == "demo" {
|
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 := apiv1.NewAPIV1Service(s.Secret, profile, store, s.telegramBot)
|
||||||
apiV1Service.Register(rootGroup)
|
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.
|
// 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")
|
return nil, errors.Wrap(err, "failed to register gRPC gateway")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue