mirror of
https://github.com/usememos/memos.git
synced 2026-01-06 16:34:17 +08:00
render the SPA differently based on the route. if a page is available as RSS feed, inject a link element into the head element of the HTML variant. this ensures feeds are discoverable. this change replaces the middleware with custom router handlers to serve either assets from the embedded filesystem or the SPA document. closes #4276
91 lines
2.3 KiB
Go
91 lines
2.3 KiB
Go
package frontend
|
|
|
|
import (
|
|
"context"
|
|
"embed"
|
|
"errors"
|
|
"io/fs"
|
|
|
|
"github.com/labstack/echo/v4"
|
|
|
|
"github.com/usememos/memos/internal/profile"
|
|
"github.com/usememos/memos/store"
|
|
)
|
|
|
|
//go:embed dist/*
|
|
var embeddedFiles embed.FS
|
|
|
|
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 (*FrontendService) Serve(_ context.Context, e *echo.Echo) error {
|
|
fs, err := fs.Sub(embeddedFiles, "dist")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
idx, err := parseFSTemplate(fs, "index.html")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
htmlMeta := map[string]string{
|
|
"viewport": "width=device-width, initial-scale=1, user-scalable=no",
|
|
}
|
|
static := echo.StaticDirectoryHandler(fs, false)
|
|
index := templateHandler(idx, templateConfig{
|
|
MetaData: htmlMeta,
|
|
})
|
|
exploreFeedTitle := func(_ echo.Context) string {
|
|
return "Public Memos"
|
|
}
|
|
userFeedTitle := func(c echo.Context) string {
|
|
u := c.Param("username")
|
|
|
|
return u + " Memos"
|
|
}
|
|
assets := func(c echo.Context) error {
|
|
p := c.Request().URL.Path
|
|
if p == "/" || p == "/index.html" {
|
|
// do not serve index.html from the filesystem
|
|
// but serve it as rendered template instead
|
|
return index(c)
|
|
}
|
|
|
|
// Set Cache-Control header for static assets.
|
|
// Since Vite generates content-hashed filenames (e.g., index-BtVjejZf.js),
|
|
// we can cache aggressively but use immutable to prevent revalidation checks.
|
|
// For frequently redeployed instances, use shorter max-age (1 hour) to avoid
|
|
// serving stale assets after redeployment.
|
|
c.Response().Header().Set(echo.HeaderCacheControl, "public, max-age=3600, immutable") // 1 hour
|
|
if err := static(c); err == nil || !errors.Is(err, echo.ErrNotFound) {
|
|
return err
|
|
}
|
|
|
|
// fallback to the index document, assuming it is a SPA route
|
|
return index(c)
|
|
}
|
|
e.GET("/", index)
|
|
e.GET("/*", assets)
|
|
e.GET("/explore", templateHandler(idx, templateConfig{
|
|
MetaData: htmlMeta,
|
|
InjectFeedURL: true,
|
|
ResolveFeedTitle: exploreFeedTitle,
|
|
}))
|
|
e.GET("/u/:username", templateHandler(idx, templateConfig{
|
|
MetaData: htmlMeta,
|
|
InjectFeedURL: true,
|
|
ResolveFeedTitle: userFeedTitle,
|
|
}))
|
|
|
|
return nil
|
|
}
|