prism/internal/signal/link.go

86 lines
1.8 KiB
Go

package signal
import (
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"time"
)
type LinkDevice struct {
client *Client
deviceName string
qrCode string
generatedAt time.Time
ttl time.Duration
}
func NewLinkDevice(client *Client, deviceName string) *LinkDevice {
return &LinkDevice{
client: client,
deviceName: deviceName,
ttl: 10 * time.Minute,
}
}
type StartLinkResponse struct {
DeviceLinkURI string `json:"deviceLinkUri"`
}
func (l *LinkDevice) GenerateQR() (string, error) {
if l.qrCode != "" && time.Since(l.generatedAt) < l.ttl {
return l.qrCode, nil
}
result, err := l.client.Call("startLink", nil)
if err != nil {
return "", fmt.Errorf("failed to start link: %w", err)
}
var response StartLinkResponse
if err := json.Unmarshal(result, &response); err != nil {
return "", fmt.Errorf("failed to parse link response: %w", err)
}
uri := response.DeviceLinkURI
if uri == "" {
return "", fmt.Errorf("empty device link URI")
}
qrURL := fmt.Sprintf("https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=%s", url.QueryEscape(uri))
//nolint:gosec
resp, err := http.Get(qrURL)
if err != nil {
return "", fmt.Errorf("failed to fetch QR code: %w", err)
}
defer resp.Body.Close()
qrData, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("failed to read QR code: %w", err)
}
base64Data := base64.StdEncoding.EncodeToString(qrData)
l.qrCode = fmt.Sprintf("data:image/png;base64,%s", base64Data)
l.generatedAt = time.Now()
go l.finishLink(uri)
return l.qrCode, nil
}
func (l *LinkDevice) finishLink(uri string) {
params := map[string]interface{}{
"deviceLinkUri": uri,
"deviceName": l.deviceName,
}
_, err := l.client.Call("finishLink", params)
if err == nil {
l.qrCode = ""
l.generatedAt = time.Time{}
}
}