refine i18n, fill in miss parts
fix resetTrust cant recover send button in UI
This commit is contained in:
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@@ -9,7 +9,7 @@
|
|||||||
"type": "go",
|
"type": "go",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"mode": "auto",
|
"mode": "auto",
|
||||||
"program": "${workspaceFolder}/main.go",
|
"program": "${workspaceFolder}",
|
||||||
"preLaunchTask": "build frontend"
|
"preLaunchTask": "build frontend"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Cr
|
|||||||
// @ts-ignore: Unused imports
|
// @ts-ignore: Unused imports
|
||||||
import * as $models from "./models.js";
|
import * as $models from "./models.js";
|
||||||
|
|
||||||
export function AddTrustedPeer(peerID: string, publicKey: string): $CancellablePromise<void> {
|
export function AddTrust(peerID: string, publicKey: string): $CancellablePromise<void> {
|
||||||
return $Call.ByID(2866399505, peerID, publicKey);
|
return $Call.ByID(2986105628, peerID, publicKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function GetAutoAccept(): $CancellablePromise<boolean> {
|
export function GetAutoAccept(): $CancellablePromise<boolean> {
|
||||||
@@ -41,8 +41,8 @@ export function GetSavePath(): $CancellablePromise<string> {
|
|||||||
return $Call.ByID(4081533263);
|
return $Call.ByID(4081533263);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function GetTrustedPeer(): $CancellablePromise<{ [_: string]: string }> {
|
export function GetTrusted(): $CancellablePromise<{ [_: string]: string }> {
|
||||||
return $Call.ByID(1253442080).then(($result: any) => {
|
return $Call.ByID(800326956).then(($result: any) => {
|
||||||
return $$createType0($result);
|
return $$createType0($result);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -57,12 +57,12 @@ export function GetWindowState(): $CancellablePromise<$models.WindowState> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function IsTrustedPeer(peerID: string): $CancellablePromise<boolean> {
|
export function IsTrusted(peerID: string): $CancellablePromise<boolean> {
|
||||||
return $Call.ByID(3452062706, peerID);
|
return $Call.ByID(1255607538, peerID);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function RemoveTrustedPeer(peerID: string): $CancellablePromise<void> {
|
export function RemoveTrust(peerID: string): $CancellablePromise<void> {
|
||||||
return $Call.ByID(909233322, peerID);
|
return $Call.ByID(732981195, peerID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -15,15 +15,15 @@ import {
|
|||||||
} from "../../bindings/mesh-drop/internal/transfer/service";
|
} from "../../bindings/mesh-drop/internal/transfer/service";
|
||||||
import { Peer } from "../../bindings/mesh-drop/internal/discovery/models";
|
import { Peer } from "../../bindings/mesh-drop/internal/discovery/models";
|
||||||
import {
|
import {
|
||||||
IsTrustedPeer,
|
IsTrusted,
|
||||||
AddTrustedPeer,
|
AddTrust,
|
||||||
RemoveTrustedPeer,
|
RemoveTrust,
|
||||||
} from "../../bindings/mesh-drop/internal/config/config";
|
} from "../../bindings/mesh-drop/internal/config/config";
|
||||||
|
|
||||||
// --- 生命周期 ---
|
// --- 生命周期 ---
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
try {
|
try {
|
||||||
isTrusted.value = await IsTrustedPeer(props.peer.id);
|
isTrusted.value = await IsTrusted(props.peer.id);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Failed to check trusted peer status:", err);
|
console.error("Failed to check trusted peer status:", err);
|
||||||
}
|
}
|
||||||
@@ -136,7 +136,7 @@ const handleSendFolder = async () => {
|
|||||||
|
|
||||||
SendFolder(props.peer, selectedIp.value, folderPath as string).catch((e) => {
|
SendFolder(props.peer, selectedIp.value, folderPath as string).catch((e) => {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
alert("Failed to send folder: " + e);
|
alert(t("discover.sendFolderFailed", { error: e }));
|
||||||
});
|
});
|
||||||
emit("transferStarted");
|
emit("transferStarted");
|
||||||
};
|
};
|
||||||
@@ -150,19 +150,21 @@ const handleSendClipboard = async () => {
|
|||||||
}
|
}
|
||||||
SendText(props.peer, selectedIp.value, text).catch((e) => {
|
SendText(props.peer, selectedIp.value, text).catch((e) => {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
alert("Failed to send clipboard: " + e);
|
alert(t("discover.sendClipboardFailed", { error: e }));
|
||||||
});
|
});
|
||||||
emit("transferStarted");
|
emit("transferStarted");
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTrust = () => {
|
const handleTrust = () => {
|
||||||
AddTrustedPeer(props.peer.id, props.peer.pk);
|
AddTrust(props.peer.id, props.peer.pk);
|
||||||
isTrusted.value = true;
|
isTrusted.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUntrust = () => {
|
const handleUntrust = () => {
|
||||||
RemoveTrustedPeer(props.peer.id);
|
RemoveTrust(props.peer.id);
|
||||||
isTrusted.value = false;
|
isTrusted.value = false;
|
||||||
|
|
||||||
|
props.peer.trust_mismatch = false;
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -226,9 +228,9 @@ const handleUntrust = () => {
|
|||||||
variant="tonal"
|
variant="tonal"
|
||||||
prepend-icon="mdi-alert"
|
prepend-icon="mdi-alert"
|
||||||
:ripple="false"
|
:ripple="false"
|
||||||
style="pointer-events: none"
|
style="pointer-events: none; min-width: 0"
|
||||||
>
|
>
|
||||||
{{ t("discover.mismatch") }}
|
<span class="text-truncate">{{ t("discover.mismatch") }}</span>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
|
||||||
<v-menu v-else>
|
<v-menu v-else>
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ onMounted(async () => {
|
|||||||
// --- 方法 ---
|
// --- 方法 ---
|
||||||
const changeSavePath = async () => {
|
const changeSavePath = async () => {
|
||||||
const opts: Dialogs.OpenFileDialogOptions = {
|
const opts: Dialogs.OpenFileDialogOptions = {
|
||||||
Title: "Select Save Path",
|
Title: t("settings.selectSavePath"),
|
||||||
CanChooseDirectories: true,
|
CanChooseDirectories: true,
|
||||||
CanChooseFiles: false,
|
CanChooseFiles: false,
|
||||||
AllowsMultipleSelection: false,
|
AllowsMultipleSelection: false,
|
||||||
|
|||||||
@@ -7,7 +7,10 @@
|
|||||||
"edit": "Edit",
|
"edit": "Edit",
|
||||||
"loading": "Loading...",
|
"loading": "Loading...",
|
||||||
"success": "Success",
|
"success": "Success",
|
||||||
"error": "Error"
|
"error": "Error",
|
||||||
|
"accept": "Accept",
|
||||||
|
"reject": "Reject",
|
||||||
|
"copy": "Copy"
|
||||||
},
|
},
|
||||||
"menu": {
|
"menu": {
|
||||||
"discover": "Discover",
|
"discover": "Discover",
|
||||||
@@ -17,7 +20,20 @@
|
|||||||
"discover": {
|
"discover": {
|
||||||
"scanning": "Scanning for peers...",
|
"scanning": "Scanning for peers...",
|
||||||
"noPeers": "No peers found",
|
"noPeers": "No peers found",
|
||||||
"send": "Send"
|
"send": "Send",
|
||||||
|
"sendFiles": "Send Files",
|
||||||
|
"sendFolder": "Send Folder",
|
||||||
|
"sendText": "Send Text",
|
||||||
|
"sendClipboard": "Send Clipboard",
|
||||||
|
"selectFolder": "Select Folder",
|
||||||
|
"clipboardEmpty": "Clipboard is empty",
|
||||||
|
"noRoute": "No Route",
|
||||||
|
"mismatch": "Trust Mismatch",
|
||||||
|
"resetTrust": "Reset Trust",
|
||||||
|
"trustPeer": "Trust Peer",
|
||||||
|
"untrustPeer": "Untrust Peer",
|
||||||
|
"sendFolderFailed": "Failed to send folder: {error}",
|
||||||
|
"sendClipboardFailed": "Failed to send clipboard: {error}"
|
||||||
},
|
},
|
||||||
"transfers": {
|
"transfers": {
|
||||||
"noTransfers": "No transfers yet",
|
"noTransfers": "No transfers yet",
|
||||||
@@ -26,7 +42,16 @@
|
|||||||
"transferring": "Transferring",
|
"transferring": "Transferring",
|
||||||
"completed": "Completed",
|
"completed": "Completed",
|
||||||
"failed": "Failed",
|
"failed": "Failed",
|
||||||
"cancelled": "Cancelled"
|
"cancelled": "Cancelled",
|
||||||
|
"selectSavePath": "Select Save Path",
|
||||||
|
"text": "Text",
|
||||||
|
"folder": "Folder",
|
||||||
|
"securityAlert": "Security Alert",
|
||||||
|
"rejected": "Rejected",
|
||||||
|
"waitingForAccept": "Waiting for accept",
|
||||||
|
"saveToFolder": "Save to Folder",
|
||||||
|
"viewContent": "View Content",
|
||||||
|
"textContent": "Text Content"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"savePath": "Save Path",
|
"savePath": "Save Path",
|
||||||
@@ -35,6 +60,7 @@
|
|||||||
"saveHistory": "Save History",
|
"saveHistory": "Save History",
|
||||||
"autoAccept": "Auto Accept",
|
"autoAccept": "Auto Accept",
|
||||||
"version": "Version",
|
"version": "Version",
|
||||||
"language": "Language"
|
"language": "Language",
|
||||||
|
"selectSavePath": "Select Save Path"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,10 @@
|
|||||||
"edit": "编辑",
|
"edit": "编辑",
|
||||||
"loading": "加载中...",
|
"loading": "加载中...",
|
||||||
"success": "成功",
|
"success": "成功",
|
||||||
"error": "错误"
|
"error": "错误",
|
||||||
|
"accept": "接收",
|
||||||
|
"reject": "拒绝",
|
||||||
|
"copy": "复制"
|
||||||
},
|
},
|
||||||
"menu": {
|
"menu": {
|
||||||
"discover": "发现",
|
"discover": "发现",
|
||||||
@@ -17,7 +20,20 @@
|
|||||||
"discover": {
|
"discover": {
|
||||||
"scanning": "正在扫描设备...",
|
"scanning": "正在扫描设备...",
|
||||||
"noPeers": "未发现设备",
|
"noPeers": "未发现设备",
|
||||||
"send": "发送"
|
"send": "发送",
|
||||||
|
"sendFiles": "发送文件",
|
||||||
|
"sendFolder": "发送文件夹",
|
||||||
|
"sendText": "发送文本",
|
||||||
|
"sendClipboard": "发送剪贴板",
|
||||||
|
"selectFolder": "选择文件夹",
|
||||||
|
"clipboardEmpty": "剪贴板为空",
|
||||||
|
"noRoute": "不可达",
|
||||||
|
"mismatch": "信任不匹配",
|
||||||
|
"resetTrust": "重置信任",
|
||||||
|
"trustPeer": "信任设备",
|
||||||
|
"untrustPeer": "取消信任",
|
||||||
|
"sendFolderFailed": "发送文件夹失败: {error}",
|
||||||
|
"sendClipboardFailed": "发送剪贴板失败: {error}"
|
||||||
},
|
},
|
||||||
"transfers": {
|
"transfers": {
|
||||||
"noTransfers": "暂无传输记录",
|
"noTransfers": "暂无传输记录",
|
||||||
@@ -26,7 +42,16 @@
|
|||||||
"transferring": "传输中",
|
"transferring": "传输中",
|
||||||
"completed": "已完成",
|
"completed": "已完成",
|
||||||
"failed": "失败",
|
"failed": "失败",
|
||||||
"cancelled": "已取消"
|
"cancelled": "已取消",
|
||||||
|
"selectSavePath": "选择保存路径",
|
||||||
|
"text": "文本",
|
||||||
|
"folder": "文件夹",
|
||||||
|
"securityAlert": "安全警告",
|
||||||
|
"rejected": "已拒绝",
|
||||||
|
"waitingForAccept": "等待接收",
|
||||||
|
"saveToFolder": "保存到文件夹",
|
||||||
|
"viewContent": "查看内容",
|
||||||
|
"textContent": "文本内容"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"savePath": "保存路径",
|
"savePath": "保存路径",
|
||||||
@@ -35,6 +60,7 @@
|
|||||||
"saveHistory": "保存历史记录",
|
"saveHistory": "保存历史记录",
|
||||||
"autoAccept": "自动接收",
|
"autoAccept": "自动接收",
|
||||||
"version": "版本",
|
"version": "版本",
|
||||||
"language": "语言"
|
"language": "语言",
|
||||||
|
"selectSavePath": "选择保存路径"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -254,7 +254,7 @@ func (c *Config) GetWindowState() WindowState {
|
|||||||
return c.WindowState
|
return c.WindowState
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) AddTrustedPeer(peerID string, publicKey string) {
|
func (c *Config) AddTrust(peerID string, publicKey string) {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
if c.TrustedPeer == nil {
|
if c.TrustedPeer == nil {
|
||||||
@@ -265,13 +265,13 @@ func (c *Config) AddTrustedPeer(peerID string, publicKey string) {
|
|||||||
_ = c.save()
|
_ = c.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) GetTrustedPeer() map[string]string {
|
func (c *Config) GetTrusted() map[string]string {
|
||||||
c.mu.RLock()
|
c.mu.RLock()
|
||||||
defer c.mu.RUnlock()
|
defer c.mu.RUnlock()
|
||||||
return c.TrustedPeer
|
return c.TrustedPeer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) RemoveTrustedPeer(peerID string) {
|
func (c *Config) RemoveTrust(peerID string) {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
delete(c.TrustedPeer, peerID)
|
delete(c.TrustedPeer, peerID)
|
||||||
@@ -279,7 +279,7 @@ func (c *Config) RemoveTrustedPeer(peerID string) {
|
|||||||
_ = c.save()
|
_ = c.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) IsTrustedPeer(peerID string) bool {
|
func (c *Config) IsTrusted(peerID string) bool {
|
||||||
c.mu.RLock()
|
c.mu.RLock()
|
||||||
defer c.mu.RUnlock()
|
defer c.mu.RUnlock()
|
||||||
_, exists := c.TrustedPeer[peerID]
|
_, exists := c.TrustedPeer[peerID]
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ func (s *Service) startListening() {
|
|||||||
|
|
||||||
// 验证身份一致性 (防止 ID 欺骗)
|
// 验证身份一致性 (防止 ID 欺骗)
|
||||||
trustMismatch := false
|
trustMismatch := false
|
||||||
trustedKeys := s.config.GetTrustedPeer()
|
trustedKeys := s.config.GetTrusted()
|
||||||
if knownKey, ok := trustedKeys[packet.ID]; ok {
|
if knownKey, ok := trustedKeys[packet.ID]; ok {
|
||||||
if knownKey != packet.PublicKey {
|
if knownKey != packet.PublicKey {
|
||||||
slog.Warn("SECURITY ALERT: Peer ID mismatch with known public key (Spoofing attempt?)", "id", packet.ID, "known_key", knownKey, "received_key", packet.PublicKey)
|
slog.Warn("SECURITY ALERT: Peer ID mismatch with known public key (Spoofing attempt?)", "id", packet.ID, "known_key", knownKey, "received_key", packet.PublicKey)
|
||||||
@@ -236,6 +236,13 @@ func (s *Service) startListening() {
|
|||||||
// 当发现 ID 欺骗时,不更新 peer,而是标记为 trustMismatch
|
// 当发现 ID 欺骗时,不更新 peer,而是标记为 trustMismatch
|
||||||
// 用户可以手动重新添加信任
|
// 用户可以手动重新添加信任
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// 不存在于信任列表
|
||||||
|
// 存在之前在信任列表,但是不匹配被用户手动重置了,此时需要将 peer.TrustMismatch 标记为 false
|
||||||
|
// 否则在 handleHeartbeat 里会一直标记为不匹配
|
||||||
|
if peer, ok := s.peers[packet.ID]; ok {
|
||||||
|
peer.TrustMismatch = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s.handleHeartbeat(packet, remoteAddr.IP.String(), trustMismatch)
|
s.handleHeartbeat(packet, remoteAddr.IP.String(), trustMismatch)
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ func (s *Service) handleAsk(c *gin.Context) {
|
|||||||
task.Sender.TrustMismatch = peer.TrustMismatch
|
task.Sender.TrustMismatch = peer.TrustMismatch
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.config.GetAutoAccept() || (s.config.IsTrustedPeer(task.Sender.ID) && !task.Sender.TrustMismatch) {
|
if s.config.GetAutoAccept() || (s.config.IsTrusted(task.Sender.ID) && !task.Sender.TrustMismatch) {
|
||||||
task.DecisionChan <- Decision{
|
task.DecisionChan <- Decision{
|
||||||
ID: task.ID,
|
ID: task.ID,
|
||||||
Accepted: true,
|
Accepted: true,
|
||||||
|
|||||||
7
main.go
7
main.go
@@ -81,6 +81,9 @@ func main() {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 设置系统托盘
|
||||||
|
setupSystray(app, window)
|
||||||
|
|
||||||
// 窗口文件拖拽事件
|
// 窗口文件拖拽事件
|
||||||
window.OnWindowEvent(events.Common.WindowFilesDropped, func(event *application.WindowEvent) {
|
window.OnWindowEvent(events.Common.WindowFilesDropped, func(event *application.WindowEvent) {
|
||||||
files := event.Context().DroppedFiles()
|
files := event.Context().DroppedFiles()
|
||||||
@@ -91,10 +94,6 @@ func main() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// 窗口关闭事件
|
|
||||||
// window.OnWindowEvent(events.Common.WindowClosing, func(event *application.WindowEvent) {
|
|
||||||
// })
|
|
||||||
|
|
||||||
// 应用关闭事件
|
// 应用关闭事件
|
||||||
app.OnShutdown(func() {
|
app.OnShutdown(func() {
|
||||||
x, y := window.Position()
|
x, y := window.Position()
|
||||||
|
|||||||
7
systray.go
Normal file
7
systray.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "github.com/wailsapp/wails/v3/pkg/application"
|
||||||
|
|
||||||
|
func setupSystray(app *application.App, window *application.WebviewWindow) {}
|
||||||
35
systray_windows.go
Normal file
35
systray_windows.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/wailsapp/wails/v3/pkg/application"
|
||||||
|
"github.com/wailsapp/wails/v3/pkg/events"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setupSystray(app *application.App, window *application.WebviewWindow) {
|
||||||
|
systray := app.SystemTray.New()
|
||||||
|
systray.SetIcon(icon)
|
||||||
|
systray.SetLabel("Mesh Drop")
|
||||||
|
|
||||||
|
menu := app.NewMenu()
|
||||||
|
menu.Add("Quit").OnClick(func(ctx *application.Context) {
|
||||||
|
app.Quit()
|
||||||
|
})
|
||||||
|
|
||||||
|
systray.OnClick(func() {
|
||||||
|
if window.IsVisible() {
|
||||||
|
window.Hide()
|
||||||
|
} else {
|
||||||
|
window.Show()
|
||||||
|
window.Focus()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
systray.SetMenu(menu)
|
||||||
|
|
||||||
|
window.OnWindowEvent(events.Common.WindowClosing, func(event *application.WindowEvent) {
|
||||||
|
event.Cancel()
|
||||||
|
window.Hide()
|
||||||
|
})
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user