@memtensor/memos-local-openclaw-plugin 1.0.4-beta.16 → 1.0.4-beta.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +94 -27
- package/dist/hub/server.d.ts +1 -0
- package/dist/hub/server.d.ts.map +1 -1
- package/dist/hub/server.js +74 -7
- package/dist/hub/server.js.map +1 -1
- package/dist/ingest/providers/index.js +2 -2
- package/dist/ingest/providers/index.js.map +1 -1
- package/dist/shared/llm-call.d.ts.map +1 -1
- package/dist/shared/llm-call.js +2 -1
- package/dist/shared/llm-call.js.map +1 -1
- package/dist/viewer/html.d.ts.map +1 -1
- package/dist/viewer/html.js +214 -67
- package/dist/viewer/html.js.map +1 -1
- package/dist/viewer/server.d.ts +6 -0
- package/dist/viewer/server.d.ts.map +1 -1
- package/dist/viewer/server.js +152 -42
- package/dist/viewer/server.js.map +1 -1
- package/index.ts +7 -4
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/src/hub/server.ts +70 -8
- package/src/ingest/providers/index.ts +2 -2
- package/src/shared/llm-call.ts +2 -1
- package/src/viewer/html.ts +214 -67
- package/src/viewer/server.ts +145 -41
package/dist/viewer/html.js
CHANGED
|
@@ -1657,7 +1657,7 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1657
1657
|
<li><span data-i18n="guide.join.s1">Ask your team admin for the Server Address and Team Token</span></li>
|
|
1658
1658
|
<li><span data-i18n="guide.join.s2">Enable sharing above, select "Client" mode</span></li>
|
|
1659
1659
|
<li><span data-i18n="guide.join.s3">Fill in Server Address and Team Token, click "Test Connection"</span></li>
|
|
1660
|
-
<li><span data-i18n="guide.join.s4">Save
|
|
1660
|
+
<li><span data-i18n="guide.join.s4">Click "Save & Apply" — the service restarts automatically (page refreshes)</span></li>
|
|
1661
1661
|
</ol>
|
|
1662
1662
|
<button class="btn-guide" onclick="guideGoToHub('client')" data-i18n="guide.join.btn">\u2192 Configure Client Mode</button>
|
|
1663
1663
|
</div>
|
|
@@ -1669,7 +1669,7 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1669
1669
|
<div class="team-guide-opt-desc" data-i18n="guide.hub.desc">Be the team server. Run it on this device so others can connect and share memories with you.</div>
|
|
1670
1670
|
<ol class="team-guide-steps">
|
|
1671
1671
|
<li><span data-i18n="guide.hub.s1">Enable sharing above, select "Server" mode</span></li>
|
|
1672
|
-
<li><span data-i18n="guide.hub.s2">Set a team name,
|
|
1672
|
+
<li><span data-i18n="guide.hub.s2">Set a team name, click "Save & Apply" — the service restarts automatically</span></li>
|
|
1673
1673
|
<li><span data-i18n="guide.hub.s3">Share the Server Address and Team Token with your team members</span></li>
|
|
1674
1674
|
<li><span data-i18n="guide.hub.s4">Approve join requests in the Admin Panel</span></li>
|
|
1675
1675
|
</ol>
|
|
@@ -1730,7 +1730,7 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1730
1730
|
<div style="font-weight:700;color:var(--text);margin-bottom:4px" data-i18n="settings.hub.clientSteps.title">Quick Setup (3 steps)</div>
|
|
1731
1731
|
<div><span style="color:var(--accent)">1.</span> <span data-i18n="settings.hub.clientSteps.s1">Ask your team admin for the Server Address and Team Token</span></div>
|
|
1732
1732
|
<div><span style="color:var(--accent)">2.</span> <span data-i18n="settings.hub.clientSteps.s2">Fill them in below, click "Test Connection" to verify</span></div>
|
|
1733
|
-
<div><span style="color:var(--accent)">3.</span> <span data-i18n="settings.hub.clientSteps.s3">Click "Save
|
|
1733
|
+
<div><span style="color:var(--accent)">3.</span> <span data-i18n="settings.hub.clientSteps.s3">Click "Save & Apply" — the service will restart and page refreshes automatically</span></div>
|
|
1734
1734
|
</div>
|
|
1735
1735
|
<div class="settings-grid">
|
|
1736
1736
|
<div class="settings-field full-width">
|
|
@@ -2126,8 +2126,13 @@ const I18N={
|
|
|
2126
2126
|
'notif.userJoin':'New user requests to join the team',
|
|
2127
2127
|
'notif.userOnline':'User came online',
|
|
2128
2128
|
'notif.userOffline':'User went offline',
|
|
2129
|
+
'notif.userLeft':'User has left the team',
|
|
2129
2130
|
'notif.membershipApproved':'Your team join request has been approved',
|
|
2130
2131
|
'notif.membershipRejected':'Your team join request has been declined',
|
|
2132
|
+
'notif.membershipRemoved':'You have been removed from the team by the admin',
|
|
2133
|
+
'notif.hubShutdown':'The team server has been shut down',
|
|
2134
|
+
'notif.rolePromoted':'You have been promoted to admin',
|
|
2135
|
+
'notif.roleDemoted':'You have been changed to member',
|
|
2131
2136
|
'notif.clearAll':'Clear all',
|
|
2132
2137
|
'notif.timeAgo.just':'just now',
|
|
2133
2138
|
'notif.timeAgo.min':'{n}m ago',
|
|
@@ -2278,12 +2283,12 @@ const I18N={
|
|
|
2278
2283
|
'settings.test.ok':'Connected',
|
|
2279
2284
|
'settings.test.fail':'Failed',
|
|
2280
2285
|
'settings.session.expired':'Session expired, please refresh the page to log in again',
|
|
2281
|
-
'settings.save':'Save
|
|
2286
|
+
'settings.save':'Save & Apply',
|
|
2282
2287
|
'settings.reset':'Reset',
|
|
2283
2288
|
'settings.saved':'Saved',
|
|
2284
|
-
'settings.restart.hint':'
|
|
2285
|
-
'settings.restart.autoRefresh':'
|
|
2286
|
-
'settings.restart.waiting':'Configuration saved.
|
|
2289
|
+
'settings.restart.hint':'Changes will take effect after the service restarts automatically.',
|
|
2290
|
+
'settings.restart.autoRefresh':'Service restarting, page will refresh automatically...',
|
|
2291
|
+
'settings.restart.waiting':'Configuration saved. Service is restarting...',
|
|
2287
2292
|
'settings.save.fail':'Failed to save settings',
|
|
2288
2293
|
'settings.save.emb.required':'Embedding model is required. Please configure an embedding model before saving.',
|
|
2289
2294
|
'settings.save.emb.fail':'Embedding model test failed, cannot save',
|
|
@@ -2410,16 +2415,16 @@ const I18N={
|
|
|
2410
2415
|
'settings.hub.tokenCopied':'Team Token copied!',
|
|
2411
2416
|
'settings.hub.hubSteps.title':'Quick Setup (3 steps)',
|
|
2412
2417
|
'settings.hub.hubSteps.s1':'Fill in Team Name below (or keep default)',
|
|
2413
|
-
'settings.hub.hubSteps.s2':'Click "Save
|
|
2418
|
+
'settings.hub.hubSteps.s2':'Click "Save & Apply" — the service will restart automatically',
|
|
2414
2419
|
'settings.hub.hubSteps.s3':'Share the Server Address and Team Token below with your team members',
|
|
2415
2420
|
'settings.hub.clientSteps.title':'Quick Setup (3 steps)',
|
|
2416
2421
|
'settings.hub.clientSteps.s1':'Ask your team admin for the Server Address and Team Token',
|
|
2417
2422
|
'settings.hub.clientSteps.s2':'Fill them in below, click "Test Connection" to verify',
|
|
2418
|
-
'settings.hub.clientSteps.s3':'Click "Save
|
|
2423
|
+
'settings.hub.clientSteps.s3':'Click "Save & Apply" — the service will restart and page refreshes automatically',
|
|
2419
2424
|
'settings.hub.shareInfo.title':'Share this info with your team members:',
|
|
2420
2425
|
'settings.hub.shareInfo.yourIP':'your-IP',
|
|
2421
2426
|
'settings.hub.shareInfo.clickCopy':'Click to copy',
|
|
2422
|
-
'settings.hub.restartAlert':'Team sharing config saved!
|
|
2427
|
+
'settings.hub.restartAlert':'Team sharing config saved! The service will restart automatically to apply changes.',
|
|
2423
2428
|
'settings.hub.hubAddress':'Server Address',
|
|
2424
2429
|
'settings.hub.hubAddress.hint':'Team server address, e.g. 192.168.1.100:18800',
|
|
2425
2430
|
'settings.hub.teamTokenClient':'Team Token',
|
|
@@ -2440,6 +2445,10 @@ const I18N={
|
|
|
2440
2445
|
'sidebar.hub':'\u{1F310} Team Sharing',
|
|
2441
2446
|
'sharing.sidebar.connected':'Connected',
|
|
2442
2447
|
'sharing.sidebar.disconnected':'Disconnected',
|
|
2448
|
+
'sharing.sidebar.hubRunning':'Hub Running',
|
|
2449
|
+
'sharing.sidebar.teamName':'Team',
|
|
2450
|
+
'sharing.sidebar.members':'Members',
|
|
2451
|
+
'sharing.sidebar.online':'online',
|
|
2443
2452
|
'sharing.sidebar.pending':'Pending Approval',
|
|
2444
2453
|
'sharing.sidebar.rejected':'Rejected',
|
|
2445
2454
|
'sharing.sidebar.starting':'Starting...',
|
|
@@ -2456,6 +2465,11 @@ const I18N={
|
|
|
2456
2465
|
'sharing.retryJoin':'Retry Join',
|
|
2457
2466
|
'sharing.retryJoin.hint':'Clears local data and re-submits the join request',
|
|
2458
2467
|
'sharing.retryJoin.confirm':'This will clear your current connection and re-submit a join request. Continue?',
|
|
2468
|
+
'sharing.leaveTeam':'Leave Team',
|
|
2469
|
+
'sharing.leaveTeam.confirm':'You are about to leave team "{team}".\\n\\nWhat will happen:\\n\\u2022 You will disconnect from the team server\\n\\u2022 The team admin will be notified that you left\\n\\u2022 You will no longer receive shared memories, tasks, or skills\\n\\u2022 Your local data is preserved and not affected\\n\\u2022 You can rejoin later if the admin approves\\n\\nAre you sure?',
|
|
2470
|
+
'sharing.leaveTeam.success':'You have left the team. Sharing has been disabled.',
|
|
2471
|
+
'sharing.leaveTeam.fail':'Failed to leave team',
|
|
2472
|
+
'sharing.team.default':'the team',
|
|
2459
2473
|
'sharing.retryJoin.success':'Join request re-submitted. Waiting for admin approval.',
|
|
2460
2474
|
'sharing.retryJoin.fail':'Failed to retry join',
|
|
2461
2475
|
'sharing.ownerRemoved':'(removed)',
|
|
@@ -2509,6 +2523,7 @@ const I18N={
|
|
|
2509
2523
|
'admin.editName':'Edit Name',
|
|
2510
2524
|
'admin.lastAdminHint':'Last admin — cannot remove or demote',
|
|
2511
2525
|
'admin.ownerHint':'Hub owner — cannot be demoted or removed',
|
|
2526
|
+
'admin.selfHint':'This is you',
|
|
2512
2527
|
'admin.editNamePrompt':'Enter new username:',
|
|
2513
2528
|
'confirm.promoteAdmin':'Promote this user to admin? They will be able to manage all team members and resources.',
|
|
2514
2529
|
'confirm.demoteMember':'Demote this admin to member?',
|
|
@@ -2570,6 +2585,8 @@ const I18N={
|
|
|
2570
2585
|
'toast.userApproved':'User approved',
|
|
2571
2586
|
'sharing.approved.toast':'Your join request has been approved!',
|
|
2572
2587
|
'sharing.rejected.toast':'Your join request was rejected by the admin.',
|
|
2588
|
+
'sharing.hubOffline.toast':'Team server is offline. Will reconnect automatically when it comes back.',
|
|
2589
|
+
'sharing.hubReconnected.toast':'Team server is back online! Connection restored.',
|
|
2573
2590
|
'toast.userRejected':'User rejected',
|
|
2574
2591
|
'toast.approveFail':'Approve failed',
|
|
2575
2592
|
'toast.rejectFail':'Reject failed',
|
|
@@ -2729,9 +2746,10 @@ const I18N={
|
|
|
2729
2746
|
'update.dismiss':'Dismiss',
|
|
2730
2747
|
'sharing.disable.confirm.hub':'You are about to shut down the team server.\\n\\nWhat will happen:\\n\\u2022 All connected team members will be disconnected\\n\\u2022 They will no longer be able to sync memories, tasks, or skills\\n\\u2022 Shared data is preserved and will be available when you re-enable\\n\\nAre you sure?',
|
|
2731
2748
|
'sharing.disable.confirm.client':'You are about to disconnect from the team.\\n\\nWhat will happen:\\n\\u2022 You will no longer receive shared memories, tasks, or skills from the team\\n\\u2022 Your local data is preserved and will not be affected\\n\\u2022 You can reconnect later by re-enabling sharing\\n\\nAre you sure?',
|
|
2732
|
-
'sharing.disable.restartAlert':'Sharing has been disabled.
|
|
2733
|
-
'sharing.switch.hubToClient':'You are about to switch from Server to Client mode.\\n\\nWhat will happen:\\n\\u2022 The Hub server will shut down after
|
|
2734
|
-
'sharing.switch.clientToHub':'You are about to switch from Client to Server mode.\\n\\nWhat will happen:\\n\\u2022 You will disconnect from the current team\\n\\u2022 A new Hub server will start after
|
|
2749
|
+
'sharing.disable.restartAlert':'Sharing has been disabled. The service will restart automatically to apply the change.',
|
|
2750
|
+
'sharing.switch.hubToClient':'You are about to switch from Server to Client mode.\\n\\nWhat will happen:\\n\\u2022 The Hub server will shut down after the service restarts\\n\\u2022 All connected team members will be disconnected\\n\\u2022 Shared data on the Hub is preserved for future use\\n\\u2022 You will join the specified remote team as a client\\n\\nAre you sure?',
|
|
2751
|
+
'sharing.switch.clientToHub':'You are about to switch from Client to Server mode.\\n\\nWhat will happen:\\n\\u2022 You will disconnect from the current team\\n\\u2022 A new Hub server will start after the service restarts\\n\\u2022 Your local data is not affected\\n\\nAre you sure?',
|
|
2752
|
+
'sharing.switch.hubAddress':'You are about to leave the current team and join a different one.\\n\\nWhat will happen:\\n\\u2022 You will disconnect from the current team server\\n\\u2022 The current team admin will be notified that you left\\n\\u2022 You will join the new team server as a new member\\n\\u2022 Your local data is not affected\\n\\nAre you sure?',
|
|
2735
2753
|
'admin.notEnabled.title':'Team sharing is not enabled',
|
|
2736
2754
|
'admin.notEnabled.desc':'The Admin Panel is used to manage team members, shared memories, tasks, and skills. To use this feature, you need to enable team sharing first.',
|
|
2737
2755
|
'admin.notEnabled.setupHub':'Set Up as Team Server',
|
|
@@ -2749,12 +2767,12 @@ const I18N={
|
|
|
2749
2767
|
'guide.join.s1':'Ask your team admin for the Server Address and Team Token',
|
|
2750
2768
|
'guide.join.s2':'Go to Settings \u2192 Team Sharing, enable sharing, select "Client" mode',
|
|
2751
2769
|
'guide.join.s3':'Fill in Server Address and Team Token, click "Test Connection"',
|
|
2752
|
-
'guide.join.s4':'Save
|
|
2770
|
+
'guide.join.s4':'Click "Save & Apply" — the service restarts automatically (page refreshes)',
|
|
2753
2771
|
'guide.join.btn':'\u2192 Configure Client Mode',
|
|
2754
2772
|
'guide.hub.title':'Start Your Own Team Server',
|
|
2755
2773
|
'guide.hub.desc':'Be the team server. Run it on this device so others can connect and share memories with you.',
|
|
2756
2774
|
'guide.hub.s1':'Go to Settings \u2192 Team Sharing, enable sharing, select "Server" mode',
|
|
2757
|
-
'guide.hub.s2':'Set a team name,
|
|
2775
|
+
'guide.hub.s2':'Set a team name, click "Save & Apply" — the service restarts automatically',
|
|
2758
2776
|
'guide.hub.s3':'Share the Server Address and Team Token with your team members',
|
|
2759
2777
|
'guide.hub.s4':'Approve join requests in the Admin Panel',
|
|
2760
2778
|
'guide.hub.btn':'\u2192 Configure Server Mode'
|
|
@@ -2845,8 +2863,13 @@ const I18N={
|
|
|
2845
2863
|
'notif.userJoin':'有新用户申请加入团队',
|
|
2846
2864
|
'notif.userOnline':'用户上线了',
|
|
2847
2865
|
'notif.userOffline':'用户下线了',
|
|
2866
|
+
'notif.userLeft':'用户已退出团队',
|
|
2848
2867
|
'notif.membershipApproved':'你的团队加入申请已通过',
|
|
2849
2868
|
'notif.membershipRejected':'你的团队加入申请已被拒绝',
|
|
2869
|
+
'notif.membershipRemoved':'你已被管理员移出团队',
|
|
2870
|
+
'notif.hubShutdown':'团队服务已关闭',
|
|
2871
|
+
'notif.rolePromoted':'你已被提升为管理员',
|
|
2872
|
+
'notif.roleDemoted':'你已被设为普通成员',
|
|
2850
2873
|
'notif.clearAll':'清除全部',
|
|
2851
2874
|
'notif.timeAgo.just':'刚刚',
|
|
2852
2875
|
'notif.timeAgo.min':'{n}分钟前',
|
|
@@ -2997,12 +3020,12 @@ const I18N={
|
|
|
2997
3020
|
'settings.test.ok':'连接成功',
|
|
2998
3021
|
'settings.test.fail':'连接失败',
|
|
2999
3022
|
'settings.session.expired':'登录已过期,请刷新页面重新登录',
|
|
3000
|
-
'settings.save':'
|
|
3023
|
+
'settings.save':'保存并应用',
|
|
3001
3024
|
'settings.reset':'重置',
|
|
3002
3025
|
'settings.saved':'已保存',
|
|
3003
|
-
'settings.restart.hint':'
|
|
3004
|
-
'settings.restart.autoRefresh':'
|
|
3005
|
-
'settings.restart.waiting':'
|
|
3026
|
+
'settings.restart.hint':'修改将在服务自动重启后生效。',
|
|
3027
|
+
'settings.restart.autoRefresh':'服务重启中,页面将自动刷新...',
|
|
3028
|
+
'settings.restart.waiting':'配置已保存,服务正在重启...',
|
|
3006
3029
|
'settings.save.fail':'保存设置失败',
|
|
3007
3030
|
'settings.save.emb.required':'嵌入模型为必填项,请先配置嵌入模型再保存。',
|
|
3008
3031
|
'settings.save.emb.fail':'嵌入模型测试失败,无法保存',
|
|
@@ -3129,16 +3152,16 @@ const I18N={
|
|
|
3129
3152
|
'settings.hub.tokenCopied':'团队令牌已复制!',
|
|
3130
3153
|
'settings.hub.hubSteps.title':'快速配置(3 步)',
|
|
3131
3154
|
'settings.hub.hubSteps.s1':'填写下方团队名称(或保持默认)',
|
|
3132
|
-
'settings.hub.hubSteps.s2':'
|
|
3155
|
+
'settings.hub.hubSteps.s2':'点击「保存并应用」,服务将自动重启',
|
|
3133
3156
|
'settings.hub.hubSteps.s3':'将下方的服务器地址和团队令牌分享给团队成员',
|
|
3134
3157
|
'settings.hub.clientSteps.title':'快速配置(3 步)',
|
|
3135
3158
|
'settings.hub.clientSteps.s1':'向团队管理员获取服务器地址和团队令牌',
|
|
3136
3159
|
'settings.hub.clientSteps.s2':'填入下方,点击"测试连接"验证连通性',
|
|
3137
|
-
'settings.hub.clientSteps.s3':'
|
|
3160
|
+
'settings.hub.clientSteps.s3':'点击「保存并应用」,服务将自动重启(页面会自动刷新)',
|
|
3138
3161
|
'settings.hub.shareInfo.title':'请将以下信息分享给团队成员:',
|
|
3139
3162
|
'settings.hub.shareInfo.yourIP':'你的IP',
|
|
3140
3163
|
'settings.hub.shareInfo.clickCopy':'点击复制',
|
|
3141
|
-
'settings.hub.restartAlert':'
|
|
3164
|
+
'settings.hub.restartAlert':'团队共享配置已保存!服务将自动重启以应用更改。',
|
|
3142
3165
|
'settings.hub.hubAddress':'服务器地址',
|
|
3143
3166
|
'settings.hub.hubAddress.hint':'团队服务器地址,如 192.168.1.100:18800',
|
|
3144
3167
|
'settings.hub.teamTokenClient':'团队令牌',
|
|
@@ -3159,6 +3182,10 @@ const I18N={
|
|
|
3159
3182
|
'sidebar.hub':'\u{1F310} 团队共享',
|
|
3160
3183
|
'sharing.sidebar.connected':'已连接',
|
|
3161
3184
|
'sharing.sidebar.disconnected':'已断开',
|
|
3185
|
+
'sharing.sidebar.hubRunning':'服务运行中',
|
|
3186
|
+
'sharing.sidebar.teamName':'团队',
|
|
3187
|
+
'sharing.sidebar.members':'成员',
|
|
3188
|
+
'sharing.sidebar.online':'在线',
|
|
3162
3189
|
'sharing.sidebar.pending':'等待审核',
|
|
3163
3190
|
'sharing.sidebar.rejected':'已拒绝',
|
|
3164
3191
|
'sharing.sidebar.starting':'启动中...',
|
|
@@ -3175,6 +3202,11 @@ const I18N={
|
|
|
3175
3202
|
'sharing.retryJoin':'重新申请',
|
|
3176
3203
|
'sharing.retryJoin.hint':'清除本地连接数据并重新提交加入申请',
|
|
3177
3204
|
'sharing.retryJoin.confirm':'这将清除当前连接数据并重新提交加入申请,是否继续?',
|
|
3205
|
+
'sharing.leaveTeam':'退出团队',
|
|
3206
|
+
'sharing.leaveTeam.confirm':'你即将退出团队「{team}」。\\n\\n退出后将会:\\n\\u2022 断开与团队服务器的连接\\n\\u2022 团队管理员会收到你退出的通知\\n\\u2022 你将无法再接收团队共享的记忆、任务和技能\\n\\u2022 你的本地数据不受影响,会完整保留\\n\\u2022 之后可以重新申请加入(需管理员审批)\\n\\n确定要退出吗?',
|
|
3207
|
+
'sharing.leaveTeam.success':'你已退出团队,团队共享已关闭。',
|
|
3208
|
+
'sharing.leaveTeam.fail':'退出团队失败',
|
|
3209
|
+
'sharing.team.default':'该团队',
|
|
3178
3210
|
'sharing.retryJoin.success':'加入申请已重新提交,请等待管理员审核。',
|
|
3179
3211
|
'sharing.retryJoin.fail':'重新申请失败',
|
|
3180
3212
|
'sharing.ownerRemoved':'(已移除)',
|
|
@@ -3228,6 +3260,7 @@ const I18N={
|
|
|
3228
3260
|
'admin.editName':'编辑名称',
|
|
3229
3261
|
'admin.lastAdminHint':'唯一管理员 — 无法删除或降级',
|
|
3230
3262
|
'admin.ownerHint':'Hub 创建者 — 不可降级或移除',
|
|
3263
|
+
'admin.selfHint':'这是你自己',
|
|
3231
3264
|
'admin.editNamePrompt':'请输入新用户名:',
|
|
3232
3265
|
'confirm.promoteAdmin':'确定要将此用户提升为管理员吗?管理员可以管理所有团队成员和资源。',
|
|
3233
3266
|
'confirm.demoteMember':'确定要将此管理员降为普通成员吗?',
|
|
@@ -3289,6 +3322,8 @@ const I18N={
|
|
|
3289
3322
|
'toast.userApproved':'用户已批准',
|
|
3290
3323
|
'sharing.approved.toast':'您的加入申请已通过审核!',
|
|
3291
3324
|
'sharing.rejected.toast':'您的加入申请已被管理员拒绝。',
|
|
3325
|
+
'sharing.hubOffline.toast':'团队服务已离线,恢复后将自动重新连接。',
|
|
3326
|
+
'sharing.hubReconnected.toast':'团队服务已恢复上线,连接已自动恢复!',
|
|
3292
3327
|
'toast.userRejected':'用户已拒绝',
|
|
3293
3328
|
'toast.approveFail':'批准失败',
|
|
3294
3329
|
'toast.rejectFail':'拒绝失败',
|
|
@@ -3448,9 +3483,10 @@ const I18N={
|
|
|
3448
3483
|
'update.dismiss':'关闭',
|
|
3449
3484
|
'sharing.disable.confirm.hub':'你即将关闭团队服务。\\n\\n关闭后将会:\\n\\u2022 所有已连接的团队成员将断开连接\\n\\u2022 他们将无法继续同步记忆、任务和技能\\n\\u2022 已共享的数据会保留,重新开启后仍可使用\\n\\n确定要关闭吗?',
|
|
3450
3485
|
'sharing.disable.confirm.client':'你即将断开与团队的连接。\\n\\n断开后将会:\\n\\u2022 你将无法再接收团队共享的记忆、任务和技能\\n\\u2022 你的本地数据不受影响,会完整保留\\n\\u2022 之后可以随时重新开启共享来恢复连接\\n\\n确定要断开吗?',
|
|
3451
|
-
'sharing.disable.restartAlert':'
|
|
3452
|
-
'sharing.switch.hubToClient':'你即将从服务端模式切换为客户端模式。\\n\\n切换后将会:\\n\\u2022 Hub
|
|
3453
|
-
'sharing.switch.clientToHub':'你即将从客户端模式切换为服务端模式。\\n\\n切换后将会:\\n\\u2022 你将断开与当前团队的连接\\n\\u2022
|
|
3486
|
+
'sharing.disable.restartAlert':'共享已关闭,服务将自动重启以应用更改。',
|
|
3487
|
+
'sharing.switch.hubToClient':'你即将从服务端模式切换为客户端模式。\\n\\n切换后将会:\\n\\u2022 Hub 服务将在服务重启后关闭\\n\\u2022 所有已连接的团队成员将断开连接\\n\\u2022 Hub 上的共享数据会保留,以后可恢复使用\\n\\u2022 你将作为客户端加入指定的远程团队\\n\\n确定要切换吗?',
|
|
3488
|
+
'sharing.switch.clientToHub':'你即将从客户端模式切换为服务端模式。\\n\\n切换后将会:\\n\\u2022 你将断开与当前团队的连接\\n\\u2022 服务重启后将启动新的 Hub 服务\\n\\u2022 你的本地数据不受影响\\n\\n确定要切换吗?',
|
|
3489
|
+
'sharing.switch.hubAddress':'你即将离开当前团队并加入新的团队。\\n\\n操作后将会:\\n\\u2022 你将断开与当前团队服务器的连接\\n\\u2022 当前团队管理员会收到你离开的通知\\n\\u2022 你将作为新成员加入新的团队服务器\\n\\u2022 你的本地数据不受影响\\n\\n确定要切换吗?',
|
|
3454
3490
|
'admin.notEnabled.title':'团队共享尚未开启',
|
|
3455
3491
|
'admin.notEnabled.desc':'管理面板用于管理团队成员、共享的记忆、任务和技能。使用此功能前,需要先开启团队共享。',
|
|
3456
3492
|
'admin.notEnabled.setupHub':'配置为团队服务端',
|
|
@@ -3468,12 +3504,12 @@ const I18N={
|
|
|
3468
3504
|
'guide.join.s1':'向团队管理员索取服务器地址和团队令牌',
|
|
3469
3505
|
'guide.join.s2':'前往「设置 → 团队共享」,开启共享,选择「客户端」模式',
|
|
3470
3506
|
'guide.join.s3':'填写服务器地址和团队令牌,点击「测试连接」',
|
|
3471
|
-
'guide.join.s4':'
|
|
3507
|
+
'guide.join.s4':'点击「保存并应用」,服务将自动重启(页面会自动刷新)',
|
|
3472
3508
|
'guide.join.btn':'\u2192 配置客户端模式',
|
|
3473
3509
|
'guide.hub.title':'自建团队服务',
|
|
3474
3510
|
'guide.hub.desc':'将本机作为团队服务端,让其他成员连接过来共享记忆。',
|
|
3475
3511
|
'guide.hub.s1':'前往「设置 → 团队共享」,开启共享,选择「服务端」模式',
|
|
3476
|
-
'guide.hub.s2':'
|
|
3512
|
+
'guide.hub.s2':'设置团队名称,点击「保存并应用」,服务将自动重启',
|
|
3477
3513
|
'guide.hub.s3':'将服务器地址和团队令牌分享给团队成员',
|
|
3478
3514
|
'guide.hub.s4':'在管理面板中审批加入请求',
|
|
3479
3515
|
'guide.hub.btn':'\u2192 配置服务端模式'
|
|
@@ -3579,12 +3615,28 @@ async function doReset(){
|
|
|
3579
3615
|
}
|
|
3580
3616
|
|
|
3581
3617
|
var _sharingRole='client';
|
|
3618
|
+
var _loadedClientHubAddress='';
|
|
3582
3619
|
function _genToken(len){
|
|
3583
3620
|
var a=new Uint8Array(len||18);crypto.getRandomValues(a);
|
|
3584
3621
|
return btoa(String.fromCharCode.apply(null,a)).replace(/\\+/g,'-').replace(/\\//g,'_').replace(/=+$/,'');
|
|
3585
3622
|
}
|
|
3586
|
-
function onSharingToggle(){
|
|
3587
|
-
var
|
|
3623
|
+
async function onSharingToggle(){
|
|
3624
|
+
var chk=document.getElementById('cfgSharingEnabled');
|
|
3625
|
+
var on=chk.checked;
|
|
3626
|
+
if(!on && sharingStatusCache && sharingStatusCache.enabled){
|
|
3627
|
+
var prevRole=sharingStatusCache.role;
|
|
3628
|
+
var confirmMsg=prevRole==='hub'?t('sharing.disable.confirm.hub'):t('sharing.disable.confirm.client');
|
|
3629
|
+
if(!(await confirmModal(confirmMsg,{danger:true}))){
|
|
3630
|
+
chk.checked=true;
|
|
3631
|
+
return;
|
|
3632
|
+
}
|
|
3633
|
+
var cfg={sharing:{enabled:false,role:prevRole}};
|
|
3634
|
+
chk.disabled=true;
|
|
3635
|
+
var result=await doSaveConfig(cfg, null, 'hubSaved');
|
|
3636
|
+
chk.disabled=false;
|
|
3637
|
+
if(!result){chk.checked=true;return;}
|
|
3638
|
+
return;
|
|
3639
|
+
}
|
|
3588
3640
|
document.getElementById('sharingConfigPanel').style.display=on?'block':'none';
|
|
3589
3641
|
var pw=document.getElementById('sharingPanelsWrap');
|
|
3590
3642
|
if(pw) pw.style.display=on?'':'none';
|
|
@@ -3655,13 +3707,6 @@ async function testHubConnection(){
|
|
|
3655
3707
|
if(!addr){result.innerHTML='<span style="color:var(--rose)">\u274C '+t('settings.hub.test.noAddr')+'</span>';return;}
|
|
3656
3708
|
btn.disabled=true;result.innerHTML=t('settings.hub.test.testing');
|
|
3657
3709
|
try{
|
|
3658
|
-
var ipsData=await fetch('/api/local-ips').then(function(r){return r.json();});
|
|
3659
|
-
var localAddrs=['127.0.0.1','localhost','0.0.0.0'].concat(ipsData.ips||[]);
|
|
3660
|
-
var parsed=new URL(addr.indexOf('://')>-1?addr:'http://'+addr);
|
|
3661
|
-
if(localAddrs.indexOf(parsed.hostname)>=0){
|
|
3662
|
-
result.innerHTML='<span style="color:var(--rose)">\u274C '+t('sharing.cannotJoinSelf')+'</span>';
|
|
3663
|
-
btn.disabled=false;return;
|
|
3664
|
-
}
|
|
3665
3710
|
}catch(e){}
|
|
3666
3711
|
try{
|
|
3667
3712
|
var url=addr.match(/^https?:\\/\\//)?addr:'http://'+addr;
|
|
@@ -3784,16 +3829,26 @@ async function loadSharingStatus(forcePending){
|
|
|
3784
3829
|
var curStatus=conn.rejected?'rejected':conn.pendingApproval?'pending':conn.connected?'connected':'none';
|
|
3785
3830
|
var hubActive=d.role==='hub'||curStatus==='connected';
|
|
3786
3831
|
_updateScopeSelectorsVisibility(hubActive);
|
|
3787
|
-
if(_lastSharingConnStatus==='pending'&&curStatus==='rejected'){
|
|
3832
|
+
if(_lastSharingConnStatus==='pending'&&curStatus==='rejected'&&d.role==='client'){
|
|
3788
3833
|
toast(t('sharing.rejected.toast'),'error');
|
|
3789
3834
|
}
|
|
3790
|
-
if(_lastSharingConnStatus==='pending'&&curStatus==='connected'){
|
|
3835
|
+
if(_lastSharingConnStatus==='pending'&&curStatus==='connected'&&d.role==='client'){
|
|
3791
3836
|
toast(t('sharing.approved.toast'),'success');
|
|
3792
3837
|
loadMemories();loadTasks();loadSkills();
|
|
3793
3838
|
if(_notifSSE){_notifSSE.close();_notifSSE=null;_notifSSEConnected=false;}
|
|
3794
3839
|
connectNotifSSE();
|
|
3795
3840
|
loadNotifications();
|
|
3796
3841
|
}
|
|
3842
|
+
if(_lastSharingConnStatus==='connected'&&curStatus==='none'&&d.role==='client'){
|
|
3843
|
+
toast(t('sharing.hubOffline.toast'),'error');
|
|
3844
|
+
}
|
|
3845
|
+
if(_lastSharingConnStatus==='none'&&curStatus==='connected'&&d.role==='client'){
|
|
3846
|
+
toast(t('sharing.hubReconnected.toast'),'success');
|
|
3847
|
+
loadMemories();loadTasks();loadSkills();
|
|
3848
|
+
if(_notifSSE){_notifSSE.close();_notifSSE=null;_notifSSEConnected=false;}
|
|
3849
|
+
connectNotifSSE();
|
|
3850
|
+
loadNotifications();
|
|
3851
|
+
}
|
|
3797
3852
|
_lastSharingConnStatus=curStatus;
|
|
3798
3853
|
if(curStatus==='pending'&&!_clientPendingPollTimer){
|
|
3799
3854
|
_clientPendingPollTimer=setInterval(function(){loadSharingStatus(false);},5000);
|
|
@@ -3818,7 +3873,8 @@ function renderSharingSidebar(data){
|
|
|
3818
3873
|
var badgeEl=document.getElementById('sharingSidebarConnBadge');
|
|
3819
3874
|
if(!statusEl||!hintEl) return;
|
|
3820
3875
|
var conn=data&&data.connection||{};
|
|
3821
|
-
var
|
|
3876
|
+
var hs=data&&data.hubStats||{};
|
|
3877
|
+
var fp=JSON.stringify({e:!!data&&!!data.enabled,r:data&&data.role,pa:!!conn.pendingApproval,rj:!!conn.rejected,c:!!conn.connected,u:conn.user&&conn.user.username,tn:conn.teamName,cc:!!data&&!!data.clientConfigured,hu:data&&data.hubUrl,tm:hs.totalMembers,om:hs.onlineMembers,pm:hs.pendingMembers});
|
|
3822
3878
|
if(fp===_lastSidebarFingerprint) return;
|
|
3823
3879
|
_lastSidebarFingerprint=fp;
|
|
3824
3880
|
if(!data||!data.enabled){
|
|
@@ -3833,8 +3889,16 @@ function renderSharingSidebar(data){
|
|
|
3833
3889
|
badgeEl.innerHTML='<span style="display:inline-flex;align-items:center;gap:4px;font-size:10px;font-weight:600;padding:2px 8px;border-radius:9999px;background:'+color+'15;color:'+color+'"><span style="display:inline-block;width:6px;height:6px;border-radius:50%;background:'+color+(glow?';box-shadow:0 0 4px '+color:'')+'"></span>'+esc(text)+'</span>';
|
|
3834
3890
|
}
|
|
3835
3891
|
if(data.role==='hub'){
|
|
3836
|
-
setBadge('#34d399',t('sharing.sidebar.
|
|
3837
|
-
|
|
3892
|
+
setBadge('#34d399',t('sharing.sidebar.hubRunning'),true);
|
|
3893
|
+
var hs=data.hubStats||{};
|
|
3894
|
+
var html='<div class="info-grid">';
|
|
3895
|
+
if(conn.teamName) html+='<span class="label">'+t('sharing.sidebar.teamName')+'</span><span class="value" style="font-weight:600">'+esc(conn.teamName)+'</span>';
|
|
3896
|
+
html+='<span class="label">'+t('sharing.sidebar.members')+'</span><span class="value">'+(hs.totalMembers||0)+' <span style="opacity:.5;font-size:10px">/ '+t('sharing.sidebar.online')+' '+(hs.onlineMembers||0)+'</span></span>';
|
|
3897
|
+
if(hs.pendingMembers>0){
|
|
3898
|
+
html+='<span class="label">'+t('sharing.sidebar.pending')+'</span><span class="value" style="color:var(--yellow,#fbbf24);font-weight:600">'+hs.pendingMembers+'</span>';
|
|
3899
|
+
}
|
|
3900
|
+
html+='</div>';
|
|
3901
|
+
statusEl.innerHTML=html;
|
|
3838
3902
|
hintEl.textContent='';
|
|
3839
3903
|
}else if(conn.pendingApproval&&conn.user){
|
|
3840
3904
|
setBadge('#fbbf24',t('sharing.sidebar.pending'),false);
|
|
@@ -3905,7 +3969,6 @@ function renderSharingSettings(data){
|
|
|
3905
3969
|
var conn=data.connection||{};
|
|
3906
3970
|
var user=conn.user||{};
|
|
3907
3971
|
var actualRole=data.role||_sharingRole||'client';
|
|
3908
|
-
if(data.role) _sharingRole=data.role;
|
|
3909
3972
|
var prevIsAdmin=!!window._isHubAdmin;
|
|
3910
3973
|
var isAdmin=(data.admin&&data.admin.canManageUsers)||(conn.connected&&user.role==='admin')||(actualRole==='hub');
|
|
3911
3974
|
window._isHubAdmin=isAdmin;
|
|
@@ -3969,7 +4032,10 @@ function renderSharingSettings(data){
|
|
|
3969
4032
|
'<button class="btn btn-sm" onclick="updateHubUsername()" style="padding:2px 10px;font-size:11px">'+t('sharing.saveUsername')+'</button>'+
|
|
3970
4033
|
'</span>';
|
|
3971
4034
|
sh+='<span class="hic-label">'+t('sharing.team')+'</span><span class="hic-value">'+esc(conn.teamName||'-')+'</span>';
|
|
3972
|
-
sh+='</div
|
|
4035
|
+
sh+='</div>'+
|
|
4036
|
+
'<div style="border-top:1px solid var(--border);margin-top:10px;padding:10px 16px 6px;display:flex;align-items:center;justify-content:flex-end">'+
|
|
4037
|
+
'<button class="btn btn-sm" onclick="leaveTeam()" style="color:#ef4444;border-color:rgba(239,68,68,.3);font-size:11px;padding:4px 12px">'+t('sharing.leaveTeam')+'</button>'+
|
|
4038
|
+
'</div></div>';
|
|
3973
4039
|
}else{
|
|
3974
4040
|
sh+='</div><div class="hic-empty" style="color:var(--text-muted)">'+t('sharing.disconnected.hint')+'</div>'+
|
|
3975
4041
|
'<div style="margin-top:10px;padding:0 16px 14px"><button class="btn btn-sm btn-primary" id="btnRetryConn" onclick="retryConnection()">'+t('sharing.retryConnection')+'</button>'+
|
|
@@ -4021,6 +4087,22 @@ async function retryHubJoin(){
|
|
|
4021
4087
|
}catch(e){toast(t('sharing.retryJoin.fail')+': '+e.message,'error');}
|
|
4022
4088
|
}
|
|
4023
4089
|
|
|
4090
|
+
async function leaveTeam(){
|
|
4091
|
+
var teamName=(sharingStatusCache&&sharingStatusCache.connection&&sharingStatusCache.connection.teamName)||'';
|
|
4092
|
+
var msg=t('sharing.leaveTeam.confirm').replace('{team}',teamName||t('sharing.team.default'));
|
|
4093
|
+
if(!(await confirmModal(msg,{danger:true}))) return;
|
|
4094
|
+
try{
|
|
4095
|
+
var r=await fetch('/api/sharing/leave',{method:'POST',headers:{'Content-Type':'application/json'},body:'{}'});
|
|
4096
|
+
var d=await r.json();
|
|
4097
|
+
if(d.ok){
|
|
4098
|
+
toast(t('sharing.leaveTeam.success'),'success');
|
|
4099
|
+
showRestartOverlay(t('settings.restart.waiting'));
|
|
4100
|
+
}else{
|
|
4101
|
+
toast(d.error||t('sharing.leaveTeam.fail'),'error');
|
|
4102
|
+
}
|
|
4103
|
+
}catch(e){toast(t('sharing.leaveTeam.fail')+': '+e.message,'error');}
|
|
4104
|
+
}
|
|
4105
|
+
|
|
4024
4106
|
async function updateHubUsername(){
|
|
4025
4107
|
var input=document.getElementById('hubUsernameInput');
|
|
4026
4108
|
if(!input) return;
|
|
@@ -4109,8 +4191,7 @@ async function rejectSharingUser(userId,username){
|
|
|
4109
4191
|
function updateTeamGuide(sharingData){
|
|
4110
4192
|
var el=document.getElementById('teamSetupGuide');
|
|
4111
4193
|
if(!el) return;
|
|
4112
|
-
|
|
4113
|
-
el.style.display=isConfigured?'none':'block';
|
|
4194
|
+
el.style.display='block';
|
|
4114
4195
|
}
|
|
4115
4196
|
function guideGoToHub(role){
|
|
4116
4197
|
switchSettingsTab('hub',document.querySelector('.settings-tab-btn[data-tab="hub"]'));
|
|
@@ -4310,9 +4391,10 @@ function auRelativeTime(ts){
|
|
|
4310
4391
|
return t('notif.timeAgo.day').replace('{n}',Math.floor(diff/86400000));
|
|
4311
4392
|
}
|
|
4312
4393
|
|
|
4313
|
-
function renderAdminUserCard(u,adminCount){
|
|
4394
|
+
function renderAdminUserCard(u,adminCount,myUserId){
|
|
4314
4395
|
var uid=escAttr(u.id);
|
|
4315
4396
|
var uname=escAttr(u.username||'');
|
|
4397
|
+
var isSelf=!!(myUserId&&u.id===myUserId);
|
|
4316
4398
|
var online=!!u.isOnline;
|
|
4317
4399
|
var statusCls=online?'online':'offline';
|
|
4318
4400
|
|
|
@@ -4346,7 +4428,9 @@ function renderAdminUserCard(u,adminCount){
|
|
|
4346
4428
|
var infoHtml='<div class="au-info">'+infoRows.join('')+'</div>';
|
|
4347
4429
|
|
|
4348
4430
|
var actions='';
|
|
4349
|
-
if(
|
|
4431
|
+
if(isSelf){
|
|
4432
|
+
actions+='<span style="font-size:11px;color:var(--text-muted);padding:4px 0">'+t('admin.selfHint')+'</span>';
|
|
4433
|
+
}else if(u.isOwner){
|
|
4350
4434
|
actions+='<span style="font-size:11px;color:var(--text-muted);padding:4px 0">'+t('admin.ownerHint')+'</span>';
|
|
4351
4435
|
}else if(u.role!=='admin'){
|
|
4352
4436
|
actions+='<button class="btn btn-sm btn-ghost" onclick="adminToggleRole("'+uid+'","admin")" style="color:var(--accent)">'+t('admin.promoteAdmin')+'</button>';
|
|
@@ -4398,6 +4482,7 @@ function renderAdminUsers(users,pending){
|
|
|
4398
4482
|
offlineUsers.sort(function(a,b){return (b.lastActiveAt||0)-(a.lastActiveAt||0);});
|
|
4399
4483
|
var sorted=onlineUsers.concat(offlineUsers);
|
|
4400
4484
|
var adminCount=users.filter(function(x){return x.role==='admin';}).length;
|
|
4485
|
+
var myUserId=sharingStatusCache&&sharingStatusCache.connection&&sharingStatusCache.connection.user?sharingStatusCache.connection.user.id:null;
|
|
4401
4486
|
|
|
4402
4487
|
if(sorted.length===0){
|
|
4403
4488
|
html+='<div class="admin-empty"><span class="ae-icon">\u{1F465}</span>'+t('admin.noActiveUsers')+'</div>';
|
|
@@ -4406,13 +4491,13 @@ function renderAdminUsers(users,pending){
|
|
|
4406
4491
|
if(onlineUsers.length===0){
|
|
4407
4492
|
html+='<div style="font-size:12px;color:var(--text-muted);padding:8px 0 12px">\u2014</div>';
|
|
4408
4493
|
}else{
|
|
4409
|
-
for(var i=0;i<onlineUsers.length;i++) html+=renderAdminUserCard(onlineUsers[i],adminCount);
|
|
4494
|
+
for(var i=0;i<onlineUsers.length;i++) html+=renderAdminUserCard(onlineUsers[i],adminCount,myUserId);
|
|
4410
4495
|
}
|
|
4411
4496
|
html+='<div class="au-group-header"><span class="au-group-dot offline"></span>'+t('admin.offlineUsers')+' <span class="au-group-count">('+offlineUsers.length+')</span></div>';
|
|
4412
4497
|
if(offlineUsers.length===0){
|
|
4413
4498
|
html+='<div style="font-size:12px;color:var(--text-muted);padding:8px 0 12px">\u2014</div>';
|
|
4414
4499
|
}else{
|
|
4415
|
-
for(var j=0;j<offlineUsers.length;j++) html+=renderAdminUserCard(offlineUsers[j],adminCount);
|
|
4500
|
+
for(var j=0;j<offlineUsers.length;j++) html+=renderAdminUserCard(offlineUsers[j],adminCount,myUserId);
|
|
4416
4501
|
}
|
|
4417
4502
|
}
|
|
4418
4503
|
el.innerHTML=html;
|
|
@@ -6582,6 +6667,7 @@ async function loadConfig(){
|
|
|
6582
6667
|
document.getElementById('cfgHubTeamName').value=hub.teamName||'';
|
|
6583
6668
|
document.getElementById('cfgHubTeamToken').value=hub.teamToken||'';
|
|
6584
6669
|
document.getElementById('cfgClientHubAddress').value=client.hubAddress||'';
|
|
6670
|
+
_loadedClientHubAddress=client.hubAddress||'';
|
|
6585
6671
|
document.getElementById('cfgClientTeamToken').value=client.teamToken||'';
|
|
6586
6672
|
document.getElementById('cfgClientNickname').value=client.nickname||'';
|
|
6587
6673
|
document.getElementById('cfgClientUserToken').value=client.userToken||'';
|
|
@@ -6627,15 +6713,19 @@ function flashSaved(id){
|
|
|
6627
6713
|
}
|
|
6628
6714
|
|
|
6629
6715
|
async function doSaveConfig(cfg, btnEl, savedId){
|
|
6630
|
-
btnEl.disabled=true;btnEl.textContent=t('settings.test.loading');
|
|
6631
|
-
function done(){btnEl.disabled=false;btnEl.textContent=t('settings.save');}
|
|
6716
|
+
if(btnEl){btnEl.disabled=true;btnEl.textContent=t('settings.test.loading');}
|
|
6717
|
+
function done(){if(btnEl){btnEl.disabled=false;btnEl.textContent=t('settings.save');}}
|
|
6632
6718
|
try{
|
|
6633
6719
|
const r=await fetch('/api/config',{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify(cfg)});
|
|
6634
6720
|
if(r.status===401){done();toast(t('settings.session.expired'),'error');return null;}
|
|
6635
6721
|
if(!r.ok) throw new Error(await r.text());
|
|
6636
6722
|
var data=await r.json().catch(function(){return {ok:true};});
|
|
6637
6723
|
flashSaved(savedId);
|
|
6638
|
-
|
|
6724
|
+
if(data&&data.restart){
|
|
6725
|
+
showRestartOverlay(t('settings.restart.waiting'));
|
|
6726
|
+
}else{
|
|
6727
|
+
done();
|
|
6728
|
+
}
|
|
6639
6729
|
return data;
|
|
6640
6730
|
}catch(e){
|
|
6641
6731
|
toast(t('settings.save.fail')+': '+e.message,'error');
|
|
@@ -6761,7 +6851,7 @@ async function saveHubConfig(){
|
|
|
6761
6851
|
var hubTeamName=document.getElementById('cfgHubTeamName').value.trim();
|
|
6762
6852
|
var hubTeamToken=document.getElementById('cfgHubTeamToken').value.trim();
|
|
6763
6853
|
var hubAdminName=document.getElementById('cfgHubAdminName').value.trim();
|
|
6764
|
-
cfg.sharing.hub={port:
|
|
6854
|
+
if(hubPort) cfg.sharing.hub={port:Number(hubPort)}; else cfg.sharing.hub={};
|
|
6765
6855
|
if(hubTeamName) cfg.sharing.hub.teamName=hubTeamName;
|
|
6766
6856
|
if(hubTeamToken) cfg.sharing.hub.teamToken=hubTeamToken;
|
|
6767
6857
|
cfg.sharing.client={hubAddress:'',userToken:'',teamToken:''};
|
|
@@ -6778,15 +6868,9 @@ async function saveHubConfig(){
|
|
|
6778
6868
|
if(clientNickname) cfg.sharing.client.nickname=clientNickname;
|
|
6779
6869
|
if(clientTeamToken) cfg.sharing.client.teamToken=clientTeamToken;
|
|
6780
6870
|
if(clientUserToken) cfg.sharing.client.userToken=clientUserToken;
|
|
6781
|
-
cfg.sharing.hub={
|
|
6871
|
+
cfg.sharing.hub={teamName:'',teamToken:''};
|
|
6782
6872
|
if(clientAddr){
|
|
6783
6873
|
try{
|
|
6784
|
-
var ips=await fetch('/api/local-ips').then(function(r){return r.json();});
|
|
6785
|
-
var localAddrs=['127.0.0.1','localhost','0.0.0.0'].concat(ips.ips||[]);
|
|
6786
|
-
var parsed=new URL(clientAddr.indexOf('://')>-1?clientAddr:'http://'+clientAddr);
|
|
6787
|
-
if(localAddrs.indexOf(parsed.hostname)>=0){
|
|
6788
|
-
done();toast(t('sharing.cannotJoinSelf'),'error');return;
|
|
6789
|
-
}
|
|
6790
6874
|
}catch(e){}
|
|
6791
6875
|
try{
|
|
6792
6876
|
var testUrl=clientAddr.indexOf('://')>-1?clientAddr:'http://'+clientAddr;
|
|
@@ -6813,6 +6897,12 @@ async function saveHubConfig(){
|
|
|
6813
6897
|
var switchMsg=prevRole==='hub'?t('sharing.switch.hubToClient'):t('sharing.switch.clientToHub');
|
|
6814
6898
|
if(!(await confirmModal(switchMsg,{danger:true}))){done();return;}
|
|
6815
6899
|
}
|
|
6900
|
+
if(prevSharingEnabled&&sharingEnabled&&prevRole==='client'&&_sharingRole==='client'){
|
|
6901
|
+
var newAddr=(document.getElementById('cfgClientHubAddress').value||'').trim();
|
|
6902
|
+
if(_loadedClientHubAddress&&newAddr&&newAddr!==_loadedClientHubAddress){
|
|
6903
|
+
if(!(await confirmModal(t('sharing.switch.hubAddress'),{danger:true}))){done();return;}
|
|
6904
|
+
}
|
|
6905
|
+
}
|
|
6816
6906
|
|
|
6817
6907
|
var result=await doSaveConfig(cfg, saveBtn, 'hubSaved');
|
|
6818
6908
|
if(result){
|
|
@@ -7381,7 +7471,12 @@ function notifTimeAgo(ts){
|
|
|
7381
7471
|
function notifIcon(resource,type){
|
|
7382
7472
|
if(type==='user_online') return '\\u{1F7E2}';
|
|
7383
7473
|
if(type==='user_offline') return '\\u{1F534}';
|
|
7474
|
+
if(type==='user_left') return '\\u{1F6AA}';
|
|
7384
7475
|
if(type==='user_join_request') return '\\u{1F464}';
|
|
7476
|
+
if(type==='membership_removed') return '\\u{26D4}';
|
|
7477
|
+
if(type==='hub_shutdown') return '\\u{1F6D1}';
|
|
7478
|
+
if(type==='role_promoted') return '\\u{2B06}';
|
|
7479
|
+
if(type==='role_demoted') return '\\u{2B07}';
|
|
7385
7480
|
if(resource==='memory') return '\\u{1F4DD}';
|
|
7386
7481
|
if(resource==='task') return '\\u{1F4CB}';
|
|
7387
7482
|
if(resource==='skill') return '\\u{1F9E0}';
|
|
@@ -7407,12 +7502,27 @@ function notifTypeText(n){
|
|
|
7407
7502
|
if(n.type==='user_offline'){
|
|
7408
7503
|
return t('notif.userOffline');
|
|
7409
7504
|
}
|
|
7505
|
+
if(n.type==='user_left'){
|
|
7506
|
+
return t('notif.userLeft');
|
|
7507
|
+
}
|
|
7410
7508
|
if(n.type==='membership_approved'){
|
|
7411
7509
|
return t('notif.membershipApproved');
|
|
7412
7510
|
}
|
|
7413
7511
|
if(n.type==='membership_rejected'){
|
|
7414
7512
|
return t('notif.membershipRejected');
|
|
7415
7513
|
}
|
|
7514
|
+
if(n.type==='membership_removed'){
|
|
7515
|
+
return t('notif.membershipRemoved');
|
|
7516
|
+
}
|
|
7517
|
+
if(n.type==='hub_shutdown'){
|
|
7518
|
+
return t('notif.hubShutdown');
|
|
7519
|
+
}
|
|
7520
|
+
if(n.type==='role_promoted'){
|
|
7521
|
+
return t('notif.rolePromoted');
|
|
7522
|
+
}
|
|
7523
|
+
if(n.type==='role_demoted'){
|
|
7524
|
+
return t('notif.roleDemoted');
|
|
7525
|
+
}
|
|
7416
7526
|
return n.message||n.type;
|
|
7417
7527
|
}
|
|
7418
7528
|
|
|
@@ -7447,6 +7557,21 @@ function renderNotifBadge(){
|
|
|
7447
7557
|
}
|
|
7448
7558
|
}
|
|
7449
7559
|
|
|
7560
|
+
var _notifKnownTypes={membership_approved:1,membership_rejected:1,membership_removed:1,hub_shutdown:1,user_left:1,user_online:1,user_offline:1,user_join_request:1,role_promoted:1,role_demoted:1,resource_removed:1,resource_shared:1,resource_unshared:1};
|
|
7561
|
+
function notifDisplayTitle(n){
|
|
7562
|
+
if(_notifKnownTypes[n.type]) return notifTypeText(n);
|
|
7563
|
+
return n.title||notifTypeText(n);
|
|
7564
|
+
}
|
|
7565
|
+
function notifDisplayDetail(n){
|
|
7566
|
+
if(_notifKnownTypes[n.type]){
|
|
7567
|
+
if(n.type==='resource_removed'||n.type==='resource_shared'||n.type==='resource_unshared') return n.title||'';
|
|
7568
|
+
var m=n.title&&n.title.match(/["\u201C]([^"\u201D]+)["\u201D]/);
|
|
7569
|
+
if(m) return m[1];
|
|
7570
|
+
if(n.type==='user_left'||n.type==='user_online'||n.type==='user_offline'||n.type==='user_join_request') return n.title||'';
|
|
7571
|
+
return '';
|
|
7572
|
+
}
|
|
7573
|
+
return n.title||'';
|
|
7574
|
+
}
|
|
7450
7575
|
function renderNotifPanel(){
|
|
7451
7576
|
var body=document.getElementById('notifPanelBody');
|
|
7452
7577
|
if(!body) return;
|
|
@@ -7456,11 +7581,12 @@ function renderNotifPanel(){
|
|
|
7456
7581
|
}
|
|
7457
7582
|
body.innerHTML=_notifCache.map(function(n){
|
|
7458
7583
|
var cls='notif-item'+(n.read?'':' unread');
|
|
7584
|
+
var detail=notifDisplayDetail(n);
|
|
7459
7585
|
return '<div class="'+cls+'" onclick="markNotifRead("'+esc(n.id)+'")">'+
|
|
7460
7586
|
'<div class="notif-item-icon">'+notifIcon(n.resource,n.type)+'</div>'+
|
|
7461
7587
|
'<div class="notif-item-body">'+
|
|
7462
|
-
'<div class="notif-item-title">'+esc(
|
|
7463
|
-
'<div class="notif-item-name">'+esc(
|
|
7588
|
+
'<div class="notif-item-title">'+esc(notifDisplayTitle(n))+'</div>'+
|
|
7589
|
+
(detail?'<div class="notif-item-name">'+esc(detail)+'</div>':'')+
|
|
7464
7590
|
'<div class="notif-item-time">'+notifTimeAgo(n.createdAt)+'</div>'+
|
|
7465
7591
|
'</div>'+
|
|
7466
7592
|
'<div class="notif-item-dot"></div>'+
|
|
@@ -8746,7 +8872,7 @@ function confirmModal(message,opts){
|
|
|
8746
8872
|
_confirmResolve=resolve;
|
|
8747
8873
|
var overlay=document.getElementById('confirmOverlay');
|
|
8748
8874
|
document.getElementById('confirmTitle').textContent=opts.title||t('confirm.title')||'\u786E\u8BA4';
|
|
8749
|
-
document.getElementById('confirmBody').
|
|
8875
|
+
document.getElementById('confirmBody').innerText=message||'';
|
|
8750
8876
|
var okBtn=document.getElementById('confirmOkBtn');
|
|
8751
8877
|
okBtn.textContent=opts.okText||t('confirm.ok')||'\u786E\u5B9A';
|
|
8752
8878
|
okBtn.className='btn-confirm-ok'+(opts.danger?' danger':'');
|
|
@@ -8787,14 +8913,35 @@ function showRestartOverlay(msg){
|
|
|
8787
8913
|
/* ─── Update check ─── */
|
|
8788
8914
|
function waitForGatewayAndReload(maxAttempts,attempt){
|
|
8789
8915
|
attempt=attempt||0;
|
|
8916
|
+
var phase=arguments.length>2?arguments[2]:'waitDown';
|
|
8917
|
+
var MAX_WAIT_DOWN=8;
|
|
8790
8918
|
function forceReload(){window.location.href=window.location.pathname+'?_t='+Date.now();}
|
|
8791
8919
|
if(attempt>=maxAttempts){forceReload();return;}
|
|
8920
|
+
var delay=phase==='waitDown'?1500:2500;
|
|
8792
8921
|
setTimeout(function(){
|
|
8793
8922
|
fetch('/api/auth/status').then(function(r){
|
|
8794
|
-
if(
|
|
8795
|
-
|
|
8796
|
-
|
|
8797
|
-
|
|
8923
|
+
if(phase==='waitDown'){
|
|
8924
|
+
if(r.ok||r.status===401||r.status===403){
|
|
8925
|
+
if(attempt>=MAX_WAIT_DOWN){
|
|
8926
|
+
forceReload();
|
|
8927
|
+
}else{
|
|
8928
|
+
waitForGatewayAndReload(maxAttempts,attempt+1,'waitDown');
|
|
8929
|
+
}
|
|
8930
|
+
}else{
|
|
8931
|
+
waitForGatewayAndReload(maxAttempts,0,'waitUp');
|
|
8932
|
+
}
|
|
8933
|
+
}else{
|
|
8934
|
+
if(r.ok||r.status===401||r.status===403) forceReload();
|
|
8935
|
+
else waitForGatewayAndReload(maxAttempts,attempt+1,'waitUp');
|
|
8936
|
+
}
|
|
8937
|
+
}).catch(function(){
|
|
8938
|
+
if(phase==='waitDown'){
|
|
8939
|
+
waitForGatewayAndReload(maxAttempts,0,'waitUp');
|
|
8940
|
+
}else{
|
|
8941
|
+
waitForGatewayAndReload(maxAttempts,attempt+1,'waitUp');
|
|
8942
|
+
}
|
|
8943
|
+
});
|
|
8944
|
+
},delay);
|
|
8798
8945
|
}
|
|
8799
8946
|
function doUpdateInstall(packageSpec,btnEl,statusEl){
|
|
8800
8947
|
btnEl.disabled=true;
|