Files
jiang13-forum/service/permalink.go

141 lines
3.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package service
import (
"fmt"
"regexp"
"strconv"
"strings"
)
const (
SettingPermalinkEnabled = "permalink_enabled"
SettingPermalinkExt = "permalink_ext"
DefaultPermalinkExt = "html"
)
var (
permalinkExtRe = regexp.MustCompile(`(?i)^[a-z0-9]{1,16}$`)
// /post/123 或 /post/123.html
postPermalinkRe = regexp.MustCompile(`^/post/(\d+)(?:\.([A-Za-z0-9]{1,16}))?/?$`)
userPermalinkRe = regexp.MustCompile(`^/user/(\d+)(?:\.([A-Za-z0-9]{1,16}))?/?$`)
)
// PermalinkConfig 伪静态(固定链接)配置
type PermalinkConfig struct {
Enabled bool `json:"permalink_enabled"`
Ext string `json:"permalink_ext"` // 不含点,如 html / htm
}
// NormalizePermalinkExt 规范化后缀:去点、小写、仅字母数字
func NormalizePermalinkExt(raw string) (string, bool) {
ext := strings.TrimSpace(raw)
ext = strings.TrimPrefix(ext, ".")
ext = strings.ToLower(ext)
if ext == "" {
ext = DefaultPermalinkExt
}
if !permalinkExtRe.MatchString(ext) {
return "", false
}
return ext, true
}
// Permalink 读取伪静态配置
func (s *ForumSettingsService) Permalink() PermalinkConfig {
ext, ok := NormalizePermalinkExt(s.getString(SettingPermalinkExt, DefaultPermalinkExt))
if !ok {
ext = DefaultPermalinkExt
}
return PermalinkConfig{
Enabled: s.getString(SettingPermalinkEnabled, "0") == "1",
Ext: ext,
}
}
// Suffix 返回带点后缀(未启用时为空)
func (p PermalinkConfig) Suffix() string {
if !p.Enabled {
return ""
}
ext, ok := NormalizePermalinkExt(p.Ext)
if !ok {
ext = DefaultPermalinkExt
}
return "." + ext
}
// PostPath 帖子规范路径
func (p PermalinkConfig) PostPath(id uint) string {
return fmt.Sprintf("/post/%d%s", id, p.Suffix())
}
// UserPath 用户规范路径
func (p PermalinkConfig) UserPath(id uint) string {
return fmt.Sprintf("/user/%d%s", id, p.Suffix())
}
// PermalinkMatch 路径解析结果
type PermalinkMatch struct {
ID uint
Ext string // 请求里的后缀(无点);无后缀为空
Canonical string // 当前配置下的规范路径
OK bool
}
// MatchPostPath 解析帖子公开路径(不含 /edit
func (p PermalinkConfig) MatchPostPath(path string) PermalinkMatch {
m := postPermalinkRe.FindStringSubmatch(path)
if len(m) < 2 {
return PermalinkMatch{}
}
id64, err := strconv.ParseUint(m[1], 10, 64)
if err != nil || id64 == 0 {
return PermalinkMatch{}
}
ext := ""
if len(m) > 2 {
ext = strings.ToLower(m[2])
}
id := uint(id64)
return PermalinkMatch{
ID: id,
Ext: ext,
Canonical: p.PostPath(id),
OK: true,
}
}
// MatchUserPath 解析用户公开路径
func (p PermalinkConfig) MatchUserPath(path string) PermalinkMatch {
m := userPermalinkRe.FindStringSubmatch(path)
if len(m) < 2 {
return PermalinkMatch{}
}
id64, err := strconv.ParseUint(m[1], 10, 64)
if err != nil || id64 == 0 {
return PermalinkMatch{}
}
ext := ""
if len(m) > 2 {
ext = strings.ToLower(m[2])
}
id := uint(id64)
return PermalinkMatch{
ID: id,
Ext: ext,
Canonical: p.UserPath(id),
OK: true,
}
}
// NeedsCanonicalRedirect 当前请求路径是否应 301 到规范 URL
func (m PermalinkMatch) NeedsCanonicalRedirect(requestPath string) bool {
if !m.OK {
return false
}
// 去掉末尾 / 再比
req := strings.TrimSuffix(requestPath, "/")
can := strings.TrimSuffix(m.Canonical, "/")
return req != can
}