package remnawave import ( "context" "encoding/json" "fmt" "net/http" "time" ) type CreateUserInput struct { Username string ExpireAt time.Time TelegramID *int64 ExternalSquadUUID *string ActiveInternalSquads []string TrafficLimitBytes *int64 Description string } type PanelUser struct { UUID string Username string ShortUUID string Status string ExpireAt time.Time SubscriptionURL string } func (c *Client) CreateUser(ctx context.Context, in CreateUserInput) (*PanelUser, error) { payload := map[string]any{ "username": in.Username, "expireAt": in.ExpireAt.UTC().Format(time.RFC3339Nano), "status": "ACTIVE", } if in.TelegramID != nil { payload["telegramId"] = *in.TelegramID } if in.ExternalSquadUUID != nil && *in.ExternalSquadUUID != "" { payload["externalSquadUuid"] = *in.ExternalSquadUUID } if len(in.ActiveInternalSquads) > 0 { payload["activeInternalSquads"] = in.ActiveInternalSquads } if in.TrafficLimitBytes != nil { payload["trafficLimitBytes"] = *in.TrafficLimitBytes } if in.Description != "" { payload["description"] = in.Description } resp, body, err := c.post(ctx, "/api/users", payload) if err != nil { return nil, err } if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated { return nil, apiError(resp.StatusCode, body) } return parsePanelUser(body), nil } type AssignSquadsInput struct { UUID string Username string ExternalSquadUUID *string ActiveInternalSquads []string } func (c *Client) AssignSquads(ctx context.Context, in AssignSquadsInput) (*PanelUser, error) { if in.UUID == "" && in.Username == "" { return nil, fmt.Errorf("нужен uuid или username") } payload := map[string]any{} if in.UUID != "" { payload["uuid"] = in.UUID } else { payload["username"] = in.Username } if in.ExternalSquadUUID != nil { payload["externalSquadUuid"] = in.ExternalSquadUUID } if in.ActiveInternalSquads != nil { payload["activeInternalSquads"] = in.ActiveInternalSquads } resp, body, err := c.patch(ctx, "/api/users", payload) if err != nil { return nil, err } if resp.StatusCode != http.StatusOK { return nil, apiError(resp.StatusCode, body) } return parsePanelUser(body), nil } func parsePanelUser(body []byte) *PanelUser { var wrap struct { Response map[string]json.RawMessage `json:"response"` } if json.Unmarshal(body, &wrap) != nil || wrap.Response == nil { return nil } u := &PanelUser{} if raw, ok := wrap.Response["uuid"]; ok { _ = json.Unmarshal(raw, &u.UUID) } if raw, ok := wrap.Response["username"]; ok { _ = json.Unmarshal(raw, &u.Username) } if raw, ok := wrap.Response["shortUuid"]; ok { _ = json.Unmarshal(raw, &u.ShortUUID) } if raw, ok := wrap.Response["status"]; ok { _ = json.Unmarshal(raw, &u.Status) } if raw, ok := wrap.Response["expireAt"]; ok { var s string if json.Unmarshal(raw, &s) == nil { if t, err := time.Parse(time.RFC3339Nano, s); err == nil { u.ExpireAt = t } } } if raw, ok := wrap.Response["subscriptionUrl"]; ok { _ = json.Unmarshal(raw, &u.SubscriptionURL) } return u }