Files
arcrun-collector/cmd/arcrun-app/connect.go
T
Leo 361f1f1f9d 新增全新安裝的「可刪除預設庫」(leo 2026-08-08 拍板)
全新使用者第一次連上知識庫時,自動在 Documents 建一個
「Arcrun 範例庫(可刪除)」,裡面放三份 .md 示範內容,
不必自己選資料夾就能立刻走完「丟檔→知識卡→搜得到」。

四條紅線都有測試釘住(default_library_test.go/
default_library_e2e_test.go,含真的建 binary 跑 --dry-run 驗證):
1. 可刪——刪掉就真的消失,marker 檔擋住重種
2. 只有第一次——已有帳號的機器不會冒出來
3. 不污染——只在自己的資料夾裡寫檔
4. 不留幽靈資料——資料夾被刪後,config 殘留引用會被清掉(GetState 自我修復)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 00:22:03 +08:00

135 lines
4.9 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 main
// connect.go — 連線精靈的後端(t193)
//
// ⚠️ 本檔的 normalizePortalURL / fetchConfigByLogin / instanceChanged
// **逐字取自 arcrun-tray/main.go**,不是重寫。理由:
// - 那些是踩過坑才長出來的(例:instanceChanged 是 t86 個資外洩事故的防線——
// leo 2026-07-28 實測發現 youlin 時代的資料夾被同步進 geek6688 新實例)
// - 今天已經犯過一次「重寫別人修好的東西還修得更差」,不再犯第二次
// 兩邊哪天要改,**兩邊一起改**(與 trayMinCloud* 常數同一套規矩)。
import (
"bytes"
"encoding/json"
"errors"
"net/http"
neturl "net/url"
"strings"
"time"
)
type daemonConfig struct {
CypherURL string `json:"cypher_url"`
Namespace string `json:"namespace"`
Library string `json:"library"`
Email string `json:"email"`
InstanceName string `json:"instance_name"`
}
type daemonConfigResp struct {
Success bool `json:"success"`
Config daemonConfig `json:"config"`
Error string `json:"error"`
}
// normalizePortalURL 把用戶貼的東西變成可打的 origin。
// portalGUI)與 cypherAPI)是不同子域:用戶手上的是 portal,這裡換算成 API 位址。
func normalizePortalURL(raw string) (string, error) {
s := strings.TrimSpace(raw)
if s == "" {
return "", errors.New("請貼上你的知識庫網址")
}
if !strings.Contains(s, "://") {
s = "https://" + s
}
u, err := neturl.Parse(s)
if err != nil || u.Host == "" {
return "", errors.New("網址看起來不太對,請從信裡或瀏覽器網址列複製整段")
}
host := u.Host
if strings.HasPrefix(host, "arcrun-rag-ui.") {
host = "arcrun-cypher-executor." + strings.TrimPrefix(host, "arcrun-rag-ui.")
}
return "https://" + host, nil
}
func fetchConfigByLogin(portalURL, email, password string) (*daemonConfigResp, error) {
base, err := normalizePortalURL(portalURL)
if err != nil {
return nil, err
}
body, _ := json.Marshal(map[string]string{"email": strings.TrimSpace(email), "password": password})
req, err := http.NewRequest(http.MethodPost, base+"/portal/daemon/config", bytes.NewReader(body))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
res, err := (&http.Client{Timeout: 20 * time.Second}).Do(req)
if err != nil {
return nil, errors.New("連不上這個網址——請確認網址正確、網路正常")
}
defer res.Body.Close()
var out daemonConfigResp
if err := json.NewDecoder(res.Body).Decode(&out); err != nil {
return nil, errors.New("這個網址不像是 Arcrun RAG 知識庫,請再確認一次")
}
if res.StatusCode == http.StatusUnauthorized || res.StatusCode == http.StatusForbidden {
return nil, errors.New("帳號或密碼不對——用你在知識庫網站設定的那組")
}
if !out.Success {
if out.Error != "" {
return nil, errors.New(out.Error)
}
return nil, errors.New("連線失敗,請稍後再試一次")
}
return &out, nil
}
// Connect 是前端「連線」按鈕的入口:帳密換設定 → append 成新帳號(或更新既有同實例帳號)。
//
// 🔴 密碼零落地(刻意的安全設計):只在這一刻用來換設定,**不寫進 config.json**。
func (a *App) Connect(portalURL, email, password string) error {
r, err := fetchConfigByLogin(portalURL, email, password)
if err != nil {
return err
}
cfg, _ := loadCfg()
newHost := hostOf(r.Config.CypherURL)
for i := range cfg.Accounts {
if hostOf(cfg.Accounts[i].CypherURL) == newHost {
// 同一個實例=更新連線資訊,**保留既有 watch_folders**(改密碼不該清掉資料夾)
cfg.Accounts[i].CypherURL = r.Config.CypherURL
cfg.Accounts[i].Namespace = r.Config.Namespace
cfg.Accounts[i].APIKey = r.Config.Namespace
cfg.Accounts[i].Email = r.Config.Email
cfg.Accounts[i].InstanceName = r.Config.InstanceName
return saveCfg(cfg)
}
}
// 不同實例=新增一個帳號。**不覆蓋舊帳號**——t86 事故的教訓:
// 舊實例的資料夾若被帶進新實例,等於把別人的檔案同步到另一個知識庫(個資外洩)。
wasFirstEver := len(cfg.Accounts) == 0 // 這台機器至今從沒連過任何知識庫帳號
cfg.Accounts = append(cfg.Accounts, accountCfg{
InstanceName: r.Config.InstanceName,
Email: r.Config.Email,
CypherURL: r.Config.CypherURL,
Namespace: r.Config.Namespace,
APIKey: r.Config.Namespace,
})
if wasFirstEver {
// leo 2026-08-08:全新安裝自動裝一個「可刪除的預設庫」,讓使用者不必
// 自己選資料夾也能立刻看到「丟檔 → 知識卡 → 搜得到」整條路(見 default_library.go)。
seedDefaultLibraryIfFirstEver(cfg, len(cfg.Accounts)-1)
}
return saveCfg(cfg)
}
func hostOf(s string) string {
u, err := neturl.Parse(strings.TrimSpace(s))
if err != nil || u.Host == "" {
return strings.TrimSpace(s)
}
return u.Host
}