chore: move '/internal/pkg' to '/pkg'

This commit is contained in:
Fu Diwei
2025-06-17 15:54:21 +08:00
parent 30840bbba5
commit 205275b52d
611 changed files with 693 additions and 693 deletions

View File

@@ -0,0 +1,93 @@
package dtos
import "encoding/json"
type CDNConfiguration struct {
ConfigurationID string `json:"id"`
EnvironmentID string `json:"environment_id"`
Rules json.RawMessage `json:"rules"`
Origins []Origin `json:"origins"`
Hostnames []Hostname `json:"hostnames"`
Experiments *[]string `json:"experiments,omitempty"`
EdgeFunctionsSources *map[string]string `json:"edge_functions_sources,omitempty"`
EdgeFunctionInitScript *string `json:"edge_function_init_script,omitempty"`
}
type Origin struct {
Name string `json:"name"`
Type *string `json:"type,omitempty"`
Hosts []Host `json:"hosts"`
Balancer *string `json:"balancer,omitempty"`
OverrideHostHeader *string `json:"override_host_header,omitempty"`
Shields *Shields `json:"shields,omitempty"`
PciCertifiedShields *bool `json:"pci_certified_shields,omitempty"`
TLSVerify *TLSVerify `json:"tls_verify,omitempty"`
Retry *Retry `json:"retry,omitempty"`
}
type Host struct {
Weight *int64 `json:"weight,omitempty"`
DNSMaxTTL *int64 `json:"dns_max_ttl,omitempty"`
DNSPreference *string `json:"dns_preference,omitempty"`
MaxHardPool *int64 `json:"max_hard_pool,omitempty"`
DNSMinTTL *int64 `json:"dns_min_ttl,omitempty"`
Location *[]Location `json:"location,omitempty"`
MaxPool *int64 `json:"max_pool,omitempty"`
Balancer *string `json:"balancer,omitempty"`
Scheme *string `json:"scheme,omitempty"`
OverrideHostHeader *string `json:"override_host_header,omitempty"`
SNIHintAndStrictSanCheck *string `json:"sni_hint_and_strict_san_check,omitempty"`
UseSNI *bool `json:"use_sni,omitempty"`
}
type Location struct {
Port *int64 `json:"port,omitempty"`
Hostname *string `json:"hostname,omitempty"`
}
type Shields struct {
Apac *string `json:"apac,omitempty"`
Emea *string `json:"emea,omitempty"`
USWest *string `json:"us_west,omitempty"`
USEast *string `json:"us_east,omitempty"`
}
type TLSVerify struct {
UseSNI *bool `json:"use_sni,omitempty"`
SNIHintAndStrictSanCheck *string `json:"sni_hint_and_strict_san_check,omitempty"`
AllowSelfSignedCerts *bool `json:"allow_self_signed_certs,omitempty"`
PinnedCerts *[]string `json:"pinned_certs,omitempty"`
}
type Retry struct {
StatusCodes *[]int64 `json:"status_codes,omitempty"`
IgnoreRetryAfterHeader *bool `json:"ignore_retry_after_header,omitempty"`
AfterSeconds *int64 `json:"after_seconds,omitempty"`
MaxRequests *int64 `json:"max_requests,omitempty"`
MaxWaitSeconds *int64 `json:"max_wait_seconds,omitempty"`
}
type Hostname struct {
Hostname *string `json:"hostname,omitempty"`
DefaultOriginName *string `json:"default_origin_name,omitempty"`
ReportCode *int64 `json:"report_code,omitempty"`
TLS *TLS `json:"tls,omitempty"`
Directory *string `json:"directory,omitempty"`
}
type TLS struct {
NPN *bool `json:"npn,omitempty"`
ALPN *bool `json:"alpn,omitempty"`
Protocols *string `json:"protocols,omitempty"`
UseSigAlgs *bool `json:"use_sigalgs,omitempty"`
SNI *bool `json:"sni,omitempty"`
SniStrict *bool `json:"sni_strict,omitempty"`
SniHostMatch *bool `json:"sni_host_match,omitempty"`
ClientRenegotiation *bool `json:"client_renegotiation,omitempty"`
Options *string `json:"options,omitempty"`
CipherList *string `json:"cipher_list,omitempty"`
NamedCurve *string `json:"named_curve,omitempty"`
OCSP *bool `json:"oscp,omitempty"`
PEM *string `json:"pem,omitempty"`
CA *string `json:"ca,omitempty"`
}

