Files
Foundry-Scrapping-API/internal/foundry/foundry.go

237 lines
5.4 KiB
Go

package foundry
import (
"database/sql"
"errors"
"fmt"
"io"
"log/slog"
"net/http"
"net/url"
"strconv"
"strings"
data "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/requests"
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/types"
"github.com/gorilla/websocket"
)
const (
RespSessionData = "0"
RespPingCode = "2"
RespSessionId = "40"
RespCreateSessionCode = "42"
RespDataCode = "43"
ReqPongCode = "3"
ReqCreateSessionCode = "40"
ReqDataCode = "42"
)
var RequestCodes = []string{
RespSessionData,
RespPingCode,
RespSessionId,
RespCreateSessionCode,
RespDataCode,
}
var CodesRespToReq = map[string]string{
RespSessionData: ReqCreateSessionCode,
RespPingCode: ReqPongCode,
RespCreateSessionCode: ReqDataCode,
//ReqDataCode: RespCreateSessionCode,
}
type Foundry struct {
//TODO: make check of admin's authentication
isAuth bool
logger *slog.Logger
models data.Models
config requests.Config
ws *webSocketUtil
}
func NewFoundry() *Foundry {
foundry := &Foundry{config: requests.Config{SessionID: ""}, isAuth: false, ws: NewWebSocketUtil()}
// err := foundry.config.GetSessionId()
// if err != nil {
// return nil, err
// }
return foundry
}
func (foundry *Foundry) checkAuthRespAnswer(r io.Reader, respLength int) error {
authBody := make([]byte, respLength)
_, 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.config.SessionID == "" {
err := foundry.config.GetSessionId()
if err != nil {
return err
}
}
resp, err := foundry.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.channels = *types.InitWsChannels()
wsChannels := &foundry.ws.channels
defer wsChannels.Close()
go foundry.ListenAndServeWS()
var err error
for {
select {
// case msg, ok := <-foundry.ws.channels.Msg():
// if !ok {
// time.Sleep(5 * time.Microsecond)
// continue
// }
// var foundryState *json_model.FoundryState
// foundryState, err = json_model.ParseSetupModel(msg.ToByteSlice())
// if err != nil {
// return nil
// }
// foundry.models.FoundryState.Insert(foundryState)
case err = <-wsChannels.Err():
var foundryErr *FoundryError
if errors.As(err, &foundryErr) {
if foundryErr.IsFatal {
foundry.CloseWebSocketConn()
return foundryErr
}
}
case <-wsChannels.Done():
foundry.CloseWebSocketConn()
return ListenIsDone
}
}
}
func (foundry *Foundry) ConnectToFoundry() error {
var err error
if foundry == nil {
return ErrorFoundryNotInit
}
ok, err := (*foundry).config.GetSessionToken()
if err != nil {
return err
}
if (*foundry).config.Password != "" && !ok {
err := (*foundry).Authenticate()
if err != nil {
return err
}
}
foundry.logger.Info("Successefully connected to Foundry", "host", foundry.config.Host)
return nil
}
func (foundry *Foundry) ConnectToWebSocket() error {
if !foundry.isAuth {
return ErrorNotAuth
}
query := url.Values{}
query.Add("session", foundry.config.SessionID)
query.Add("EIO", "4")
query.Add("transport", "websocket")
u := url.URL{Scheme: "ws", Host: foundry.config.Host, Path: fmt.Sprintf("%s/", requests.SocketPath), RawQuery: query.Encode()}
wsHeader := http.Header{}
wsHeader.Set("Cookie", fmt.Sprintf("session=%s", foundry.config.SessionID))
wsConn, _, err := websocket.DefaultDialer.Dial(u.String(), wsHeader)
if err != nil {
return err
}
foundry.ws.wsConn = wsConn
foundry.ws.currWsId = 0
foundry.logger.Info("Successefully connected to Foundry Websocket")
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("/setup")
return foundry.ws.HandleWebsocketRequest(msg)
}
func (foundry *Foundry) HasSessionId() bool {
return foundry.config.SessionID != ""
}
func (foundry *Foundry) SetUpSessionId() error {
return foundry.config.GetSessionId()
}
func (foundry *Foundry) SetConfig(config *requests.Config) {
foundry.config = *config
}
func (foundry *Foundry) SetLogger(slogger *slog.Logger) {
foundry.logger = slogger
foundry.ws.logger = slogger
}
func (foundry *Foundry) CreateNewDataModels(db *sql.DB) {
foundry.models = data.NewModels(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();
// };