svc-api: переход на Gateway (lk-api-gateway), User-Agent+Referer для DDoS-Guard, метод Виталия /instanceOperations/default/{id} с dataDescriptor, LEGACY-пометки везде
This commit is contained in:
@@ -51,9 +51,12 @@ func (c *Client) GetService(serviceID int) (types.ServiceInfo, error) {
|
||||
return res.Service, nil
|
||||
}
|
||||
|
||||
// GetServiceOperation получает детали операции (параметры + MAN).
|
||||
// GetServiceOperation получает детали операции (параметры + dataDescriptor + valueList).
|
||||
// Использует /instanceOperations/default/{id} — метод Виталия (15.07.2026).
|
||||
// Этот эндпоинт возвращает dataDescriptor (подполя map-fixed) и valueList,
|
||||
// в отличие от /serviceOperation/{id} который даёт только плоский список.
|
||||
func (c *Client) GetServiceOperation(svcOperationID int) (types.ServiceOperationInfo, error) {
|
||||
endpoint := fmt.Sprintf("/serviceOperation/%d", svcOperationID)
|
||||
endpoint := fmt.Sprintf("/instanceOperations/default/%d", svcOperationID)
|
||||
var res types.ServiceOperationResponse
|
||||
if err := c.callAPI(endpoint, &res); err != nil {
|
||||
return types.ServiceOperationInfo{}, err
|
||||
@@ -84,7 +87,9 @@ func (c *Client) callAPI(endpoint string, out interface{}) error {
|
||||
if c.token != "" {
|
||||
req.Header.Set("Authorization", "Bearer "+c.token)
|
||||
}
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0 (compatible; Terraform-Provider-Nubes/Generator)")
|
||||
// DDoS-Guard требует браузерный User-Agent ВСЕГДА (и старый, и новый API).
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36")
|
||||
req.Header.Set("Referer", c.refererFromEndpoint())
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
lastErr = err
|
||||
@@ -123,6 +128,26 @@ func (c *Client) isProxyAPI() bool {
|
||||
return strings.Contains(c.endpoint, "index.cfm")
|
||||
}
|
||||
|
||||
// refererFromEndpoint извлекает Referer из URL API (DDoS-Guard).
|
||||
// lk-api-gateway-test.ngcloud.ru → https://deck-test.ngcloud.ru/
|
||||
// lk-api-gateway.ngcloud.ru → https://deck.ngcloud.ru/
|
||||
func (c *Client) refererFromEndpoint() string {
|
||||
ep := c.endpoint
|
||||
if strings.HasPrefix(ep, "https://") {
|
||||
ep = ep[8:]
|
||||
} else if strings.HasPrefix(ep, "http://") {
|
||||
ep = ep[7:]
|
||||
}
|
||||
host := ep
|
||||
if idx := strings.Index(host, "/"); idx >= 0 {
|
||||
host = host[:idx]
|
||||
}
|
||||
// lk-api-gateway-stand → deck-stand, lk-api-gateway → deck
|
||||
refererHost := strings.Replace(host, "lk-api-gateway-", "deck-", 1)
|
||||
refererHost = strings.Replace(refererHost, "lk-api-gateway.", "deck.", 1)
|
||||
return "https://" + refererHost + "/"
|
||||
}
|
||||
|
||||
// CollectOperations собирает и классифицирует все операции сервиса.
|
||||
func (c *Client) CollectOperations(ops []types.OperationInfo) ([]types.OperationSpec, bool, bool, bool, error) {
|
||||
result := make([]types.OperationSpec, 0, len(ops))
|
||||
@@ -140,7 +165,7 @@ func (c *Client) CollectOperations(ops []types.OperationInfo) ([]types.Operation
|
||||
}
|
||||
params := make([]types.ParamSpec, 0, len(opInfo.CfsParams))
|
||||
for _, p := range opInfo.CfsParams {
|
||||
params = append(params, types.ParamSpec{
|
||||
param := types.ParamSpec{
|
||||
ID: p.ID,
|
||||
Code: p.Code,
|
||||
DataType: strings.TrimSpace(p.DataType),
|
||||
@@ -161,7 +186,32 @@ func (c *Client) CollectOperations(ops []types.OperationInfo) ([]types.Operation
|
||||
DependsOn: p.DependsOnCfsParams,
|
||||
IsModifiable: p.IsModifiable,
|
||||
IsSensitive: p.IsSensitive,
|
||||
})
|
||||
}
|
||||
|
||||
// Разворачиваем dataDescriptor в SubParams (подполя map-fixed/array-map-fixed).
|
||||
if len(p.DataDescriptor) > 0 {
|
||||
subParams := make([]types.ParamSpec, 0, len(p.DataDescriptor))
|
||||
for code, sub := range p.DataDescriptor {
|
||||
subParams = append(subParams, types.ParamSpec{
|
||||
ID: sub.SubParamID,
|
||||
Code: code,
|
||||
DataType: strings.TrimSpace(sub.DataType),
|
||||
Required: sub.IsRequired,
|
||||
Default: normalizeDefault(sub.DefaultValue),
|
||||
// valueList в dataDescriptor — может быть строкой (comma-sep) или массивом
|
||||
ValueList: normalizeSubValueList(sub.ValueList),
|
||||
Descr: strings.TrimSpace(sub.Descr),
|
||||
Man: strings.TrimSpace(sub.Man),
|
||||
Regex: strings.TrimSpace(sub.Regex),
|
||||
// isModifiableDefinition → IsModifiable
|
||||
IsModifiable: boolPtr(sub.IsModifiableDefinition),
|
||||
IsSensitive: sub.IsSensitive,
|
||||
})
|
||||
}
|
||||
param.SubParams = subParams
|
||||
}
|
||||
|
||||
params = append(params, param)
|
||||
}
|
||||
sort.Slice(params, func(i, j int) bool { return params[i].ID < params[j].ID })
|
||||
|
||||
@@ -232,3 +282,48 @@ func normalizeDefault(value interface{}) interface{} {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
// normalizeSubValueList — приводит valueList из dataDescriptor к []string.
|
||||
// API может вернуть строку (comma-separated), массив, или null.
|
||||
func normalizeSubValueList(v interface{}) []string {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
switch t := v.(type) {
|
||||
case string:
|
||||
return splitCommaList(t)
|
||||
case []interface{}:
|
||||
out := make([]string, 0, len(t))
|
||||
for _, item := range t {
|
||||
out = append(out, fmt.Sprintf("%v", item))
|
||||
}
|
||||
return out
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// splitCommaList — разбивает comma-separated строку из dataDescriptor в []string.
|
||||
func splitCommaList(s string) []string {
|
||||
s = strings.TrimSpace(s)
|
||||
if s == "" {
|
||||
return nil
|
||||
}
|
||||
parts := strings.Split(s, ",")
|
||||
out := make([]string, 0, len(parts))
|
||||
for _, p := range parts {
|
||||
p = strings.TrimSpace(p)
|
||||
if p != "" {
|
||||
out = append(out, p)
|
||||
}
|
||||
}
|
||||
if len(out) == 0 {
|
||||
return nil
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// boolPtr — возвращает указатель на bool.
|
||||
func boolPtr(b bool) *bool {
|
||||
return &b
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user