View File

@@ -0,0 +1,29 @@
package dtos
import (
"time"
)
type Environment struct {
Type string `json:"@type"`
IdLink string `json:"@id"`
Id string `json:"id"`
PropertyID string `json:"property_id"`
LegacyAccountNumber string `json:"legacy_account_number"`
Name string `json:"name"`
CanMembersDeploy bool `json:"can_members_deploy"`
OnlyMaintainersCanDeploy bool `json:"only_maintainers_can_deploy"`
HttpRequestLogging bool `json:"http_request_logging"`
DefaultDomainName string `json:"default_domain_name"`
PciCompliance bool `json:"pci_compliance"`
DnsDomainName string `json:"dns_domain_name"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type EnvironmentsResponse struct {
Type string `json:"@type"`
Id string `json:"@id"`
TotalItems int `json:"total_items"`
Items []Environment `json:"items"`
}

View File

@@ -0,0 +1,18 @@
package dtos
import "time"
type Property struct {
IdLink string `json:"@id"`
Id string `json:"id"`
OrganizationID string `json:"organization_id"`
Slug string `json:"slug"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type Properties struct {
ID string `json:"@id"`
TotalItems int `json:"total_items"`
Items []Property `json:"items"`
}

View File

@@ -0,0 +1,18 @@
package dtos
import "time"
type PurgeResponse struct {
ID string `json:"id"`
Status string `json:"status"`
CreatedAt time.Time `json:"created_at"`
CompletedAt time.Time `json:"completed_at"`
ProgressPercentage float32 `json:"progress_percentage"`
}
type PurgeRequest struct {
EnvironmentID string `json:"environment_id"`
PurgeType string `json:"purge_type"`
Values []string `json:"values"`
Hostname *string `json:"hostname"`
}

View File

@@ -0,0 +1,30 @@
package dtos
type TLSCertResponse struct {
ID string `json:"id"`
EnvironmentID string `json:"environment_id"`
PrimaryCert string `json:"primary_cert"`
IntermediateCert string `json:"intermediate_cert"`
Expiration string `json:"expiration"`
Status string `json:"status"`
Generated bool `json:"generated"`
Serial string `json:"serial"`
CommonName string `json:"common_name"`
AlternativeNames []string `json:"alternative_names"`
ActivationError string `json:"activation_error"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
type UploadTlsCertRequest struct {
EnvironmentID string `json:"environment_id"`
PrimaryCert string `json:"primary_cert"`
IntermediateCert string `json:"intermediate_cert"`
PrivateKey string `json:"private_key"`
}
type TLSCertSResponse struct {
EnvironmentID string `json:"environment_id"`
TotalItems int32 `json:"total_items"`
Certificates []TLSCertResponse `json:"items"`
}

View File

@@ -0,0 +1,545 @@
package edgio_api
import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/Edgio/edgio-api/applications/v7/dtos"
"github.com/go-resty/resty/v2"
)
// AccessTokenResponse represents the response from the token endpoint.
type AccessTokenResponse struct {
AccessToken string `json:"access_token"`
ExpiresIn int `json:"expires_in"`
TokenType string `json:"token_type"`
Scope string `json:"scope"`
}
// TokenCache represents a cached token. The token is stored along
// with its expiry time. Because different endpoints require different
// scopes, we store the token with the scope as the key, so that we
// can fetch the token from the cache based on the scope.
type TokenCache struct {
AccessToken string
Expiry time.Time
}
type EdgioClient struct {
client *resty.Client
clientID string
clientSecret string
tokenURL string
apiURL string
tokenCache map[string]TokenCache
}
func NewEdgioClient(clientID, clientSecret, tokenURL, apiURL string) *EdgioClient {
client := resty.New().
SetTimeout(30 * time.Second).
SetRetryCount(3).
SetRetryWaitTime(5 * time.Second).
SetRetryMaxWaitTime(20 * time.Second)
if tokenURL == "" {
tokenURL = "https://id.edgio.app/connect/token"
}
if apiURL == "" {
apiURL = "https://edgioapis.com"
}
return &EdgioClient{
client: client,
clientID: clientID,
clientSecret: clientSecret,
tokenURL: tokenURL,
apiURL: apiURL,
tokenCache: make(map[string]TokenCache),
}
}
func (c *EdgioClient) getToken(scope string) (string, error) {
if cachedToken, exists := c.tokenCache[scope]; exists && time.Now().Before(cachedToken.Expiry) {
return cachedToken.AccessToken, nil
}
var tokenResp AccessTokenResponse
resp, err := c.client.R().
SetFormData(map[string]string{
"client_id": c.clientID,
"client_secret": c.clientSecret,
"grant_type": "client_credentials",
"scope": scope,
}).
SetResult(&tokenResp).
Post(c.tokenURL)
if err != nil {
return "", fmt.Errorf("failed to request token: %w", err)
}
if resp.IsError() {
return "", fmt.Errorf("unexpected status code for getToken: %d", resp.StatusCode())
}
c.tokenCache[scope] = TokenCache{
AccessToken: tokenResp.AccessToken,
Expiry: time.Now().Add(time.Duration(tokenResp.ExpiresIn) * time.Second),
}
return tokenResp.AccessToken, nil
}
func (c *EdgioClient) GetProperty(ctx context.Context, propertyID string) (*dtos.Property, error) {
token, err := c.getToken("app.accounts")
if err != nil {
return nil, fmt.Errorf("failed to get token: %w", err)
}
url := fmt.Sprintf("%s/accounts/v0.1/properties/%s", c.apiURL, propertyID)
var property dtos.Property
resp, err := c.client.R().
SetContext(ctx).
SetAuthToken(token).
SetResult(&property).
Get(url)
if err != nil {
return nil, fmt.Errorf("failed to send request: %w", err)
}
if resp.IsError() {
return nil, fmt.Errorf("unexpected status code for getSpecificProperty: %d, %s", resp.StatusCode(), resp.Request.URL)
}
return &property, nil
}
func (c *EdgioClient) GetProperties(page int, pageSize int, organizationID string) (*dtos.Properties, error) {
token, err := c.getToken("app.accounts")
if err != nil {
return nil, fmt.Errorf("failed to get token: %w", err)
}
url := fmt.Sprintf("%s/accounts/v0.1/properties", c.apiURL)
var propertiesResp dtos.Properties
resp, err := c.client.R().
SetAuthToken(token).
SetQueryParams(map[string]string{
"page": fmt.Sprintf("%d", page),
"page_size": fmt.Sprintf("%d", pageSize),
"organization_id": organizationID,
}).
SetResult(&propertiesResp).
Get(url)
if err != nil {
return nil, fmt.Errorf("failed to send request: %w", err)
}
if resp.IsError() {
return nil, fmt.Errorf("unexpected status code for getProperties: %d, %s", resp.StatusCode(), resp.String())
}
return &propertiesResp, nil
}
func (c *EdgioClient) CreateProperty(ctx context.Context, organizationID, slug string) (*dtos.Property, error) {
token, err := c.getToken("app.accounts")
if err != nil {
return nil, fmt.Errorf("failed to get token: %w", err)
}
url := fmt.Sprintf("%s/accounts/v0.1/properties", c.apiURL)
var createdProperty dtos.Property
resp, err := c.client.R().
SetContext(ctx).
SetAuthToken(token).
SetHeader("Content-Type", "application/json").
SetBody(map[string]string{
"organization_id": organizationID,
"slug": slug,
}).
SetResult(&createdProperty).
Post(url)
if err != nil {
return nil, fmt.Errorf("failed to send request: %w", err)
}
if resp.IsError() {
return nil, fmt.Errorf("unexpected status code for createProperty: %d, response: %s", resp.StatusCode(), resp.String())
}
return &createdProperty, nil
}
func (c *EdgioClient) DeleteProperty(propertyID string) error {
token, err := c.getToken("app.accounts")
if err != nil {
return fmt.Errorf("failed to get token: %w", err)
}
url := fmt.Sprintf("%s/accounts/v0.1/properties/%s", c.apiURL, propertyID)
resp, err := c.client.R().
SetAuthToken(token).
Delete(url)
if err != nil {
return fmt.Errorf("error sending DELETE request: %w", err)
}
if resp.IsError() {
return fmt.Errorf("error deleting property: status code %d", resp.StatusCode())
}
return nil
}
func (c *EdgioClient) UpdateProperty(ctx context.Context, propertyID string, slug string) (*dtos.Property, error) {
token, err := c.getToken("app.accounts")
if err != nil {
return nil, fmt.Errorf("failed to get token: %w", err)
}
url := fmt.Sprintf("%s/accounts/v0.1/properties/%s", c.apiURL, propertyID)
requestBody := map[string]interface{}{
"slug": slug,
}
var updatedProperty dtos.Property
resp, err := c.client.R().
SetContext(ctx).
SetAuthToken(token).
SetBody(requestBody).
SetResult(&updatedProperty).
Patch(url)
if err != nil {
return nil, fmt.Errorf("failed to send request: %w", err)
}
if resp.IsError() {
return nil, fmt.Errorf("unexpected status code for updateProperty: %d", resp.StatusCode())
}
return &updatedProperty, nil
}
func (c *EdgioClient) GetEnvironments(page, pageSize int, propertyID string) (*dtos.EnvironmentsResponse, error) {
token, err := c.getToken("app.accounts")
if err != nil {
return nil, fmt.Errorf("failed to get token: %w", err)
}
url := fmt.Sprintf("%s/accounts/v0.1/environments", c.apiURL)
resp, err := c.client.R().
SetAuthToken(token).
SetQueryParams(map[string]string{
"page": fmt.Sprintf("%d", page),
"page_size": fmt.Sprintf("%d", pageSize),
"property_id": propertyID,
}).
SetResult(&dtos.EnvironmentsResponse{}).
Get(url)
if err != nil {
return nil, err
}
if resp.IsError() {
return nil, fmt.Errorf("error response: %s", resp.String())
}
return resp.Result().(*dtos.EnvironmentsResponse), nil
}
func (c *EdgioClient) GetEnvironment(environmentID string) (*dtos.Environment, error) {
token, err := c.getToken("app.accounts")
if err != nil {
return nil, fmt.Errorf("failed to get token: %w", err)
}
url := fmt.Sprintf("%s/accounts/v0.1/environments/%s", c.apiURL, environmentID)
resp, err := c.client.R().
SetPathParams(map[string]string{
"environment_id": environmentID,
}).
SetAuthToken(token).
SetResult(&dtos.Environment{}).
Get(url)
if err != nil {
return nil, err
}
if resp.IsError() {
return nil, fmt.Errorf("error response: %s", resp.String())
}
return resp.Result().(*dtos.Environment), nil
}
func (c *EdgioClient) CreateEnvironment(propertyID, name string, onlyMaintainersCanDeploy, httpRequestLogging bool) (*dtos.Environment, error) {
token, err := c.getToken("app.accounts")
if err != nil {
return nil, fmt.Errorf("failed to get token: %w", err)
}
url := fmt.Sprintf("%s/accounts/v0.1/environments", c.apiURL)
body := map[string]interface{}{
"property_id": propertyID,
"name": name,
"only_maintainers_can_deploy": onlyMaintainersCanDeploy,
"http_request_logging": httpRequestLogging,
}
resp, err := c.client.R().
SetBody(body).
SetAuthToken(token).
SetResult(&dtos.Environment{}).
Post(url)
if err != nil {
return nil, err
}
if resp.IsError() {
return nil, fmt.Errorf("error response: %s", resp.String())
}
return resp.Result().(*dtos.Environment), nil
}
func (c *EdgioClient) UpdateEnvironment(environmentID, name string, onlyMaintainersCanDeploy, httpRequestLogging, preserveCache bool) (*dtos.Environment, error) {
token, err := c.getToken("app.accounts")
if err != nil {
return nil, fmt.Errorf("failed to get token: %w", err)
}
url := fmt.Sprintf("%s/accounts/v0.1/environments/%s", c.apiURL, environmentID)
body := map[string]interface{}{
"name": name,
// as can_members_deploy is depricated, but update api is not
// we need to use it to map onlyMaintainersCanDeploy
"only_maintainers_can_deploy": onlyMaintainersCanDeploy,
"http_request_logging": httpRequestLogging,
"preserve_cache": preserveCache,
}
resp, err := c.client.R().
SetPathParams(map[string]string{
"environment_id": environmentID,
}).
SetBody(body).
SetAuthToken(token).
SetResult(&dtos.Environment{}).
Patch(url)
if err != nil {
return nil, err
}
if resp.IsError() {
return nil, fmt.Errorf("error response: %s", resp.String())
}
return resp.Result().(*dtos.Environment), nil
}
func (c *EdgioClient) DeleteEnvironment(environmentID string) error {
token, err := c.getToken("app.accounts")
if err != nil {
return fmt.Errorf("failed to get token: %w", err)
}
url := fmt.Sprintf("%s/accounts/v0.1/environments/%s", c.apiURL, environmentID)
resp, err := c.client.R().
SetPathParams(map[string]string{
"environment_id": environmentID,
}).
SetAuthToken(token).
SetResult(&dtos.Environment{}).
Delete(url)
if err != nil {
return err
}
if resp.IsError() {
return fmt.Errorf("error response: %s", resp.String())
}
return nil
}
func (c *EdgioClient) GetTlsCert(tlsCertId string) (*dtos.TLSCertResponse, error) {
token, err := c.getToken("app.config")
if err != nil {
return nil, fmt.Errorf("failed to get token: %w", err)
}
url := fmt.Sprintf("%s/config/v0.1/tls-certs/%s", c.apiURL, tlsCertId)
var tlsCertResponse dtos.TLSCertResponse
resp, err := c.client.R().
SetAuthToken(token).
SetResult(&tlsCertResponse).
Get(url)
if err != nil {
return nil, fmt.Errorf("error response: %s", err)
}
if resp.IsError() {
return nil, fmt.Errorf("error response: %s", resp.String())
}
return &tlsCertResponse, nil
}
func (c *EdgioClient) UploadTlsCert(req dtos.UploadTlsCertRequest) (*dtos.TLSCertResponse, error) {
token, err := c.getToken("app.config")
if err != nil {
return nil, fmt.Errorf("failed to get token: %w", err)
}
url := fmt.Sprintf("%s/config/v0.1/tls-certs", c.apiURL)
response := &dtos.TLSCertResponse{}
resp, err := c.client.R().
SetAuthToken(token).
SetHeader("Content-Type", "application/json").
SetBody(req).
SetResult(response).
Post(url)
if err != nil {
return nil, fmt.Errorf("failed to upload TLS certificate: %w", err)
}
if resp.IsError() {
return nil, fmt.Errorf("API responded with error: %s", resp.String())
}
return response, nil
}
func (c *EdgioClient) GenerateTlsCert(environmentId string) (*dtos.TLSCertResponse, error) {
token, err := c.getToken("app.config")
if err != nil {
return nil, fmt.Errorf("failed to get token: %w", err)
}
url := fmt.Sprintf("%s/config/v0.1/tls-certs/generate", c.apiURL)
request := map[string]interface{}{
"environment_id": environmentId,
}
response := &dtos.TLSCertResponse{}
resp, err := c.client.R().
SetAuthToken(token).
SetHeader("Content-Type", "application/json").
SetBody(request).
SetResult(response).
Post(url)
if err != nil {
return nil, fmt.Errorf("failed to upload TLS certificate: %w", err)
}
if resp.IsError() {
return nil, fmt.Errorf("API responded with error: %s", resp.String())
}
return response, nil
}
func (c *EdgioClient) GetTlsCerts(page int, pageSize int, environmentID string) (*dtos.TLSCertSResponse, error) {
token, err := c.getToken("app.config")
if err != nil {
return nil, fmt.Errorf("failed to get token: %w", err)
}
url := fmt.Sprintf("%s/config/v0.1/tls-certs", c.apiURL)
var tlsCertsResponse dtos.TLSCertSResponse
resp, err := c.client.R().
SetAuthToken(token).
SetQueryParams(map[string]string{
"page": fmt.Sprintf("%d", page),
"page_size": fmt.Sprintf("%d", pageSize),
"environment_id": environmentID,
}).
SetResult(&tlsCertsResponse).
Get(url)
if err != nil {
return nil, fmt.Errorf("failed to send request: %w", err)
}
if resp.IsError() {
return nil, fmt.Errorf("unexpected status code for getTlsCerts: %d", resp.StatusCode())
}
return &tlsCertsResponse, nil
}
func (c *EdgioClient) UploadCdnConfiguration(config *dtos.CDNConfiguration) (*dtos.CDNConfiguration, error) {
fmt.Println("------------------------------------------------------------------------- uploading")
token, err := c.getToken("app.config")
if err != nil {
return nil, fmt.Errorf("failed to get token: %w", err)
}
url := fmt.Sprintf("%s/config/v0.1/configs", c.apiURL)
var response dtos.CDNConfiguration
// Convert config to json
jsonBody, _ := json.MarshalIndent(config, "", " ")
jsonString := string(jsonBody)
fmt.Println("------------------------- config report code: ", config.Hostnames[0].ReportCode == nil)
fmt.Println("------------------------- config report code value: ", config.Hostnames[0].ReportCode)
fmt.Println("----------------------------------- jsonBody: ", jsonString)
resp, err := c.client.R().
SetAuthToken(token).
SetHeader("Content-Type", "application/json").
SetBody(config).
SetResult(&response).
Post(url)
if err != nil {
return nil, fmt.Errorf("failed to upload CDN configuration: %w", err)
}
if resp.IsError() {
return nil, fmt.Errorf("unexpected status code for uploadCdnConfiguration: %d, %s", resp.StatusCode(), resp.String())
}
return &response, nil
}
func (c *EdgioClient) GetCDNConfiguration(configID string) (*dtos.CDNConfiguration, error) {
fmt.Println("------------------------------------------------------------------------- reading config")
token, err := c.getToken("app.config")
if err != nil {
return nil, fmt.Errorf("failed to get token: %w", err)
}
url := fmt.Sprintf("https://edgioapis.com/config/v0.1/configs/%s", configID)
var response dtos.CDNConfiguration
resp, err := c.client.R().
SetAuthToken(token).
SetResult(&response).
Get(url)
if err != nil {
return nil, fmt.Errorf("failed to get CDN configuration: %w", err)
}
if resp.IsError() {
return nil, fmt.Errorf("unexpected status code for GetCDNConfiguration: %d", resp.StatusCode())
}
return &response, nil
}

View File

@@ -0,0 +1,26 @@
package edgio_api
import (
"context"
"github.com/Edgio/edgio-api/applications/v7/dtos"
)
type EdgioClientInterface interface {
GetProperty(ctx context.Context, propertyID string) (*dtos.Property, error)
GetProperties(page int, pageSize int, organizationID string) (*dtos.Properties, error)
CreateProperty(ctx context.Context, organizationID, slug string) (*dtos.Property, error)
DeleteProperty(propertyID string) error
UpdateProperty(ctx context.Context, propertyID string, slug string) (*dtos.Property, error)
GetEnvironments(page, pageSize int, propertyID string) (*dtos.EnvironmentsResponse, error)
GetEnvironment(environmentID string) (*dtos.Environment, error)
CreateEnvironment(propertyID, name string, onlyMaintainersCanDeploy, httpRequestLogging bool) (*dtos.Environment, error)
UpdateEnvironment(environmentID, name string, onlyMaintainersCanDeploy, httpRequestLogging, preserveCache bool) (*dtos.Environment, error)
DeleteEnvironment(environmentID string) error
GetTlsCert(tlsCertId string) (*dtos.TLSCertResponse, error)
UploadTlsCert(req dtos.UploadTlsCertRequest) (*dtos.TLSCertResponse, error)
GenerateTlsCert(environmentId string) (*dtos.TLSCertResponse, error)
GetTlsCerts(page int, pageSize int, environmentID string) (*dtos.TLSCertSResponse, error)
UploadCdnConfiguration(config *dtos.CDNConfiguration) (*dtos.CDNConfiguration, error)
GetCDNConfiguration(configID string) (*dtos.CDNConfiguration, error)
}