neko/server/internal/http/legacy/session.go
2024-07-22 23:30:45 +02:00

161 lines
3.3 KiB
Go

package legacy
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
oldTypes "github.com/demodesk/neko/internal/http/legacy/types"
"github.com/demodesk/neko/internal/api"
"github.com/demodesk/neko/pkg/types"
"github.com/gorilla/websocket"
)
var (
ErrWebsocketSend = fmt.Errorf("failed to send message to websocket")
ErrBackendRespone = fmt.Errorf("error response from backend")
)
type session struct {
url string
id string
token string
profile types.MemberProfile
client *http.Client
lastHostID string
lockedControls bool
lockedLogins bool
lockedFileTransfer bool
sessions map[string]*oldTypes.Member
connClient *websocket.Conn
connBackend *websocket.Conn
}
func newSession(url string) *session {
return &session{
url: url,
client: http.DefaultClient,
sessions: make(map[string]*oldTypes.Member),
}
}
func (s *session) apiReq(method, path string, request, response any) error {
body, err := json.Marshal(request)
if err != nil {
return err
}
req, err := http.NewRequest(method, s.url+path, bytes.NewReader(body))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
if s.token != "" {
req.Header.Set("Authorization", "Bearer "+s.token)
}
res, err := s.client.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
if res.StatusCode < 200 || res.StatusCode >= 300 {
body, _ := io.ReadAll(res.Body)
// try to unmarsal as json error message
var apiErr struct {
Message string `json:"message"`
}
if err := json.Unmarshal(body, &apiErr); err == nil {
return fmt.Errorf("%w: %s", ErrBackendRespone, apiErr.Message)
}
// return raw body if failed to unmarshal
return fmt.Errorf("unexpected status code: %d, body: %s", res.StatusCode, body)
}
if res.Body == nil {
return nil
}
return json.NewDecoder(res.Body).Decode(response)
}
// send message to client (in old format)
func (s *session) toClient(payload any) error {
msg, err := json.Marshal(payload)
if err != nil {
return err
}
err = s.connClient.WriteMessage(websocket.TextMessage, msg)
if err != nil {
return fmt.Errorf("%w: %s", ErrWebsocketSend, err)
}
return nil
}
// send message to backend (in new format)
func (s *session) toBackend(event string, payload any) error {
rawPayload, err := json.Marshal(payload)
if err != nil {
return err
}
msg, err := json.Marshal(&types.WebSocketMessage{
Event: event,
Payload: rawPayload,
})
if err != nil {
return err
}
err = s.connBackend.WriteMessage(websocket.TextMessage, msg)
if err != nil {
return fmt.Errorf("%w: %s", ErrWebsocketSend, err)
}
return nil
}
func (s *session) create(password string) (string, error) {
data := api.SessionDataPayload{}
// pefrom login with arbitrary username that will be changed later
err := s.apiReq(http.MethodPost, "/api/login", api.SessionLoginPayload{
Username: "admin",
Password: password,
}, &data)
if err != nil {
return "", err
}
s.id = data.ID
s.token = data.Token
s.profile = data.Profile
if s.token == "" {
return "", fmt.Errorf("token not found")
}
return data.Token, nil
}
func (s *session) destroy() {
defer s.client.CloseIdleConnections()
// logout session
err := s.apiReq(http.MethodPost, "/api/logout", nil, nil)
if err != nil {
log.Println("failed to logout session:", err)
}
}