From 2394ef7f15a1adf4b5e4e3ff432a98ffebc85118 Mon Sep 17 00:00:00 2001 From: wood chen Date: Sat, 14 Jun 2025 19:03:58 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E9=9D=99=E6=80=81=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91=EF=BC=8C=E6=96=B0?= =?UTF-8?q?=E5=A2=9EresolveFilePath=E6=96=B9=E6=B3=95=E4=BB=A5=E5=A4=84?= =?UTF-8?q?=E7=90=86Next.js=E9=9D=99=E6=80=81=E5=AF=BC=E5=87=BA=E7=9A=84?= =?UTF-8?q?=E8=B7=AF=E7=94=B1=E9=97=AE=E9=A2=98=EF=BC=9B=E5=9C=A8=E8=B7=AF?= =?UTF-8?q?=E7=94=B1=E5=A4=84=E7=90=86=E5=99=A8=E4=B8=AD=E8=A7=84=E8=8C=83?= =?UTF-8?q?=E5=8C=96=E8=B7=AF=E5=BE=84=E4=BB=A5=E8=A7=A3=E5=86=B3=E5=B0=BE?= =?UTF-8?q?=E6=96=9C=E6=9D=A0=E9=97=AE=E9=A2=98=EF=BC=9B=E7=A6=81=E7=94=A8?= =?UTF-8?q?=E5=B0=BE=E6=96=9C=E6=9D=A0=E4=BB=A5=E9=81=BF=E5=85=8D=E8=B7=AF?= =?UTF-8?q?=E7=94=B1=E5=86=B2=E7=AA=81=EF=BC=9B=E5=9C=A8=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E7=AB=AF=E7=82=B9=E9=A1=B5=E9=9D=A2=E4=B8=AD=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E7=BC=96=E8=BE=91=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=AB=AF=E7=82=B9=E4=BF=A1=E6=81=AF=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- handlers/static_handler.go | 45 ++++++++- router/router.go | 14 +++ web/app/admin/page.tsx | 21 ++++- web/app/page.tsx | 2 +- web/components/admin/EndpointsTab.tsx | 126 ++++++++++++++++++++++++-- web/next.config.ts | 2 +- 6 files changed, 196 insertions(+), 14 deletions(-) diff --git a/handlers/static_handler.go b/handlers/static_handler.go index dd26aa0..621a1cb 100644 --- a/handlers/static_handler.go +++ b/handlers/static_handler.go @@ -27,8 +27,8 @@ func (s *StaticHandler) ServeStatic(w http.ResponseWriter, r *http.Request) { path = "/index.html" } - // 构建文件路径 - filePath := filepath.Join(s.staticDir, path) + // 处理 Next.js 静态导出的路由问题 + filePath := s.resolveFilePath(path) // 检查文件是否存在 if _, err := os.Stat(filePath); os.IsNotExist(err) { @@ -50,6 +50,47 @@ func (s *StaticHandler) ServeStatic(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, filePath) } +// resolveFilePath 解析文件路径,处理 Next.js 静态导出的路由问题 +func (s *StaticHandler) resolveFilePath(path string) string { + // 移除查询参数和锚点 + if idx := strings.Index(path, "?"); idx != -1 { + path = path[:idx] + } + if idx := strings.Index(path, "#"); idx != -1 { + path = path[:idx] + } + + // 构建初始文件路径 + filePath := filepath.Join(s.staticDir, path) + + // 如果路径以斜杠结尾,尝试查找 index.html + if strings.HasSuffix(path, "/") { + indexPath := filepath.Join(filePath, "index.html") + if _, err := os.Stat(indexPath); err == nil { + return indexPath + } + } else { + // 如果路径不以斜杠结尾,先检查是否存在对应的文件 + if _, err := os.Stat(filePath); err == nil { + return filePath + } + + // 如果文件不存在,尝试查找对应目录下的 index.html + indexPath := filepath.Join(filePath, "index.html") + if _, err := os.Stat(indexPath); err == nil { + return indexPath + } + + // 尝试添加 .html 扩展名 + htmlPath := filePath + ".html" + if _, err := os.Stat(htmlPath); err == nil { + return htmlPath + } + } + + return filePath +} + // isFrontendRoute 判断是否是前端路由 func (s *StaticHandler) isFrontendRoute(path string) bool { // 前端路由通常以 /admin 开头 diff --git a/router/router.go b/router/router.go index a1433f3..5c9476b 100644 --- a/router/router.go +++ b/router/router.go @@ -143,6 +143,20 @@ func (r *Router) HandleFunc(pattern string, handler func(http.ResponseWriter, *h } func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { + // 规范化路径,处理尾斜杠问题 + path := req.URL.Path + + // 对于前端路由,统一处理尾斜杠 + if strings.HasPrefix(path, "/admin") && path != "/admin" && strings.HasSuffix(path, "/") { + // 移除尾斜杠并重定向 + newPath := strings.TrimSuffix(path, "/") + if req.URL.RawQuery != "" { + newPath += "?" + req.URL.RawQuery + } + http.Redirect(w, req, newPath, http.StatusMovedPermanently) + return + } + // 首先检查是否是静态文件请求或前端路由 if r.staticHandler != nil && r.shouldServeStatic(req.URL.Path) { r.staticHandler.ServeStatic(w, req) diff --git a/web/app/admin/page.tsx b/web/app/admin/page.tsx index 86b9b69..cfd1fc6 100644 --- a/web/app/admin/page.tsx +++ b/web/app/admin/page.tsx @@ -26,7 +26,7 @@ export default function AdminPage() { const createEndpoint = async (endpointData: Partial) => { try { - const response = await authenticatedFetch('/api/admin/endpoints/', { + const response = await authenticatedFetch('/api/admin/endpoints', { method: 'POST', body: JSON.stringify(endpointData), }) @@ -42,10 +42,29 @@ export default function AdminPage() { } } + const updateEndpoint = async (id: number, endpointData: Partial) => { + try { + const response = await authenticatedFetch(`/api/admin/endpoints/${id}`, { + method: 'PUT', + body: JSON.stringify(endpointData), + }) + + if (response.ok) { + loadEndpoints() // 重新加载数据 + } else { + alert('更新端点失败') + } + } catch (error) { + console.error('Failed to update endpoint:', error) + alert('更新端点失败') + } + } + return ( ) diff --git a/web/app/page.tsx b/web/app/page.tsx index bdac866..d158928 100644 --- a/web/app/page.tsx +++ b/web/app/page.tsx @@ -216,7 +216,7 @@ export default function Home() {
) => void + onUpdateEndpoint: (id: number, data: Partial) => void onUpdateEndpoints: () => void } // 可拖拽的表格行组件 -function SortableTableRow({ endpoint, onManageDataSources }: { +function SortableTableRow({ endpoint, onManageDataSources, onEditEndpoint }: { endpoint: APIEndpoint onManageDataSources: (endpoint: APIEndpoint) => void + onEditEndpoint: (endpoint: APIEndpoint) => void }) { const { attributes, @@ -96,20 +98,31 @@ function SortableTableRow({ endpoint, onManageDataSources }: { {new Date(endpoint.created_at).toLocaleDateString()} - +
+ + +
) } -export default function EndpointsTab({ endpoints, onCreateEndpoint, onUpdateEndpoints }: EndpointsTabProps) { +export default function EndpointsTab({ endpoints, onCreateEndpoint, onUpdateEndpoint, onUpdateEndpoints }: EndpointsTabProps) { const [showCreateForm, setShowCreateForm] = useState(false) + const [showEditForm, setShowEditForm] = useState(false) + const [editingEndpoint, setEditingEndpoint] = useState(null) const [selectedEndpoint, setSelectedEndpoint] = useState(null) const [formData, setFormData] = useState({ name: '', @@ -133,6 +146,28 @@ export default function EndpointsTab({ endpoints, onCreateEndpoint, onUpdateEndp setShowCreateForm(false) } + const handleEditSubmit = (e: React.FormEvent) => { + e.preventDefault() + if (editingEndpoint) { + onUpdateEndpoint(editingEndpoint.id, formData) + setFormData({ name: '', url: '', description: '', is_active: true, show_on_homepage: true }) + setShowEditForm(false) + setEditingEndpoint(null) + } + } + + const handleEditEndpoint = (endpoint: APIEndpoint) => { + setEditingEndpoint(endpoint) + setFormData({ + name: endpoint.name, + url: endpoint.url, + description: endpoint.description, + is_active: endpoint.is_active, + show_on_homepage: endpoint.show_on_homepage + }) + setShowEditForm(true) + } + const loadEndpointDataSources = async (endpointId: number) => { try { const response = await authenticatedFetch(`/api/admin/endpoints/${endpointId}/data-sources`) @@ -274,6 +309,78 @@ export default function EndpointsTab({ endpoints, onCreateEndpoint, onUpdateEndp
)} + {showEditForm && editingEndpoint && ( +
+

编辑端点

+
+
+ + setFormData({ ...formData, name: e.target.value })} + required + /> +
+
+ + setFormData({ ...formData, url: e.target.value })} + placeholder="例如: pic/anime" + required + /> +
+
+ +