diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index d783639..58e924a 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -4,10 +4,13 @@
-
+
+
+
-
-
+
+
+
@@ -26,13 +29,13 @@
-
+
{
"customColor": "",
@@ -43,32 +46,32 @@
- {
- "keyToString": {
- "DefaultGoTemplateProperty": "Go File",
- "Go 构建.Backend.executor": "Run",
- "RunOnceActivity.ShowReadmeOnStart": "true",
- "RunOnceActivity.go.formatter.settings.were.checked": "true",
- "RunOnceActivity.go.migrated.go.modules.settings": "true",
- "RunOnceActivity.go.modules.automatic.dependencies.download": "true",
- "RunOnceActivity.go.modules.go.list.on.any.changes.was.set": "true",
- "git-widget-placeholder": "master",
- "go.import.settings.migrated": "true",
- "go.sdk.automatically.set": "true",
- "last_opened_file_path": "/Users/littlesheep/Documents/Projects/Hydrogen/Passport/pkg/server/ui",
- "node.js.detected.package.eslint": "true",
- "node.js.selected.package.eslint": "(autodetect)",
- "nodejs_package_manager_path": "npm",
- "run.code.analysis.last.selected.profile": "pProject Default",
- "settings.editor.selected.configurable": "preferences.lookFeel",
- "vue.rearranger.settings.migration": "true"
+
+}]]>
@@ -122,7 +125,19 @@
@@ -152,7 +167,8 @@
-
+
+
true
diff --git a/pkg/cmd/main.go b/pkg/cmd/main.go
index f451f18..ac2eec5 100644
--- a/pkg/cmd/main.go
+++ b/pkg/cmd/main.go
@@ -72,6 +72,7 @@ func main() {
quartz.AddFunc("@every 60m", services.DoAutoSignoff)
quartz.AddFunc("@every 60m", services.DoAutoAuthCleanup)
quartz.AddFunc("@every 60m", services.DoAutoDatabaseCleanup)
+ quartz.AddFunc("@every 5m", services.KexCleanup)
quartz.Start()
// Messages
diff --git a/pkg/models/unified.go b/pkg/models/unified.go
new file mode 100644
index 0000000..bea3048
--- /dev/null
+++ b/pkg/models/unified.go
@@ -0,0 +1,21 @@
+package models
+
+import jsoniter "github.com/json-iterator/go"
+
+type UnifiedCommand struct {
+ Action string `json:"w"`
+ Message string `json:"m"`
+ Payload any `json:"p"`
+}
+
+func UnifiedCommandFromError(err error) UnifiedCommand {
+ return UnifiedCommand{
+ Action: "error",
+ Message: err.Error(),
+ }
+}
+
+func (v UnifiedCommand) Marshal() []byte {
+ data, _ := jsoniter.Marshal(v)
+ return data
+}
diff --git a/pkg/server/notify_ws.go b/pkg/server/notify_ws.go
deleted file mode 100644
index 2c1615f..0000000
--- a/pkg/server/notify_ws.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package server
-
-import (
- "git.solsynth.dev/hydrogen/passport/pkg/models"
- "git.solsynth.dev/hydrogen/passport/pkg/services"
- "github.com/gofiber/contrib/websocket"
-)
-
-func listenNotifications(c *websocket.Conn) {
- user := c.Locals("principal").(models.Account)
-
- // Push connection
- services.ClientRegister(user, c)
-
- // Event loop
- var err error
- for {
- if _, _, err = c.ReadMessage(); err != nil {
- break
- }
- }
-
- // Pop connection
- services.ClientUnregister(user, c)
-}
diff --git a/pkg/server/startup.go b/pkg/server/startup.go
index 1b9a766..d119d19 100644
--- a/pkg/server/startup.go
+++ b/pkg/server/startup.go
@@ -1,13 +1,13 @@
package server
import (
+ "github.com/gofiber/contrib/websocket"
"net/http"
"strings"
"git.solsynth.dev/hydrogen/passport/pkg"
"git.solsynth.dev/hydrogen/passport/pkg/i18n"
"git.solsynth.dev/hydrogen/passport/pkg/server/ui"
- "github.com/gofiber/contrib/websocket"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/fiber/v2/middleware/favicon"
@@ -74,8 +74,6 @@ func NewServer() {
notify.Post("/subscribe", authMiddleware, addNotifySubscriber)
notify.Put("/batch/read", authMiddleware, markNotificationReadBatch)
notify.Put("/:notificationId/read", authMiddleware, markNotificationRead)
-
- notify.Get("/listen", authMiddleware, websocket.New(listenNotifications))
}
me := api.Group("/users/me").Name("Myself Operations")
@@ -136,6 +134,8 @@ func NewServer() {
{
developers.Post("/notify", notifyUser)
}
+
+ api.Get("/ws", authMiddleware, websocket.New(listenWebsocket))
}
A.Use(favicon.New(favicon.Config{
diff --git a/pkg/server/ws.go b/pkg/server/ws.go
new file mode 100644
index 0000000..2800136
--- /dev/null
+++ b/pkg/server/ws.go
@@ -0,0 +1,77 @@
+package server
+
+import (
+ "fmt"
+ "git.solsynth.dev/hydrogen/passport/pkg/models"
+ "git.solsynth.dev/hydrogen/passport/pkg/services"
+ "github.com/gofiber/contrib/websocket"
+ jsoniter "github.com/json-iterator/go"
+ "github.com/samber/lo"
+)
+
+func listenWebsocket(c *websocket.Conn) {
+ user := c.Locals("principal").(models.Account)
+
+ // Push connection
+ services.ClientRegister(user, c)
+
+ // Event loop
+ var task models.UnifiedCommand
+
+ var messageType int
+ var payload []byte
+ var packet []byte
+ var err error
+
+ for {
+ if messageType, packet, err = c.ReadMessage(); err != nil {
+ break
+ } else if err := jsoniter.Unmarshal(packet, &task); err != nil {
+ _ = c.WriteMessage(messageType, models.UnifiedCommand{
+ Action: "error",
+ Message: "unable to unmarshal your command, requires json request",
+ }.Marshal())
+ continue
+ } else {
+ payload, _ = jsoniter.Marshal(task.Payload)
+ }
+
+ var message *models.UnifiedCommand
+ switch task.Action {
+ case "kex.request":
+ var req struct {
+ RequestID string `json:"request_id"`
+ KeypairID string `json:"keypair_id"`
+ OwnerID uint `json:"owner_id"`
+ Deadline int64 `json:"deadline"`
+ }
+ _ = jsoniter.Unmarshal(payload, &req)
+ if len(req.RequestID) <= 0 || len(req.KeypairID) <= 0 || req.OwnerID <= 0 {
+ message = lo.ToPtr(models.UnifiedCommandFromError(fmt.Errorf("invalid request")))
+ }
+ services.KexRequest(c, req.RequestID, req.KeypairID, user.ID, req.Deadline)
+ case "kex.provide":
+ var req struct {
+ RequestID string `json:"request_id"`
+ KeypairID string `json:"keypair_id"`
+ PublicKey []byte `json:"public_key"`
+ }
+ _ = jsoniter.Unmarshal(payload, &req)
+ if len(req.RequestID) <= 0 || len(req.KeypairID) <= 0 {
+ message = lo.ToPtr(models.UnifiedCommandFromError(fmt.Errorf("invalid request")))
+ }
+ services.KexProvide(user.ID, req.RequestID, req.KeypairID, payload)
+ default:
+ message = lo.ToPtr(models.UnifiedCommandFromError(fmt.Errorf("unknown action")))
+ }
+
+ if message != nil {
+ if err = c.WriteMessage(messageType, message.Marshal()); err != nil {
+ break
+ }
+ }
+ }
+
+ // Pop connection
+ services.ClientUnregister(user, c)
+}
diff --git a/pkg/services/e2ee.go b/pkg/services/e2ee.go
new file mode 100644
index 0000000..34aaf40
--- /dev/null
+++ b/pkg/services/e2ee.go
@@ -0,0 +1,77 @@
+package services
+
+import (
+ "git.solsynth.dev/hydrogen/passport/pkg/models"
+ "github.com/gofiber/contrib/websocket"
+ "github.com/gofiber/fiber/v2"
+ "time"
+)
+
+type kexRequest struct {
+ OwnerID uint
+ Conn *websocket.Conn
+ Deadline time.Time
+}
+
+var kexRequests = make(map[string]map[string]kexRequest)
+
+func KexRequest(conn *websocket.Conn, requestId, keypairId string, ownerId uint, deadline int64) {
+ if kexRequests[keypairId] == nil {
+ kexRequests[keypairId] = make(map[string]kexRequest)
+ }
+
+ ddl := time.Now().Add(time.Second * time.Duration(deadline))
+ request := kexRequest{
+ OwnerID: ownerId,
+ Conn: conn,
+ Deadline: ddl,
+ }
+
+ flag := false
+ for conn := range wsConn[ownerId] {
+ if conn.WriteMessage(1, models.UnifiedCommand{
+ Action: "kex.request",
+ Payload: fiber.Map{
+ "request_id": requestId,
+ "keypair_id": keypairId,
+ "owner_id": ownerId,
+ "deadline": deadline,
+ },
+ }.Marshal()) == nil {
+ flag = true
+ }
+ }
+
+ if flag {
+ kexRequests[keypairId][requestId] = request
+ }
+}
+
+func KexProvide(userId uint, requestId, keypairId string, pkt []byte) {
+ if kexRequests[keypairId] == nil {
+ return
+ }
+
+ val, ok := kexRequests[keypairId][requestId]
+ if !ok {
+ return
+ } else if val.OwnerID != userId {
+ return
+ } else {
+ _ = val.Conn.WriteMessage(1, pkt)
+ }
+}
+
+func KexCleanup() {
+ if len(kexRequests) <= 0 {
+ return
+ }
+
+ for kp, data := range kexRequests {
+ for idx, req := range data {
+ if req.Deadline.Unix() <= time.Now().Unix() {
+ delete(kexRequests[kp], idx)
+ }
+ }
+ }
+}
diff --git a/pkg/services/notifications.go b/pkg/services/notifications.go
index 8f9de11..2a7d7d3 100644
--- a/pkg/services/notifications.go
+++ b/pkg/services/notifications.go
@@ -60,7 +60,10 @@ func NewNotification(notification models.Notification) error {
func PushNotification(notification models.Notification) error {
raw, _ := jsoniter.Marshal(notification)
for conn := range wsConn[notification.RecipientID] {
- _ = conn.WriteMessage(1, raw)
+ _ = conn.WriteMessage(1, models.UnifiedCommand{
+ Action: "notifications.new",
+ Payload: raw,
+ }.Marshal())
}
var subscribers []models.NotificationSubscriber