feat: opt static cache
This commit is contained in:
+41
-106
@@ -1,77 +1,66 @@
|
|||||||
# URL 前缀配置
|
# URL 路径前缀配置
|
||||||
|
|
||||||
Message Nest 支持配置 URL 前缀,允许您将应用部署在子路径下(例如 `/message-nest`),而不是根路径。
|
Message-Nest 支持配置 URL 路径前缀,允许您在子路径下部署应用。
|
||||||
|
|
||||||
## 工作原理
|
|
||||||
|
|
||||||
配置 URL 前缀后:
|
|
||||||
1. 后端会在所有路由前添加指定的前缀
|
|
||||||
2. 前端页面加载时,后端会在 HTML 中自动注入前缀配置
|
|
||||||
3. 前端的所有 API 请求会自动使用该前缀
|
|
||||||
|
|
||||||
**无需手动配置前端**,前后端会自动同步!
|
|
||||||
|
|
||||||
## 使用场景
|
|
||||||
|
|
||||||
- 在同一域名下部署多个应用
|
|
||||||
- 通过反向代理(如 Nginx)将应用映射到子路径
|
|
||||||
- 在企业内网中统一管理多个服务
|
|
||||||
|
|
||||||
## 配置方式
|
## 配置方式
|
||||||
|
|
||||||
### 方式一:配置文件
|
### 方式 1: 配置文件
|
||||||
|
|
||||||
编辑 `conf/app.ini` 文件,在 `[server]` 部分添加:
|
在 `conf/app.ini` 中添加:
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
[server]
|
[server]
|
||||||
RunMode = release
|
UrlPrefix = /message
|
||||||
HttpPort = 8000
|
|
||||||
UrlPrefix = /message-nest
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 方式二:环境变量
|
### 方式 2: 环境变量
|
||||||
|
|
||||||
设置环境变量:
|
设置环境变量:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export URL_PREFIX=/message-nest
|
export URL_PREFIX=/message
|
||||||
```
|
```
|
||||||
|
|
||||||
或在 Docker 中:
|
## 使用说明
|
||||||
|
|
||||||
```bash
|
1. **配置前缀**:在配置文件或环境变量中设置 `UrlPrefix`
|
||||||
docker run -e URL_PREFIX=/message-nest ...
|
2. **重启服务**:修改配置后需要重启 Message-Nest 服务
|
||||||
|
3. **访问应用**:使用新的 URL 访问,例如:`http://your-domain.com/message`
|
||||||
|
|
||||||
|
## 示例
|
||||||
|
|
||||||
|
### 默认访问(无前缀)
|
||||||
|
```
|
||||||
|
http://localhost:8000/
|
||||||
|
http://localhost:8000/api/v1/sendways/list
|
||||||
```
|
```
|
||||||
|
|
||||||
## 配置说明
|
### 配置前缀后
|
||||||
|
```ini
|
||||||
|
[server]
|
||||||
|
UrlPrefix = /message
|
||||||
|
```
|
||||||
|
|
||||||
- **路径格式**:可以带或不带前导斜杠 `/`,系统会自动处理
|
访问地址变为:
|
||||||
- **示例值**:
|
```
|
||||||
- `/message-nest`
|
http://localhost:8000/message/
|
||||||
- `/api/message-nest`
|
http://localhost:8000/message/api/v1/sendways/list
|
||||||
- `message-nest`(会自动转换为 `/message-nest`)
|
```
|
||||||
- **默认值**:空字符串(部署在根路径)
|
|
||||||
|
|
||||||
## 访问方式
|
## 注意事项
|
||||||
|
|
||||||
配置 URL 前缀后,访问地址会变为:
|
- 前缀会自动添加 `/` 前缀(如果没有的话)
|
||||||
|
- 前端静态资源会自动使用相对路径,无需额外配置
|
||||||
|
- API 请求会自动添加路径前缀
|
||||||
|
- 修改配置后必须重启服务才能生效
|
||||||
|
|
||||||
- **原地址**:`http://your-domain.com/`
|
## Nginx 反向代理示例
|
||||||
- **新地址**:`http://your-domain.com/message-nest/`
|
|
||||||
|
|
||||||
所有 API 端点也会自动添加前缀:
|
|
||||||
|
|
||||||
- **原 API**:`http://your-domain.com/api/v1/sendways/list`
|
|
||||||
- **新 API**:`http://your-domain.com/message-nest/api/v1/sendways/list`
|
|
||||||
|
|
||||||
## Nginx 反向代理配置示例
|
|
||||||
|
|
||||||
如果使用 Nginx 反向代理,配置示例:
|
如果使用 Nginx 反向代理,配置示例:
|
||||||
|
|
||||||
```nginx
|
```nginx
|
||||||
location /message-nest/ {
|
location /message/ {
|
||||||
proxy_pass http://localhost:8000/message-nest/;
|
proxy_pass http://localhost:8000/message/;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
@@ -79,63 +68,9 @@ location /message-nest/ {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## 注意事项
|
## 技术实现
|
||||||
|
|
||||||
::: warning 注意
|
- 后端使用 Gin 的路由组(RouterGroup)实现路径前缀
|
||||||
1. **修改配置后需要重启服务**才能生效
|
- 前端使用 Vite 的 `base: './'` 配置生成相对路径
|
||||||
2. **前后端会自动同步**前缀配置,后端会在 HTML 中注入配置
|
- 后端在 HTML 中注入 `<base>` 标签和配置脚本
|
||||||
3. **已有的 API 调用**会自动适配新的前缀
|
- 前端 API 请求自动添加路径前缀
|
||||||
4. **静态资源**(CSS、JS、图片等)也会自动使用新的前缀
|
|
||||||
5. **开发模式**下,如果前端单独运行,需要确保后端 API 的前缀配置正确
|
|
||||||
:::
|
|
||||||
|
|
||||||
## 常见问题
|
|
||||||
|
|
||||||
### Q: 修改前缀后无法访问?
|
|
||||||
|
|
||||||
**A:** 请检查:
|
|
||||||
1. 是否已重启服务
|
|
||||||
2. 前缀格式是否正确
|
|
||||||
3. 如果使用反向代理,检查代理配置是否正确
|
|
||||||
|
|
||||||
### Q: 可以使用多级路径吗?
|
|
||||||
|
|
||||||
**A:** 可以,例如:`/api/message-nest` 或 `/services/messaging/nest`
|
|
||||||
|
|
||||||
### Q: URL 前缀会影响性能吗?
|
|
||||||
|
|
||||||
**A:** 不会,URL 前缀只是路径的一部分,不会影响应用性能。
|
|
||||||
|
|
||||||
## 示例
|
|
||||||
|
|
||||||
### 示例 1:部署在子路径
|
|
||||||
|
|
||||||
```ini
|
|
||||||
# conf/app.ini
|
|
||||||
[server]
|
|
||||||
UrlPrefix = /message-nest
|
|
||||||
```
|
|
||||||
|
|
||||||
访问地址:`http://your-domain.com/message-nest/`
|
|
||||||
|
|
||||||
### 示例 2:多级子路径
|
|
||||||
|
|
||||||
```ini
|
|
||||||
# conf/app.ini
|
|
||||||
[server]
|
|
||||||
UrlPrefix = /api/services/message-nest
|
|
||||||
```
|
|
||||||
|
|
||||||
访问地址:`http://your-domain.com/api/services/message-nest/`
|
|
||||||
|
|
||||||
### 示例 3:Docker 环境变量
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker run -d \
|
|
||||||
-p 8000:8000 \
|
|
||||||
-e URL_PREFIX=/message-nest \
|
|
||||||
-e DB_TYPE=sqlite \
|
|
||||||
message-nest:latest
|
|
||||||
```
|
|
||||||
|
|
||||||
访问地址:`http://localhost:8000/message-nest/`
|
|
||||||
|
|||||||
+50
-3
@@ -3,16 +3,63 @@ package middleware
|
|||||||
import (
|
import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StaticCacheMiddleware add embed file static cache
|
// StaticCacheMiddleware add embed file static cache
|
||||||
func StaticCacheMiddleware() gin.HandlerFunc {
|
func StaticCacheMiddleware() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
// Apply the Cache-Control header to the static files
|
path := c.Request.URL.Path
|
||||||
if strings.HasPrefix(c.Request.URL.Path, "/assets/") {
|
|
||||||
c.Header("Cache-Control", "private, max-age=86400")
|
// 对静态资源应用缓存策略
|
||||||
|
if strings.HasPrefix(path, "/assets/") || strings.Contains(path, "/assets/") {
|
||||||
|
// 检查文件类型
|
||||||
|
if isVersionedAsset(path) {
|
||||||
|
// 带版本号的资源(如 index-Bw4BKttg.js)可以长期缓存
|
||||||
|
// 使用 immutable 指令,浏览器在缓存期内不会重新验证
|
||||||
|
c.Header("Cache-Control", "public, max-age=31536000, immutable")
|
||||||
|
} else {
|
||||||
|
// 其他静态资源使用较短的缓存时间
|
||||||
|
c.Header("Cache-Control", "public, max-age=86400")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加 ETag 支持
|
||||||
|
c.Header("ETag", `"`+path+`"`)
|
||||||
|
|
||||||
|
// 添加 Expires 头(兼容旧浏览器)
|
||||||
|
expires := time.Now().Add(24 * time.Hour).UTC().Format(time.RFC1123)
|
||||||
|
c.Header("Expires", expires)
|
||||||
|
|
||||||
|
// 添加 Vary 头,告诉缓存服务器根据这些头部区分缓存
|
||||||
|
c.Header("Vary", "Accept-Encoding")
|
||||||
|
} else if path == "/" || strings.HasSuffix(path, ".html") {
|
||||||
|
// HTML 文件不缓存或使用协商缓存
|
||||||
|
c.Header("Cache-Control", "no-cache, must-revalidate")
|
||||||
|
c.Header("Pragma", "no-cache")
|
||||||
|
c.Header("Expires", "0")
|
||||||
|
}
|
||||||
|
|
||||||
// Continue to the next middleware or handler
|
// Continue to the next middleware or handler
|
||||||
c.Next()
|
c.Next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isVersionedAsset 检查是否是带版本号的资源文件
|
||||||
|
// Vite 构建的文件通常包含 hash,如:index-Bw4BKttg.js
|
||||||
|
func isVersionedAsset(path string) bool {
|
||||||
|
// 检查是否包含 hash 模式(通常是 -[hash].js 或 -[hash].css)
|
||||||
|
return strings.Contains(path, "-") &&
|
||||||
|
(strings.HasSuffix(path, ".js") ||
|
||||||
|
strings.HasSuffix(path, ".css") ||
|
||||||
|
strings.HasSuffix(path, ".woff") ||
|
||||||
|
strings.HasSuffix(path, ".woff2") ||
|
||||||
|
strings.HasSuffix(path, ".ttf") ||
|
||||||
|
strings.HasSuffix(path, ".eot") ||
|
||||||
|
strings.HasSuffix(path, ".svg") ||
|
||||||
|
strings.HasSuffix(path, ".png") ||
|
||||||
|
strings.HasSuffix(path, ".jpg") ||
|
||||||
|
strings.HasSuffix(path, ".jpeg") ||
|
||||||
|
strings.HasSuffix(path, ".gif") ||
|
||||||
|
strings.HasSuffix(path, ".webp") ||
|
||||||
|
strings.HasSuffix(path, ".ico"))
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user