refactor foundry lib structure; add transport classes for db,http,websocket; add action interface instead of multiple function that accepts action classes; add changing foundry status on websocket responses
This commit is contained in:
@@ -1,81 +1,35 @@
|
||||
package foundry
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/requests"
|
||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/actions"
|
||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/transport"
|
||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/types"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
type Foundry struct {
|
||||
var (
|
||||
ListenIsDone = errors.New("Listen for websocket data in foundry is stopped")
|
||||
)
|
||||
|
||||
type FoundryApi struct {
|
||||
//TODO: make check of admin's authentication
|
||||
isAuth bool
|
||||
Transport *transport.FoundryTransport
|
||||
IsReadReady bool
|
||||
|
||||
logger *slog.Logger
|
||||
ws *webSocketUtil
|
||||
Status types.FoundryStatus
|
||||
}
|
||||
|
||||
func NewFoundry() *Foundry {
|
||||
foundry := &Foundry{isAuth: false, ws: NewWebSocketUtil()}
|
||||
|
||||
// err := foundry.config.GetSessionId()
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
return foundry
|
||||
func NewFoundry() *FoundryApi {
|
||||
return &FoundryApi{}
|
||||
}
|
||||
|
||||
func (foundry *Foundry) checkAuthRespAnswer(r io.Reader, respLength int) error {
|
||||
authBody := make([]byte, respLength)
|
||||
func (foundry *FoundryApi) StartListen() error {
|
||||
foundry.Transport.ReadChan = *types.InitWsChannels()
|
||||
|
||||
_, err := r.Read(authBody)
|
||||
if err != nil && err != io.EOF {
|
||||
return err
|
||||
}
|
||||
|
||||
if !strings.Contains(string(authBody), "Admin authentication successful") {
|
||||
return ErrorAuthPassWrong
|
||||
}
|
||||
foundry.isAuth = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (foundry *Foundry) Authenticate() error {
|
||||
if foundry.ws.config.SessionID == "" {
|
||||
err := foundry.ws.config.GetSessionId()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := foundry.ws.config.PostAuthenticationData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
contentLen, err := strconv.Atoi(resp.Header.Get("Content-Length"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return foundry.checkAuthRespAnswer(resp.Body, contentLen)
|
||||
}
|
||||
|
||||
func (foundry *Foundry) StartListen() error {
|
||||
foundry.ws.readChan = *types.InitWsChannels()
|
||||
|
||||
wsChannels := &foundry.ws.readChan
|
||||
defer wsChannels.Close()
|
||||
wsChannels := &foundry.Transport.ReadChan
|
||||
defer foundry.Transport.ReadChan.Close()
|
||||
go foundry.ListenAndServeWS()
|
||||
|
||||
var err error
|
||||
@@ -93,115 +47,108 @@ func (foundry *Foundry) StartListen() error {
|
||||
// }
|
||||
// foundry.models.FoundryState.Insert(foundryState)
|
||||
case err = <-wsChannels.Err():
|
||||
var foundryErr *FoundryError
|
||||
var foundryErr *types.FoundryError
|
||||
if errors.As(err, &foundryErr) {
|
||||
if foundryErr.IsFatal {
|
||||
foundry.CloseWebSocketConn()
|
||||
foundry.Transport.CloseWebSocketConn()
|
||||
return foundryErr
|
||||
}
|
||||
foundry.logger.Warn("Got error when listening or served websocket", "err", err.Error())
|
||||
foundry.Transport.Logger.Warn("Got error when listening or served", "err", err.Error(), "type", foundryErr.Type, "direction", foundryErr.Direction)
|
||||
}
|
||||
case <-wsChannels.Done():
|
||||
foundry.CloseWebSocketConn()
|
||||
foundry.Transport.CloseWebSocketConn()
|
||||
return ListenIsDone
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (foundry *Foundry) ConnectToFoundry() error {
|
||||
func (foundry *FoundryApi) ServeWebSocket() {
|
||||
for {
|
||||
select {
|
||||
case message, ok := <-foundry.Transport.ReadChan.Msg():
|
||||
if !ok {
|
||||
foundry.Transport.Logger.Debug("Read channel is closed", "type", types.WebSocketCode)
|
||||
return
|
||||
}
|
||||
foundry.Transport.Logger.Debug("Data has been received\n", "msg", message.Code, "type", types.WebSocketCode) //, "msg", message.MsgJson)
|
||||
|
||||
switch message.Code {
|
||||
case types.RespPingCode, types.RespSessionDataCode:
|
||||
err := foundry.Transport.SendOnlyCodeRequest(message.Code)
|
||||
if err != nil {
|
||||
foundry.IsReadReady = false
|
||||
foundry.Transport.ReadChan.Err() <- &types.FoundryError{Direction: types.WriterCode, Err: err, Type: types.WebSocketCode}
|
||||
continue
|
||||
}
|
||||
case types.RespServerChangeCode:
|
||||
foundry.IsReadReady = true
|
||||
data, err := actions.SplitToTypeAndData([]byte(message.MsgJson))
|
||||
if err != nil {
|
||||
foundry.Transport.ReadChan.Err() <- &types.FoundryError{Direction: types.ReaderCode, Err: err, Type: types.WebSocketCode}
|
||||
continue
|
||||
}
|
||||
|
||||
if data == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
err = data.Action(foundry.Transport, &foundry.Status)
|
||||
if err != nil {
|
||||
foundry.Transport.ReadChan.Err() <- &types.FoundryError{Direction: types.ReaderCode, Err: err, Type: types.WebSocketCode}
|
||||
continue
|
||||
}
|
||||
case types.RespDataCode:
|
||||
foundry.Transport.ExchangeChan.Msgs[message.Id] = make(chan []byte, 1)
|
||||
foundry.Transport.ExchangeChan.Msgs[message.Id] <- []byte(message.MsgJson)
|
||||
go foundry.Transport.CloseMsgChannel(message.Id, 5*time.Second)
|
||||
default:
|
||||
}
|
||||
case <-foundry.Transport.ReadChan.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (foundry *FoundryApi) ListenAndServeWS() {
|
||||
go foundry.Transport.ListenWebSocket()
|
||||
foundry.ServeWebSocket()
|
||||
}
|
||||
|
||||
func (foundry *FoundryApi) HandleWSRequest(msgType string) ([]byte, error) {
|
||||
msg := types.NewWsMessageByPage(msgType, foundry.Transport.CurrWsId)
|
||||
|
||||
return foundry.Transport.HandleWebsocketRequest(msg)
|
||||
}
|
||||
|
||||
func (foundry *FoundryApi) ConnectToWebSocket() error {
|
||||
var err error
|
||||
|
||||
if foundry == nil {
|
||||
return ErrorFoundryNotInit
|
||||
}
|
||||
ok, err := (*foundry).ws.config.GetSessionToken()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if (*foundry).ws.config.Password != "" && !ok {
|
||||
err := (*foundry).Authenticate()
|
||||
if !foundry.Transport.HasSessionId() {
|
||||
err = foundry.Transport.InitSessionId()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
foundry.logger.Info("Successefully connected to Foundry", "host", foundry.ws.config.Host)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (foundry *Foundry) ConnectToWebSocket() error {
|
||||
if !foundry.isAuth {
|
||||
return ErrorNotAuth
|
||||
}
|
||||
|
||||
query := url.Values{}
|
||||
query.Add("session", foundry.ws.config.SessionID)
|
||||
query.Add("EIO", "4")
|
||||
query.Add("transport", "websocket")
|
||||
|
||||
u := url.URL{Scheme: "ws", Host: foundry.ws.config.Host, Path: fmt.Sprintf("%s/", requests.SocketPath), RawQuery: query.Encode()}
|
||||
|
||||
wsHeader := http.Header{}
|
||||
wsHeader.Set("Cookie", fmt.Sprintf("session=%s", foundry.ws.config.SessionID))
|
||||
|
||||
wsConn, _, err := websocket.DefaultDialer.Dial(u.String(), wsHeader)
|
||||
err = foundry.Transport.ConnectToFoundry()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
foundry.ws.wsConn = wsConn
|
||||
foundry.ws.currWsId = 0
|
||||
foundry.logger.Info("Successefully connected to Foundry Websocket")
|
||||
err = foundry.Transport.InitWebSocketConnection()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
status, err := foundry.Transport.Http.GetStatus()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
foundry.Status = *types.NewFoundryStatus(status)
|
||||
|
||||
err = foundry.StartListen()
|
||||
if err != nil && err != ListenIsDone {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (foundry *Foundry) CloseWebSocketConn() {
|
||||
foundry.ws.wsConn.Close()
|
||||
}
|
||||
|
||||
func (foundry *Foundry) ListenAndServeWS() {
|
||||
go foundry.ws.ListenWebSocket()
|
||||
foundry.ws.ServeWebSocket()
|
||||
}
|
||||
|
||||
func (foundry *Foundry) HandleWSRequest(msgType string) ([]byte, error) {
|
||||
msg := foundry.ws.CreateWSMessageByPage(msgType)
|
||||
|
||||
return foundry.ws.HandleWebsocketRequest(msg)
|
||||
}
|
||||
|
||||
func (foundry *Foundry) HasSessionId() bool {
|
||||
return foundry.ws.config.SessionID != ""
|
||||
}
|
||||
|
||||
func (foundry *Foundry) SetUpSessionId() error {
|
||||
return foundry.ws.config.GetSessionId()
|
||||
}
|
||||
|
||||
func (foundry *Foundry) SetConfig(config *requests.Config) {
|
||||
foundry.ws.config = *config
|
||||
}
|
||||
|
||||
func (foundry *Foundry) SetLogger(slogger *slog.Logger) {
|
||||
foundry.logger = slogger
|
||||
foundry.ws.logger = slogger
|
||||
}
|
||||
|
||||
func (foundry *Foundry) CreateNewDataModels(db *sql.DB) {
|
||||
foundry.ws.CreateNewDataModels(db)
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Poll the server to see if a World has become active, and automatically refresh if so.
|
||||
// * @returns {Promise<void>}
|
||||
// */
|
||||
// async #pollActive() {
|
||||
// const poll = () => {
|
||||
// const status = foundry.utils.getRoute("/api/status");
|
||||
// this.#activePollState.last = Date.now();
|
||||
// this.#activePollState.polling = foundry.utils.fetchJsonWithTimeout(status, {}, {
|
||||
// timeoutMs: 10000
|
||||
// }).catch(() => {});
|
||||
// this.#pollActive();
|
||||
// };
|
||||
|
||||
Reference in New Issue
Block a user