Files
Commilitia-Drop/desktop/main.go
T
admin f21fa5b5e8 cdrop — 跨 OS 剪贴板与文件传输服务
Commilitia Drop:自托管的跨设备剪贴板同步与点对点文件传输。

- 后端 Go(chi / SQLite WAL / SSE Hub / WebRTC signaling + 状态机 / Relay ring buffer),编译进单个 distroless 镜像(前端 go:embed)。
- 前端 React + TanStack Router + Zustand,自实现 SSE + WebRTC P2P,NAT 受阻时回退服务端中继;聚珍(Juzhen)CJK 综合排版。
- 桌面端 Wails v2(macOS / Windows),瘦客户端复用 web。
- 鉴权 OIDC PKCE(自建 Casdoor 等),refresh_token 信封加密存系统密钥库;iOS Shortcut 用 HS256 scoped token。

架构文档与变更记录见 docs 分支(PROJECT_BRIEF / FRONTEND_DESIGN / CHANGELOG)。

本次为公开发布初始提交:完整开发历史(含部署细节)留存于私有归档,公开仓库自此干净起步。
2026-06-15 21:38:28 +08:00

106 lines
3.1 KiB
Go

package main
import (
"embed"
"os"
"runtime"
"github.com/wailsapp/wails/v2"
"github.com/wailsapp/wails/v2/pkg/options"
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
"cdrop-desktop/platform"
)
//go:embed all:frontend/dist
var assets embed.FS
func main() {
// Create an instance of the app structure
app := NewApp()
// Reverse-proxy /api/* to the backend so the embedded web UI can keep its
// same-origin relative URLs (see platform.NewAPIProxy). SSE streams through.
apiProxy, err := platform.NewAPIProxy(app.apiBase)
if err != nil {
println("Error:", err.Error())
return
}
// Windows: Wails' custom-scheme AssetServer buffers a handler's response until
// it returns, so the long-lived SSE never reaches the WebView (device list
// never loads, status stuck on "connecting"). Serve /api from a loopback HTTP
// origin instead — WebView2's normal network stack streams it — and point the
// page there via the injected api_base. macOS streams the custom scheme fine,
// so it keeps api_base="" (same-origin relative /api through apiProxy).
webAPIBase := ""
if runtime.GOOS == "windows" {
if base, lerr := platform.StartLocalAPIServer(app.apiBase); lerr == nil {
webAPIBase = base
} else {
println("local api server:", lerr.Error())
}
}
// Tell the backend this is a native desktop client, not a browser, so the
// device list shows the right kind. The page forwards it as X-Device-Type.
deviceType := ""
switch runtime.GOOS {
case "darwin":
deviceType = "macos"
case "windows":
deviceType = "windows"
case "linux":
deviceType = "linux"
}
// Restore the persisted login + device name by injecting them into index.html
// at load — the wails:// scheme has no persistent WebView storage (see
// session.go).
session, _ := platform.LoadSession()
// When the OS starts us at login, the autostart entry appends a --hidden flag
// (see SetLaunchAtLogin). Come up straight to the menu bar / tray with no
// window — the user didn't open the app, so don't steal focus. The frontend
// still loads, so clipboard sync runs in the background. A manual launch has
// no flag and shows the window as usual.
hidden := launchedHidden()
app.startHidden = hidden
// Create application with options
err = wails.Run(&options.App{
Title: "cdrop",
Width: 1024,
Height: 768,
StartHidden: hidden,
AssetServer: &assetserver.Options{
Assets: assets,
Handler: apiProxy,
Middleware: platform.SessionInjectMiddleware(session, platform.ResolveDeviceName(), webAPIBase, deviceType),
},
BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1},
Menu: app.buildMenu(),
OnStartup: app.startup,
OnBeforeClose: app.beforeClose,
Bind: []interface{}{
app,
},
})
if err != nil {
println("Error:", err.Error())
}
}
// launchedHidden reports whether this process was started by the login autostart
// entry, which appends a --hidden flag to the launch command (see
// SetLaunchAtLogin on each platform). Manual launches never carry it.
func launchedHidden() bool {
for _, arg := range os.Args[1:] {
if arg == "--hidden" {
return true
}
}
return false
}