diff --git a/homepage/index.html b/homepage/index.html index 5ee37b5..1b696b0 100644 --- a/homepage/index.html +++ b/homepage/index.html @@ -205,6 +205,34 @@ body{ /* ── Footer ── */ .footer{text-align:center;padding:24px;font-size:13px;color:var(--text-faint);letter-spacing:.15em;border-top:1px solid var(--glass-edge);margin-top:auto;position:relative;z-index:1;font-weight:400} + +/* ── 登录态 ── */ +#login-bar{display:flex;align-items:center;justify-content:space-between;padding:18px 24px;background:rgba(11,20,36,.5);border:1px solid var(--glass-edge);border-radius:18px;gap:16px;flex-wrap:wrap} +#login-bar .hint{font-size:14px;color:var(--text-soft)} +#login-bar .hint strong{color:var(--light);font-weight:600} +.login-in{display:flex;gap:10px;align-items:center;flex-wrap:wrap} +.login-in input{padding:10px 16px;background:rgba(255,255,255,.035);border:1px solid var(--glass-edge);border-radius:12px;color:var(--text);font-size:14px;outline:none;transition:all .25s;font-family:inherit;width:130px;font-weight:400} +.login-in input:focus{border-color:rgba(90,208,224,.3);box-shadow:0 0 16px rgba(42,157,191,.08)} +.login-in input::placeholder{color:var(--text-faint)} +.login-in button{padding:10px 24px;background:linear-gradient(135deg,rgba(42,157,191,.3),rgba(123,93,191,.2));border:1px solid var(--glass-edge);border-radius:12px;color:var(--light);font-size:14px;font-weight:500;cursor:pointer;transition:all .25s;font-family:inherit;letter-spacing:.08em} +.login-in button:hover{background:linear-gradient(135deg,rgba(42,157,191,.5),rgba(123,93,191,.35));border-color:rgba(90,208,224,.2)} +.login-in button:disabled{opacity:.4;cursor:not-allowed} +#logged-in-bar{display:none;align-items:center;justify-content:space-between;padding:16px 24px;background:rgba(64,184,122,.06);border:1px solid rgba(64,184,122,.15);border-radius:18px;gap:16px;flex-wrap:wrap} +#logged-in-bar .user-badge{display:flex;align-items:center;gap:10px} +#logged-in-bar .user-avatar{width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,rgba(42,157,191,.4),rgba(123,93,191,.3));display:flex;align-items:center;justify-content:center;font-size:16px;color:var(--light);font-weight:600} +#logged-in-bar .user-name{font-size:15px;font-weight:600;color:var(--light)} +#logged-in-bar .user-status{font-size:12px;color:var(--green)} +.logged-actions{display:flex;gap:10px;flex-wrap:wrap} +.logged-actions button{padding:10px 22px;border-radius:12px;font-size:14px;font-weight:500;cursor:pointer;transition:all .25s;font-family:inherit} +.logged-actions .btn-primary{background:linear-gradient(135deg,rgba(42,157,191,.35),rgba(123,93,191,.25));border:1px solid var(--glass-edge);color:var(--light)} +.logged-actions .btn-primary:hover{background:linear-gradient(135deg,rgba(42,157,191,.55),rgba(123,93,191,.4))} +.logged-actions .btn-secondary{background:rgba(255,255,255,.03);border:1px solid var(--glass-edge);color:var(--text-soft)} +.logged-actions .btn-secondary:hover{background:rgba(255,255,255,.06);color:var(--light)} +.logged-actions .btn-logout{background:transparent;border:1px solid rgba(224,74,116,.2);color:var(--heart);font-size:13px} +.logged-actions .btn-logout:hover{background:rgba(224,74,116,.08)} + +.login-error{color:var(--heart);font-size:13px;margin-top:6px;display:none} +.login-loading{color:var(--text-soft);font-size:13px;margin-top:6px;display:none} @@ -240,19 +268,27 @@ body{
- -
-
- - -
+ +
+
登录你的代码仓库账号 · 使用全部功能
+ + +
+ +
+
+
?
+
已登录 · 光湖联邦成员
+
+
+ + +
@@ -402,10 +438,64 @@ body{ /* ── Clock ── */ (function t(){const e=document.getElementById('feed-now-time');if(e)e.textContent=new Date().toLocaleTimeString('zh-CN',{hour:'2-digit',minute:'2-digit'});setTimeout(t,30000)})(); -/* ── Login ── */ -function login(){const u=document.getElementById('login-user').value.trim(),p=document.getElementById('login-pass').value;if(!u||!p){alert('请输入账号和密码');return}window.location.href='/code/user/login?redirect_to=/'+u} +/* ── 登录系统 ── */ +async function handleLogin(){ + const u=document.getElementById('login-user').value.trim(),p=document.getElementById('login-pass').value; + if(!u||!p){showLoginError('请输入账号和密码');return} + showLoginLoading(true); + try{ + const r=await fetch('/api/verify',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({user:u,password:p})}); + const d=await r.json(); + if(d.ok){ + localStorage.setItem('current_user',d.username); + localStorage.setItem('is_admin',d.is_admin); + localStorage.setItem('logged_in','true'); + localStorage.setItem('login_time',Date.now().toString()); + showLoggedIn(d.username); + document.getElementById('login-pass').value=''; + }else{ + showLoginError(d.error||'登录失败'); + } + }catch(e){showLoginError('连接失败')} + showLoginLoading(false); +} +function showLoginError(msg){const e=document.getElementById('login-error');e.textContent=msg;e.style.display='block';setTimeout(()=>e.style.display='none',3000)} +function showLoginLoading(v){document.getElementById('login-loading').style.display=v?'block':'none';document.getElementById('login-btn').disabled=v} -/* ── Training ── */ +function showLoggedIn(username){ + document.getElementById('login-bar').style.display='none'; + document.getElementById('logged-in-bar').style.display='flex'; + document.getElementById('user-name').textContent=username; + document.getElementById('user-avatar').textContent=username[0].toUpperCase(); +} +function goToRepo(){ + const u=localStorage.getItem('current_user'); + window.location.href='/code/'+u; +} +function dismissRepo(){ + // 已登录状态保持不变,关闭跳转提示 +} +function logout(){ + localStorage.removeItem('current_user'); + localStorage.removeItem('is_admin'); + localStorage.removeItem('logged_in'); + localStorage.removeItem('login_time'); + document.getElementById('logged-in-bar').style.display='none'; + document.getElementById('login-bar').style.display='flex'; + document.getElementById('login-user').value=''; +} + +// 页面加载时检查登录状态 +(function(){ + const u=localStorage.getItem('current_user'); + if(u&&localStorage.getItem('logged_in')==='true'){ + showLoggedIn(u); + } +})(); + +// 重写 sendCardMsg 和 sendMsg 带上用户信息 +async function sendCardMsg(){const i=document.getElementById('card-chat-input'),m=i.value.trim();if(!m)return;i.value='';const ms=document.getElementById('chat-card-msgs');const ud=document.createElement('div');ud.className='c-msg user';ud.innerHTML='
'+m.replace(//g,'>')+'
';ms.appendChild(ud);const ld=document.createElement('div');ld.className='c-msg bot loading';ld.innerHTML='
✦ 思考中...
';ms.appendChild(ld);ms.scrollTop=ms.scrollHeight;try{const u=localStorage.getItem('current_user')||'';const r=await fetch('/api/chat',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({message:m,user:u})});const d=await r.json();ld.className='c-msg bot';ld.innerHTML='
'+d.reply.replace(//g,'>').replace(/\n/g,'
')+'
'}catch(e){ld.className='c-msg bot';ld.innerHTML='
连接暂不可用
'}ms.scrollTop=ms.scrollHeight} +async function sendMsg(){const i=document.getElementById('chat-input'),m=i.value.trim();if(!m)return;i.value='';const ms=document.getElementById('chat-msgs');const ud=document.createElement('div');ud.className='c-msg user';ud.innerHTML='
'+m.replace(//g,'>')+'
';ms.appendChild(ud);const ld=document.createElement('div');ld.className='c-msg bot loading';ld.innerHTML='
✦ 思考中...
';ms.appendChild(ld);ms.scrollTop=ms.scrollHeight;try{const u=localStorage.getItem('current_user')||'';const r=await fetch('/api/chat',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({message:m,user:u})});const d=await r.json();ld.className='c-msg bot';ld.innerHTML='
'+d.reply.replace(//g,'>').replace(/\n/g,'
')+'
'}catch(e){ld.className='c-msg bot';ld.innerHTML='
连接暂不可用
'}ms.scrollTop=ms.scrollHeight} async function upTr(){try{const r=await fetch('/training-status.json');if(!r.ok)return;const d=await r.json();if(d.step===undefined)return;const s=d.step||0,t=d.total||12243,p=Math.min(s/t*100,100);document.getElementById('train-step').textContent=s.toLocaleString()+' / '+t.toLocaleString();document.getElementById('train-pct').textContent=p.toFixed(1)+'%';document.getElementById('train-bar').style.width=p+'%';if(d.loss!==undefined)document.getElementById('train-loss').textContent=d.loss;const e=document.getElementById('train-eta');if(e)e.textContent='预计今日完成 · Step '+s.toLocaleString();const z=document.getElementById('persona-zhu-status');if(z)z.textContent='训练中 · '+p.toFixed(0)+'%';const tag=document.getElementById('status-tag');if(tag)tag.textContent='● 训练中 · Step '+s.toLocaleString()}catch(e){}} setInterval(upTr,30000);upTr(); @@ -413,14 +503,9 @@ setInterval(upTr,30000);upTr(); async function connectNotion(){try{window.location.href='/api/notion/connect?user_key=default'}catch(e){alert('Notion 连接暂时不可用')}} async function checkNt(){try{const r=await fetch('/api/notion/status');const d=await r.json();if(!d.ok)return;const b=document.getElementById('notion-body'),l=document.getElementById('notion-label');if(d.connected_users&&d.connected_users.length>0){const u=d.connected_users[0];l.textContent='✅ '+u.workspace;b.innerHTML='
✅ Notion 已连接
工作区: '+u.workspace+'
'}else{l.textContent='未连接';b.innerHTML='
连接 Notion 后,铸渊可读取和记录你的页面
✅ 按语言路径读取 · ✅ 新建页面 · ❌ 不编辑/删除现有
'}}catch(e){}} setInterval(checkNt,15000);checkNt(); - -/* ── Chat ── */ +/* ── 浮窗对话 ── */ let chatOpen=false; function toggleChat(){chatOpen=!chatOpen;document.getElementById('chat-panel').classList.toggle('open')} -async function sendMsg(){const i=document.getElementById('chat-input'),m=i.value.trim();if(!m)return;i.value='';const ms=document.getElementById('chat-msgs');const ud=document.createElement('div');ud.className='c-msg user';ud.innerHTML='
'+m.replace(//g,'>')+'
';ms.appendChild(ud);const ld=document.createElement('div');ld.className='c-msg bot loading';ld.innerHTML='
✦ 思考中...
';ms.appendChild(ld);ms.scrollTop=ms.scrollHeight;try{const r=await fetch('/api/chat',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({message:m})});const d=await r.json();ld.className='c-msg bot';ld.innerHTML='
'+d.reply.replace(//g,'>').replace(/\n/g,'
')+'
'}catch(e){ld.className='c-msg bot';ld.innerHTML='
连接暂不可用
'}ms.scrollTop=ms.scrollHeight} - -/* ── 页面中央对话 ── */ -async function sendCardMsg(){const i=document.getElementById('card-chat-input'),m=i.value.trim();if(!m)return;i.value='';const ms=document.getElementById('chat-card-msgs');const ud=document.createElement('div');ud.className='c-msg user';ud.innerHTML='
'+m.replace(//g,'>')+'
';ms.appendChild(ud);const ld=document.createElement('div');ld.className='c-msg bot loading';ld.innerHTML='
✦ 思考中...
';ms.appendChild(ld);ms.scrollTop=ms.scrollHeight;try{const r=await fetch('/api/chat',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({message:m})});const d=await r.json();ld.className='c-msg bot';ld.innerHTML='
'+d.reply.replace(//g,'>').replace(/\n/g,'
')+'
'}catch(e){ld.className='c-msg bot';ld.innerHTML='
连接暂不可用
'}ms.scrollTop=ms.scrollHeight} diff --git a/homepage/training-status.json b/homepage/training-status.json index 7c90086..b9a539e 100644 --- a/homepage/training-status.json +++ b/homepage/training-status.json @@ -1 +1 @@ -{"step": 2710, "total": 12243, "loss": "1.693", "updated": "2026-05-19T11:05:02.358331+00:00Z"} \ No newline at end of file +{"step": 2830, "total": 12243, "loss": "1.738", "updated": "2026-05-19T11:09:02.193455+00:00Z"} \ No newline at end of file diff --git a/homepage/zhuyuan-agent.py b/homepage/zhuyuan-agent.py index 1f2f941..5f593cb 100644 --- a/homepage/zhuyuan-agent.py +++ b/homepage/zhuyuan-agent.py @@ -4,10 +4,12 @@ 铸渊Agent API服务 v2 光湖代码仓库 · 引导主控人格 · Notion工具集成 """ -import json, os, glob, re, urllib.request +import json, os, glob, re, urllib.request, base64 from http.server import HTTPServer, BaseHTTPRequestHandler PORT = 3905 +FORGEJO_URL = "https://guanghulab.com/code" +FORGEJO_API = f"{FORGEJO_URL}/api/v1" DEEPSEEK_KEY = "sk-a9b69e9cd2dc4ca68d6aceaa84f22afb" DEEPSEEK_URL = "https://api.deepseek.com/chat/completions" NOTION_MCP = "http://127.0.0.1:3915" @@ -231,6 +233,24 @@ def call_deepseek(messages): except Exception as e: return f"抱歉,出错了:{str(e)}" +# ── Forgejo 验证 ── +def verify_forgejo(user, password): + """通过 Forgejo API 验证用户凭据""" + try: + auth = base64.b64encode(f"{user}:{password}".encode()).decode() + req = urllib.request.Request(f"{FORGEJO_API}/user") + req.add_header("Authorization", f"Basic {auth}") + with urllib.request.urlopen(req, timeout=10) as resp: + data = json.loads(resp.read()) + return {"ok": True, "username": data.get("login"), "id": data.get("id"), + "is_admin": data.get("is_admin", False)} + except urllib.request.HTTPError as e: + if e.code == 401: + return {"ok": False, "error": "账号或密码错误"} + return {"ok": False, "error": f"验证失败: {e.code}"} + except Exception as e: + return {"ok": False, "error": f"连接失败: {str(e)}"} + # ── HTTP 处理器 ── class AgentHandler(BaseHTTPRequestHandler): def do_OPTIONS(self): @@ -248,18 +268,40 @@ class AgentHandler(BaseHTTPRequestHandler): user_msg = data.get('message', '').strip() history = data.get('history', []) + current_user = data.get('user', '') if not user_msg: self.json_response({'reply': '请说点什么吧。'}) return - messages = [{"role": "system", "content": build_system_prompt()}] + # 构建系统提示,带上用户信息 + sys_prompt = build_system_prompt() + if current_user: + sys_prompt += f"\n\n【当前用户】\n正在和你对话的用户是: {current_user}。\n如果涉及Notion操作,使用此用户的身份。如果是冰朔(bingshuo),按冰朔的权限规则处理。" + + messages = [{"role": "system", "content": sys_prompt}] for h in history[-10:]: messages.append(h) messages.append({"role": "user", "content": user_msg}) reply = call_deepseek(messages) self.json_response({'reply': reply}) + + elif self.path == '/verify': + length = int(self.headers.get('Content-Length', 0)) + body = self.rfile.read(length).decode() if length else '{}' + data = json.loads(body) if body else {} + + user = data.get('user', '').strip() + password = data.get('password', '').strip() + + if not user or not password: + self.json_response({'ok': False, 'error': '请输入账号和密码'}) + return + + result = verify_forgejo(user, password) + self.json_response(result) + else: self.json_response({'error': 'not found'}, 404)