diff --git a/server/Makefile b/server/Makefile index 792a8ff..fdbc3b3 100644 --- a/server/Makefile +++ b/server/Makefile @@ -1,4 +1,4 @@ -.PHONY: build test test-unit test-integration vet lint generate migrate-up migrate-down build-codegen deps +.PHONY: build test test-unit test-integration vet lint generate check-generate migrate-up migrate-down build-codegen deps # ── deps ─────────────────────────────────────────────────────────────────────── deps: @@ -30,7 +30,11 @@ lint: # ── generate ─────────────────────────────────────────────────────────────────── generate: - @echo "generate: not yet configured (see task 1d)" + go generate ./... + +check-generate: ## CI guard: regenerate and fail if generated files changed + go generate ./... + git diff --exit-code server/api/gen # ── migrate ──────────────────────────────────────────────────────────────────── migrate-up: diff --git a/server/api/cfg-client.yaml b/server/api/cfg-client.yaml new file mode 100644 index 0000000..47e3b26 --- /dev/null +++ b/server/api/cfg-client.yaml @@ -0,0 +1,5 @@ +package: gen +generate: + client: true + embedded-spec: false +output: client.gen.go diff --git a/server/api/cfg-server.yaml b/server/api/cfg-server.yaml new file mode 100644 index 0000000..7c1e998 --- /dev/null +++ b/server/api/cfg-server.yaml @@ -0,0 +1,5 @@ +package: gen +generate: + chi-server: true + embedded-spec: true +output: server.gen.go diff --git a/server/api/cfg-types.yaml b/server/api/cfg-types.yaml new file mode 100644 index 0000000..22199fe --- /dev/null +++ b/server/api/cfg-types.yaml @@ -0,0 +1,5 @@ +package: gen +generate: + models: true + embedded-spec: false +output: types.gen.go diff --git a/server/api/gen/client.gen.go b/server/api/gen/client.gen.go new file mode 100644 index 0000000..35d9925 --- /dev/null +++ b/server/api/gen/client.gen.go @@ -0,0 +1,2565 @@ +// Package gen provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.7.1 DO NOT EDIT. +package gen + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strings" + + "github.com/oapi-codegen/runtime" + openapi_types "github.com/oapi-codegen/runtime/types" +) + +// RequestEditorFn is the function signature for the RequestEditor callback function +type RequestEditorFn func(ctx context.Context, req *http.Request) error + +// Doer performs HTTP requests. +// +// The standard http.Client implements this interface. +type HttpRequestDoer interface { + Do(req *http.Request) (*http.Response, error) +} + +// Client which conforms to the OpenAPI3 specification for this service. +type Client struct { + // The endpoint of the server conforming to this interface, with scheme, + // https://api.deepmap.com for example. This can contain a path relative + // to the server, such as https://api.deepmap.com/dev-test, and all the + // paths in the swagger spec will be appended to the server. + Server string + + // Doer for performing requests, typically a *http.Client with any + // customized settings, such as certificate chains. + Client HttpRequestDoer + + // A list of callbacks for modifying requests which are generated before sending over + // the network. + RequestEditors []RequestEditorFn +} + +// ClientOption allows setting custom parameters during construction +type ClientOption func(*Client) error + +// Creates a new Client, with reasonable defaults +func NewClient(server string, opts ...ClientOption) (*Client, error) { + // create a client with sane default values + client := Client{ + Server: server, + } + // mutate client and add all optional params + for _, o := range opts { + if err := o(&client); err != nil { + return nil, err + } + } + // ensure the server URL always has a trailing slash + if !strings.HasSuffix(client.Server, "/") { + client.Server += "/" + } + // create httpClient, if not already present + if client.Client == nil { + client.Client = &http.Client{} + } + return &client, nil +} + +// WithHTTPClient allows overriding the default Doer, which is +// automatically created using http.Client. This is useful for tests. +func WithHTTPClient(doer HttpRequestDoer) ClientOption { + return func(c *Client) error { + c.Client = doer + return nil + } +} + +// WithRequestEditorFn allows setting up a callback function, which will be +// called right before sending the request. This can be used to mutate the request. +func WithRequestEditorFn(fn RequestEditorFn) ClientOption { + return func(c *Client) error { + c.RequestEditors = append(c.RequestEditors, fn) + return nil + } +} + +// The interface specification for the client above. +type ClientInterface interface { + // AdsUnlockWithBody request with any body + AdsUnlockWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + AdsUnlock(ctx context.Context, body AdsUnlockJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // SendVerificationCodeWithBody request with any body + SendVerificationCodeWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + SendVerificationCode(ctx context.Context, body SendVerificationCodeJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // LoginWithBody request with any body + LoginWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + Login(ctx context.Context, body LoginJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // RefreshTokenWithBody request with any body + RefreshTokenWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + RefreshToken(ctx context.Context, body RefreshTokenJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // RegisterWithBody request with any body + RegisterWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + Register(ctx context.Context, body RegisterJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetMe request + GetMe(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // ListDevices request + ListDevices(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // DeleteDevice request + DeleteDevice(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) + + // ListNodes request + ListNodes(ctx context.Context, params *ListNodesParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // ConnectNodeWithBody request with any body + ConnectNodeWithBody(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + ConnectNode(ctx context.Context, id openapi_types.UUID, body ConnectNodeJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // DisconnectNodeWithBody request with any body + DisconnectNodeWithBody(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + DisconnectNode(ctx context.Context, id openapi_types.UUID, body DisconnectNodeJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // ListNotices request + ListNotices(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // ListPlans request + ListPlans(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // RedeemCodeWithBody request with any body + RedeemCodeWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + RedeemCode(ctx context.Context, body RedeemCodeJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetUsage request + GetUsage(ctx context.Context, params *GetUsageParams, reqEditors ...RequestEditorFn) (*http.Response, error) +} + +func (c *Client) AdsUnlockWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewAdsUnlockRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) AdsUnlock(ctx context.Context, body AdsUnlockJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewAdsUnlockRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) SendVerificationCodeWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewSendVerificationCodeRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) SendVerificationCode(ctx context.Context, body SendVerificationCodeJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewSendVerificationCodeRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) LoginWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewLoginRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) Login(ctx context.Context, body LoginJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewLoginRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) RefreshTokenWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewRefreshTokenRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) RefreshToken(ctx context.Context, body RefreshTokenJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewRefreshTokenRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) RegisterWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewRegisterRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) Register(ctx context.Context, body RegisterJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewRegisterRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) GetMe(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetMeRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) ListDevices(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewListDevicesRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) DeleteDevice(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteDeviceRequest(c.Server, id) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) ListNodes(ctx context.Context, params *ListNodesParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewListNodesRequest(c.Server, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) ConnectNodeWithBody(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewConnectNodeRequestWithBody(c.Server, id, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) ConnectNode(ctx context.Context, id openapi_types.UUID, body ConnectNodeJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewConnectNodeRequest(c.Server, id, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) DisconnectNodeWithBody(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDisconnectNodeRequestWithBody(c.Server, id, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) DisconnectNode(ctx context.Context, id openapi_types.UUID, body DisconnectNodeJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDisconnectNodeRequest(c.Server, id, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) ListNotices(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewListNoticesRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) ListPlans(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewListPlansRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) RedeemCodeWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewRedeemCodeRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) RedeemCode(ctx context.Context, body RedeemCodeJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewRedeemCodeRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) GetUsage(ctx context.Context, params *GetUsageParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetUsageRequest(c.Server, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// NewAdsUnlockRequest calls the generic AdsUnlock builder with application/json body +func NewAdsUnlockRequest(server string, body AdsUnlockJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewAdsUnlockRequestWithBody(server, "application/json", bodyReader) +} + +// NewAdsUnlockRequestWithBody generates requests for AdsUnlock with any type of body +func NewAdsUnlockRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/ads/unlock") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewSendVerificationCodeRequest calls the generic SendVerificationCode builder with application/json body +func NewSendVerificationCodeRequest(server string, body SendVerificationCodeJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewSendVerificationCodeRequestWithBody(server, "application/json", bodyReader) +} + +// NewSendVerificationCodeRequestWithBody generates requests for SendVerificationCode with any type of body +func NewSendVerificationCodeRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/auth/code") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewLoginRequest calls the generic Login builder with application/json body +func NewLoginRequest(server string, body LoginJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewLoginRequestWithBody(server, "application/json", bodyReader) +} + +// NewLoginRequestWithBody generates requests for Login with any type of body +func NewLoginRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/auth/login") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewRefreshTokenRequest calls the generic RefreshToken builder with application/json body +func NewRefreshTokenRequest(server string, body RefreshTokenJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewRefreshTokenRequestWithBody(server, "application/json", bodyReader) +} + +// NewRefreshTokenRequestWithBody generates requests for RefreshToken with any type of body +func NewRefreshTokenRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/auth/refresh") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewRegisterRequest calls the generic Register builder with application/json body +func NewRegisterRequest(server string, body RegisterJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewRegisterRequestWithBody(server, "application/json", bodyReader) +} + +// NewRegisterRequestWithBody generates requests for Register with any type of body +func NewRegisterRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/auth/register") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGetMeRequest generates requests for GetMe +func NewGetMeRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/me") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewListDevicesRequest generates requests for ListDevices +func NewListDevicesRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/me/devices") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewDeleteDeviceRequest generates requests for DeleteDevice +func NewDeleteDeviceRequest(server string, id openapi_types.UUID) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/me/devices/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewListNodesRequest generates requests for ListNodes +func NewListNodesRequest(server string, params *ListNodesParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/nodes") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.IfVersion != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "if_version", *params.IfVersion, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: "int64"}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewConnectNodeRequest calls the generic ConnectNode builder with application/json body +func NewConnectNodeRequest(server string, id openapi_types.UUID, body ConnectNodeJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewConnectNodeRequestWithBody(server, id, "application/json", bodyReader) +} + +// NewConnectNodeRequestWithBody generates requests for ConnectNode with any type of body +func NewConnectNodeRequestWithBody(server string, id openapi_types.UUID, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/nodes/%s/connect", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewDisconnectNodeRequest calls the generic DisconnectNode builder with application/json body +func NewDisconnectNodeRequest(server string, id openapi_types.UUID, body DisconnectNodeJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewDisconnectNodeRequestWithBody(server, id, "application/json", bodyReader) +} + +// NewDisconnectNodeRequestWithBody generates requests for DisconnectNode with any type of body +func NewDisconnectNodeRequestWithBody(server string, id openapi_types.UUID, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/nodes/%s/disconnect", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewListNoticesRequest generates requests for ListNotices +func NewListNoticesRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/notices") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewListPlansRequest generates requests for ListPlans +func NewListPlansRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/plans") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewRedeemCodeRequest calls the generic RedeemCode builder with application/json body +func NewRedeemCodeRequest(server string, body RedeemCodeJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewRedeemCodeRequestWithBody(server, "application/json", bodyReader) +} + +// NewRedeemCodeRequestWithBody generates requests for RedeemCode with any type of body +func NewRedeemCodeRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/redeem") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGetUsageRequest generates requests for GetUsage +func NewGetUsageRequest(server string, params *GetUsageParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/usage") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.Days != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "days", *params.Days, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +func (c *Client) applyEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error { + for _, r := range c.RequestEditors { + if err := r(ctx, req); err != nil { + return err + } + } + for _, r := range additionalEditors { + if err := r(ctx, req); err != nil { + return err + } + } + return nil +} + +// ClientWithResponses builds on ClientInterface to offer response payloads +type ClientWithResponses struct { + ClientInterface +} + +// NewClientWithResponses creates a new ClientWithResponses, which wraps +// Client with return type handling +func NewClientWithResponses(server string, opts ...ClientOption) (*ClientWithResponses, error) { + client, err := NewClient(server, opts...) + if err != nil { + return nil, err + } + return &ClientWithResponses{client}, nil +} + +// WithBaseURL overrides the baseURL. +func WithBaseURL(baseURL string) ClientOption { + return func(c *Client) error { + newBaseURL, err := url.Parse(baseURL) + if err != nil { + return err + } + c.Server = newBaseURL.String() + return nil + } +} + +// ClientWithResponsesInterface is the interface specification for the client with responses above. +type ClientWithResponsesInterface interface { + // AdsUnlockWithBodyWithResponse request with any body + AdsUnlockWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*AdsUnlockResponse, error) + + AdsUnlockWithResponse(ctx context.Context, body AdsUnlockJSONRequestBody, reqEditors ...RequestEditorFn) (*AdsUnlockResponse, error) + + // SendVerificationCodeWithBodyWithResponse request with any body + SendVerificationCodeWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SendVerificationCodeResponse, error) + + SendVerificationCodeWithResponse(ctx context.Context, body SendVerificationCodeJSONRequestBody, reqEditors ...RequestEditorFn) (*SendVerificationCodeResponse, error) + + // LoginWithBodyWithResponse request with any body + LoginWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*LoginResponse, error) + + LoginWithResponse(ctx context.Context, body LoginJSONRequestBody, reqEditors ...RequestEditorFn) (*LoginResponse, error) + + // RefreshTokenWithBodyWithResponse request with any body + RefreshTokenWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*RefreshTokenResponse, error) + + RefreshTokenWithResponse(ctx context.Context, body RefreshTokenJSONRequestBody, reqEditors ...RequestEditorFn) (*RefreshTokenResponse, error) + + // RegisterWithBodyWithResponse request with any body + RegisterWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*RegisterResponse, error) + + RegisterWithResponse(ctx context.Context, body RegisterJSONRequestBody, reqEditors ...RequestEditorFn) (*RegisterResponse, error) + + // GetMeWithResponse request + GetMeWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetMeResponse, error) + + // ListDevicesWithResponse request + ListDevicesWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*ListDevicesResponse, error) + + // DeleteDeviceWithResponse request + DeleteDeviceWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*DeleteDeviceResponse, error) + + // ListNodesWithResponse request + ListNodesWithResponse(ctx context.Context, params *ListNodesParams, reqEditors ...RequestEditorFn) (*ListNodesResponse, error) + + // ConnectNodeWithBodyWithResponse request with any body + ConnectNodeWithBodyWithResponse(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ConnectNodeResponse, error) + + ConnectNodeWithResponse(ctx context.Context, id openapi_types.UUID, body ConnectNodeJSONRequestBody, reqEditors ...RequestEditorFn) (*ConnectNodeResponse, error) + + // DisconnectNodeWithBodyWithResponse request with any body + DisconnectNodeWithBodyWithResponse(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*DisconnectNodeResponse, error) + + DisconnectNodeWithResponse(ctx context.Context, id openapi_types.UUID, body DisconnectNodeJSONRequestBody, reqEditors ...RequestEditorFn) (*DisconnectNodeResponse, error) + + // ListNoticesWithResponse request + ListNoticesWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*ListNoticesResponse, error) + + // ListPlansWithResponse request + ListPlansWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*ListPlansResponse, error) + + // RedeemCodeWithBodyWithResponse request with any body + RedeemCodeWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*RedeemCodeResponse, error) + + RedeemCodeWithResponse(ctx context.Context, body RedeemCodeJSONRequestBody, reqEditors ...RequestEditorFn) (*RedeemCodeResponse, error) + + // GetUsageWithResponse request + GetUsageWithResponse(ctx context.Context, params *GetUsageParams, reqEditors ...RequestEditorFn) (*GetUsageResponse, error) +} + +type AdsUnlockResponse struct { + Body []byte + HTTPResponse *http.Response + JSON400 *BadRequest + JSON401 *Unauthorized + JSON403 *Forbidden + JSON429 *TooManyRequests + JSON500 *Internal +} + +// Status returns HTTPResponse.Status +func (r AdsUnlockResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r AdsUnlockResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r AdsUnlockResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type SendVerificationCodeResponse struct { + Body []byte + HTTPResponse *http.Response + JSON400 *BadRequest + JSON429 *TooManyRequests + JSON500 *Internal +} + +// Status returns HTTPResponse.Status +func (r SendVerificationCodeResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r SendVerificationCodeResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r SendVerificationCodeResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type LoginResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *TokenPair + JSON400 *BadRequest + JSON401 *Unauthorized + JSON429 *TooManyRequests + JSON500 *Internal +} + +// Status returns HTTPResponse.Status +func (r LoginResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r LoginResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r LoginResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type RefreshTokenResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *TokenPair + JSON400 *BadRequest + JSON401 *Unauthorized + JSON429 *TooManyRequests + JSON500 *Internal +} + +// Status returns HTTPResponse.Status +func (r RefreshTokenResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r RefreshTokenResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r RefreshTokenResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type RegisterResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *TokenPair + JSON400 *BadRequest + JSON409 *Conflict + JSON429 *TooManyRequests + JSON500 *Internal +} + +// Status returns HTTPResponse.Status +func (r RegisterResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r RegisterResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r RegisterResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetMeResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *Me + JSON401 *Unauthorized + JSON500 *Internal +} + +// Status returns HTTPResponse.Status +func (r GetMeResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetMeResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetMeResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type ListDevicesResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *struct { + Devices []Device `json:"devices"` + } + JSON401 *Unauthorized + JSON500 *Internal +} + +// Status returns HTTPResponse.Status +func (r ListDevicesResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r ListDevicesResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r ListDevicesResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type DeleteDeviceResponse struct { + Body []byte + HTTPResponse *http.Response + JSON401 *Unauthorized + JSON403 *Forbidden + JSON404 *NotFound + JSON500 *Internal +} + +// Status returns HTTPResponse.Status +func (r DeleteDeviceResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DeleteDeviceResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeleteDeviceResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type ListNodesResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *NodeDirectory + JSON401 *Unauthorized + JSON500 *Internal +} + +// Status returns HTTPResponse.Status +func (r ListNodesResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r ListNodesResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r ListNodesResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type ConnectNodeResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *ConnectCredential + JSON400 *BadRequest + JSON401 *Unauthorized + JSON403 *Forbidden + JSON404 *NotFound + JSON429 *TooManyRequests + JSON500 *Internal +} + +// Status returns HTTPResponse.Status +func (r ConnectNodeResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r ConnectNodeResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r ConnectNodeResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type DisconnectNodeResponse struct { + Body []byte + HTTPResponse *http.Response + JSON400 *BadRequest + JSON401 *Unauthorized + JSON404 *NotFound + JSON500 *Internal +} + +// Status returns HTTPResponse.Status +func (r DisconnectNodeResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DisconnectNodeResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DisconnectNodeResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type ListNoticesResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *struct { + Notices []Notice `json:"notices"` + } + JSON401 *Unauthorized + JSON500 *Internal +} + +// Status returns HTTPResponse.Status +func (r ListNoticesResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r ListNoticesResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r ListNoticesResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type ListPlansResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *struct { + Plans []Plan `json:"plans"` + } + JSON401 *Unauthorized + JSON500 *Internal +} + +// Status returns HTTPResponse.Status +func (r ListPlansResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r ListPlansResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r ListPlansResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type RedeemCodeResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *RedeemResult + JSON400 *BadRequest + JSON401 *Unauthorized + JSON404 *NotFound + JSON409 *Conflict + JSON429 *TooManyRequests + JSON500 *Internal +} + +// Status returns HTTPResponse.Status +func (r RedeemCodeResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r RedeemCodeResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r RedeemCodeResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetUsageResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *struct { + Points []UsagePoint `json:"points"` + } + JSON400 *BadRequest + JSON401 *Unauthorized + JSON500 *Internal +} + +// Status returns HTTPResponse.Status +func (r GetUsageResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetUsageResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetUsageResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +// AdsUnlockWithBodyWithResponse request with arbitrary body returning *AdsUnlockResponse +func (c *ClientWithResponses) AdsUnlockWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*AdsUnlockResponse, error) { + rsp, err := c.AdsUnlockWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseAdsUnlockResponse(rsp) +} + +func (c *ClientWithResponses) AdsUnlockWithResponse(ctx context.Context, body AdsUnlockJSONRequestBody, reqEditors ...RequestEditorFn) (*AdsUnlockResponse, error) { + rsp, err := c.AdsUnlock(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseAdsUnlockResponse(rsp) +} + +// SendVerificationCodeWithBodyWithResponse request with arbitrary body returning *SendVerificationCodeResponse +func (c *ClientWithResponses) SendVerificationCodeWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SendVerificationCodeResponse, error) { + rsp, err := c.SendVerificationCodeWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseSendVerificationCodeResponse(rsp) +} + +func (c *ClientWithResponses) SendVerificationCodeWithResponse(ctx context.Context, body SendVerificationCodeJSONRequestBody, reqEditors ...RequestEditorFn) (*SendVerificationCodeResponse, error) { + rsp, err := c.SendVerificationCode(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseSendVerificationCodeResponse(rsp) +} + +// LoginWithBodyWithResponse request with arbitrary body returning *LoginResponse +func (c *ClientWithResponses) LoginWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*LoginResponse, error) { + rsp, err := c.LoginWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseLoginResponse(rsp) +} + +func (c *ClientWithResponses) LoginWithResponse(ctx context.Context, body LoginJSONRequestBody, reqEditors ...RequestEditorFn) (*LoginResponse, error) { + rsp, err := c.Login(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseLoginResponse(rsp) +} + +// RefreshTokenWithBodyWithResponse request with arbitrary body returning *RefreshTokenResponse +func (c *ClientWithResponses) RefreshTokenWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*RefreshTokenResponse, error) { + rsp, err := c.RefreshTokenWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseRefreshTokenResponse(rsp) +} + +func (c *ClientWithResponses) RefreshTokenWithResponse(ctx context.Context, body RefreshTokenJSONRequestBody, reqEditors ...RequestEditorFn) (*RefreshTokenResponse, error) { + rsp, err := c.RefreshToken(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseRefreshTokenResponse(rsp) +} + +// RegisterWithBodyWithResponse request with arbitrary body returning *RegisterResponse +func (c *ClientWithResponses) RegisterWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*RegisterResponse, error) { + rsp, err := c.RegisterWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseRegisterResponse(rsp) +} + +func (c *ClientWithResponses) RegisterWithResponse(ctx context.Context, body RegisterJSONRequestBody, reqEditors ...RequestEditorFn) (*RegisterResponse, error) { + rsp, err := c.Register(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseRegisterResponse(rsp) +} + +// GetMeWithResponse request returning *GetMeResponse +func (c *ClientWithResponses) GetMeWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetMeResponse, error) { + rsp, err := c.GetMe(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetMeResponse(rsp) +} + +// ListDevicesWithResponse request returning *ListDevicesResponse +func (c *ClientWithResponses) ListDevicesWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*ListDevicesResponse, error) { + rsp, err := c.ListDevices(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseListDevicesResponse(rsp) +} + +// DeleteDeviceWithResponse request returning *DeleteDeviceResponse +func (c *ClientWithResponses) DeleteDeviceWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*DeleteDeviceResponse, error) { + rsp, err := c.DeleteDevice(ctx, id, reqEditors...) + if err != nil { + return nil, err + } + return ParseDeleteDeviceResponse(rsp) +} + +// ListNodesWithResponse request returning *ListNodesResponse +func (c *ClientWithResponses) ListNodesWithResponse(ctx context.Context, params *ListNodesParams, reqEditors ...RequestEditorFn) (*ListNodesResponse, error) { + rsp, err := c.ListNodes(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseListNodesResponse(rsp) +} + +// ConnectNodeWithBodyWithResponse request with arbitrary body returning *ConnectNodeResponse +func (c *ClientWithResponses) ConnectNodeWithBodyWithResponse(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ConnectNodeResponse, error) { + rsp, err := c.ConnectNodeWithBody(ctx, id, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseConnectNodeResponse(rsp) +} + +func (c *ClientWithResponses) ConnectNodeWithResponse(ctx context.Context, id openapi_types.UUID, body ConnectNodeJSONRequestBody, reqEditors ...RequestEditorFn) (*ConnectNodeResponse, error) { + rsp, err := c.ConnectNode(ctx, id, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseConnectNodeResponse(rsp) +} + +// DisconnectNodeWithBodyWithResponse request with arbitrary body returning *DisconnectNodeResponse +func (c *ClientWithResponses) DisconnectNodeWithBodyWithResponse(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*DisconnectNodeResponse, error) { + rsp, err := c.DisconnectNodeWithBody(ctx, id, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseDisconnectNodeResponse(rsp) +} + +func (c *ClientWithResponses) DisconnectNodeWithResponse(ctx context.Context, id openapi_types.UUID, body DisconnectNodeJSONRequestBody, reqEditors ...RequestEditorFn) (*DisconnectNodeResponse, error) { + rsp, err := c.DisconnectNode(ctx, id, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseDisconnectNodeResponse(rsp) +} + +// ListNoticesWithResponse request returning *ListNoticesResponse +func (c *ClientWithResponses) ListNoticesWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*ListNoticesResponse, error) { + rsp, err := c.ListNotices(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseListNoticesResponse(rsp) +} + +// ListPlansWithResponse request returning *ListPlansResponse +func (c *ClientWithResponses) ListPlansWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*ListPlansResponse, error) { + rsp, err := c.ListPlans(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseListPlansResponse(rsp) +} + +// RedeemCodeWithBodyWithResponse request with arbitrary body returning *RedeemCodeResponse +func (c *ClientWithResponses) RedeemCodeWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*RedeemCodeResponse, error) { + rsp, err := c.RedeemCodeWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseRedeemCodeResponse(rsp) +} + +func (c *ClientWithResponses) RedeemCodeWithResponse(ctx context.Context, body RedeemCodeJSONRequestBody, reqEditors ...RequestEditorFn) (*RedeemCodeResponse, error) { + rsp, err := c.RedeemCode(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseRedeemCodeResponse(rsp) +} + +// GetUsageWithResponse request returning *GetUsageResponse +func (c *ClientWithResponses) GetUsageWithResponse(ctx context.Context, params *GetUsageParams, reqEditors ...RequestEditorFn) (*GetUsageResponse, error) { + rsp, err := c.GetUsage(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetUsageResponse(rsp) +} + +// ParseAdsUnlockResponse parses an HTTP response from a AdsUnlockWithResponse call +func ParseAdsUnlockResponse(rsp *http.Response) (*AdsUnlockResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &AdsUnlockResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest BadRequest + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest Unauthorized + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest Forbidden + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: + var dest TooManyRequests + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON429 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest Internal + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseSendVerificationCodeResponse parses an HTTP response from a SendVerificationCodeWithResponse call +func ParseSendVerificationCodeResponse(rsp *http.Response) (*SendVerificationCodeResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &SendVerificationCodeResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest BadRequest + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: + var dest TooManyRequests + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON429 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest Internal + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseLoginResponse parses an HTTP response from a LoginWithResponse call +func ParseLoginResponse(rsp *http.Response) (*LoginResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &LoginResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TokenPair + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest BadRequest + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest Unauthorized + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: + var dest TooManyRequests + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON429 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest Internal + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseRefreshTokenResponse parses an HTTP response from a RefreshTokenWithResponse call +func ParseRefreshTokenResponse(rsp *http.Response) (*RefreshTokenResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &RefreshTokenResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TokenPair + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest BadRequest + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest Unauthorized + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: + var dest TooManyRequests + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON429 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest Internal + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseRegisterResponse parses an HTTP response from a RegisterWithResponse call +func ParseRegisterResponse(rsp *http.Response) (*RegisterResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &RegisterResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TokenPair + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest BadRequest + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 409: + var dest Conflict + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON409 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: + var dest TooManyRequests + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON429 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest Internal + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseGetMeResponse parses an HTTP response from a GetMeWithResponse call +func ParseGetMeResponse(rsp *http.Response) (*GetMeResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetMeResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest Me + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest Unauthorized + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest Internal + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseListDevicesResponse parses an HTTP response from a ListDevicesWithResponse call +func ParseListDevicesResponse(rsp *http.Response) (*ListDevicesResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &ListDevicesResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest struct { + Devices []Device `json:"devices"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest Unauthorized + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest Internal + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseDeleteDeviceResponse parses an HTTP response from a DeleteDeviceWithResponse call +func ParseDeleteDeviceResponse(rsp *http.Response) (*DeleteDeviceResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DeleteDeviceResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest Unauthorized + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest Forbidden + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest NotFound + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest Internal + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseListNodesResponse parses an HTTP response from a ListNodesWithResponse call +func ParseListNodesResponse(rsp *http.Response) (*ListNodesResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &ListNodesResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest NodeDirectory + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest Unauthorized + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest Internal + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseConnectNodeResponse parses an HTTP response from a ConnectNodeWithResponse call +func ParseConnectNodeResponse(rsp *http.Response) (*ConnectNodeResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &ConnectNodeResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ConnectCredential + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest BadRequest + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest Unauthorized + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest Forbidden + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest NotFound + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: + var dest TooManyRequests + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON429 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest Internal + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseDisconnectNodeResponse parses an HTTP response from a DisconnectNodeWithResponse call +func ParseDisconnectNodeResponse(rsp *http.Response) (*DisconnectNodeResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DisconnectNodeResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest BadRequest + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest Unauthorized + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest NotFound + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest Internal + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseListNoticesResponse parses an HTTP response from a ListNoticesWithResponse call +func ParseListNoticesResponse(rsp *http.Response) (*ListNoticesResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &ListNoticesResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest struct { + Notices []Notice `json:"notices"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest Unauthorized + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest Internal + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseListPlansResponse parses an HTTP response from a ListPlansWithResponse call +func ParseListPlansResponse(rsp *http.Response) (*ListPlansResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &ListPlansResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest struct { + Plans []Plan `json:"plans"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest Unauthorized + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest Internal + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseRedeemCodeResponse parses an HTTP response from a RedeemCodeWithResponse call +func ParseRedeemCodeResponse(rsp *http.Response) (*RedeemCodeResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &RedeemCodeResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest RedeemResult + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest BadRequest + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest Unauthorized + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest NotFound + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 409: + var dest Conflict + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON409 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: + var dest TooManyRequests + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON429 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest Internal + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseGetUsageResponse parses an HTTP response from a GetUsageWithResponse call +func ParseGetUsageResponse(rsp *http.Response) (*GetUsageResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetUsageResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest struct { + Points []UsagePoint `json:"points"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest BadRequest + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest Unauthorized + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest Internal + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} diff --git a/server/api/gen/gen.go b/server/api/gen/gen.go new file mode 100644 index 0000000..55de12d --- /dev/null +++ b/server/api/gen/gen.go @@ -0,0 +1,7 @@ +// Package gen contains oapi-codegen generated files for the Pangolin API. +// Run `go generate ./...` (or `make generate`) to regenerate. +package gen + +//go:generate go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen --config=../cfg-types.yaml ../openapi.yaml +//go:generate go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen --config=../cfg-server.yaml ../openapi.yaml +//go:generate go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen --config=../cfg-client.yaml ../openapi.yaml diff --git a/server/api/gen/server.gen.go b/server/api/gen/server.gen.go new file mode 100644 index 0000000..f5a536f --- /dev/null +++ b/server/api/gen/server.gen.go @@ -0,0 +1,903 @@ +// Package gen provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.7.1 DO NOT EDIT. +package gen + +import ( + "bytes" + "compress/flate" + "context" + "encoding/base64" + "errors" + "fmt" + "net/http" + "net/url" + "path" + "strings" + + "github.com/getkin/kin-openapi/openapi3" + "github.com/go-chi/chi/v5" + "github.com/oapi-codegen/runtime" + openapi_types "github.com/oapi-codegen/runtime/types" +) + +// ServerInterface represents all server handlers. +type ServerInterface interface { + // 激励视频广告解锁当日时长 + // (POST /ads/unlock) + AdsUnlock(w http.ResponseWriter, r *http.Request) + // 发送邮箱验证码 + // (POST /auth/code) + SendVerificationCode(w http.ResponseWriter, r *http.Request) + // 登录 + // (POST /auth/login) + Login(w http.ResponseWriter, r *http.Request) + // 刷新 Access Token + // (POST /auth/refresh) + RefreshToken(w http.ResponseWriter, r *http.Request) + // 注册账户 + // (POST /auth/register) + Register(w http.ResponseWriter, r *http.Request) + // 获取当前账户信息 + // (GET /me) + GetMe(w http.ResponseWriter, r *http.Request) + // 获取设备列表 + // (GET /me/devices) + ListDevices(w http.ResponseWriter, r *http.Request) + // 移除设备 + // (DELETE /me/devices/{id}) + DeleteDevice(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) + // 获取节点目录 + // (GET /nodes) + ListNodes(w http.ResponseWriter, r *http.Request, params ListNodesParams) + // 连接节点 — 下发连接凭证 + // (POST /nodes/{id}/connect) + ConnectNode(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) + // 断开节点连接 + // (POST /nodes/{id}/disconnect) + DisconnectNode(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) + // 获取公告列表 + // (GET /notices) + ListNotices(w http.ResponseWriter, r *http.Request) + // 获取套餐目录 + // (GET /plans) + ListPlans(w http.ResponseWriter, r *http.Request) + // 兑换激活码 + // (POST /redeem) + RedeemCode(w http.ResponseWriter, r *http.Request) + // 获取用量曲线 + // (GET /usage) + GetUsage(w http.ResponseWriter, r *http.Request, params GetUsageParams) +} + +// Unimplemented server implementation that returns http.StatusNotImplemented for each endpoint. + +type Unimplemented struct{} + +// 激励视频广告解锁当日时长 +// (POST /ads/unlock) +func (_ Unimplemented) AdsUnlock(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + +// 发送邮箱验证码 +// (POST /auth/code) +func (_ Unimplemented) SendVerificationCode(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + +// 登录 +// (POST /auth/login) +func (_ Unimplemented) Login(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + +// 刷新 Access Token +// (POST /auth/refresh) +func (_ Unimplemented) RefreshToken(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + +// 注册账户 +// (POST /auth/register) +func (_ Unimplemented) Register(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + +// 获取当前账户信息 +// (GET /me) +func (_ Unimplemented) GetMe(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + +// 获取设备列表 +// (GET /me/devices) +func (_ Unimplemented) ListDevices(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + +// 移除设备 +// (DELETE /me/devices/{id}) +func (_ Unimplemented) DeleteDevice(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) { + w.WriteHeader(http.StatusNotImplemented) +} + +// 获取节点目录 +// (GET /nodes) +func (_ Unimplemented) ListNodes(w http.ResponseWriter, r *http.Request, params ListNodesParams) { + w.WriteHeader(http.StatusNotImplemented) +} + +// 连接节点 — 下发连接凭证 +// (POST /nodes/{id}/connect) +func (_ Unimplemented) ConnectNode(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) { + w.WriteHeader(http.StatusNotImplemented) +} + +// 断开节点连接 +// (POST /nodes/{id}/disconnect) +func (_ Unimplemented) DisconnectNode(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) { + w.WriteHeader(http.StatusNotImplemented) +} + +// 获取公告列表 +// (GET /notices) +func (_ Unimplemented) ListNotices(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + +// 获取套餐目录 +// (GET /plans) +func (_ Unimplemented) ListPlans(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + +// 兑换激活码 +// (POST /redeem) +func (_ Unimplemented) RedeemCode(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + +// 获取用量曲线 +// (GET /usage) +func (_ Unimplemented) GetUsage(w http.ResponseWriter, r *http.Request, params GetUsageParams) { + w.WriteHeader(http.StatusNotImplemented) +} + +// ServerInterfaceWrapper converts contexts to parameters. +type ServerInterfaceWrapper struct { + Handler ServerInterface + HandlerMiddlewares []MiddlewareFunc + ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) +} + +type MiddlewareFunc func(http.Handler) http.Handler + +// AdsUnlock operation middleware +func (siw *ServerInterfaceWrapper) AdsUnlock(w http.ResponseWriter, r *http.Request) { + + ctx := r.Context() + + ctx = context.WithValue(ctx, BearerAuthScopes, []string{}) + + r = r.WithContext(ctx) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.AdsUnlock(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// SendVerificationCode operation middleware +func (siw *ServerInterfaceWrapper) SendVerificationCode(w http.ResponseWriter, r *http.Request) { + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.SendVerificationCode(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// Login operation middleware +func (siw *ServerInterfaceWrapper) Login(w http.ResponseWriter, r *http.Request) { + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.Login(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// RefreshToken operation middleware +func (siw *ServerInterfaceWrapper) RefreshToken(w http.ResponseWriter, r *http.Request) { + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.RefreshToken(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// Register operation middleware +func (siw *ServerInterfaceWrapper) Register(w http.ResponseWriter, r *http.Request) { + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.Register(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// GetMe operation middleware +func (siw *ServerInterfaceWrapper) GetMe(w http.ResponseWriter, r *http.Request) { + + ctx := r.Context() + + ctx = context.WithValue(ctx, BearerAuthScopes, []string{}) + + r = r.WithContext(ctx) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GetMe(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// ListDevices operation middleware +func (siw *ServerInterfaceWrapper) ListDevices(w http.ResponseWriter, r *http.Request) { + + ctx := r.Context() + + ctx = context.WithValue(ctx, BearerAuthScopes, []string{}) + + r = r.WithContext(ctx) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.ListDevices(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// DeleteDevice operation middleware +func (siw *ServerInterfaceWrapper) DeleteDevice(w http.ResponseWriter, r *http.Request) { + + var err error + _ = err + + // ------------- Path parameter "id" ------------- + var id openapi_types.UUID + + err = runtime.BindStyledParameterWithOptions("simple", "id", chi.URLParam(r, "id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "uuid"}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err}) + return + } + + ctx := r.Context() + + ctx = context.WithValue(ctx, BearerAuthScopes, []string{}) + + r = r.WithContext(ctx) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.DeleteDevice(w, r, id) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// ListNodes operation middleware +func (siw *ServerInterfaceWrapper) ListNodes(w http.ResponseWriter, r *http.Request) { + + var err error + _ = err + + ctx := r.Context() + + ctx = context.WithValue(ctx, BearerAuthScopes, []string{}) + + r = r.WithContext(ctx) + + // Parameter object where we will unmarshal all parameters from the context + var params ListNodesParams + + // ------------- Optional query parameter "if_version" ------------- + + err = runtime.BindQueryParameterWithOptions("form", true, false, "if_version", r.URL.Query(), ¶ms.IfVersion, runtime.BindQueryParameterOptions{Type: "integer", Format: "int64"}) + if err != nil { + var requiredError *runtime.RequiredParameterError + if errors.As(err, &requiredError) { + siw.ErrorHandlerFunc(w, r, &RequiredParamError{ParamName: "if_version"}) + } else { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "if_version", Err: err}) + } + return + } + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.ListNodes(w, r, params) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// ConnectNode operation middleware +func (siw *ServerInterfaceWrapper) ConnectNode(w http.ResponseWriter, r *http.Request) { + + var err error + _ = err + + // ------------- Path parameter "id" ------------- + var id openapi_types.UUID + + err = runtime.BindStyledParameterWithOptions("simple", "id", chi.URLParam(r, "id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "uuid"}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err}) + return + } + + ctx := r.Context() + + ctx = context.WithValue(ctx, BearerAuthScopes, []string{}) + + r = r.WithContext(ctx) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.ConnectNode(w, r, id) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// DisconnectNode operation middleware +func (siw *ServerInterfaceWrapper) DisconnectNode(w http.ResponseWriter, r *http.Request) { + + var err error + _ = err + + // ------------- Path parameter "id" ------------- + var id openapi_types.UUID + + err = runtime.BindStyledParameterWithOptions("simple", "id", chi.URLParam(r, "id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "uuid"}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err}) + return + } + + ctx := r.Context() + + ctx = context.WithValue(ctx, BearerAuthScopes, []string{}) + + r = r.WithContext(ctx) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.DisconnectNode(w, r, id) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// ListNotices operation middleware +func (siw *ServerInterfaceWrapper) ListNotices(w http.ResponseWriter, r *http.Request) { + + ctx := r.Context() + + ctx = context.WithValue(ctx, BearerAuthScopes, []string{}) + + r = r.WithContext(ctx) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.ListNotices(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// ListPlans operation middleware +func (siw *ServerInterfaceWrapper) ListPlans(w http.ResponseWriter, r *http.Request) { + + ctx := r.Context() + + ctx = context.WithValue(ctx, BearerAuthScopes, []string{}) + + r = r.WithContext(ctx) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.ListPlans(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// RedeemCode operation middleware +func (siw *ServerInterfaceWrapper) RedeemCode(w http.ResponseWriter, r *http.Request) { + + ctx := r.Context() + + ctx = context.WithValue(ctx, BearerAuthScopes, []string{}) + + r = r.WithContext(ctx) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.RedeemCode(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// GetUsage operation middleware +func (siw *ServerInterfaceWrapper) GetUsage(w http.ResponseWriter, r *http.Request) { + + var err error + _ = err + + ctx := r.Context() + + ctx = context.WithValue(ctx, BearerAuthScopes, []string{}) + + r = r.WithContext(ctx) + + // Parameter object where we will unmarshal all parameters from the context + var params GetUsageParams + + // ------------- Optional query parameter "days" ------------- + + err = runtime.BindQueryParameterWithOptions("form", true, false, "days", r.URL.Query(), ¶ms.Days, runtime.BindQueryParameterOptions{Type: "integer", Format: ""}) + if err != nil { + var requiredError *runtime.RequiredParameterError + if errors.As(err, &requiredError) { + siw.ErrorHandlerFunc(w, r, &RequiredParamError{ParamName: "days"}) + } else { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "days", Err: err}) + } + return + } + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GetUsage(w, r, params) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +type UnescapedCookieParamError struct { + ParamName string + Err error +} + +func (e *UnescapedCookieParamError) Error() string { + return fmt.Sprintf("error unescaping cookie parameter '%s'", e.ParamName) +} + +func (e *UnescapedCookieParamError) Unwrap() error { + return e.Err +} + +type UnmarshalingParamError struct { + ParamName string + Err error +} + +func (e *UnmarshalingParamError) Error() string { + return fmt.Sprintf("Error unmarshaling parameter %s as JSON: %s", e.ParamName, e.Err.Error()) +} + +func (e *UnmarshalingParamError) Unwrap() error { + return e.Err +} + +type RequiredParamError struct { + ParamName string +} + +func (e *RequiredParamError) Error() string { + return fmt.Sprintf("Query argument %s is required, but not found", e.ParamName) +} + +type RequiredHeaderError struct { + ParamName string + Err error +} + +func (e *RequiredHeaderError) Error() string { + return fmt.Sprintf("Header parameter %s is required, but not found", e.ParamName) +} + +func (e *RequiredHeaderError) Unwrap() error { + return e.Err +} + +type InvalidParamFormatError struct { + ParamName string + Err error +} + +func (e *InvalidParamFormatError) Error() string { + return fmt.Sprintf("Invalid format for parameter %s: %s", e.ParamName, e.Err.Error()) +} + +func (e *InvalidParamFormatError) Unwrap() error { + return e.Err +} + +type TooManyValuesForParamError struct { + ParamName string + Count int +} + +func (e *TooManyValuesForParamError) Error() string { + return fmt.Sprintf("Expected one value for %s, got %d", e.ParamName, e.Count) +} + +// Handler creates http.Handler with routing matching OpenAPI spec. +func Handler(si ServerInterface) http.Handler { + return HandlerWithOptions(si, ChiServerOptions{}) +} + +type ChiServerOptions struct { + BaseURL string + BaseRouter chi.Router + Middlewares []MiddlewareFunc + ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) +} + +// HandlerFromMux creates http.Handler with routing matching OpenAPI spec based on the provided mux. +func HandlerFromMux(si ServerInterface, r chi.Router) http.Handler { + return HandlerWithOptions(si, ChiServerOptions{ + BaseRouter: r, + }) +} + +func HandlerFromMuxWithBaseURL(si ServerInterface, r chi.Router, baseURL string) http.Handler { + return HandlerWithOptions(si, ChiServerOptions{ + BaseURL: baseURL, + BaseRouter: r, + }) +} + +// HandlerWithOptions creates http.Handler with additional options +func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handler { + r := options.BaseRouter + + if r == nil { + r = chi.NewRouter() + } + if options.ErrorHandlerFunc == nil { + options.ErrorHandlerFunc = func(w http.ResponseWriter, r *http.Request, err error) { + http.Error(w, err.Error(), http.StatusBadRequest) + } + } + wrapper := ServerInterfaceWrapper{ + Handler: si, + HandlerMiddlewares: options.Middlewares, + ErrorHandlerFunc: options.ErrorHandlerFunc, + } + + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/ads/unlock", wrapper.AdsUnlock) + }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/auth/code", wrapper.SendVerificationCode) + }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/auth/login", wrapper.Login) + }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/auth/refresh", wrapper.RefreshToken) + }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/auth/register", wrapper.Register) + }) + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/me", wrapper.GetMe) + }) + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/me/devices", wrapper.ListDevices) + }) + r.Group(func(r chi.Router) { + r.Delete(options.BaseURL+"/me/devices/{id}", wrapper.DeleteDevice) + }) + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/nodes", wrapper.ListNodes) + }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/nodes/{id}/connect", wrapper.ConnectNode) + }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/nodes/{id}/disconnect", wrapper.DisconnectNode) + }) + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/notices", wrapper.ListNotices) + }) + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/plans", wrapper.ListPlans) + }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/redeem", wrapper.RedeemCode) + }) + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/usage", wrapper.GetUsage) + }) + + return r +} + +// Base64 encoded, compressed with deflate, json marshaled OpenAPI spec. +// Stored as a slice of fixed-width chunks rather than one concatenated +// const string: with thousands of chunks the chained `+` fold is several +// times slower for the Go compiler than parsing a slice literal. +var swaggerSpec = []string{ + "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", +} + +// decodeSpec returns the embedded OpenAPI spec as raw JSON bytes, +// after base64-decoding and flate-decompressing the embedded blob. +func decodeSpec() ([]byte, error) { + encoded := strings.Join(swaggerSpec, "") + compressed, err := base64.StdEncoding.DecodeString(encoded) + if err != nil { + return nil, fmt.Errorf("error base64 decoding spec: %w", err) + } + zr := flate.NewReader(bytes.NewReader(compressed)) + var buf bytes.Buffer + if _, err := buf.ReadFrom(zr); err != nil { + return nil, fmt.Errorf("read flate: %w", err) + } + if err := zr.Close(); err != nil { + return nil, fmt.Errorf("close flate reader: %w", err) + } + + return buf.Bytes(), nil +} + +var rawSpec = decodeSpecCached() + +// a naive cache of the decoded OpenAPI spec +func decodeSpecCached() func() ([]byte, error) { + data, err := decodeSpec() + return func() ([]byte, error) { + return data, err + } +} + +// Constructs a synthetic filesystem for resolving external references when loading openapi specifications. +func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { + res := make(map[string]func() ([]byte, error)) + if len(pathToFile) > 0 { + res[pathToFile] = rawSpec + } + + return res +} + +// GetSpec returns the OpenAPI specification corresponding to the generated +// code in this file. External references in the spec are resolved through +// PathToRawSpec; externally-referenced files must be embedded in their +// corresponding Go packages (via the import-mapping feature). URL-based +// external refs are not supported. +func GetSpec() (swagger *openapi3.T, err error) { + resolvePath := PathToRawSpec("") + + loader := openapi3.NewLoader() + loader.IsExternalRefsAllowed = true + loader.ReadFromURIFunc = func(loader *openapi3.Loader, url *url.URL) ([]byte, error) { + pathToFile := url.String() + pathToFile = path.Clean(pathToFile) + getSpec, ok := resolvePath[pathToFile] + if !ok { + err1 := fmt.Errorf("path not found: %s", pathToFile) + return nil, err1 + } + return getSpec() + } + var specData []byte + specData, err = rawSpec() + if err != nil { + return + } + swagger, err = loader.LoadFromData(specData) + if err != nil { + return + } + return +} + +// GetSpecJSON returns the raw JSON bytes of the embedded OpenAPI +// specification: decompressed but not unmarshaled. External references +// are not resolved here; the bytes are the spec exactly as embedded by +// codegen. The result is cached at package init time, so repeated calls +// are cheap. +func GetSpecJSON() ([]byte, error) { + return rawSpec() +} + +// GetSwagger returns the OpenAPI specification corresponding to the +// generated code in this file. +// +// Deprecated: GetSwagger predates kin-openapi renaming openapi3.Swagger +// to openapi3.T. Use [GetSpec] instead. This wrapper is retained for +// backwards compatibility. +func GetSwagger() (*openapi3.T, error) { + return GetSpec() +} diff --git a/server/api/gen/types.gen.go b/server/api/gen/types.gen.go new file mode 100644 index 0000000..d759152 --- /dev/null +++ b/server/api/gen/types.gen.go @@ -0,0 +1,551 @@ +// Package gen provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.7.1 DO NOT EDIT. +package gen + +import ( + "time" + + openapi_types "github.com/oapi-codegen/runtime/types" +) + +const ( + BearerAuthScopes bearerAuthContextKey = "bearerAuth.Scopes" +) + +// Defines values for ConnectCredentialProtocol. +const ( + Hysteria2 ConnectCredentialProtocol = "hysteria2" + Reality ConnectCredentialProtocol = "reality" +) + +// Valid indicates whether the value is a known member of the ConnectCredentialProtocol enum. +func (e ConnectCredentialProtocol) Valid() bool { + switch e { + case Hysteria2: + return true + case Reality: + return true + default: + return false + } +} + +// Defines values for DevicePlatform. +const ( + Android DevicePlatform = "android" + Ios DevicePlatform = "ios" + Linux DevicePlatform = "linux" + Macos DevicePlatform = "macos" + Windows DevicePlatform = "windows" +) + +// Valid indicates whether the value is a known member of the DevicePlatform enum. +func (e DevicePlatform) Valid() bool { + switch e { + case Android: + return true + case Ios: + return true + case Linux: + return true + case Macos: + return true + case Windows: + return true + default: + return false + } +} + +// Defines values for NodeTier. +const ( + NodeTierFree NodeTier = "free" + NodeTierPro NodeTier = "pro" +) + +// Valid indicates whether the value is a known member of the NodeTier enum. +func (e NodeTier) Valid() bool { + switch e { + case NodeTierFree: + return true + case NodeTierPro: + return true + default: + return false + } +} + +// Defines values for PlanCode. +const ( + PlanCodeFree PlanCode = "free" + PlanCodePro PlanCode = "pro" + PlanCodeTeam PlanCode = "team" +) + +// Valid indicates whether the value is a known member of the PlanCode enum. +func (e PlanCode) Valid() bool { + switch e { + case PlanCodeFree: + return true + case PlanCodePro: + return true + case PlanCodeTeam: + return true + default: + return false + } +} + +// Defines values for RedeemResultPlanCode. +const ( + RedeemResultPlanCodeFree RedeemResultPlanCode = "free" + RedeemResultPlanCodePro RedeemResultPlanCode = "pro" + RedeemResultPlanCodeTeam RedeemResultPlanCode = "team" +) + +// Valid indicates whether the value is a known member of the RedeemResultPlanCode enum. +func (e RedeemResultPlanCode) Valid() bool { + switch e { + case RedeemResultPlanCodeFree: + return true + case RedeemResultPlanCodePro: + return true + case RedeemResultPlanCodeTeam: + return true + default: + return false + } +} + +// Defines values for SubscriptionInfoPlanCode. +const ( + SubscriptionInfoPlanCodeFree SubscriptionInfoPlanCode = "free" + SubscriptionInfoPlanCodePro SubscriptionInfoPlanCode = "pro" + SubscriptionInfoPlanCodeTeam SubscriptionInfoPlanCode = "team" +) + +// Valid indicates whether the value is a known member of the SubscriptionInfoPlanCode enum. +func (e SubscriptionInfoPlanCode) Valid() bool { + switch e { + case SubscriptionInfoPlanCodeFree: + return true + case SubscriptionInfoPlanCodePro: + return true + case SubscriptionInfoPlanCodeTeam: + return true + default: + return false + } +} + +// Defines values for SubscriptionInfoSource. +const ( + Admin SubscriptionInfoSource = "admin" + Code SubscriptionInfoSource = "code" + Trial SubscriptionInfoSource = "trial" +) + +// Valid indicates whether the value is a known member of the SubscriptionInfoSource enum. +func (e SubscriptionInfoSource) Valid() bool { + switch e { + case Admin: + return true + case Code: + return true + case Trial: + return true + default: + return false + } +} + +// Defines values for UserInfoStatus. +const ( + Active UserInfoStatus = "active" + Suspended UserInfoStatus = "suspended" +) + +// Valid indicates whether the value is a known member of the UserInfoStatus enum. +func (e UserInfoStatus) Valid() bool { + switch e { + case Active: + return true + case Suspended: + return true + default: + return false + } +} + +// ConnectCredential sing-box 连接凭证,支持 REALITY 主线 + Hysteria2 备线。 +// 每用户一个数据面 UUID(dp_uuid),与账号 UUID 解耦、可独立轮换; +// 节点侧只见 dp_uuid,零账号信息,单节点被抄不泄露用户库。 +// +// **字段稳定性**:本 schema 可能随数据面树(任务 #5)最终敲定后 bump, +// 在正式实现前以该任务产出为权威依据。 +type ConnectCredential struct { + // Flow VLESS flow 参数,REALITY 协议时使用(通常为 `xtls-rprx-vision`) + Flow *string `json:"flow,omitempty"` + + // Hysteria2 Hysteria2 备线参数(仅 protocol=hysteria2 或客户端协议降级时使用) + Hysteria2 *Hysteria2Params `json:"hysteria2,omitempty"` + + // Port 主协议端口(REALITY 用 TCP 443;Hysteria2 用 UDP 443) + Port int `json:"port"` + + // Protocol 优先协议(客户端按网络状况自动选;服务端返回当前最优) + Protocol ConnectCredentialProtocol `json:"protocol"` + + // Reality REALITY 协议参数(仅 protocol=reality 时有意义) + Reality *RealityParams `json:"reality,omitempty"` + + // Server 节点服务器地址(IP 或域名) + Server string `json:"server"` + + // TtlSeconds 凭证有效秒数。免费版 = 当日剩余分钟数 × 60;付费版 = 86400(24h,在线自动续期)。 + TtlSeconds int `json:"ttl_seconds"` + + // Uuid 用户数据面 UUID(dp_uuid),即 VLESS uuid 字段。 + // Hysteria2 auth password 由同一 UUID 派生(客户端无需单独字段)。 + Uuid openapi_types.UUID `json:"uuid"` +} + +// ConnectCredentialProtocol 优先协议(客户端按网络状况自动选;服务端返回当前最优) +type ConnectCredentialProtocol string + +// Device defines model for Device. +type Device struct { + // LastSeen 最后活跃时间(UTC ISO-8601) + LastSeen time.Time `json:"last_seen"` + + // Name 设备名称(客户端上报,如「iPhone 15 Pro」) + Name string `json:"name"` + + // Platform 设备平台 + Platform DevicePlatform `json:"platform"` + + // Uuid 设备 UUID + Uuid openapi_types.UUID `json:"uuid"` +} + +// DevicePlatform 设备平台 +type DevicePlatform string + +// Error 统一错误响应体,三字段均 required。文案遵守脱敏口径(不含红线词)。 +type Error struct { + // Code 机器可读错误码,例如 `auth.code_expired` / `redeem.already_redeemed` + Code string `json:"code"` + + // MessageEn 英文错误文案(面向用户展示) + MessageEn string `json:"message_en"` + + // MessageZh 中文错误文案(面向用户展示) + MessageZh string `json:"message_zh"` +} + +// Hysteria2Params Hysteria2 备线参数(仅 protocol=hysteria2 或客户端协议降级时使用) +type Hysteria2Params struct { + // Obfs 混淆模式(可选,如 `salamander`);null 表示不启用混淆 + Obfs *string `json:"obfs,omitempty"` + + // Password Hysteria2 auth password(由 dp_uuid 派生,服务端同源生成) + Password string `json:"password"` + + // Port Hysteria2 UDP 端口(通常 443) + Port int `json:"port"` +} + +// Me defines model for Me. +type Me struct { + Subscription SubscriptionInfo `json:"subscription"` + TodayUsage TodayUsageSummary `json:"today_usage"` + User UserInfo `json:"user"` +} + +// Node 节点目录条目。**不含任何密钥或连接参数**(reality_pbk/sni/password 等), +// 这些信息仅在 POST /nodes/{id}/connect 成功后下发。 +type Node struct { + // Load 节点当前负载百分比(0~100),由 agent 每 30s 上报到 Redis + Load int `json:"load"` + + // NameEn 节点英文名称 + NameEn string `json:"name_en"` + + // NameZh 节点中文名称 + NameZh string `json:"name_zh"` + + // Region 地区代码,2 字母大写(HK/JP/SG/US …) + Region string `json:"region"` + + // Signal 信号格数(0~4),由 agent 上报延迟换算,客户端展示信号条 + Signal int `json:"signal"` + + // Tier 节点等级(free 节点免费版可用,pro 节点需付费订阅) + Tier NodeTier `json:"tier"` + + // Uuid 节点 UUID(connect 时作为 path 参数) + Uuid openapi_types.UUID `json:"uuid"` +} + +// NodeTier 节点等级(free 节点免费版可用,pro 节点需付费订阅) +type NodeTier string + +// NodeDirectory defines model for NodeDirectory. +type NodeDirectory struct { + Nodes []Node `json:"nodes"` + + // Version 目录全局版本号;任何节点变更 bump 此值,客户端据此决定是否刷新缓存 + Version int64 `json:"version"` +} + +// Notice defines model for Notice. +type Notice struct { + // BodyEn 公告英文正文(Markdown) + BodyEn string `json:"body_en"` + + // BodyZh 公告中文正文(Markdown) + BodyZh string `json:"body_zh"` + + // Id 公告 UUID + Id openapi_types.UUID `json:"id"` + + // PublishedAt 发布时间(UTC ISO-8601) + PublishedAt time.Time `json:"published_at"` + + // TitleEn 公告英文标题 + TitleEn string `json:"title_en"` + + // TitleZh 公告中文标题 + TitleZh string `json:"title_zh"` +} + +// Plan defines model for Plan. +type Plan struct { + // AdGate 首次连接是否需要激励广告解锁(仅 free=true) + AdGate bool `json:"ad_gate"` + + // Code 套餐唯一代码 + Code PlanCode `json:"code"` + + // DailyMinutes 每日可用分钟数上限;null 表示无限制(pro/team) + DailyMinutes *int `json:"daily_minutes,omitempty"` + + // MaxDevices 最大同时在线设备数(free=1, pro=5, team=10) + MaxDevices int `json:"max_devices"` +} + +// PlanCode 套餐唯一代码 +type PlanCode string + +// RealityParams REALITY 协议参数(仅 protocol=reality 时有意义) +type RealityParams struct { + // PublicKey REALITY 服务端公钥(x25519) + PublicKey string `json:"public_key"` + + // ShortId REALITY short ID(十六进制字符串) + ShortId string `json:"short_id"` + + // Sni TLS SNI 伪装域名 + Sni string `json:"sni"` +} + +// RedeemResult 激活码兑换结果。幂等设计:同一用户重复提交同一码返回首次结果(非报错)。 +type RedeemResult struct { + // ExpiresAt 兑换后的订阅到期时间(UTC ISO-8601) + ExpiresAt time.Time `json:"expires_at"` + + // ExtendedDays 本次顺延的天数(叠加而非覆盖) + ExtendedDays int `json:"extended_days"` + + // PlanCode 本次兑换激活的套餐代码 + PlanCode RedeemResultPlanCode `json:"plan_code"` +} + +// RedeemResultPlanCode 本次兑换激活的套餐代码 +type RedeemResultPlanCode string + +// SubscriptionInfo defines model for SubscriptionInfo. +type SubscriptionInfo struct { + // ExpiresAt 订阅到期时间(UTC ISO-8601),免费版无到期时间时为 null + ExpiresAt *time.Time `json:"expires_at,omitempty"` + + // PlanCode 当前套餐代码 + PlanCode SubscriptionInfoPlanCode `json:"plan_code"` + + // Source 订阅来源(试用 / 激活码 / 管理员) + Source SubscriptionInfoSource `json:"source"` +} + +// SubscriptionInfoPlanCode 当前套餐代码 +type SubscriptionInfoPlanCode string + +// SubscriptionInfoSource 订阅来源(试用 / 激活码 / 管理员) +type SubscriptionInfoSource string + +// TodayUsageSummary defines model for TodayUsageSummary. +type TodayUsageSummary struct { + // AdUnlocked 免费版今日是否已完成激励广告解锁 + AdUnlocked *bool `json:"ad_unlocked,omitempty"` + + // MinutesRemaining 今日剩余分钟数,付费无限制时为 null + MinutesRemaining *int `json:"minutes_remaining"` + + // MinutesUsed 今日已使用分钟数 + MinutesUsed int `json:"minutes_used"` +} + +// TokenPair defines model for TokenPair. +type TokenPair struct { + // AccessToken JWT access token,有效期 15 分钟 + AccessToken string `json:"access_token"` + + // ExpiresIn access_token 有效秒数(固定 900) + ExpiresIn int `json:"expires_in"` + + // RefreshToken Refresh token,有效期 30 天 + RefreshToken string `json:"refresh_token"` +} + +// UsagePoint 单日用量数据点(无目的地/DNS/流量内容,仅字节数与分钟数,符合无日志承诺) +type UsagePoint struct { + // BytesDown 当日下行字节数 + BytesDown int64 `json:"bytes_down"` + + // BytesUp 当日上行字节数 + BytesUp int64 `json:"bytes_up"` + + // Date 日期(UTC,YYYY-MM-DD) + Date openapi_types.Date `json:"date"` + + // MinutesUsed 当日已用分钟数 + MinutesUsed int `json:"minutes_used"` +} + +// UserInfo defines model for UserInfo. +type UserInfo struct { + Email openapi_types.Email `json:"email"` + + // Status 账户状态 + Status UserInfoStatus `json:"status"` + + // Uuid 用户全局唯一标识(对外口径,永不暴露自增 ID) + Uuid openapi_types.UUID `json:"uuid"` +} + +// UserInfoStatus 账户状态 +type UserInfoStatus string + +// BadRequest 统一错误响应体,三字段均 required。文案遵守脱敏口径(不含红线词)。 +type BadRequest = Error + +// Conflict 统一错误响应体,三字段均 required。文案遵守脱敏口径(不含红线词)。 +type Conflict = Error + +// Forbidden 统一错误响应体,三字段均 required。文案遵守脱敏口径(不含红线词)。 +type Forbidden = Error + +// Internal 统一错误响应体,三字段均 required。文案遵守脱敏口径(不含红线词)。 +type Internal = Error + +// NotFound 统一错误响应体,三字段均 required。文案遵守脱敏口径(不含红线词)。 +type NotFound = Error + +// TooManyRequests 统一错误响应体,三字段均 required。文案遵守脱敏口径(不含红线词)。 +type TooManyRequests = Error + +// Unauthorized 统一错误响应体,三字段均 required。文案遵守脱敏口径(不含红线词)。 +type Unauthorized = Error + +// bearerAuthContextKey is the context key for bearerAuth security scheme +type bearerAuthContextKey string + +// AdsUnlockJSONBody defines parameters for AdsUnlock. +type AdsUnlockJSONBody struct { + // AdToken 广告 SDK 签发的服务端回执 token + AdToken string `json:"ad_token"` + + // DeviceId 发起广告的设备 UUID + DeviceId openapi_types.UUID `json:"device_id"` +} + +// SendVerificationCodeJSONBody defines parameters for SendVerificationCode. +type SendVerificationCodeJSONBody struct { + Email openapi_types.Email `json:"email"` +} + +// LoginJSONBody defines parameters for Login. +type LoginJSONBody struct { + Email openapi_types.Email `json:"email"` + Password string `json:"password"` +} + +// RefreshTokenJSONBody defines parameters for RefreshToken. +type RefreshTokenJSONBody struct { + // RefreshToken 有效的 refresh token(30 天有效期) + RefreshToken string `json:"refresh_token"` +} + +// RegisterJSONBody defines parameters for Register. +type RegisterJSONBody struct { + // Code 6 位数字验证码 + Code string `json:"code"` + Email openapi_types.Email `json:"email"` + + // Password 用户密码,argon2id 存储 + Password string `json:"password"` +} + +// ListNodesParams defines parameters for ListNodes. +type ListNodesParams struct { + // IfVersion 客户端已缓存的目录版本号;命中则返回 304 + IfVersion *int64 `form:"if_version,omitempty" json:"if_version,omitempty"` +} + +// ConnectNodeJSONBody defines parameters for ConnectNode. +type ConnectNodeJSONBody struct { + // DeviceId 发起连接的设备 UUID + DeviceId openapi_types.UUID `json:"device_id"` +} + +// DisconnectNodeJSONBody defines parameters for DisconnectNode. +type DisconnectNodeJSONBody struct { + // DeviceId 发起断开的设备 UUID + DeviceId openapi_types.UUID `json:"device_id"` +} + +// RedeemCodeJSONBody defines parameters for RedeemCode. +type RedeemCodeJSONBody struct { + // Code Crockford Base32 激活码,16 位含校验位 + Code string `json:"code"` +} + +// GetUsageParams defines parameters for GetUsage. +type GetUsageParams struct { + // Days 查询最近几天,默认 7,最大 90 + Days *int `form:"days,omitempty" json:"days,omitempty"` +} + +// AdsUnlockJSONRequestBody defines body for AdsUnlock for application/json ContentType. +type AdsUnlockJSONRequestBody AdsUnlockJSONBody + +// SendVerificationCodeJSONRequestBody defines body for SendVerificationCode for application/json ContentType. +type SendVerificationCodeJSONRequestBody SendVerificationCodeJSONBody + +// LoginJSONRequestBody defines body for Login for application/json ContentType. +type LoginJSONRequestBody LoginJSONBody + +// RefreshTokenJSONRequestBody defines body for RefreshToken for application/json ContentType. +type RefreshTokenJSONRequestBody RefreshTokenJSONBody + +// RegisterJSONRequestBody defines body for Register for application/json ContentType. +type RegisterJSONRequestBody RegisterJSONBody + +// ConnectNodeJSONRequestBody defines body for ConnectNode for application/json ContentType. +type ConnectNodeJSONRequestBody ConnectNodeJSONBody + +// DisconnectNodeJSONRequestBody defines body for DisconnectNode for application/json ContentType. +type DisconnectNodeJSONRequestBody DisconnectNodeJSONBody + +// RedeemCodeJSONRequestBody defines body for RedeemCode for application/json ContentType. +type RedeemCodeJSONRequestBody RedeemCodeJSONBody diff --git a/server/api/openapi.yaml b/server/api/openapi.yaml index 9ff7fa7..20319ea 100644 --- a/server/api/openapi.yaml +++ b/server/api/openapi.yaml @@ -557,7 +557,7 @@ components: type: http scheme: bearer bearerFormat: JWT - description: JWT RS256,`Authorization: Bearer ` + description: "JWT RS256,`Authorization: Bearer `" # ── 统一错误响应 ──────────────────────────────────────────── diff --git a/server/cmd/server/main.go b/server/cmd/server/main.go index 0af43dc..0742874 100644 --- a/server/cmd/server/main.go +++ b/server/cmd/server/main.go @@ -9,6 +9,9 @@ import ( "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" + + "github.com/wangjia/pangolin/server/api/gen" + "github.com/wangjia/pangolin/server/internal/httpapi" ) func main() { @@ -27,12 +30,18 @@ func main() { r.Use(middleware.Logger) r.Use(middleware.Recoverer) + // Health check — outside the versioned API. r.Get("/healthz", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _ = json.NewEncoder(w).Encode(map[string]string{"status": "ok"}) }) + // Mount all /v1/... routes. HandlerFromMuxWithBaseURL registers every route + // from the OpenAPI spec onto the provided chi router with the given prefix, + // so the router itself is the http.Handler we serve. + gen.HandlerFromMuxWithBaseURL(&httpapi.UnimplementedServer{}, r, "/v1") + log.Printf("pangolin server listening on %s", *addr) if err := http.ListenAndServe(*addr, r); err != nil { log.Fatalf("server error: %v", err) diff --git a/server/go.mod b/server/go.mod index 3a95d70..f9f7c1d 100644 --- a/server/go.mod +++ b/server/go.mod @@ -4,10 +4,12 @@ go 1.25.0 require ( github.com/alicebob/miniredis/v2 v2.38.0 + github.com/getkin/kin-openapi v0.135.0 github.com/go-chi/chi/v5 v5.2.1 github.com/go-sql-driver/mysql v1.8.1 github.com/golang-migrate/migrate/v4 v4.19.1 github.com/oapi-codegen/oapi-codegen/v2 v2.7.1 + github.com/oapi-codegen/runtime v1.4.1 github.com/redis/go-redis/v9 v9.20.1 github.com/testcontainers/testcontainers-go v0.34.0 github.com/testcontainers/testcontainers-go/modules/mysql v0.34.0 @@ -45,9 +47,10 @@ require ( github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/andybalholm/brotli v1.0.4 // indirect + github.com/andybalholm/brotli v1.1.0 // indirect github.com/apache/arrow/go/v10 v10.0.1 // indirect github.com/apache/thrift v0.16.0 // indirect + github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/aws/aws-sdk-go v1.49.6 // indirect github.com/aws/aws-sdk-go-v2 v1.16.16 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.8 // indirect @@ -87,8 +90,7 @@ require ( github.com/felixge/httpsnoop v1.0.4 // indirect github.com/form3tech-oss/jwt-go v3.2.5+incompatible // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.1 // indirect - github.com/getkin/kin-openapi v0.135.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/go-jose/go-jose/v4 v4.1.4 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -96,7 +98,7 @@ require ( github.com/go-openapi/jsonpointer v0.22.4 // indirect github.com/go-openapi/swag/jsonname v0.25.4 // indirect github.com/go-stack/stack v1.8.0 // indirect - github.com/goccy/go-json v0.9.11 // indirect + github.com/goccy/go-json v0.10.2 // indirect github.com/gocql/gocql v0.0.0-20210515062232-b7ef815b4556 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gogo/protobuf v1.3.2 // indirect @@ -138,8 +140,8 @@ require ( github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.9.1 // indirect - github.com/mattn/go-colorable v0.1.6 // indirect - github.com/mattn/go-isatty v0.0.16 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/microsoft/go-mssqldb v1.0.0 // indirect github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 // indirect @@ -217,7 +219,7 @@ require ( golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4 // indirect golang.org/x/term v0.40.0 // indirect golang.org/x/text v0.34.0 // indirect - golang.org/x/time v0.12.0 // indirect + golang.org/x/time v0.14.0 // indirect golang.org/x/tools v0.42.0 // indirect golang.org/x/tools/godoc v0.1.0-deprecated // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect diff --git a/server/go.sum b/server/go.sum index ecdd037..263c0ff 100644 --- a/server/go.sum +++ b/server/go.sum @@ -677,20 +677,24 @@ github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0 github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= github.com/alicebob/miniredis/v2 v2.38.0 h1:nZAzCR+Lj+Vxk4ZXzm2NuKq2O33RXj1XxJ2e2uP9jiw= github.com/alicebob/miniredis/v2 v2.38.0/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM= -github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/arrow/go/v10 v10.0.1 h1:n9dERvixoC/1JjDmBcs9FPaEryoANa2sCgVFo6ez9cI= github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI= github.com/apache/thrift v0.16.0 h1:qEy6UW60iVOlUy+b9ZR0d5WzUWYGOo4HfopoyBaNmoY= github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= +github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= +github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= github.com/aws/aws-sdk-go v1.49.6 h1:yNldzF5kzLBRvKlKz1S0bkvc2+04R1kt13KfBWQBfFA= github.com/aws/aws-sdk-go v1.49.6/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/aws/aws-sdk-go-v2 v1.16.16 h1:M1fj4FE2lB4NzRb9Y0xdWsn2P0+2UHVxwKyOa4YJNjk= @@ -735,6 +739,7 @@ github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYE github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= github.com/bkaradzic/go-lz4 v1.0.0 h1:RXc4wYsyz985CkXXeX04y4VnZFGG8Rd43pRaHsOXAKk= github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4= +github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= @@ -862,8 +867,8 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4 github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/fsouza/fake-gcs-server v1.17.0 h1:OeH75kBZcZa3ZE+zz/mFdJ2btt9FgqfjI7gIh9+5fvk= github.com/fsouza/fake-gcs-server v1.17.0/go.mod h1:D1rTE4YCyHFNa99oyJJ5HyclvN/0uQR+pM/VdlL83bw= -github.com/gabriel-vasile/mimetype v1.4.1 h1:TRWk7se+TOjCYgRth7+1/OYLNiRNIotknkFtf/dnN7Q= -github.com/gabriel-vasile/mimetype v1.4.1/go.mod h1:05Vi0w3Y9c/lNvJOdmIwvrrAhX3rYhfQQCaf9VJcv7M= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/getkin/kin-openapi v0.135.0 h1:751SjYfbiwqukYuVjwYEIKNfrSwS5YpA7DZnKSwQgtg= github.com/getkin/kin-openapi v0.135.0/go.mod h1:6dd5FJl6RdX4usBtFBaQhk9q62Yb2J0Mk5IhUO/QqFI= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -909,8 +914,9 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= -github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk= github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gocql/gocql v0.0.0-20210515062232-b7ef815b4556 h1:N/MD/sr6o61X+iZBAT2qEUF023s4KbA8RWfKzl0L6MQ= github.com/gocql/gocql v0.0.0-20210515062232-b7ef815b4556/go.mod h1:DL0ekTmBSTdlNF25Orwt/JMzqIq3EJ4MVa/J/uK64OY= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= @@ -1166,6 +1172,7 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM= @@ -1224,16 +1231,18 @@ github.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUt github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= @@ -1283,8 +1292,12 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oapi-codegen/nullable v1.1.0 h1:eAh8JVc5430VtYVnq00Hrbpag9PFRGWLjxR1/3KntMs= +github.com/oapi-codegen/nullable v1.1.0/go.mod h1:KUZ3vUzkmEKY90ksAmit2+5juDIhIZhfDl+0PwOQlFY= github.com/oapi-codegen/oapi-codegen/v2 v2.7.1 h1:a7Ab7YlpqkVG5HKrTaeFstm32Z5QOnyjnbsCO0jiMYM= github.com/oapi-codegen/oapi-codegen/v2 v2.7.1/go.mod h1:qzFy6iuobJw/hD1aRILee4G87/ShmhR0xYCwcUtZMCw= +github.com/oapi-codegen/runtime v1.4.1 h1:9nwLoI+KrWxzbBcp0jO/R8uXqbik/HUyCvPeU68Y/qo= +github.com/oapi-codegen/runtime v1.4.1/go.mod h1:GwV7hC2hviaMzj+ITfHVRESK5J2W/GefVwIND/bMGvU= github.com/oasdiff/yaml v0.0.9 h1:zQOvd2UKoozsSsAknnWoDJlSK4lC0mpmjfDsfqNwX48= github.com/oasdiff/yaml v0.0.9/go.mod h1:8lvhgJG4xiKPj3HN5lDow4jZHPlx1i7dIwzkdAo6oAM= github.com/oasdiff/yaml3 v0.0.9 h1:rWPrKccrdUm8J0F3sGuU+fuh9+1K/RdJlWF7O/9yw2g= @@ -1383,6 +1396,7 @@ github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= +github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= @@ -1415,8 +1429,8 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= -github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= -github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/vmware-labs/yaml-jsonpath v0.3.2 h1:/5QKeCBGdsInyDCyVNLbXyilb61MXGi9NP674f9Hobk= github.com/vmware-labs/yaml-jsonpath v0.3.2/go.mod h1:U6whw1z03QyqgWdgXxvVnQ90zN1BWz5V+51Ewf8k+rQ= github.com/woodsbury/decimal128 v1.4.0 h1:xJATj7lLu4f2oObouMt2tgGiElE5gO6mSWUjQsBgUlc= @@ -1546,8 +1560,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 h1:pVgRXcIictcr+lBQIFeiwuwtDIs4eL21OuM9nyAADmo= -golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 h1:985EYyeCOxTpcgOTJpflJUwOeEz0CQOdPt73OzpE9F8= +golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -1851,8 +1865,8 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= -golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/server/internal/httpapi/unimplemented.go b/server/internal/httpapi/unimplemented.go new file mode 100644 index 0000000..2bca73b --- /dev/null +++ b/server/internal/httpapi/unimplemented.go @@ -0,0 +1,110 @@ +// Package httpapi contains HTTP API handler stubs for the Pangolin v1 API. +// All endpoints return HTTP 501 with a structured bilingual error body until +// real business logic is wired in (tasks #2-#8). +package httpapi + +import ( + "encoding/json" + "net/http" + + openapi_types "github.com/oapi-codegen/runtime/types" + + "github.com/wangjia/pangolin/server/api/gen" +) + +// notImplError is the fixed JSON error body returned by every 501 stub. +// Intentionally hand-written here (not from internal/apierr) so that this +// package stays free of dependencies on task-1f which runs in parallel. +var notImplError = struct { + Code string `json:"code"` + MessageZH string `json:"message_zh"` + MessageEn string `json:"message_en"` +}{ + Code: "NOT_IMPLEMENTED", + MessageZH: "接口尚未实现", + MessageEn: "Not implemented yet", +} + +// writeNotImpl writes a 501 Not Implemented response with the structured error body. +func writeNotImpl(w http.ResponseWriter) { + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(http.StatusNotImplemented) + _ = json.NewEncoder(w).Encode(notImplError) +} + +// UnimplementedServer satisfies gen.ServerInterface and returns 501 for every +// endpoint. Individual methods should be replaced as each module is implemented. +type UnimplementedServer struct{} + +// Compile-time assertion: UnimplementedServer must satisfy ServerInterface. +var _ gen.ServerInterface = (*UnimplementedServer)(nil) + +// ── Auth ───────────────────────────────────────────────────────────────────── + +func (s *UnimplementedServer) SendVerificationCode(w http.ResponseWriter, r *http.Request) { + writeNotImpl(w) +} + +func (s *UnimplementedServer) Register(w http.ResponseWriter, r *http.Request) { + writeNotImpl(w) +} + +func (s *UnimplementedServer) Login(w http.ResponseWriter, r *http.Request) { + writeNotImpl(w) +} + +func (s *UnimplementedServer) RefreshToken(w http.ResponseWriter, r *http.Request) { + writeNotImpl(w) +} + +// ── Account ────────────────────────────────────────────────────────────────── + +func (s *UnimplementedServer) GetMe(w http.ResponseWriter, r *http.Request) { + writeNotImpl(w) +} + +func (s *UnimplementedServer) ListDevices(w http.ResponseWriter, r *http.Request) { + writeNotImpl(w) +} + +func (s *UnimplementedServer) DeleteDevice(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) { + writeNotImpl(w) +} + +// ── Commerce ───────────────────────────────────────────────────────────────── + +func (s *UnimplementedServer) RedeemCode(w http.ResponseWriter, r *http.Request) { + writeNotImpl(w) +} + +func (s *UnimplementedServer) AdsUnlock(w http.ResponseWriter, r *http.Request) { + writeNotImpl(w) +} + +func (s *UnimplementedServer) ListPlans(w http.ResponseWriter, r *http.Request) { + writeNotImpl(w) +} + +// ── Nodes ──────────────────────────────────────────────────────────────────── + +func (s *UnimplementedServer) ListNodes(w http.ResponseWriter, r *http.Request, params gen.ListNodesParams) { + writeNotImpl(w) +} + +func (s *UnimplementedServer) ConnectNode(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) { + writeNotImpl(w) +} + +func (s *UnimplementedServer) DisconnectNode(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) { + writeNotImpl(w) +} + +// ── Usage & Notices ────────────────────────────────────────────────────────── + +func (s *UnimplementedServer) GetUsage(w http.ResponseWriter, r *http.Request, params gen.GetUsageParams) { + writeNotImpl(w) +} + +func (s *UnimplementedServer) ListNotices(w http.ResponseWriter, r *http.Request) { + writeNotImpl(w) +} diff --git a/server/internal/httpapi/unimplemented_test.go b/server/internal/httpapi/unimplemented_test.go new file mode 100644 index 0000000..e0b8272 --- /dev/null +++ b/server/internal/httpapi/unimplemented_test.go @@ -0,0 +1,119 @@ +package httpapi_test + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/go-chi/chi/v5" + "github.com/go-chi/chi/v5/middleware" + + "github.com/wangjia/pangolin/server/api/gen" + "github.com/wangjia/pangolin/server/internal/httpapi" +) + +func newTestServer() http.Handler { + r := chi.NewRouter() + r.Use(middleware.Recoverer) + gen.HandlerFromMuxWithBaseURL(&httpapi.UnimplementedServer{}, r, "/v1") + return r +} + +type errorBody struct { + Code string `json:"code"` + MessageZH string `json:"message_zh"` + MessageEn string `json:"message_en"` +} + +func Test501StructuredBody(t *testing.T) { + srv := newTestServer() + + // All defined /v1 routes should return 501 with the structured body. + routes := []struct { + method string + path string + }{ + {"GET", "/v1/plans"}, + {"GET", "/v1/nodes"}, + {"GET", "/v1/me"}, + {"GET", "/v1/me/devices"}, + {"GET", "/v1/usage"}, + {"GET", "/v1/notices"}, + {"POST", "/v1/auth/code"}, + {"POST", "/v1/auth/register"}, + {"POST", "/v1/auth/login"}, + {"POST", "/v1/auth/refresh"}, + {"POST", "/v1/redeem"}, + {"POST", "/v1/ads/unlock"}, + {"DELETE", "/v1/me/devices/00000000-0000-0000-0000-000000000001"}, + {"POST", "/v1/nodes/00000000-0000-0000-0000-000000000001/connect"}, + {"POST", "/v1/nodes/00000000-0000-0000-0000-000000000001/disconnect"}, + } + + for _, tc := range routes { + t.Run(tc.method+" "+tc.path, func(t *testing.T) { + req := httptest.NewRequest(tc.method, tc.path, nil) + rec := httptest.NewRecorder() + srv.ServeHTTP(rec, req) + + if rec.Code != http.StatusNotImplemented { + t.Errorf("expected 501, got %d", rec.Code) + } + + var body errorBody + if err := json.NewDecoder(rec.Body).Decode(&body); err != nil { + t.Fatalf("could not decode body: %v", err) + } + if body.Code != "NOT_IMPLEMENTED" { + t.Errorf("code: want NOT_IMPLEMENTED, got %q", body.Code) + } + if body.MessageZH == "" { + t.Error("message_zh is empty") + } + if body.MessageEn == "" { + t.Error("message_en is empty") + } + }) + } +} + +func Test404ForUndefinedRoutes(t *testing.T) { + srv := newTestServer() + + paths := []string{ + "/v1/nonexistent", + "/v2/plans", + "/totally/wrong", + } + + for _, p := range paths { + t.Run(p, func(t *testing.T) { + req := httptest.NewRequest("GET", p, nil) + rec := httptest.NewRecorder() + srv.ServeHTTP(rec, req) + + if rec.Code != http.StatusNotFound { + t.Errorf("path %s: expected 404, got %d", p, rec.Code) + } + }) + } +} + +func TestHealthzNotAffected(t *testing.T) { + r := chi.NewRouter() + r.Get("/healthz", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`{"status":"ok"}`)) + }) + gen.HandlerFromMuxWithBaseURL(&httpapi.UnimplementedServer{}, r, "/v1") + + req := httptest.NewRequest("GET", "/healthz", nil) + rec := httptest.NewRecorder() + r.ServeHTTP(rec, req) + + if rec.Code != http.StatusOK { + t.Errorf("healthz: expected 200, got %d", rec.Code) + } +}