221 lines
4.9 KiB
Go
221 lines
4.9 KiB
Go
package omadaapi
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"sync"
|
|
"time"
|
|
|
|
"git.tordarus.net/tordarus/ezhttp"
|
|
"git.tordarus.net/tordarus/omada-api/model"
|
|
)
|
|
|
|
type Api struct {
|
|
tmpl *http.Request
|
|
|
|
config ApiConfig
|
|
|
|
accessToken string
|
|
refreshToken string
|
|
|
|
expiration time.Time
|
|
refreshMutex sync.RWMutex
|
|
}
|
|
|
|
type ApiConfig struct {
|
|
BasePath string
|
|
OmadaID string
|
|
ClientID string
|
|
ClientSecret string
|
|
Username string
|
|
Password string
|
|
}
|
|
|
|
func NewApi(config ApiConfig) (*Api, error) {
|
|
tmpl := ezhttp.Request(ezhttp.URL(config.BasePath))
|
|
|
|
api := &Api{
|
|
tmpl: tmpl,
|
|
config: config,
|
|
}
|
|
|
|
if err := api.InitSession(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return api, nil
|
|
}
|
|
|
|
func (api *Api) InitSession() error {
|
|
api.refreshMutex.Lock()
|
|
defer api.refreshMutex.Unlock()
|
|
return api.initSessionNoMutexLock()
|
|
}
|
|
|
|
func (api *Api) initSessionNoMutexLock() error {
|
|
loginResponse, err := api.Login()
|
|
if err != nil {
|
|
return fmt.Errorf("login request failed: %w", err)
|
|
}
|
|
|
|
if loginResponse.ErrorCode != 0 {
|
|
return fmt.Errorf("login request failed: %s", loginResponse.Message)
|
|
}
|
|
|
|
authCodeResponse, err := api.AuthCode(loginResponse.Result.CsrfToken, loginResponse.Result.SessionID)
|
|
if err != nil {
|
|
return fmt.Errorf("auth code request failed: %w", err)
|
|
}
|
|
|
|
if authCodeResponse.ErrorCode != 0 {
|
|
return fmt.Errorf("auth code request failed: %s", authCodeResponse.Message)
|
|
}
|
|
|
|
authTokenResponse, err := api.AuthToken(*authCodeResponse.Result)
|
|
if err != nil {
|
|
return fmt.Errorf("auth token request failed: %w", err)
|
|
}
|
|
|
|
if authTokenResponse.ErrorCode != 0 {
|
|
return fmt.Errorf("auth token request failed: %s", authTokenResponse.Message)
|
|
}
|
|
|
|
api.expiration = time.Now().Add(time.Duration(authTokenResponse.Result.ExpiresIn) * time.Second)
|
|
api.accessToken = authTokenResponse.Result.AccessToken
|
|
api.refreshToken = authTokenResponse.Result.RefreshToken
|
|
|
|
api.tmpl = ezhttp.Request(
|
|
ezhttp.Template(api.tmpl),
|
|
ezhttp.RemoveAllHeaders(),
|
|
ezhttp.Auth(api.getAuthHeader),
|
|
)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (api *Api) Login() (*model.LoginResponse, error) {
|
|
reqBody := model.LoginRequest{
|
|
Username: api.config.Username,
|
|
Password: api.config.Password,
|
|
}
|
|
|
|
req := ezhttp.Request(
|
|
ezhttp.Template(api.tmpl),
|
|
ezhttp.Method("POST"),
|
|
ezhttp.AppendPath("/openapi/authorize/login"),
|
|
ezhttp.Query("client_id", api.config.ClientID, "omadac_id", api.config.OmadaID),
|
|
ezhttp.Body(ezhttp.JSON(reqBody)),
|
|
)
|
|
|
|
resp, err := ezhttp.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
response, err := ezhttp.ParseJsonResponse[model.LoginResponse](resp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return response, nil
|
|
}
|
|
|
|
func (api *Api) AuthCode(csrfToken, sessionID string) (*model.AuthCodeResponse, error) {
|
|
req := ezhttp.Request(
|
|
ezhttp.Template(api.tmpl),
|
|
ezhttp.Method("POST"),
|
|
ezhttp.AppendPath("/openapi/authorize/code"),
|
|
ezhttp.Query("client_id", api.config.ClientID, "omadac_id", api.config.OmadaID, "response_type", "code"),
|
|
ezhttp.Headers("Csrf-Token", csrfToken, "Cookie", fmt.Sprintf("TPOMADA_SESSIONID=%s", sessionID)),
|
|
)
|
|
|
|
resp, err := ezhttp.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
response, err := ezhttp.ParseJsonResponse[model.AuthCodeResponse](resp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return response, nil
|
|
}
|
|
|
|
func (api *Api) AuthToken(authCode string) (*model.AuthTokenResponse, error) {
|
|
req := ezhttp.Request(
|
|
ezhttp.Template(api.tmpl),
|
|
ezhttp.Method("POST"),
|
|
ezhttp.AppendPath("/openapi/authorize/token"),
|
|
ezhttp.Query(
|
|
"code", authCode,
|
|
"grant_type", "authorization_code",
|
|
"client_id", api.config.ClientID,
|
|
"client_secret", api.config.ClientSecret,
|
|
),
|
|
)
|
|
|
|
resp, err := ezhttp.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
response, err := ezhttp.ParseJsonResponse[model.AuthTokenResponse](resp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return response, nil
|
|
}
|
|
|
|
func (api *Api) Refresh() error {
|
|
api.refreshMutex.Lock()
|
|
defer api.refreshMutex.Unlock()
|
|
|
|
req := ezhttp.Request(
|
|
ezhttp.Template(api.tmpl),
|
|
ezhttp.Method("POST"),
|
|
ezhttp.AppendPath("/openapi/authorize/token"),
|
|
ezhttp.Query(
|
|
"client_id", api.config.ClientID,
|
|
"client_secret", api.config.ClientSecret,
|
|
"refresh_token", api.refreshToken,
|
|
"grant_type", "refresh_token",
|
|
),
|
|
)
|
|
|
|
resp, err := ezhttp.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
response, err := ezhttp.ParseJsonResponse[model.AuthTokenResponse](resp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if response.ErrorCode == ErrCodeRefreshTokenExpired {
|
|
return api.initSessionNoMutexLock()
|
|
}
|
|
|
|
api.expiration = time.Now().Add(time.Duration(response.Result.ExpiresIn) * time.Second)
|
|
api.accessToken = response.Result.AccessToken
|
|
api.refreshToken = response.Result.RefreshToken
|
|
|
|
return nil
|
|
}
|
|
|
|
func (api *Api) doRequest(r *http.Request) (*http.Response, error) {
|
|
api.refreshMutex.RLock()
|
|
defer api.refreshMutex.RUnlock()
|
|
return ezhttp.Do(r)
|
|
}
|
|
|
|
func (api *Api) getAuthHeader() string {
|
|
return "AccessToken=" + api.accessToken
|
|
}
|