Files
photoprism/internal/service/hub/feedback.go
2025-09-26 02:38:49 +02:00

118 lines
3.1 KiB
Go

package hub
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"runtime"
"time"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/pkg/service/http/header"
"github.com/photoprism/photoprism/pkg/txt"
)
// FeedbackURL is the service endpoint for submitting user feedback.
var FeedbackURL = ServiceURL + "/%s/feedback"
// Feedback represents user feedback submitted through the user interface.
type Feedback struct {
Category string `json:"Category"`
Subject string `json:"Subject"`
Message string `json:"Message"`
UserName string `json:"UserName"`
UserEmail string `json:"UserEmail"`
UserAgent string `json:"UserAgent"`
ApiKey string `json:"ApiKey"`
ClientVersion string `json:"ClientVersion"`
ClientSerial string `json:"ClientSerial"`
ClientOS string `json:"ClientOS"`
ClientArch string `json:"ClientArch"`
ClientCPU int `json:"ClientCPU"`
ClientEnv string `json:"ClientEnv"`
PartnerID string `json:"PartnerID"`
}
// NewFeedback creates a new hub feedback instance.
func NewFeedback(version, serial, env, partnerId string) *Feedback {
return &Feedback{
ClientVersion: version,
ClientSerial: serial,
ClientOS: runtime.GOOS,
ClientArch: runtime.GOARCH,
ClientCPU: runtime.NumCPU(),
ClientEnv: env,
PartnerID: partnerId,
}
}
// SendFeedback sends a feedback message.
func (c *Config) SendFeedback(frm form.Feedback) (err error) {
feedback := NewFeedback(c.Version, c.Serial, c.Env, c.PartnerID)
feedback.Category = frm.Category
feedback.Subject = txt.Shorten(frm.Message, 50, "...")
feedback.Message = frm.Message
feedback.UserName = frm.UserName
feedback.UserEmail = frm.UserEmail
feedback.UserAgent = frm.UserAgent
feedback.ApiKey = c.Key
// Create new http.Client instance.
//
// NOTE: Timeout specifies a time limit for requests made by
// this Client. The timeout includes connection time, any
// redirects, and reading the response body. The timer remains
// running after Get, Head, Post, or Do return and will
// interrupt reading of the Response.Body.
client := &http.Client{Timeout: 60 * time.Second}
endpointUrl := fmt.Sprintf(FeedbackURL, c.Key)
method := http.MethodPost
var req *http.Request
log.Debugf("sending feedback to %s", ApiHost())
if j, reqErr := json.Marshal(feedback); reqErr != nil {
return reqErr
} else if req, reqErr = http.NewRequest(method, endpointUrl, bytes.NewReader(j)); reqErr != nil {
return reqErr
}
// Set user agent.
if c.UserAgent != "" {
req.Header.Set(header.UserAgent, c.UserAgent)
} else {
req.Header.Set(header.UserAgent, "PhotoPrism/Test")
}
req.Header.Add(header.AcceptLanguage, frm.UserLocales)
req.Header.Add(header.ContentType, header.ContentTypeJson)
var r *http.Response
for i := 0; i < 3; i++ {
r, err = client.Do(req)
if err == nil {
break
}
}
if err != nil {
return err
} else if r.StatusCode >= 400 {
err = fmt.Errorf("sending feedback to %s failed (error %d)", ApiHost(), r.StatusCode)
return err
}
err = json.NewDecoder(r.Body).Decode(c)
if err != nil {
return err
}
return nil
}