omada-api/api.go

217 lines
4.9 KiB
Go
Raw Permalink Normal View History

2025-02-01 17:38:29 +01:00
package omadaapi
import (
2025-02-02 10:54:39 +01:00
"context"
2025-02-01 17:38:29 +01:00
"fmt"
"net/http"
2025-02-02 10:54:39 +01:00
"sync"
"time"
2025-02-01 17:38:29 +01:00
"git.tordarus.net/tordarus/ezhttp"
"git.tordarus.net/tordarus/omada-api/model"
)
type Api struct {
tmpl *http.Request
config ApiConfig
accessToken string
refreshToken string
2025-02-02 10:54:39 +01:00
expiration time.Time
refreshMutex sync.RWMutex
2025-02-01 17:38:29 +01:00
}
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,
}
loginResponse, err := api.Login()
if err != nil {
return nil, fmt.Errorf("login request failed: %w", err)
}
if loginResponse.ErrorCode != 0 {
return nil, fmt.Errorf("login request failed: %s", loginResponse.Message)
}
authCodeResponse, err := api.AuthCode(loginResponse.Result.CsrfToken, loginResponse.Result.SessionID)
if err != nil {
return nil, fmt.Errorf("auth code request failed: %w", err)
}
if authCodeResponse.ErrorCode != 0 {
return nil, fmt.Errorf("auth code request failed: %s", authCodeResponse.Message)
}
authTokenResponse, err := api.AuthToken(*authCodeResponse.Result)
if err != nil {
return nil, fmt.Errorf("auth token request failed: %w", err)
}
if authTokenResponse.ErrorCode != 0 {
return nil, fmt.Errorf("auth token request failed: %s", authTokenResponse.Message)
}
2025-02-02 10:54:39 +01:00
api.expiration = time.Now().Add(time.Duration(authTokenResponse.Result.ExpiresIn-1) * time.Second)
2025-02-01 17:38:29 +01:00
api.accessToken = authTokenResponse.Result.AccessToken
api.refreshToken = authTokenResponse.Result.RefreshToken
api.tmpl = ezhttp.Request(
ezhttp.Template(api.tmpl),
ezhttp.Headers("Authorization", "AccessToken="+api.accessToken),
)
return api, nil
}
func (api *Api) Login() (*model.LoginResponse, error) {
2025-02-02 10:54:39 +01:00
api.refreshMutex.RLock()
defer api.refreshMutex.RUnlock()
2025-02-01 17:38:29 +01:00
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.Body)
if err != nil {
return nil, err
}
return response, nil
}
func (api *Api) AuthCode(csrfToken, sessionID string) (*model.AuthCodeResponse, error) {
2025-02-02 10:54:39 +01:00
api.refreshMutex.RLock()
defer api.refreshMutex.RUnlock()
2025-02-01 17:38:29 +01:00
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.Body)
if err != nil {
return nil, err
}
return response, nil
}
func (api *Api) AuthToken(authCode string) (*model.AuthTokenResponse, error) {
2025-02-02 10:54:39 +01:00
api.refreshMutex.RLock()
defer api.refreshMutex.RUnlock()
2025-02-01 17:38:29 +01:00
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.Body)
if err != nil {
return nil, err
}
return response, nil
}
2025-02-02 10:54:39 +01:00
func (api *Api) AutoRefresh(ctx context.Context) error {
for {
select {
case <-ctx.Done():
return nil
case <-time.After(time.Until(api.expiration)):
if err := api.Refresh(); err != nil {
return err
}
}
}
}
func (api *Api) Refresh() error {
fmt.Println("refresh")
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.Body)
if err != nil {
return err
}
api.expiration = time.Now().Add(time.Duration(response.Result.ExpiresIn-1) * time.Second)
api.accessToken = response.Result.AccessToken
api.refreshToken = response.Result.RefreshToken
fmt.Println("refresh successful")
return nil
}