diff --git a/internal/handlers/devices.go b/internal/handlers/devices.go index b736518..f62d45e 100644 --- a/internal/handlers/devices.go +++ b/internal/handlers/devices.go @@ -100,6 +100,10 @@ type PendingRegistration struct { UserID uuid.UUID ExpiresAt time.Time CreatedAt time.Time + Approved bool + AuthToken string + DeviceID [16]byte + SyncEndpoints map[string]string } var pendingRegistrations = make(map[string]*PendingRegistration) @@ -173,60 +177,21 @@ func (h *DeviceHandler) CheckRegistrationStatus(c *echo.Context) error { return c.JSON(http.StatusGone, map[string]string{"error": "registration expired"}) } - if registration.UserID == (uuid.UUID{}) { + if registration.Approved { + delete(pendingRegistrations, req.RegistrationID) + return c.JSON(http.StatusOK, DeviceAuthStatusResponse{ - Status: "pending", - Message: "awaiting user approval", - ExpiresIn: int(time.Until(registration.ExpiresAt).Seconds()), + Status: "approved", + AuthToken: registration.AuthToken, + DeviceID: registration.DeviceID, + SyncEndpoints: registration.SyncEndpoints, }) } - authToken, err := generateDeviceToken() - if err != nil { - return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to generate auth token"}) - } - - userUUID := registration.UserID - pgUserID := pgtype.UUID{Bytes: [16]byte(userUUID), Valid: true} - - syncEnabled := pgtype.Bool{Bool: true, Valid: true} - autoSync := pgtype.Bool{Bool: true, Valid: true} - syncFreq := pgtype.Int4{Int32: 5, Valid: true} - - device, err := h.db.CreateDevice(c.Request().Context(), database.CreateDeviceParams{ - UserID: pgUserID, - DeviceName: registration.DeviceName, - DeviceType: registration.DeviceType, - DeviceIdentifier: registration.DeviceIdentifier, - AuthToken: authToken, - SyncEnabled: syncEnabled, - AutoSync: autoSync, - SyncFrequencyMinutes: syncFreq, - DeviceMetadata: []byte("{}"), - }) - - if err != nil { - return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to create device"}) - } - - delete(pendingRegistrations, req.RegistrationID) - - syncEndpoints := map[string]string{} - switch registration.DeviceType { - case "koreader": - syncEndpoints["progress"] = fmt.Sprintf("%s/api/sync/koreader/progress", h.cfg.BaseURL) - syncEndpoints["metadata"] = fmt.Sprintf("%s/api/sync/koreader/metadata", h.cfg.BaseURL) - syncEndpoints["bookmarks"] = fmt.Sprintf("%s/api/sync/koreader/bookmarks", h.cfg.BaseURL) - case "kobo": - syncEndpoints["markup"] = fmt.Sprintf("%s/api/sync/kobo/markup", h.cfg.BaseURL) - syncEndpoints["library"] = fmt.Sprintf("%s/api/sync/kobo/library", h.cfg.BaseURL) - } - return c.JSON(http.StatusOK, DeviceAuthStatusResponse{ - Status: "approved", - AuthToken: authToken, - DeviceID: device.ID.Bytes, - SyncEndpoints: syncEndpoints, + Status: "pending", + Message: "awaiting user approval", + ExpiresIn: int(time.Until(registration.ExpiresAt).Seconds()), }) } @@ -597,14 +562,63 @@ func (h *DeviceHandler) ApproveDevice(c *echo.Context) error { return c.JSON(http.StatusGone, map[string]string{"error": "registration expired"}) } + if registration.Approved { + return c.JSON(http.StatusOK, map[string]interface{}{ + "message": "device already approved", + "device_name": registration.DeviceName, + "device_type": registration.DeviceType, + "approved": true, + }) + } + + authToken, err := generateDeviceToken() + if err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to generate auth token"}) + } + + pgUserID := pgtype.UUID{Bytes: [16]byte(userUUID), Valid: true} + syncEnabled := pgtype.Bool{Bool: true, Valid: true} + autoSync := pgtype.Bool{Bool: true, Valid: true} + syncFreq := pgtype.Int4{Int32: 5, Valid: true} + + device, err := h.db.CreateDevice(c.Request().Context(), database.CreateDeviceParams{ + UserID: pgUserID, + DeviceName: registration.DeviceName, + DeviceType: registration.DeviceType, + DeviceIdentifier: registration.DeviceIdentifier, + AuthToken: authToken, + SyncEnabled: syncEnabled, + AutoSync: autoSync, + SyncFrequencyMinutes: syncFreq, + DeviceMetadata: []byte("{}"), + }) + if err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to create device"}) + } + + syncEndpoints := map[string]string{} + switch registration.DeviceType { + case "koreader": + syncEndpoints["progress"] = fmt.Sprintf("%s/api/sync/koreader/progress", h.cfg.BaseURL) + syncEndpoints["metadata"] = fmt.Sprintf("%s/api/sync/koreader/metadata", h.cfg.BaseURL) + syncEndpoints["bookmarks"] = fmt.Sprintf("%s/api/sync/koreader/bookmarks", h.cfg.BaseURL) + case "kobo": + syncEndpoints["markup"] = fmt.Sprintf("%s/api/sync/kobo/markup", h.cfg.BaseURL) + syncEndpoints["library"] = fmt.Sprintf("%s/api/sync/kobo/library", h.cfg.BaseURL) + } + registration.UserID = userUUID + registration.Approved = true + registration.AuthToken = authToken + registration.DeviceID = device.ID.Bytes + registration.SyncEndpoints = syncEndpoints return c.JSON(http.StatusOK, map[string]interface{}{ "message": "device approved successfully", "device_name": registration.DeviceName, "device_type": registration.DeviceType, "registration_id": registrationID, - "approved": true, // Fixed: Add confirmation field for test compatibility + "approved": true, }) } @@ -624,15 +638,9 @@ func (h *DeviceHandler) RejectDevice(c *echo.Context) error { } func (h *DeviceHandler) GetPendingRegistrationsData(c *echo.Context) ([]map[string]interface{}, error) { - userID := c.Get("user_id").(string) - userUUID, err := uuid.Parse(userID) - if err != nil { - return nil, err - } - registrations := []map[string]interface{}{} for _, reg := range pendingRegistrations { - if reg.UserID == userUUID || reg.UserID == (uuid.UUID{}) { + if reg.UserID == (uuid.UUID{}) { registrations = append(registrations, map[string]interface{}{ "registration_id": reg.RegistrationID, "device_name": reg.DeviceName, @@ -640,7 +648,7 @@ func (h *DeviceHandler) GetPendingRegistrationsData(c *echo.Context) ([]map[stri "device_identifier": reg.DeviceIdentifier, "expires_at": reg.ExpiresAt, "created_at": reg.CreatedAt, - "is_approved": reg.UserID != (uuid.UUID{}), + "is_approved": false, }) } }