feat: add static file bypass auth + graph.html API_BASE fix
- Add /static/* route bypass in auth middleware - Localize d3.min.js (was CDN blocked) - Fix graph.html API_BASE to server IP for remote access - Add error logging in graph load function - Deploy binary at /usr/local/bin/zhiyid (13.5MB, correct cmd/zhiyid path)
This commit is contained in:
parent
4a67916cb5
commit
2e8ca614fd
|
|
@ -6,6 +6,7 @@ import (
|
|||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
|
@ -128,8 +129,8 @@ func Auth(next http.Handler) http.Handler {
|
|||
}
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// /health 和 /metrics 不需要认证
|
||||
if r.URL.Path == "/health" || r.URL.Path == "/metrics" {
|
||||
// /health, /metrics, /static/* 不需要认证
|
||||
if r.URL.Path == "/health" || r.URL.Path == "/metrics" || strings.HasPrefix(r.URL.Path, "/static/") {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"encoding/json"
|
||||
"net/http"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/xiaoxue/memoryweave/internal/governance"
|
||||
)
|
||||
|
|
@ -71,14 +72,18 @@ func (ga *GraphAPI) Navigate(w http.ResponseWriter, r *http.Request) {
|
|||
respond(w, 200, map[string]interface{}{"paths": paths, "entity": req.Entity, "count": len(paths)})
|
||||
}
|
||||
|
||||
// normalizeEntity 规整实体名:去特殊字符 + n_前缀
|
||||
// normalizeEntity 规整实体名:去特殊字符 + n_前缀,保留中文和 Unicode 字符
|
||||
func normalizeEntity(entity string) string {
|
||||
if strings.HasPrefix(entity, "n_") {
|
||||
entity = entity[2:]
|
||||
}
|
||||
// 过滤特殊字符
|
||||
// 过滤特殊字符,保留 Unicode 字母、数字、下划线、中横线
|
||||
clean := strings.Map(func(r rune) rune {
|
||||
if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '_' || r == '-' {
|
||||
if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '_' || r == '-' || r == ' ' {
|
||||
return r
|
||||
}
|
||||
// 保留中文和其他 Unicode 字母(如韩文、日文等)
|
||||
if unicode.IsLetter(r) {
|
||||
return r
|
||||
}
|
||||
return '_'
|
||||
|
|
@ -87,7 +92,7 @@ func normalizeEntity(entity string) string {
|
|||
for strings.Contains(clean, "__") {
|
||||
clean = strings.ReplaceAll(clean, "__", "_")
|
||||
}
|
||||
clean = strings.Trim(clean, "_")
|
||||
clean = strings.Trim(clean, "_ ")
|
||||
if clean == "" {
|
||||
return "n_unknown"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// AutoGraphUpdater 自动维护知识图谱
|
||||
|
|
@ -76,12 +77,19 @@ type DistillInput struct {
|
|||
}
|
||||
|
||||
func entityID(name string) string {
|
||||
// 保留 Unicode 字母、数字、下划线、中横线、空格(转下划线)
|
||||
clean := strings.Map(func(r rune) rune {
|
||||
if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '_' || r == '-' {
|
||||
if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '_' || r == '-' || r == ' ' {
|
||||
return r
|
||||
}
|
||||
// 保留 Unicode 字母(如中文)
|
||||
if unicode.IsLetter(r) {
|
||||
return r
|
||||
}
|
||||
return '_' // 特殊字符→下划线
|
||||
}, strings.ToLower(strings.ReplaceAll(strings.TrimSpace(name), " ", "_")))
|
||||
}, strings.ToLower(strings.TrimSpace(name)))
|
||||
// 空格转下划线
|
||||
clean = strings.ReplaceAll(clean, " ", "_")
|
||||
// 合并连续下划线
|
||||
for strings.Contains(clean, "__") {
|
||||
clean = strings.ReplaceAll(clean, "__", "_")
|
||||
|
|
@ -89,12 +97,16 @@ func entityID(name string) string {
|
|||
return "n_" + strings.Trim(clean, "_")
|
||||
}
|
||||
|
||||
// cleanEntityName 清洗实体名用于展示(去掉特殊字符,限制长度)
|
||||
// cleanEntityName 清洗实体名用于展示(保留 Unicode 字符)
|
||||
func cleanEntityName(raw string) string {
|
||||
clean := strings.Map(func(r rune) rune {
|
||||
if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == ' ' || r == '-' || r == '_' {
|
||||
return r
|
||||
}
|
||||
// 保留 Unicode 字母(中文、韩文、日文等)
|
||||
if unicode.IsLetter(r) || unicode.IsDigit(r) {
|
||||
return r
|
||||
}
|
||||
return ' '
|
||||
}, strings.TrimSpace(raw))
|
||||
clean = strings.Join(strings.Fields(clean), " ")
|
||||
|
|
|
|||
|
|
@ -240,7 +240,35 @@ func containsRelation(rel, substr string) bool {
|
|||
return rel == substr
|
||||
}
|
||||
|
||||
// GetGraph 内存图谱的导出(空实现)
|
||||
// GetGraph 内存图谱的导出
|
||||
func (g *InMemoryGraph) GetGraph(namespace string) ([]map[string]interface{}, []map[string]interface{}) {
|
||||
return []map[string]interface{}{}, []map[string]interface{}{}
|
||||
g.mu.RLock()
|
||||
defer g.mu.RUnlock()
|
||||
var nodes []map[string]interface{}
|
||||
var edges []map[string]interface{}
|
||||
// 导出匹配 namespace 的节点
|
||||
for _, n := range g.nodes {
|
||||
if namespace == "" || n.Namespace == namespace {
|
||||
nodes = append(nodes, map[string]interface{}{
|
||||
"id": n.ID,
|
||||
"name": n.Name,
|
||||
"type": n.Type,
|
||||
"namespace": n.Namespace,
|
||||
})
|
||||
}
|
||||
}
|
||||
// 导出匹配 namespace 的边
|
||||
for _, e := range g.edges {
|
||||
if namespace == "" || e.Namespace == namespace {
|
||||
edges = append(edges, map[string]interface{}{
|
||||
"id": e.ID,
|
||||
"source": e.Source,
|
||||
"target": e.Target,
|
||||
"relation": e.Relation,
|
||||
"weight": e.Weight,
|
||||
"namespace": e.Namespace,
|
||||
})
|
||||
}
|
||||
}
|
||||
return nodes, edges
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -4,7 +4,7 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>织忆 MemoryWeave — 知识图谱可视化</title>
|
||||
<script src="https://d3js.org/d3.v7.min.js"></script>
|
||||
<script src="/static/d3.min.js"></script>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
|
||||
|
|
@ -327,7 +327,7 @@
|
|||
event: '#f85149'
|
||||
};
|
||||
|
||||
const API_BASE = 'http://localhost:7821';
|
||||
const API_BASE = 'http://100.65.23.30:7821';
|
||||
const API_KEY = 'zhiyi-dev-key-2026';
|
||||
let nodes = [], links = [];
|
||||
let simulation, svg, g, linkGroup, nodeGroup, labelGroup;
|
||||
|
|
@ -354,7 +354,7 @@
|
|||
const resp = await fetch(url, {
|
||||
headers: { 'X-API-Key': API_KEY }
|
||||
});
|
||||
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
|
||||
if (!resp.ok) { const t = await resp.text(); console.error('API error:', resp.status, t.substring(0, 500)); throw new Error(`HTTP ${resp.status}`); }
|
||||
|
||||
const data = await resp.json();
|
||||
nodes = data.nodes || [];
|
||||
|
|
@ -369,6 +369,12 @@
|
|||
loading.style.display = 'none';
|
||||
error.style.display = 'block';
|
||||
error.textContent = `加载失败: ${e.message}`;
|
||||
console.error('Graph load error:', e);
|
||||
// Show response status if available
|
||||
if (typeof resp !== 'undefined' && resp) {
|
||||
console.log('Response status:', resp.status);
|
||||
resp.text().then(t => console.log('Response body:', t.substring(0, 500)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue