f21fa5b5e8
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)。 本次为公开发布初始提交:完整开发历史(含部署细节)留存于私有归档,公开仓库自此干净起步。
106 lines
3.1 KiB
Go
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
|
|
}
|