From b4a0041fb5aae471b272b113395fbf58f7746f13 Mon Sep 17 00:00:00 2001 From: Rhys Lloyd Date: Fri, 25 Jul 2025 23:07:46 -0700 Subject: [PATCH 01/18] openapi: remove migration operation --- openapi.yaml | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/openapi.yaml b/openapi.yaml index 5e58560..d233778 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -80,21 +80,6 @@ paths: application/json: schema: $ref: "#/components/schemas/Error" - /migrate-maps: - post: - summary: Perform maps migration - operationId: migrateMaps - tags: - - Maps - responses: - "200": - description: Successful response - default: - description: General Error - content: - application/json: - schema: - $ref: "#/components/schemas/Error" /maps: get: summary: Get list of maps -- 2.49.1 From 061b7b2a012d6a8e0d7bcb8e96a9811f79756cf4 Mon Sep 17 00:00:00 2001 From: Rhys Lloyd Date: Fri, 25 Jul 2025 23:08:04 -0700 Subject: [PATCH 02/18] openapi: generate --- pkg/api/oas_client_gen.go | 111 -- pkg/api/oas_handlers_gen.go | 180 --- pkg/api/oas_operations_gen.go | 1 - pkg/api/oas_response_decoders_gen.go | 60 - pkg/api/oas_response_encoders_gen.go | 7 - pkg/api/oas_router_gen.go | 2052 +++++++++++++------------- pkg/api/oas_schemas_gen.go | 3 - pkg/api/oas_security_gen.go | 1 - pkg/api/oas_server_gen.go | 6 - pkg/api/oas_unimplemented_gen.go | 9 - 10 files changed, 990 insertions(+), 1440 deletions(-) diff --git a/pkg/api/oas_client_gen.go b/pkg/api/oas_client_gen.go index 67be7d2..bc9a36b 100644 --- a/pkg/api/oas_client_gen.go +++ b/pkg/api/oas_client_gen.go @@ -301,12 +301,6 @@ type Invoker interface { // // GET /submissions ListSubmissions(ctx context.Context, params ListSubmissionsParams) (*Submissions, error) - // MigrateMaps invokes migrateMaps operation. - // - // Perform maps migration. - // - // POST /migrate-maps - MigrateMaps(ctx context.Context) error // ReleaseSubmissions invokes releaseSubmissions operation. // // Release a set of uploaded maps. @@ -6127,111 +6121,6 @@ func (c *Client) sendListSubmissions(ctx context.Context, params ListSubmissions return result, nil } -// MigrateMaps invokes migrateMaps operation. -// -// Perform maps migration. -// -// POST /migrate-maps -func (c *Client) MigrateMaps(ctx context.Context) error { - _, err := c.sendMigrateMaps(ctx) - return err -} - -func (c *Client) sendMigrateMaps(ctx context.Context) (res *MigrateMapsOK, err error) { - otelAttrs := []attribute.KeyValue{ - otelogen.OperationID("migrateMaps"), - semconv.HTTPRequestMethodKey.String("POST"), - semconv.HTTPRouteKey.String("/migrate-maps"), - } - - // Run stopwatch. - startTime := time.Now() - defer func() { - // Use floating point division here for higher precision (instead of Millisecond method). - elapsedDuration := time.Since(startTime) - c.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), metric.WithAttributes(otelAttrs...)) - }() - - // Increment request counter. - c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...)) - - // Start a span for this request. - ctx, span := c.cfg.Tracer.Start(ctx, MigrateMapsOperation, - trace.WithAttributes(otelAttrs...), - clientSpanKind, - ) - // Track stage for error reporting. - var stage string - defer func() { - if err != nil { - span.RecordError(err) - span.SetStatus(codes.Error, stage) - c.errors.Add(ctx, 1, metric.WithAttributes(otelAttrs...)) - } - span.End() - }() - - stage = "BuildURL" - u := uri.Clone(c.requestURL(ctx)) - var pathParts [1]string - pathParts[0] = "/migrate-maps" - uri.AddPathParts(u, pathParts[:]...) - - stage = "EncodeRequest" - r, err := ht.NewRequest(ctx, "POST", u) - if err != nil { - return res, errors.Wrap(err, "create request") - } - - { - type bitset = [1]uint8 - var satisfied bitset - { - stage = "Security:CookieAuth" - switch err := c.securityCookieAuth(ctx, MigrateMapsOperation, r); { - case err == nil: // if NO error - satisfied[0] |= 1 << 0 - case errors.Is(err, ogenerrors.ErrSkipClientSecurity): - // Skip this security. - default: - return res, errors.Wrap(err, "security \"CookieAuth\"") - } - } - - if ok := func() bool { - nextRequirement: - for _, requirement := range []bitset{ - {0b00000001}, - } { - for i, mask := range requirement { - if satisfied[i]&mask != mask { - continue nextRequirement - } - } - return true - } - return false - }(); !ok { - return res, ogenerrors.ErrSecurityRequirementIsNotSatisfied - } - } - - stage = "SendRequest" - resp, err := c.cfg.Client.Do(r) - if err != nil { - return res, errors.Wrap(err, "do request") - } - defer resp.Body.Close() - - stage = "DecodeResponse" - result, err := decodeMigrateMapsResponse(resp) - if err != nil { - return res, errors.Wrap(err, "decode response") - } - - return result, nil -} - // ReleaseSubmissions invokes releaseSubmissions operation. // // Release a set of uploaded maps. diff --git a/pkg/api/oas_handlers_gen.go b/pkg/api/oas_handlers_gen.go index 1ed5ef0..197e414 100644 --- a/pkg/api/oas_handlers_gen.go +++ b/pkg/api/oas_handlers_gen.go @@ -8433,186 +8433,6 @@ func (s *Server) handleListSubmissionsRequest(args [0]string, argsEscaped bool, } } -// handleMigrateMapsRequest handles migrateMaps operation. -// -// Perform maps migration. -// -// POST /migrate-maps -func (s *Server) handleMigrateMapsRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { - statusWriter := &codeRecorder{ResponseWriter: w} - w = statusWriter - otelAttrs := []attribute.KeyValue{ - otelogen.OperationID("migrateMaps"), - semconv.HTTPRequestMethodKey.String("POST"), - semconv.HTTPRouteKey.String("/migrate-maps"), - } - - // Start a span for this request. - ctx, span := s.cfg.Tracer.Start(r.Context(), MigrateMapsOperation, - trace.WithAttributes(otelAttrs...), - serverSpanKind, - ) - defer span.End() - - // Add Labeler to context. - labeler := &Labeler{attrs: otelAttrs} - ctx = contextWithLabeler(ctx, labeler) - - // Run stopwatch. - startTime := time.Now() - defer func() { - elapsedDuration := time.Since(startTime) - - attrSet := labeler.AttributeSet() - attrs := attrSet.ToSlice() - code := statusWriter.status - if code != 0 { - codeAttr := semconv.HTTPResponseStatusCode(code) - attrs = append(attrs, codeAttr) - span.SetAttributes(codeAttr) - } - attrOpt := metric.WithAttributes(attrs...) - - // Increment request counter. - s.requests.Add(ctx, 1, attrOpt) - - // Use floating point division here for higher precision (instead of Millisecond method). - s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) - }() - - var ( - recordError = func(stage string, err error) { - span.RecordError(err) - - // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status - // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, - // unless there was another error (e.g., network error receiving the response body; or 3xx codes with - // max redirects exceeded), in which case status MUST be set to Error. - code := statusWriter.status - if code >= 100 && code < 500 { - span.SetStatus(codes.Error, stage) - } - - attrSet := labeler.AttributeSet() - attrs := attrSet.ToSlice() - if code != 0 { - attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) - } - - s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) - } - err error - opErrContext = ogenerrors.OperationContext{ - Name: MigrateMapsOperation, - ID: "migrateMaps", - } - ) - { - type bitset = [1]uint8 - var satisfied bitset - { - sctx, ok, err := s.securityCookieAuth(ctx, MigrateMapsOperation, r) - if err != nil { - err = &ogenerrors.SecurityError{ - OperationContext: opErrContext, - Security: "CookieAuth", - Err: err, - } - if encodeErr := encodeErrorResponse(s.h.NewError(ctx, err), w, span); encodeErr != nil { - defer recordError("Security:CookieAuth", err) - } - return - } - if ok { - satisfied[0] |= 1 << 0 - ctx = sctx - } - } - - if ok := func() bool { - nextRequirement: - for _, requirement := range []bitset{ - {0b00000001}, - } { - for i, mask := range requirement { - if satisfied[i]&mask != mask { - continue nextRequirement - } - } - return true - } - return false - }(); !ok { - err = &ogenerrors.SecurityError{ - OperationContext: opErrContext, - Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied, - } - if encodeErr := encodeErrorResponse(s.h.NewError(ctx, err), w, span); encodeErr != nil { - defer recordError("Security", err) - } - return - } - } - - var response *MigrateMapsOK - if m := s.cfg.Middleware; m != nil { - mreq := middleware.Request{ - Context: ctx, - OperationName: MigrateMapsOperation, - OperationSummary: "Perform maps migration", - OperationID: "migrateMaps", - Body: nil, - Params: middleware.Parameters{}, - Raw: r, - } - - type ( - Request = struct{} - Params = struct{} - Response = *MigrateMapsOK - ) - response, err = middleware.HookMiddleware[ - Request, - Params, - Response, - ]( - m, - mreq, - nil, - func(ctx context.Context, request Request, params Params) (response Response, err error) { - err = s.h.MigrateMaps(ctx) - return response, err - }, - ) - } else { - err = s.h.MigrateMaps(ctx) - } - if err != nil { - if errRes, ok := errors.Into[*ErrorStatusCode](err); ok { - if err := encodeErrorResponse(errRes, w, span); err != nil { - defer recordError("Internal", err) - } - return - } - if errors.Is(err, ht.ErrNotImplemented) { - s.cfg.ErrorHandler(ctx, w, r, err) - return - } - if err := encodeErrorResponse(s.h.NewError(ctx, err), w, span); err != nil { - defer recordError("Internal", err) - } - return - } - - if err := encodeMigrateMapsResponse(response, w, span); err != nil { - defer recordError("EncodeResponse", err) - if !errors.Is(err, ht.ErrInternalServerErrorResponse) { - s.cfg.ErrorHandler(ctx, w, r, err) - } - return - } -} - // handleReleaseSubmissionsRequest handles releaseSubmissions operation. // // Release a set of uploaded maps. diff --git a/pkg/api/oas_operations_gen.go b/pkg/api/oas_operations_gen.go index 066af28..b5a33a8 100644 --- a/pkg/api/oas_operations_gen.go +++ b/pkg/api/oas_operations_gen.go @@ -51,7 +51,6 @@ const ( ListScriptsOperation OperationName = "ListScripts" ListSubmissionAuditEventsOperation OperationName = "ListSubmissionAuditEvents" ListSubmissionsOperation OperationName = "ListSubmissions" - MigrateMapsOperation OperationName = "MigrateMaps" ReleaseSubmissionsOperation OperationName = "ReleaseSubmissions" SessionRolesOperation OperationName = "SessionRoles" SessionUserOperation OperationName = "SessionUser" diff --git a/pkg/api/oas_response_decoders_gen.go b/pkg/api/oas_response_decoders_gen.go index b39e901..958d0d9 100644 --- a/pkg/api/oas_response_decoders_gen.go +++ b/pkg/api/oas_response_decoders_gen.go @@ -3595,66 +3595,6 @@ func decodeListSubmissionsResponse(resp *http.Response) (res *Submissions, _ err return res, errors.Wrap(defRes, "error") } -func decodeMigrateMapsResponse(resp *http.Response) (res *MigrateMapsOK, _ error) { - switch resp.StatusCode { - case 200: - // Code 200. - return &MigrateMapsOK{}, nil - } - // Convenient error response. - defRes, err := func() (res *ErrorStatusCode, err error) { - ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) - if err != nil { - return res, errors.Wrap(err, "parse media type") - } - switch { - case ct == "application/json": - buf, err := io.ReadAll(resp.Body) - if err != nil { - return res, err - } - d := jx.DecodeBytes(buf) - - var response Error - if err := func() error { - if err := response.Decode(d); err != nil { - return err - } - if err := d.Skip(); err != io.EOF { - return errors.New("unexpected trailing data") - } - return nil - }(); err != nil { - err = &ogenerrors.DecodeBodyError{ - ContentType: ct, - Body: buf, - Err: err, - } - return res, err - } - // Validate response. - if err := func() error { - if err := response.Validate(); err != nil { - return err - } - return nil - }(); err != nil { - return res, errors.Wrap(err, "validate") - } - return &ErrorStatusCode{ - StatusCode: resp.StatusCode, - Response: response, - }, nil - default: - return res, validate.InvalidContentType(ct) - } - }() - if err != nil { - return res, errors.Wrapf(err, "default (code %d)", resp.StatusCode) - } - return res, errors.Wrap(defRes, "error") -} - func decodeReleaseSubmissionsResponse(resp *http.Response) (res *ReleaseSubmissionsCreated, _ error) { switch resp.StatusCode { case 201: diff --git a/pkg/api/oas_response_encoders_gen.go b/pkg/api/oas_response_encoders_gen.go index fb4db75..fb923c6 100644 --- a/pkg/api/oas_response_encoders_gen.go +++ b/pkg/api/oas_response_encoders_gen.go @@ -484,13 +484,6 @@ func encodeListSubmissionsResponse(response *Submissions, w http.ResponseWriter, return nil } -func encodeMigrateMapsResponse(response *MigrateMapsOK, w http.ResponseWriter, span trace.Span) error { - w.WriteHeader(200) - span.SetStatus(codes.Ok, http.StatusText(200)) - - return nil -} - func encodeReleaseSubmissionsResponse(response *ReleaseSubmissionsCreated, w http.ResponseWriter, span trace.Span) error { w.WriteHeader(201) span.SetStatus(codes.Ok, http.StatusText(201)) diff --git a/pkg/api/oas_router_gen.go b/pkg/api/oas_router_gen.go index aa62a2d..1dc226d 100644 --- a/pkg/api/oas_router_gen.go +++ b/pkg/api/oas_router_gen.go @@ -61,9 +61,9 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { break } switch elem[0] { - case 'm': // Prefix: "m" + case 'm': // Prefix: "map" - if l := len("m"); len(elem) >= l && elem[0:l] == "m" { + if l := len("map"); len(elem) >= l && elem[0:l] == "map" { elem = elem[l:] } else { break @@ -73,500 +73,50 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { break } switch elem[0] { - case 'a': // Prefix: "ap" + case 'f': // Prefix: "fixes" - if l := len("ap"); len(elem) >= l && elem[0:l] == "ap" { + if l := len("fixes"); len(elem) >= l && elem[0:l] == "fixes" { elem = elem[l:] } else { break } if len(elem) == 0 { - break + switch r.Method { + case "GET": + s.handleListMapfixesRequest([0]string{}, elemIsEscaped, w, r) + case "POST": + s.handleCreateMapfixRequest([0]string{}, elemIsEscaped, w, r) + default: + s.notAllowed(w, r, "GET,POST") + } + + return } switch elem[0] { - case 'f': // Prefix: "fixes" + case '/': // Prefix: "/" - if l := len("fixes"); len(elem) >= l && elem[0:l] == "fixes" { + if l := len("/"); len(elem) >= l && elem[0:l] == "/" { elem = elem[l:] } else { break } - if len(elem) == 0 { - switch r.Method { - case "GET": - s.handleListMapfixesRequest([0]string{}, elemIsEscaped, w, r) - case "POST": - s.handleCreateMapfixRequest([0]string{}, elemIsEscaped, w, r) - default: - s.notAllowed(w, r, "GET,POST") - } - - return - } - switch elem[0] { - case '/': // Prefix: "/" - - if l := len("/"); len(elem) >= l && elem[0:l] == "/" { - elem = elem[l:] - } else { - break - } - - // Param: "MapfixID" - // Match until "/" - idx := strings.IndexByte(elem, '/') - if idx < 0 { - idx = len(elem) - } - args[0] = elem[:idx] - elem = elem[idx:] - - if len(elem) == 0 { - switch r.Method { - case "GET": - s.handleGetMapfixRequest([1]string{ - args[0], - }, elemIsEscaped, w, r) - default: - s.notAllowed(w, r, "GET") - } - - return - } - switch elem[0] { - case '/': // Prefix: "/" - - if l := len("/"); len(elem) >= l && elem[0:l] == "/" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - break - } - switch elem[0] { - case 'a': // Prefix: "audit-events" - - if l := len("audit-events"); len(elem) >= l && elem[0:l] == "audit-events" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - // Leaf node. - switch r.Method { - case "GET": - s.handleListMapfixAuditEventsRequest([1]string{ - args[0], - }, elemIsEscaped, w, r) - default: - s.notAllowed(w, r, "GET") - } - - return - } - - case 'c': // Prefix: "com" - - if l := len("com"); len(elem) >= l && elem[0:l] == "com" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - break - } - switch elem[0] { - case 'm': // Prefix: "ment" - - if l := len("ment"); len(elem) >= l && elem[0:l] == "ment" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - // Leaf node. - switch r.Method { - case "POST": - s.handleCreateMapfixAuditCommentRequest([1]string{ - args[0], - }, elemIsEscaped, w, r) - default: - s.notAllowed(w, r, "POST") - } - - return - } - - case 'p': // Prefix: "pleted" - - if l := len("pleted"); len(elem) >= l && elem[0:l] == "pleted" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - // Leaf node. - switch r.Method { - case "POST": - s.handleSetMapfixCompletedRequest([1]string{ - args[0], - }, elemIsEscaped, w, r) - default: - s.notAllowed(w, r, "POST") - } - - return - } - - } - - case 'm': // Prefix: "model" - - if l := len("model"); len(elem) >= l && elem[0:l] == "model" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - // Leaf node. - switch r.Method { - case "POST": - s.handleUpdateMapfixModelRequest([1]string{ - args[0], - }, elemIsEscaped, w, r) - default: - s.notAllowed(w, r, "POST") - } - - return - } - - case 's': // Prefix: "status/" - - if l := len("status/"); len(elem) >= l && elem[0:l] == "status/" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - break - } - switch elem[0] { - case 'r': // Prefix: "re" - - if l := len("re"); len(elem) >= l && elem[0:l] == "re" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - break - } - switch elem[0] { - case 'j': // Prefix: "ject" - - if l := len("ject"); len(elem) >= l && elem[0:l] == "ject" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - // Leaf node. - switch r.Method { - case "POST": - s.handleActionMapfixRejectRequest([1]string{ - args[0], - }, elemIsEscaped, w, r) - default: - s.notAllowed(w, r, "POST") - } - - return - } - - case 'q': // Prefix: "quest-changes" - - if l := len("quest-changes"); len(elem) >= l && elem[0:l] == "quest-changes" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - // Leaf node. - switch r.Method { - case "POST": - s.handleActionMapfixRequestChangesRequest([1]string{ - args[0], - }, elemIsEscaped, w, r) - default: - s.notAllowed(w, r, "POST") - } - - return - } - - case 's': // Prefix: "set-" - - if l := len("set-"); len(elem) >= l && elem[0:l] == "set-" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - break - } - switch elem[0] { - case 's': // Prefix: "submitting" - - if l := len("submitting"); len(elem) >= l && elem[0:l] == "submitting" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - // Leaf node. - switch r.Method { - case "POST": - s.handleActionMapfixResetSubmittingRequest([1]string{ - args[0], - }, elemIsEscaped, w, r) - default: - s.notAllowed(w, r, "POST") - } - - return - } - - case 'u': // Prefix: "uploading" - - if l := len("uploading"); len(elem) >= l && elem[0:l] == "uploading" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - // Leaf node. - switch r.Method { - case "POST": - s.handleActionMapfixValidatedRequest([1]string{ - args[0], - }, elemIsEscaped, w, r) - default: - s.notAllowed(w, r, "POST") - } - - return - } - - case 'v': // Prefix: "validating" - - if l := len("validating"); len(elem) >= l && elem[0:l] == "validating" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - // Leaf node. - switch r.Method { - case "POST": - s.handleActionMapfixAcceptedRequest([1]string{ - args[0], - }, elemIsEscaped, w, r) - default: - s.notAllowed(w, r, "POST") - } - - return - } - - } - - case 't': // Prefix: "try-validate" - - if l := len("try-validate"); len(elem) >= l && elem[0:l] == "try-validate" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - // Leaf node. - switch r.Method { - case "POST": - s.handleActionMapfixRetryValidateRequest([1]string{ - args[0], - }, elemIsEscaped, w, r) - default: - s.notAllowed(w, r, "POST") - } - - return - } - - case 'v': // Prefix: "voke" - - if l := len("voke"); len(elem) >= l && elem[0:l] == "voke" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - // Leaf node. - switch r.Method { - case "POST": - s.handleActionMapfixRevokeRequest([1]string{ - args[0], - }, elemIsEscaped, w, r) - default: - s.notAllowed(w, r, "POST") - } - - return - } - - } - - case 't': // Prefix: "trigger-" - - if l := len("trigger-"); len(elem) >= l && elem[0:l] == "trigger-" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - break - } - switch elem[0] { - case 's': // Prefix: "submit" - - if l := len("submit"); len(elem) >= l && elem[0:l] == "submit" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - switch r.Method { - case "POST": - s.handleActionMapfixTriggerSubmitRequest([1]string{ - args[0], - }, elemIsEscaped, w, r) - default: - s.notAllowed(w, r, "POST") - } - - return - } - switch elem[0] { - case '-': // Prefix: "-unchecked" - - if l := len("-unchecked"); len(elem) >= l && elem[0:l] == "-unchecked" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - // Leaf node. - switch r.Method { - case "POST": - s.handleActionMapfixTriggerSubmitUncheckedRequest([1]string{ - args[0], - }, elemIsEscaped, w, r) - default: - s.notAllowed(w, r, "POST") - } - - return - } - - } - - case 'u': // Prefix: "upload" - - if l := len("upload"); len(elem) >= l && elem[0:l] == "upload" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - // Leaf node. - switch r.Method { - case "POST": - s.handleActionMapfixTriggerUploadRequest([1]string{ - args[0], - }, elemIsEscaped, w, r) - default: - s.notAllowed(w, r, "POST") - } - - return - } - - case 'v': // Prefix: "validate" - - if l := len("validate"); len(elem) >= l && elem[0:l] == "validate" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - // Leaf node. - switch r.Method { - case "POST": - s.handleActionMapfixTriggerValidateRequest([1]string{ - args[0], - }, elemIsEscaped, w, r) - default: - s.notAllowed(w, r, "POST") - } - - return - } - - } - - } - - } - - } - - } - - case 's': // Prefix: "s" - - if l := len("s"); len(elem) >= l && elem[0:l] == "s" { - elem = elem[l:] - } else { - break + // Param: "MapfixID" + // Match until "/" + idx := strings.IndexByte(elem, '/') + if idx < 0 { + idx = len(elem) } + args[0] = elem[:idx] + elem = elem[idx:] if len(elem) == 0 { switch r.Method { case "GET": - s.handleListMapsRequest([0]string{}, elemIsEscaped, w, r) + s.handleGetMapfixRequest([1]string{ + args[0], + }, elemIsEscaped, w, r) default: s.notAllowed(w, r, "GET") } @@ -582,31 +132,13 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { break } - // Param: "MapID" - // Match until "/" - idx := strings.IndexByte(elem, '/') - if idx < 0 { - idx = len(elem) - } - args[0] = elem[:idx] - elem = elem[idx:] - if len(elem) == 0 { - switch r.Method { - case "GET": - s.handleGetMapRequest([1]string{ - args[0], - }, elemIsEscaped, w, r) - default: - s.notAllowed(w, r, "GET") - } - - return + break } switch elem[0] { - case '/': // Prefix: "/download" + case 'a': // Prefix: "audit-events" - if l := len("/download"); len(elem) >= l && elem[0:l] == "/download" { + if l := len("audit-events"); len(elem) >= l && elem[0:l] == "audit-events" { elem = elem[l:] } else { break @@ -616,7 +148,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Leaf node. switch r.Method { case "GET": - s.handleDownloadMapAssetRequest([1]string{ + s.handleListMapfixAuditEventsRequest([1]string{ args[0], }, elemIsEscaped, w, r) default: @@ -626,31 +158,465 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } + case 'c': // Prefix: "com" + + if l := len("com"); len(elem) >= l && elem[0:l] == "com" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + break + } + switch elem[0] { + case 'm': // Prefix: "ment" + + if l := len("ment"); len(elem) >= l && elem[0:l] == "ment" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch r.Method { + case "POST": + s.handleCreateMapfixAuditCommentRequest([1]string{ + args[0], + }, elemIsEscaped, w, r) + default: + s.notAllowed(w, r, "POST") + } + + return + } + + case 'p': // Prefix: "pleted" + + if l := len("pleted"); len(elem) >= l && elem[0:l] == "pleted" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch r.Method { + case "POST": + s.handleSetMapfixCompletedRequest([1]string{ + args[0], + }, elemIsEscaped, w, r) + default: + s.notAllowed(w, r, "POST") + } + + return + } + + } + + case 'm': // Prefix: "model" + + if l := len("model"); len(elem) >= l && elem[0:l] == "model" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch r.Method { + case "POST": + s.handleUpdateMapfixModelRequest([1]string{ + args[0], + }, elemIsEscaped, w, r) + default: + s.notAllowed(w, r, "POST") + } + + return + } + + case 's': // Prefix: "status/" + + if l := len("status/"); len(elem) >= l && elem[0:l] == "status/" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + break + } + switch elem[0] { + case 'r': // Prefix: "re" + + if l := len("re"); len(elem) >= l && elem[0:l] == "re" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + break + } + switch elem[0] { + case 'j': // Prefix: "ject" + + if l := len("ject"); len(elem) >= l && elem[0:l] == "ject" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch r.Method { + case "POST": + s.handleActionMapfixRejectRequest([1]string{ + args[0], + }, elemIsEscaped, w, r) + default: + s.notAllowed(w, r, "POST") + } + + return + } + + case 'q': // Prefix: "quest-changes" + + if l := len("quest-changes"); len(elem) >= l && elem[0:l] == "quest-changes" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch r.Method { + case "POST": + s.handleActionMapfixRequestChangesRequest([1]string{ + args[0], + }, elemIsEscaped, w, r) + default: + s.notAllowed(w, r, "POST") + } + + return + } + + case 's': // Prefix: "set-" + + if l := len("set-"); len(elem) >= l && elem[0:l] == "set-" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + break + } + switch elem[0] { + case 's': // Prefix: "submitting" + + if l := len("submitting"); len(elem) >= l && elem[0:l] == "submitting" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch r.Method { + case "POST": + s.handleActionMapfixResetSubmittingRequest([1]string{ + args[0], + }, elemIsEscaped, w, r) + default: + s.notAllowed(w, r, "POST") + } + + return + } + + case 'u': // Prefix: "uploading" + + if l := len("uploading"); len(elem) >= l && elem[0:l] == "uploading" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch r.Method { + case "POST": + s.handleActionMapfixValidatedRequest([1]string{ + args[0], + }, elemIsEscaped, w, r) + default: + s.notAllowed(w, r, "POST") + } + + return + } + + case 'v': // Prefix: "validating" + + if l := len("validating"); len(elem) >= l && elem[0:l] == "validating" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch r.Method { + case "POST": + s.handleActionMapfixAcceptedRequest([1]string{ + args[0], + }, elemIsEscaped, w, r) + default: + s.notAllowed(w, r, "POST") + } + + return + } + + } + + case 't': // Prefix: "try-validate" + + if l := len("try-validate"); len(elem) >= l && elem[0:l] == "try-validate" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch r.Method { + case "POST": + s.handleActionMapfixRetryValidateRequest([1]string{ + args[0], + }, elemIsEscaped, w, r) + default: + s.notAllowed(w, r, "POST") + } + + return + } + + case 'v': // Prefix: "voke" + + if l := len("voke"); len(elem) >= l && elem[0:l] == "voke" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch r.Method { + case "POST": + s.handleActionMapfixRevokeRequest([1]string{ + args[0], + }, elemIsEscaped, w, r) + default: + s.notAllowed(w, r, "POST") + } + + return + } + + } + + case 't': // Prefix: "trigger-" + + if l := len("trigger-"); len(elem) >= l && elem[0:l] == "trigger-" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + break + } + switch elem[0] { + case 's': // Prefix: "submit" + + if l := len("submit"); len(elem) >= l && elem[0:l] == "submit" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + switch r.Method { + case "POST": + s.handleActionMapfixTriggerSubmitRequest([1]string{ + args[0], + }, elemIsEscaped, w, r) + default: + s.notAllowed(w, r, "POST") + } + + return + } + switch elem[0] { + case '-': // Prefix: "-unchecked" + + if l := len("-unchecked"); len(elem) >= l && elem[0:l] == "-unchecked" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch r.Method { + case "POST": + s.handleActionMapfixTriggerSubmitUncheckedRequest([1]string{ + args[0], + }, elemIsEscaped, w, r) + default: + s.notAllowed(w, r, "POST") + } + + return + } + + } + + case 'u': // Prefix: "upload" + + if l := len("upload"); len(elem) >= l && elem[0:l] == "upload" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch r.Method { + case "POST": + s.handleActionMapfixTriggerUploadRequest([1]string{ + args[0], + }, elemIsEscaped, w, r) + default: + s.notAllowed(w, r, "POST") + } + + return + } + + case 'v': // Prefix: "validate" + + if l := len("validate"); len(elem) >= l && elem[0:l] == "validate" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch r.Method { + case "POST": + s.handleActionMapfixTriggerValidateRequest([1]string{ + args[0], + }, elemIsEscaped, w, r) + default: + s.notAllowed(w, r, "POST") + } + + return + } + + } + + } + } } } - case 'i': // Prefix: "igrate-maps" + case 's': // Prefix: "s" - if l := len("igrate-maps"); len(elem) >= l && elem[0:l] == "igrate-maps" { + if l := len("s"); len(elem) >= l && elem[0:l] == "s" { elem = elem[l:] } else { break } if len(elem) == 0 { - // Leaf node. switch r.Method { - case "POST": - s.handleMigrateMapsRequest([0]string{}, elemIsEscaped, w, r) + case "GET": + s.handleListMapsRequest([0]string{}, elemIsEscaped, w, r) default: - s.notAllowed(w, r, "POST") + s.notAllowed(w, r, "GET") } return } + switch elem[0] { + case '/': // Prefix: "/" + + if l := len("/"); len(elem) >= l && elem[0:l] == "/" { + elem = elem[l:] + } else { + break + } + + // Param: "MapID" + // Match until "/" + idx := strings.IndexByte(elem, '/') + if idx < 0 { + idx = len(elem) + } + args[0] = elem[:idx] + elem = elem[idx:] + + if len(elem) == 0 { + switch r.Method { + case "GET": + s.handleGetMapRequest([1]string{ + args[0], + }, elemIsEscaped, w, r) + default: + s.notAllowed(w, r, "GET") + } + + return + } + switch elem[0] { + case '/': // Prefix: "/download" + + if l := len("/download"); len(elem) >= l && elem[0:l] == "/download" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch r.Method { + case "GET": + s.handleDownloadMapAssetRequest([1]string{ + args[0], + }, elemIsEscaped, w, r) + default: + s.notAllowed(w, r, "GET") + } + + return + } + + } + + } } @@ -1515,9 +1481,9 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) { break } switch elem[0] { - case 'm': // Prefix: "m" + case 'm': // Prefix: "map" - if l := len("m"); len(elem) >= l && elem[0:l] == "m" { + if l := len("map"); len(elem) >= l && elem[0:l] == "map" { elem = elem[l:] } else { break @@ -1527,43 +1493,63 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) { break } switch elem[0] { - case 'a': // Prefix: "ap" + case 'f': // Prefix: "fixes" - if l := len("ap"); len(elem) >= l && elem[0:l] == "ap" { + if l := len("fixes"); len(elem) >= l && elem[0:l] == "fixes" { elem = elem[l:] } else { break } if len(elem) == 0 { - break + switch method { + case "GET": + r.name = ListMapfixesOperation + r.summary = "Get list of mapfixes" + r.operationID = "listMapfixes" + r.pathPattern = "/mapfixes" + r.args = args + r.count = 0 + return r, true + case "POST": + r.name = CreateMapfixOperation + r.summary = "Trigger the validator to create a mapfix" + r.operationID = "createMapfix" + r.pathPattern = "/mapfixes" + r.args = args + r.count = 0 + return r, true + default: + return + } } switch elem[0] { - case 'f': // Prefix: "fixes" + case '/': // Prefix: "/" - if l := len("fixes"); len(elem) >= l && elem[0:l] == "fixes" { + if l := len("/"); len(elem) >= l && elem[0:l] == "/" { elem = elem[l:] } else { break } + // Param: "MapfixID" + // Match until "/" + idx := strings.IndexByte(elem, '/') + if idx < 0 { + idx = len(elem) + } + args[0] = elem[:idx] + elem = elem[idx:] + if len(elem) == 0 { switch method { case "GET": - r.name = ListMapfixesOperation - r.summary = "Get list of mapfixes" - r.operationID = "listMapfixes" - r.pathPattern = "/mapfixes" + r.name = GetMapfixOperation + r.summary = "Retrieve map with ID" + r.operationID = "getMapfix" + r.pathPattern = "/mapfixes/{MapfixID}" r.args = args - r.count = 0 - return r, true - case "POST": - r.name = CreateMapfixOperation - r.summary = "Trigger the validator to create a mapfix" - r.operationID = "createMapfix" - r.pathPattern = "/mapfixes" - r.args = args - r.count = 0 + r.count = 1 return r, true default: return @@ -1578,537 +1564,13 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) { break } - // Param: "MapfixID" - // Match until "/" - idx := strings.IndexByte(elem, '/') - if idx < 0 { - idx = len(elem) - } - args[0] = elem[:idx] - elem = elem[idx:] - if len(elem) == 0 { - switch method { - case "GET": - r.name = GetMapfixOperation - r.summary = "Retrieve map with ID" - r.operationID = "getMapfix" - r.pathPattern = "/mapfixes/{MapfixID}" - r.args = args - r.count = 1 - return r, true - default: - return - } - } - switch elem[0] { - case '/': // Prefix: "/" - - if l := len("/"); len(elem) >= l && elem[0:l] == "/" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - break - } - switch elem[0] { - case 'a': // Prefix: "audit-events" - - if l := len("audit-events"); len(elem) >= l && elem[0:l] == "audit-events" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - // Leaf node. - switch method { - case "GET": - r.name = ListMapfixAuditEventsOperation - r.summary = "Retrieve a list of audit events" - r.operationID = "listMapfixAuditEvents" - r.pathPattern = "/mapfixes/{MapfixID}/audit-events" - r.args = args - r.count = 1 - return r, true - default: - return - } - } - - case 'c': // Prefix: "com" - - if l := len("com"); len(elem) >= l && elem[0:l] == "com" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - break - } - switch elem[0] { - case 'm': // Prefix: "ment" - - if l := len("ment"); len(elem) >= l && elem[0:l] == "ment" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - // Leaf node. - switch method { - case "POST": - r.name = CreateMapfixAuditCommentOperation - r.summary = "Post a comment to the audit log" - r.operationID = "createMapfixAuditComment" - r.pathPattern = "/mapfixes/{MapfixID}/comment" - r.args = args - r.count = 1 - return r, true - default: - return - } - } - - case 'p': // Prefix: "pleted" - - if l := len("pleted"); len(elem) >= l && elem[0:l] == "pleted" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - // Leaf node. - switch method { - case "POST": - r.name = SetMapfixCompletedOperation - r.summary = "Called by maptest when a player completes the map" - r.operationID = "setMapfixCompleted" - r.pathPattern = "/mapfixes/{MapfixID}/completed" - r.args = args - r.count = 1 - return r, true - default: - return - } - } - - } - - case 'm': // Prefix: "model" - - if l := len("model"); len(elem) >= l && elem[0:l] == "model" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - // Leaf node. - switch method { - case "POST": - r.name = UpdateMapfixModelOperation - r.summary = "Update model following role restrictions" - r.operationID = "updateMapfixModel" - r.pathPattern = "/mapfixes/{MapfixID}/model" - r.args = args - r.count = 1 - return r, true - default: - return - } - } - - case 's': // Prefix: "status/" - - if l := len("status/"); len(elem) >= l && elem[0:l] == "status/" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - break - } - switch elem[0] { - case 'r': // Prefix: "re" - - if l := len("re"); len(elem) >= l && elem[0:l] == "re" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - break - } - switch elem[0] { - case 'j': // Prefix: "ject" - - if l := len("ject"); len(elem) >= l && elem[0:l] == "ject" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - // Leaf node. - switch method { - case "POST": - r.name = ActionMapfixRejectOperation - r.summary = "Role Reviewer changes status from Submitted -> Rejected" - r.operationID = "actionMapfixReject" - r.pathPattern = "/mapfixes/{MapfixID}/status/reject" - r.args = args - r.count = 1 - return r, true - default: - return - } - } - - case 'q': // Prefix: "quest-changes" - - if l := len("quest-changes"); len(elem) >= l && elem[0:l] == "quest-changes" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - // Leaf node. - switch method { - case "POST": - r.name = ActionMapfixRequestChangesOperation - r.summary = "Role Reviewer changes status from Validated|Accepted|Submitted -> ChangesRequested" - r.operationID = "actionMapfixRequestChanges" - r.pathPattern = "/mapfixes/{MapfixID}/status/request-changes" - r.args = args - r.count = 1 - return r, true - default: - return - } - } - - case 's': // Prefix: "set-" - - if l := len("set-"); len(elem) >= l && elem[0:l] == "set-" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - break - } - switch elem[0] { - case 's': // Prefix: "submitting" - - if l := len("submitting"); len(elem) >= l && elem[0:l] == "submitting" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - // Leaf node. - switch method { - case "POST": - r.name = ActionMapfixResetSubmittingOperation - r.summary = "Role Submitter manually resets submitting softlock and changes status from Submitting -> UnderConstruction" - r.operationID = "actionMapfixResetSubmitting" - r.pathPattern = "/mapfixes/{MapfixID}/status/reset-submitting" - r.args = args - r.count = 1 - return r, true - default: - return - } - } - - case 'u': // Prefix: "uploading" - - if l := len("uploading"); len(elem) >= l && elem[0:l] == "uploading" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - // Leaf node. - switch method { - case "POST": - r.name = ActionMapfixValidatedOperation - r.summary = "Role Admin manually resets uploading softlock and changes status from Uploading -> Validated" - r.operationID = "actionMapfixValidated" - r.pathPattern = "/mapfixes/{MapfixID}/status/reset-uploading" - r.args = args - r.count = 1 - return r, true - default: - return - } - } - - case 'v': // Prefix: "validating" - - if l := len("validating"); len(elem) >= l && elem[0:l] == "validating" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - // Leaf node. - switch method { - case "POST": - r.name = ActionMapfixAcceptedOperation - r.summary = "Role Reviewer manually resets validating softlock and changes status from Validating -> Accepted" - r.operationID = "actionMapfixAccepted" - r.pathPattern = "/mapfixes/{MapfixID}/status/reset-validating" - r.args = args - r.count = 1 - return r, true - default: - return - } - } - - } - - case 't': // Prefix: "try-validate" - - if l := len("try-validate"); len(elem) >= l && elem[0:l] == "try-validate" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - // Leaf node. - switch method { - case "POST": - r.name = ActionMapfixRetryValidateOperation - r.summary = "Role Reviewer re-runs validation and changes status from Accepted -> Validating" - r.operationID = "actionMapfixRetryValidate" - r.pathPattern = "/mapfixes/{MapfixID}/status/retry-validate" - r.args = args - r.count = 1 - return r, true - default: - return - } - } - - case 'v': // Prefix: "voke" - - if l := len("voke"); len(elem) >= l && elem[0:l] == "voke" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - // Leaf node. - switch method { - case "POST": - r.name = ActionMapfixRevokeOperation - r.summary = "Role Submitter changes status from Submitted|ChangesRequested -> UnderConstruction" - r.operationID = "actionMapfixRevoke" - r.pathPattern = "/mapfixes/{MapfixID}/status/revoke" - r.args = args - r.count = 1 - return r, true - default: - return - } - } - - } - - case 't': // Prefix: "trigger-" - - if l := len("trigger-"); len(elem) >= l && elem[0:l] == "trigger-" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - break - } - switch elem[0] { - case 's': // Prefix: "submit" - - if l := len("submit"); len(elem) >= l && elem[0:l] == "submit" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - switch method { - case "POST": - r.name = ActionMapfixTriggerSubmitOperation - r.summary = "Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting" - r.operationID = "actionMapfixTriggerSubmit" - r.pathPattern = "/mapfixes/{MapfixID}/status/trigger-submit" - r.args = args - r.count = 1 - return r, true - default: - return - } - } - switch elem[0] { - case '-': // Prefix: "-unchecked" - - if l := len("-unchecked"); len(elem) >= l && elem[0:l] == "-unchecked" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - // Leaf node. - switch method { - case "POST": - r.name = ActionMapfixTriggerSubmitUncheckedOperation - r.summary = "Role Reviewer changes status from ChangesRequested -> Submitting" - r.operationID = "actionMapfixTriggerSubmitUnchecked" - r.pathPattern = "/mapfixes/{MapfixID}/status/trigger-submit-unchecked" - r.args = args - r.count = 1 - return r, true - default: - return - } - } - - } - - case 'u': // Prefix: "upload" - - if l := len("upload"); len(elem) >= l && elem[0:l] == "upload" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - // Leaf node. - switch method { - case "POST": - r.name = ActionMapfixTriggerUploadOperation - r.summary = "Role Admin changes status from Validated -> Uploading" - r.operationID = "actionMapfixTriggerUpload" - r.pathPattern = "/mapfixes/{MapfixID}/status/trigger-upload" - r.args = args - r.count = 1 - return r, true - default: - return - } - } - - case 'v': // Prefix: "validate" - - if l := len("validate"); len(elem) >= l && elem[0:l] == "validate" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - // Leaf node. - switch method { - case "POST": - r.name = ActionMapfixTriggerValidateOperation - r.summary = "Role Reviewer triggers validation and changes status from Submitted -> Validating" - r.operationID = "actionMapfixTriggerValidate" - r.pathPattern = "/mapfixes/{MapfixID}/status/trigger-validate" - r.args = args - r.count = 1 - return r, true - default: - return - } - } - - } - - } - - } - - } - - } - - case 's': // Prefix: "s" - - if l := len("s"); len(elem) >= l && elem[0:l] == "s" { - elem = elem[l:] - } else { - break - } - - if len(elem) == 0 { - switch method { - case "GET": - r.name = ListMapsOperation - r.summary = "Get list of maps" - r.operationID = "listMaps" - r.pathPattern = "/maps" - r.args = args - r.count = 0 - return r, true - default: - return - } - } - switch elem[0] { - case '/': // Prefix: "/" - - if l := len("/"); len(elem) >= l && elem[0:l] == "/" { - elem = elem[l:] - } else { break } - - // Param: "MapID" - // Match until "/" - idx := strings.IndexByte(elem, '/') - if idx < 0 { - idx = len(elem) - } - args[0] = elem[:idx] - elem = elem[idx:] - - if len(elem) == 0 { - switch method { - case "GET": - r.name = GetMapOperation - r.summary = "Retrieve map with ID" - r.operationID = "getMap" - r.pathPattern = "/maps/{MapID}" - r.args = args - r.count = 1 - return r, true - default: - return - } - } switch elem[0] { - case '/': // Prefix: "/download" + case 'a': // Prefix: "audit-events" - if l := len("/download"); len(elem) >= l && elem[0:l] == "/download" { + if l := len("audit-events"); len(elem) >= l && elem[0:l] == "audit-events" { elem = elem[l:] } else { break @@ -2118,10 +1580,10 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) { // Leaf node. switch method { case "GET": - r.name = DownloadMapAssetOperation - r.summary = "Download the map asset" - r.operationID = "downloadMapAsset" - r.pathPattern = "/maps/{MapID}/download" + r.name = ListMapfixAuditEventsOperation + r.summary = "Retrieve a list of audit events" + r.operationID = "listMapfixAuditEvents" + r.pathPattern = "/mapfixes/{MapfixID}/audit-events" r.args = args r.count = 1 return r, true @@ -2130,28 +1592,434 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) { } } + case 'c': // Prefix: "com" + + if l := len("com"); len(elem) >= l && elem[0:l] == "com" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + break + } + switch elem[0] { + case 'm': // Prefix: "ment" + + if l := len("ment"); len(elem) >= l && elem[0:l] == "ment" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch method { + case "POST": + r.name = CreateMapfixAuditCommentOperation + r.summary = "Post a comment to the audit log" + r.operationID = "createMapfixAuditComment" + r.pathPattern = "/mapfixes/{MapfixID}/comment" + r.args = args + r.count = 1 + return r, true + default: + return + } + } + + case 'p': // Prefix: "pleted" + + if l := len("pleted"); len(elem) >= l && elem[0:l] == "pleted" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch method { + case "POST": + r.name = SetMapfixCompletedOperation + r.summary = "Called by maptest when a player completes the map" + r.operationID = "setMapfixCompleted" + r.pathPattern = "/mapfixes/{MapfixID}/completed" + r.args = args + r.count = 1 + return r, true + default: + return + } + } + + } + + case 'm': // Prefix: "model" + + if l := len("model"); len(elem) >= l && elem[0:l] == "model" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch method { + case "POST": + r.name = UpdateMapfixModelOperation + r.summary = "Update model following role restrictions" + r.operationID = "updateMapfixModel" + r.pathPattern = "/mapfixes/{MapfixID}/model" + r.args = args + r.count = 1 + return r, true + default: + return + } + } + + case 's': // Prefix: "status/" + + if l := len("status/"); len(elem) >= l && elem[0:l] == "status/" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + break + } + switch elem[0] { + case 'r': // Prefix: "re" + + if l := len("re"); len(elem) >= l && elem[0:l] == "re" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + break + } + switch elem[0] { + case 'j': // Prefix: "ject" + + if l := len("ject"); len(elem) >= l && elem[0:l] == "ject" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch method { + case "POST": + r.name = ActionMapfixRejectOperation + r.summary = "Role Reviewer changes status from Submitted -> Rejected" + r.operationID = "actionMapfixReject" + r.pathPattern = "/mapfixes/{MapfixID}/status/reject" + r.args = args + r.count = 1 + return r, true + default: + return + } + } + + case 'q': // Prefix: "quest-changes" + + if l := len("quest-changes"); len(elem) >= l && elem[0:l] == "quest-changes" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch method { + case "POST": + r.name = ActionMapfixRequestChangesOperation + r.summary = "Role Reviewer changes status from Validated|Accepted|Submitted -> ChangesRequested" + r.operationID = "actionMapfixRequestChanges" + r.pathPattern = "/mapfixes/{MapfixID}/status/request-changes" + r.args = args + r.count = 1 + return r, true + default: + return + } + } + + case 's': // Prefix: "set-" + + if l := len("set-"); len(elem) >= l && elem[0:l] == "set-" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + break + } + switch elem[0] { + case 's': // Prefix: "submitting" + + if l := len("submitting"); len(elem) >= l && elem[0:l] == "submitting" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch method { + case "POST": + r.name = ActionMapfixResetSubmittingOperation + r.summary = "Role Submitter manually resets submitting softlock and changes status from Submitting -> UnderConstruction" + r.operationID = "actionMapfixResetSubmitting" + r.pathPattern = "/mapfixes/{MapfixID}/status/reset-submitting" + r.args = args + r.count = 1 + return r, true + default: + return + } + } + + case 'u': // Prefix: "uploading" + + if l := len("uploading"); len(elem) >= l && elem[0:l] == "uploading" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch method { + case "POST": + r.name = ActionMapfixValidatedOperation + r.summary = "Role Admin manually resets uploading softlock and changes status from Uploading -> Validated" + r.operationID = "actionMapfixValidated" + r.pathPattern = "/mapfixes/{MapfixID}/status/reset-uploading" + r.args = args + r.count = 1 + return r, true + default: + return + } + } + + case 'v': // Prefix: "validating" + + if l := len("validating"); len(elem) >= l && elem[0:l] == "validating" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch method { + case "POST": + r.name = ActionMapfixAcceptedOperation + r.summary = "Role Reviewer manually resets validating softlock and changes status from Validating -> Accepted" + r.operationID = "actionMapfixAccepted" + r.pathPattern = "/mapfixes/{MapfixID}/status/reset-validating" + r.args = args + r.count = 1 + return r, true + default: + return + } + } + + } + + case 't': // Prefix: "try-validate" + + if l := len("try-validate"); len(elem) >= l && elem[0:l] == "try-validate" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch method { + case "POST": + r.name = ActionMapfixRetryValidateOperation + r.summary = "Role Reviewer re-runs validation and changes status from Accepted -> Validating" + r.operationID = "actionMapfixRetryValidate" + r.pathPattern = "/mapfixes/{MapfixID}/status/retry-validate" + r.args = args + r.count = 1 + return r, true + default: + return + } + } + + case 'v': // Prefix: "voke" + + if l := len("voke"); len(elem) >= l && elem[0:l] == "voke" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch method { + case "POST": + r.name = ActionMapfixRevokeOperation + r.summary = "Role Submitter changes status from Submitted|ChangesRequested -> UnderConstruction" + r.operationID = "actionMapfixRevoke" + r.pathPattern = "/mapfixes/{MapfixID}/status/revoke" + r.args = args + r.count = 1 + return r, true + default: + return + } + } + + } + + case 't': // Prefix: "trigger-" + + if l := len("trigger-"); len(elem) >= l && elem[0:l] == "trigger-" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + break + } + switch elem[0] { + case 's': // Prefix: "submit" + + if l := len("submit"); len(elem) >= l && elem[0:l] == "submit" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + switch method { + case "POST": + r.name = ActionMapfixTriggerSubmitOperation + r.summary = "Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting" + r.operationID = "actionMapfixTriggerSubmit" + r.pathPattern = "/mapfixes/{MapfixID}/status/trigger-submit" + r.args = args + r.count = 1 + return r, true + default: + return + } + } + switch elem[0] { + case '-': // Prefix: "-unchecked" + + if l := len("-unchecked"); len(elem) >= l && elem[0:l] == "-unchecked" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch method { + case "POST": + r.name = ActionMapfixTriggerSubmitUncheckedOperation + r.summary = "Role Reviewer changes status from ChangesRequested -> Submitting" + r.operationID = "actionMapfixTriggerSubmitUnchecked" + r.pathPattern = "/mapfixes/{MapfixID}/status/trigger-submit-unchecked" + r.args = args + r.count = 1 + return r, true + default: + return + } + } + + } + + case 'u': // Prefix: "upload" + + if l := len("upload"); len(elem) >= l && elem[0:l] == "upload" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch method { + case "POST": + r.name = ActionMapfixTriggerUploadOperation + r.summary = "Role Admin changes status from Validated -> Uploading" + r.operationID = "actionMapfixTriggerUpload" + r.pathPattern = "/mapfixes/{MapfixID}/status/trigger-upload" + r.args = args + r.count = 1 + return r, true + default: + return + } + } + + case 'v': // Prefix: "validate" + + if l := len("validate"); len(elem) >= l && elem[0:l] == "validate" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch method { + case "POST": + r.name = ActionMapfixTriggerValidateOperation + r.summary = "Role Reviewer triggers validation and changes status from Submitted -> Validating" + r.operationID = "actionMapfixTriggerValidate" + r.pathPattern = "/mapfixes/{MapfixID}/status/trigger-validate" + r.args = args + r.count = 1 + return r, true + default: + return + } + } + + } + + } + } } } - case 'i': // Prefix: "igrate-maps" + case 's': // Prefix: "s" - if l := len("igrate-maps"); len(elem) >= l && elem[0:l] == "igrate-maps" { + if l := len("s"); len(elem) >= l && elem[0:l] == "s" { elem = elem[l:] } else { break } if len(elem) == 0 { - // Leaf node. switch method { - case "POST": - r.name = MigrateMapsOperation - r.summary = "Perform maps migration" - r.operationID = "migrateMaps" - r.pathPattern = "/migrate-maps" + case "GET": + r.name = ListMapsOperation + r.summary = "Get list of maps" + r.operationID = "listMaps" + r.pathPattern = "/maps" r.args = args r.count = 0 return r, true @@ -2159,6 +2027,66 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) { return } } + switch elem[0] { + case '/': // Prefix: "/" + + if l := len("/"); len(elem) >= l && elem[0:l] == "/" { + elem = elem[l:] + } else { + break + } + + // Param: "MapID" + // Match until "/" + idx := strings.IndexByte(elem, '/') + if idx < 0 { + idx = len(elem) + } + args[0] = elem[:idx] + elem = elem[idx:] + + if len(elem) == 0 { + switch method { + case "GET": + r.name = GetMapOperation + r.summary = "Retrieve map with ID" + r.operationID = "getMap" + r.pathPattern = "/maps/{MapID}" + r.args = args + r.count = 1 + return r, true + default: + return + } + } + switch elem[0] { + case '/': // Prefix: "/download" + + if l := len("/download"); len(elem) >= l && elem[0:l] == "/download" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch method { + case "GET": + r.name = DownloadMapAssetOperation + r.summary = "Download the map asset" + r.operationID = "downloadMapAsset" + r.pathPattern = "/maps/{MapID}/download" + r.args = args + r.count = 1 + return r, true + default: + return + } + } + + } + + } } diff --git a/pkg/api/oas_schemas_gen.go b/pkg/api/oas_schemas_gen.go index 0d4af90..b65b80d 100644 --- a/pkg/api/oas_schemas_gen.go +++ b/pkg/api/oas_schemas_gen.go @@ -664,9 +664,6 @@ func (s *Mapfixes) SetMapfixes(val []Mapfix) { s.Mapfixes = val } -// MigrateMapsOK is response for MigrateMaps operation. -type MigrateMapsOK struct{} - // Ref: #/components/schemas/Operation type Operation struct { OperationID int32 `json:"OperationID"` diff --git a/pkg/api/oas_security_gen.go b/pkg/api/oas_security_gen.go index 4fb1167..100c173 100644 --- a/pkg/api/oas_security_gen.go +++ b/pkg/api/oas_security_gen.go @@ -67,7 +67,6 @@ var operationRolesCookieAuth = map[string][]string{ DeleteScriptPolicyOperation: []string{}, DownloadMapAssetOperation: []string{}, GetOperationOperation: []string{}, - MigrateMapsOperation: []string{}, ReleaseSubmissionsOperation: []string{}, SessionRolesOperation: []string{}, SessionUserOperation: []string{}, diff --git a/pkg/api/oas_server_gen.go b/pkg/api/oas_server_gen.go index ee1ddc3..bc8d72e 100644 --- a/pkg/api/oas_server_gen.go +++ b/pkg/api/oas_server_gen.go @@ -280,12 +280,6 @@ type Handler interface { // // GET /submissions ListSubmissions(ctx context.Context, params ListSubmissionsParams) (*Submissions, error) - // MigrateMaps implements migrateMaps operation. - // - // Perform maps migration. - // - // POST /migrate-maps - MigrateMaps(ctx context.Context) error // ReleaseSubmissions implements releaseSubmissions operation. // // Release a set of uploaded maps. diff --git a/pkg/api/oas_unimplemented_gen.go b/pkg/api/oas_unimplemented_gen.go index 238e748..2c74330 100644 --- a/pkg/api/oas_unimplemented_gen.go +++ b/pkg/api/oas_unimplemented_gen.go @@ -420,15 +420,6 @@ func (UnimplementedHandler) ListSubmissions(ctx context.Context, params ListSubm return r, ht.ErrNotImplemented } -// MigrateMaps implements migrateMaps operation. -// -// Perform maps migration. -// -// POST /migrate-maps -func (UnimplementedHandler) MigrateMaps(ctx context.Context) error { - return ht.ErrNotImplemented -} - // ReleaseSubmissions implements releaseSubmissions operation. // // Release a set of uploaded maps. -- 2.49.1 From ac05f4acdc94ba9c72ab99783428bda85ca5c129 Mon Sep 17 00:00:00 2001 From: Rhys Lloyd Date: Fri, 25 Jul 2025 23:07:37 -0700 Subject: [PATCH 03/18] submissions: remove migration operation --- pkg/service/maps.go | 30 ------------------------------ pkg/web_api/maps.go | 23 ----------------------- 2 files changed, 53 deletions(-) diff --git a/pkg/service/maps.go b/pkg/service/maps.go index beb6809..ad29c0f 100644 --- a/pkg/service/maps.go +++ b/pkg/service/maps.go @@ -2,7 +2,6 @@ package service import ( "context" - "time" "git.itzana.me/strafesnet/go-grpc/maps" "git.itzana.me/strafesnet/maps-service/pkg/datastore" @@ -81,35 +80,6 @@ func (update MapFilter) SetSubmitter(submitter uint64) { datastore.OptionalMap(update).Add("submitter", submitter) } -func (svc *Service) TEMP_DoMapsMigration(ctx context.Context) (error) { - // get all maps - maps, err := svc.maps.List(ctx, &maps.ListRequest{}) - if err != nil { - return err - } - // create all maps - for _, item := range maps.Maps { - migrated := model.Map{ - ID: item.ID, - DisplayName: item.DisplayName, - Creator: item.Creator, - GameID: uint32(item.GameID), - Date: time.Unix(item.Date, 0), - // CreatedAt: time.Time{}, - // UpdatedAt: time.Time{}, - // Submitter: 0, - // Thumbnail: 0, - // AssetVersion: 0, - // LoadCount: 0, - // Modes: 0, - } - _, err := svc.db.Maps().Create(ctx, migrated) - if err != nil { - return err - } - } - return nil -} func (svc *Service) CreateMap(ctx context.Context, item model.Map) (int64, error) { // 2 jobs: // create map on maps-service diff --git a/pkg/web_api/maps.go b/pkg/web_api/maps.go index 1526692..97574b1 100644 --- a/pkg/web_api/maps.go +++ b/pkg/web_api/maps.go @@ -9,29 +9,6 @@ import ( "git.itzana.me/strafesnet/maps-service/pkg/service" ) -// MigrateMaps implements migrateMaps operation. -// -// Perform maps migration. -// -// POST /migrate -func (svc *Service) MigrateMaps(ctx context.Context) (error) { - userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle) - if !ok { - return ErrUserInfo - } - - has_role, err := userInfo.HasRoleSubmissionRelease() - if err != nil { - return err - } - - if !has_role { - return ErrPermissionDeniedNeedRoleSubmissionRelease - } - - return svc.inner.TEMP_DoMapsMigration(ctx) -} - // ListMaps implements listMaps operation. // // Get list of maps. -- 2.49.1 From 69ebef5fb8b0eb325f6c368d2a06facabaf8f8a9 Mon Sep 17 00:00:00 2001 From: Rhys Lloyd Date: Tue, 5 Aug 2025 21:29:13 -0700 Subject: [PATCH 04/18] validator: use rustls --- Cargo.lock | 445 ++++++++++++++++---------------------- validation/Cargo.toml | 2 +- validation/api/Cargo.toml | 6 +- 3 files changed, 194 insertions(+), 259 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ad8822b..0a53900 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -87,7 +87,7 @@ dependencies = [ "once_cell", "pin-project", "portable-atomic", - "rand", + "rand 0.8.5", "regex", "ring", "rustls-native-certs", @@ -97,7 +97,7 @@ dependencies = [ "serde_json", "serde_nanos", "serde_repr", - "thiserror", + "thiserror 1.0.69", "time", "tokio", "tokio-rustls", @@ -188,7 +188,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -281,6 +281,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.41" @@ -335,9 +341,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ "cfg-if", ] @@ -469,22 +475,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" -[[package]] -name = "errno" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" -dependencies = [ - "libc", - "windows-sys 0.60.2", -] - -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" - [[package]] name = "fiat-crypto" version = "0.2.9" @@ -507,21 +497,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "form_urlencoded" version = "1.2.1" @@ -637,8 +612,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi 0.11.1+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -648,9 +625,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi", "wasi 0.14.2+wasi-0.2.4", + "wasm-bindgen", ] [[package]] @@ -771,6 +750,7 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", + "webpki-roots 1.0.1", ] [[package]] @@ -786,22 +766,6 @@ dependencies = [ "tower-service", ] -[[package]] -name = "hyper-tls" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" -dependencies = [ - "bytes", - "http-body-util", - "hyper", - "hyper-util", - "native-tls", - "tokio", - "tokio-native-tls", - "tower-service", -] - [[package]] name = "hyper-util" version = "0.1.14" @@ -1055,12 +1019,6 @@ version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" -[[package]] -name = "linux-raw-sys" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" - [[package]] name = "litemap" version = "0.8.0" @@ -1083,6 +1041,12 @@ version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + [[package]] name = "lz4" version = "1.28.1" @@ -1171,23 +1135,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "native-tls" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - [[package]] name = "nkeys" version = "0.4.4" @@ -1199,7 +1146,7 @@ dependencies = [ "ed25519-dalek", "getrandom 0.2.16", "log", - "rand", + "rand 0.8.5", "signatory", ] @@ -1209,7 +1156,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc895af95856f929163a0aa20c26a78d26bfdc839f51b9d5aa7a5b79e52b7e83" dependencies = [ - "rand", + "rand 0.8.5", ] [[package]] @@ -1242,50 +1189,12 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" -[[package]] -name = "openssl" -version = "0.10.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" -dependencies = [ - "bitflags 2.9.1", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "openssl-probe" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" -[[package]] -name = "openssl-sys" -version = "0.9.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "parking_lot" version = "0.12.4" @@ -1306,7 +1215,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -1469,6 +1378,61 @@ dependencies = [ "serde", ] +[[package]] +name = "quinn" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror 2.0.12", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e" +dependencies = [ + "bytes", + "getrandom 0.3.3", + "lru-slab", + "rand 0.9.2", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.12", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcebb1209ee276352ef14ff8732e24cc2b02bbac986cd74a4c81bcb2f9881970" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.59.0", +] + [[package]] name = "quote" version = "1.0.40" @@ -1491,8 +1455,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", ] [[package]] @@ -1502,7 +1476,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", ] [[package]] @@ -1514,11 +1498,20 @@ dependencies = [ "getrandom 0.2.16", ] +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", +] + [[package]] name = "rbx_asset" -version = "0.4.8" +version = "0.4.9" source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/" -checksum = "8c067713a3201d0d2de3445ebecc8001952d914319863a9c7bc8f9212fdb1a17" +checksum = "32c7c8efca16fc09ac623ea41cde2bf48e2da761ac8edd575b0b7022f5dc5bd5" dependencies = [ "bytes", "chrono", @@ -1542,7 +1535,7 @@ dependencies = [ "rbx_dom_weak", "rbx_reflection", "rbx_reflection_database", - "thiserror", + "thiserror 1.0.69", "zstd", ] @@ -1566,7 +1559,7 @@ checksum = "1b6d0d62baa613556b058a5f94a53b01cf0ccde0ea327ce03056e335b982e77e" dependencies = [ "rbx_types", "serde", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -1591,9 +1584,9 @@ dependencies = [ "bitflags 1.3.2", "blake3", "lazy_static", - "rand", + "rand 0.8.5", "serde", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -1666,22 +1659,22 @@ dependencies = [ "http-body-util", "hyper", "hyper-rustls", - "hyper-tls", "hyper-util", "js-sys", "log", "mime", "mime_guess", - "native-tls", "percent-encoding", "pin-project-lite", + "quinn", + "rustls", "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", "tokio", - "tokio-native-tls", + "tokio-rustls", "tower", "tower-http", "tower-service", @@ -1689,6 +1682,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", + "webpki-roots 1.0.1", ] [[package]] @@ -1745,6 +1739,12 @@ version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + [[package]] name = "rustc_version" version = "0.4.1" @@ -1754,19 +1754,6 @@ dependencies = [ "semver", ] -[[package]] -name = "rustix" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" -dependencies = [ - "bitflags 2.9.1", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.59.0", -] - [[package]] name = "rustls" version = "0.23.28" @@ -1809,6 +1796,7 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" dependencies = [ + "web-time", "zeroize", ] @@ -1986,7 +1974,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1e303f8205714074f6068773f0e29527e0453937fe837c9717d066635b65f31" dependencies = [ "pkcs8", - "rand_core", + "rand_core 0.6.4", "signature", "zeroize", ] @@ -1998,7 +1986,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -2115,26 +2103,22 @@ dependencies = [ "libc", ] -[[package]] -name = "tempfile" -version = "3.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" -dependencies = [ - "fastrand", - "getrandom 0.3.3", - "once_cell", - "rustix", - "windows-sys 0.59.0", -] - [[package]] name = "thiserror" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", ] [[package]] @@ -2148,6 +2132,17 @@ dependencies = [ "syn", ] +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "time" version = "0.3.41" @@ -2189,6 +2184,21 @@ dependencies = [ "zerovec", ] +[[package]] +name = "tinyvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.45.1" @@ -2217,16 +2227,6 @@ dependencies = [ "syn", ] -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - [[package]] name = "tokio-rustls" version = "0.26.2" @@ -2273,7 +2273,7 @@ dependencies = [ "futures-sink", "http", "httparse", - "rand", + "rand 0.8.5", "ring", "rustls-pki-types", "tokio", @@ -2461,12 +2461,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "version_check" version = "0.9.5" @@ -2578,6 +2572,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webpki-roots" version = "0.26.11" @@ -2672,7 +2676,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -2681,16 +2685,7 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.2", + "windows-targets", ] [[package]] @@ -2699,30 +2694,14 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" -dependencies = [ - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] @@ -2731,96 +2710,48 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" - [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" - [[package]] name = "wit-bindgen-rt" version = "0.39.0" diff --git a/validation/Cargo.toml b/validation/Cargo.toml index 9067bb7..1421471 100644 --- a/validation/Cargo.toml +++ b/validation/Cargo.toml @@ -6,7 +6,7 @@ edition = "2024" [dependencies] async-nats = "0.42.0" futures = "0.3.31" -rbx_asset = { version = "0.4.7", registry = "strafesnet" } +rbx_asset = { version = "0.4.9", features = ["gzip", "rustls-tls"], default-features = false, registry = "strafesnet" } rbx_binary = "1.0.0" rbx_dom_weak = "3.0.0" rbx_reflection_database = "1.0.3" diff --git a/validation/api/Cargo.toml b/validation/api/Cargo.toml index a52749e..9982ecf 100644 --- a/validation/api/Cargo.toml +++ b/validation/api/Cargo.toml @@ -12,7 +12,11 @@ authors = ["Rhys Lloyd "] [dependencies] chrono = { version = "0.4.41", features = ["serde"] } -reqwest = { version = "0", features = ["json"] } +reqwest = { version = "0", features = [ + "json", "rustls-tls", + # default features + "charset", "http2", "system-proxy" +], default-features = false } serde = { version = "1", features = ["derive"] } serde_json = "1" serde_repr = "0.1.19" -- 2.49.1 From f9455e7317cf439ff379faf8645f49de21bab8f9 Mon Sep 17 00:00:00 2001 From: Rhys Lloyd Date: Tue, 5 Aug 2025 21:30:39 -0700 Subject: [PATCH 05/18] validator: update deps --- Cargo.lock | 111 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 67 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0a53900..b728c94 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -266,9 +266,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.27" +version = "1.2.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" +checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2" dependencies = [ "jobserver", "libc", @@ -443,9 +443,9 @@ dependencies = [ [[package]] name = "ed25519-dalek" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" dependencies = [ "curve25519-dalek", "ed25519", @@ -750,7 +750,7 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", - "webpki-roots 1.0.1", + "webpki-roots 1.0.2", ] [[package]] @@ -768,9 +768,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.14" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb" +checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" dependencies = [ "base64 0.22.1", "bytes", @@ -784,7 +784,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2", + "socket2 0.6.0", "system-configuration", "tokio", "tower-service", @@ -933,6 +933,17 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "io-uring" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "libc", +] + [[package]] name = "ipnet" version = "2.11.0" @@ -1137,9 +1148,9 @@ dependencies = [ [[package]] name = "nkeys" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f49e787f4c61cbd0f9320b31cc26e58719f6aa5068e34697dd3aea361412fe3" +checksum = "879011babc47a1c7fdf5a935ae3cfe94f34645ca0cac1c7f6424b36fc743d1bf" dependencies = [ "data-encoding", "ed25519", @@ -1391,7 +1402,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2", + "socket2 0.5.10", "thiserror 2.0.12", "tokio", "tracing", @@ -1428,7 +1439,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2", + "socket2 0.5.10", "tracing", "windows-sys 0.59.0", ] @@ -1606,9 +1617,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.13" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ "bitflags 2.9.1", ] @@ -1644,9 +1655,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.21" +version = "0.12.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8cea6b35bcceb099f30173754403d2eba0a5dc18cea3630fccd88251909288" +checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" dependencies = [ "base64 0.22.1", "bytes", @@ -1682,7 +1693,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots 1.0.1", + "webpki-roots 1.0.2", ] [[package]] @@ -1723,9 +1734,9 @@ dependencies = [ [[package]] name = "rust-grpc" -version = "1.3.0" +version = "1.3.4" source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/" -checksum = "23dc6efbef932befc29c64b9484be3686c97217f6197a91c2028c2eeee022f46" +checksum = "ffab535c98c3a298cd092126036d5f8e40b9e600d24941823fb67a788be387ee" dependencies = [ "prost", "prost-types", @@ -1735,9 +1746,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc-hash" @@ -1756,14 +1767,14 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.28" +version = "0.23.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7160e3e10bf4535308537f3c4e1641468cd0e485175d6163087c0393c7d46643" +checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" dependencies = [ "once_cell", "ring", "rustls-pki-types", - "rustls-webpki 0.103.3", + "rustls-webpki 0.103.4", "subtle", "zeroize", ] @@ -1812,9 +1823,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.3" +version = "0.103.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" +checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" dependencies = [ "ring", "rustls-pki-types", @@ -1899,9 +1910,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.142" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" dependencies = [ "itoa", "memchr", @@ -1960,9 +1971,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.5" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ "libc", ] @@ -2017,6 +2028,16 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "socket2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "spki" version = "0.7.3" @@ -2201,19 +2222,21 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.45.1" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", "bytes", + "io-uring", "libc", "mio", "pin-project-lite", "signal-hook-registry", - "socket2", + "slab", + "socket2 0.6.0", "tokio-macros", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2250,9 +2273,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.15" +version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" dependencies = [ "bytes", "futures-core", @@ -2302,7 +2325,7 @@ dependencies = [ "percent-encoding", "pin-project", "prost", - "socket2", + "socket2 0.5.10", "tokio", "tokio-stream", "tower", @@ -2588,14 +2611,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.1", + "webpki-roots 1.0.2", ] [[package]] name = "webpki-roots" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8782dd5a41a24eed3a4f40b606249b3e236ca61adf1f25ea4d45c73de122b502" +checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" dependencies = [ "rustls-pki-types", ] @@ -2769,9 +2792,9 @@ checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "xml-rs" -version = "0.8.26" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62ce76d9b56901b19a74f19431b0d8b3bc7ca4ad685a746dfd78ca8f4fc6bda" +checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7" [[package]] name = "yoke" @@ -2857,9 +2880,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.2" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" dependencies = [ "yoke", "zerofrom", -- 2.49.1 From 1e4e513dc116d4b99c8fc06dc5fd40ed673074d3 Mon Sep 17 00:00:00 2001 From: Rhys Lloyd Date: Tue, 5 Aug 2025 22:06:58 -0700 Subject: [PATCH 06/18] web: update deps --- web/bun.lock | 3 +++ 1 file changed, 3 insertions(+) diff --git a/web/bun.lock b/web/bun.lock index e2e2c31..ae9b05b 100644 --- a/web/bun.lock +++ b/web/bun.lock @@ -8,6 +8,7 @@ "@emotion/styled": "^11.14.0", "@mui/icons-material": "^6.1.10", "@mui/material": "^6.1.10", + "date-fns": "^4.1.0", "next": "^15.1.0", "react": "^19.0.0", "react-dom": "^19.0.0", @@ -393,6 +394,8 @@ "data-view-byte-offset": ["data-view-byte-offset@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" } }, "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ=="], + "date-fns": ["date-fns@4.1.0", "", {}, "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg=="], + "debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], -- 2.49.1 From 48314f5d181fae7cdab6ec2994dab03290eb3218 Mon Sep 17 00:00:00 2001 From: Rhys Lloyd Date: Wed, 6 Aug 2025 05:49:30 +0000 Subject: [PATCH 07/18] Refactor Docker (#254) Refactor docker to use makefile build commands Reviewed-on: https://git.itzana.me/StrafesNET/maps-service/pulls/254 Co-authored-by: Rhys Lloyd Co-committed-by: Rhys Lloyd --- {validation/.cargo => .cargo}/config.toml | 0 .drone.yml | 109 +++++++++++----------- .gitignore | 1 + Containerfile | 33 ------- Dockerfile | 3 + Makefile | 46 +++++++-- compose.yaml | 6 +- validation/Containerfile | 27 +----- 8 files changed, 101 insertions(+), 124 deletions(-) rename {validation/.cargo => .cargo}/config.toml (100%) delete mode 100644 Containerfile create mode 100644 Dockerfile diff --git a/validation/.cargo/config.toml b/.cargo/config.toml similarity index 100% rename from validation/.cargo/config.toml rename to .cargo/config.toml diff --git a/.drone.yml b/.drone.yml index 731074c..467d7eb 100644 --- a/.drone.yml +++ b/.drone.yml @@ -7,7 +7,43 @@ platform: arch: amd64 steps: - - name: api + - name: build-backend + image: golang:1.24.0 + environment: + GIT_USER: + from_secret: GIT_USER + GIT_PASS: + from_secret: GIT_PASS + commands: + - echo "machine git.itzana.me login $${GIT_USER} password $${GIT_PASS}" > ~/.netrc + - chmod 600 ~/.netrc + - make build-backend + when: + branch: + - master + - staging + + - name: build-validator + image: clux/muslrust:1.86.0-stable + commands: + - make build-validator + when: + branch: + - master + - staging + + - name: build-frontend + image: oven/bun:1.2.8 + commands: + - apt-get update + - apt-get install make + - make build-frontend + when: + branch: + - master + - staging + + - name: image-backend image: plugins/docker settings: registry: registry.itzana.me @@ -19,8 +55,10 @@ steps: from_secret: REGISTRY_USER password: from_secret: REGISTRY_PASS - dockerfile: Containerfile + dockerfile: Dockerfile context: . + depends_on: + - build-backend when: branch: - master @@ -28,7 +66,7 @@ steps: event: - push - - name: frontend + - name: image-frontend image: plugins/docker settings: registry: registry.itzana.me @@ -42,6 +80,8 @@ steps: from_secret: REGISTRY_PASS dockerfile: web/Containerfile context: web + depends_on: + - build-frontend when: branch: - master @@ -49,7 +89,7 @@ steps: event: - push - - name: validator + - name: image-validator image: plugins/docker settings: registry: registry.itzana.me @@ -63,6 +103,8 @@ steps: from_secret: REGISTRY_PASS dockerfile: validation/Containerfile context: validation + depends_on: + - build-validator when: branch: - master @@ -83,9 +125,9 @@ steps: PASSWORD: from_secret: ARGO_PASS depends_on: - - api - - frontend - - validator + - image-backend + - image-frontend + - image-validator when: branch: - master @@ -94,64 +136,19 @@ steps: - push # pr dry run - - name: api-pr - image: plugins/docker - settings: - registry: registry.itzana.me - repo: registry.itzana.me/strafesnet/maptest-validator - tags: - - ${DRONE_BRANCH}-${DRONE_BUILD_NUMBER} - - ${DRONE_BRANCH} - dockerfile: Containerfile - context: . - dry_run: true - when: - event: - - pull_request - - - name: frontend-pr - image: plugins/docker - settings: - registry: registry.itzana.me - repo: registry.itzana.me/strafesnet/maptest-validator - tags: - - ${DRONE_BRANCH}-${DRONE_BUILD_NUMBER} - - ${DRONE_BRANCH} - dockerfile: web/Containerfile - context: web - dry_run: true - when: - event: - - pull_request - - - name: validator-pr - image: plugins/docker - settings: - registry: registry.itzana.me - repo: registry.itzana.me/strafesnet/maptest-validator - tags: - - ${DRONE_BRANCH}-${DRONE_BUILD_NUMBER} - - ${DRONE_BRANCH} - dockerfile: validation/Containerfile - context: validation - dry_run: true - when: - event: - - pull_request - - name: build-pr image: alpine commands: - echo "Success!" depends_on: - - api-pr - - frontend-pr - - validator-pr + - build-backend + - build-validator + - build-frontend when: event: - pull_request --- kind: signature -hmac: 11e6d7f1eb839d3798fdcb642ca5523c011bd14c1f3a0343a9c3106bab9ef142 +hmac: 9880a1bb4725c81e38b5d185bbfccaf70ddf8021299557d1815f78e78817c5e6 ... diff --git a/.gitignore b/.gitignore index 6db043d..6fb4a5d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ +build .idea /target diff --git a/Containerfile b/Containerfile deleted file mode 100644 index 92d7328..0000000 --- a/Containerfile +++ /dev/null @@ -1,33 +0,0 @@ -# Stage 1: Build -FROM registry.itzana.me/docker-proxy/golang:1.24 AS builder - -# Set the working directory in the container -WORKDIR /app - -# Copy go.mod and go.sum files -COPY go.mod go.sum ./ - -# Copy the entire project -COPY . . - -# Build the Go application -RUN CGO_ENABLED=0 GOOS=linux go build -o service ./cmd/maps-service/service.go - -# Stage 2: Run -FROM registry.itzana.me/docker-proxy/alpine:3.21 - -# Set up a non-root user for security -RUN addgroup -S appgroup && adduser -S appuser -G appgroup -USER appuser - -# Set the working directory in the container -WORKDIR /home/appuser - -# Copy the built application from the builder stage -COPY --from=builder /app/service . - -# Expose application port (adjust if needed) -EXPOSE 8081 - -# Command to run the application -ENTRYPOINT ["./service"] diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4d10493 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,3 @@ +FROM alpine +COPY build/server / +ENTRYPOINT ["/server"] diff --git a/Makefile b/Makefile index ece4ecb..8ca1e28 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,42 @@ -submissions: - DOCKER_BUILDKIT=1 docker build . -f Containerfile -t maps-service-submissions +clean: + rm -rf build + rm -rf web/build -web: - docker build web -f web/Containerfile -t maps-service-web +# build +build-backend: + CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o build/server cmd/maps-service/service.go -validation: - docker build validation -f validation/Containerfile -t maps-service-validation +build-validator: + cargo build --release --target x86_64-unknown-linux-musl --bin maps-validation -all: submissions web validation +build-frontend: + rm -rf web/build + cd web && bun install --frozen-lockfile + cd web && bun run build -.PHONY: submissions web validation +build: build-backend build-validator build-frontend + +# image +image-backend: + docker build . -t maptest-api + +image-validator: + docker build . -f validation/Containerfile -t maptest-validator + +image-frontend: + docker build web -f web/Containerfile -t maptest-frontend + +# docker +docker-backend: + make build-backend + make image-backend +docker-validator: + make build-validator + make image-validator +docker-frontend: + make build-frontend + make image-frontend + +docker: docker-backend docker-validator docker-frontend + +.PHONY: clean build-backend build-validator build-frontend build image-backend image-validator image-frontend docker-backend docker-validator docker-frontend docker diff --git a/compose.yaml b/compose.yaml index a1b9fc2..3f149e2 100644 --- a/compose.yaml +++ b/compose.yaml @@ -13,7 +13,7 @@ services: submissions: image: - maps-service-submissions + maptest-api container_name: submissions command: [ # debug @@ -45,7 +45,7 @@ services: web: image: - maps-service-web + maptest-frontend networks: - maps-service-network ports: @@ -56,7 +56,7 @@ services: validation: image: - maps-service-validation + maptest-validator container_name: validation env_file: - ../auth-compose/strafesnet_staging.env diff --git a/validation/Containerfile b/validation/Containerfile index 43d3ca7..fcebf00 100644 --- a/validation/Containerfile +++ b/validation/Containerfile @@ -1,24 +1,3 @@ -# Using the `rust-musl-builder` as base image, instead of -# the official Rust toolchain -FROM registry.itzana.me/docker-proxy/clux/muslrust:1.86.0-stable AS chef -USER root -RUN cargo install cargo-chef -WORKDIR /app - -FROM chef AS planner -COPY . . -RUN cargo chef prepare --recipe-path recipe.json - -FROM chef AS builder -COPY --from=planner /app/recipe.json recipe.json -COPY api ./api -# Notice that we are specifying the --target flag! -RUN cargo chef cook --release --target x86_64-unknown-linux-musl --recipe-path recipe.json -COPY . . -RUN cargo build --release --target x86_64-unknown-linux-musl --bin maps-validation - -FROM registry.itzana.me/docker-proxy/alpine:3.21 AS runtime -RUN addgroup -S myuser && adduser -S myuser -G myuser -COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/maps-validation /usr/local/bin/ -USER myuser -ENTRYPOINT ["/usr/local/bin/maps-validation"] +FROM alpine:3.21 AS runtime +COPY /target/x86_64-unknown-linux-musl/release/maps-validation / +ENTRYPOINT ["/maps-validation"] -- 2.49.1 From b0c723be1b31c8508299d85e561aa5c4952d7ea5 Mon Sep 17 00:00:00 2001 From: Rhys Lloyd Date: Tue, 5 Aug 2025 22:53:58 -0700 Subject: [PATCH 08/18] docker: fix validator context --- .drone.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.drone.yml b/.drone.yml index 467d7eb..6c2e8b0 100644 --- a/.drone.yml +++ b/.drone.yml @@ -102,7 +102,7 @@ steps: password: from_secret: REGISTRY_PASS dockerfile: validation/Containerfile - context: validation + context: . depends_on: - build-validator when: @@ -149,6 +149,6 @@ steps: - pull_request --- kind: signature -hmac: 9880a1bb4725c81e38b5d185bbfccaf70ddf8021299557d1815f78e78817c5e6 +hmac: c4222688ca348daadee16490168aafe9a3a3dd0c835954296ddb1bddddf0df4b ... -- 2.49.1 From d196da949cf4a04c7144810240394f7074fdb0f8 Mon Sep 17 00:00:00 2001 From: Rhys Lloyd Date: Wed, 6 Aug 2025 06:07:12 +0000 Subject: [PATCH 09/18] Public API (#253) Closes #229 This is a MVP and only includes maps. Reviewed-on: https://git.itzana.me/StrafesNET/maps-service/pulls/253 Reviewed-by: itzaname Co-authored-by: Rhys Lloyd Co-committed-by: Rhys Lloyd --- cmd/maps-service/service.go | 10 ++ docs/docs.go | 242 +++++++++++++++++++++++++++++ docs/swagger.json | 217 ++++++++++++++++++++++++++ docs/swagger.yaml | 141 +++++++++++++++++ generate.go | 1 + go.mod | 64 ++++++-- go.sum | 177 +++++++++++++++++---- pkg/cmds/api.go | 57 +++++++ pkg/public_api/dto/map.go | 47 ++++++ pkg/public_api/dto/response.go | 52 +++++++ pkg/public_api/handlers/handler.go | 98 ++++++++++++ pkg/public_api/handlers/maps.go | 153 ++++++++++++++++++ pkg/public_api/router.go | 159 +++++++++++++++++++ 13 files changed, 1378 insertions(+), 40 deletions(-) create mode 100644 docs/docs.go create mode 100644 docs/swagger.json create mode 100644 docs/swagger.yaml create mode 100644 pkg/cmds/api.go create mode 100644 pkg/public_api/dto/map.go create mode 100644 pkg/public_api/dto/response.go create mode 100644 pkg/public_api/handlers/handler.go create mode 100644 pkg/public_api/handlers/maps.go create mode 100644 pkg/public_api/router.go diff --git a/cmd/maps-service/service.go b/cmd/maps-service/service.go index 33993eb..44a9184 100644 --- a/cmd/maps-service/service.go +++ b/cmd/maps-service/service.go @@ -7,6 +7,16 @@ import ( "os" ) +// @title StrafesNET Maps API +// @version 1.0 +// @description Obtain an api key at https://dev.strafes.net +// @description Requires Maps:Read permission +// @BasePath /public-api/v1 + +// @securityDefinitions.apikey ApiKeyAuth +// @in header +// @name X-API-Key + func main() { log.SetLevel(log.InfoLevel) log.SetFormatter(&log.JSONFormatter{}) diff --git a/docs/docs.go b/docs/docs.go new file mode 100644 index 0000000..4060b38 --- /dev/null +++ b/docs/docs.go @@ -0,0 +1,242 @@ +// Package docs Code generated by swaggo/swag. DO NOT EDIT +package docs + +import "github.com/swaggo/swag" + +const docTemplate = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{escape .Description}}", + "title": "{{.Title}}", + "contact": {}, + "version": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": { + "/map": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get a list of maps", + "produces": [ + "application/json" + ], + "tags": [ + "maps" + ], + "summary": "List maps", + "parameters": [ + { + "maximum": 100, + "minimum": 1, + "type": "integer", + "default": 10, + "description": "Page size (max 100)", + "name": "page_size", + "in": "query" + }, + { + "minimum": 1, + "type": "integer", + "default": 1, + "description": "Page number", + "name": "page_number", + "in": "query" + }, + { + "type": "integer", + "name": "game_id", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/PagedResponse-Map" + } + }, + "default": { + "description": "General error response", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/map/{id}": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get a specific map by its ID", + "produces": [ + "application/json" + ], + "tags": [ + "maps" + ], + "summary": "Get map by ID", + "parameters": [ + { + "type": "integer", + "description": "Map ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/Response-Map" + } + }, + "404": { + "description": "Map not found", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "General error response", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + } + }, + "definitions": { + "Error": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + } + }, + "Map": { + "type": "object", + "properties": { + "asset_version": { + "type": "integer" + }, + "created_at": { + "type": "string" + }, + "creator": { + "type": "string" + }, + "date": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "game_id": { + "type": "integer" + }, + "id": { + "type": "integer" + }, + "load_count": { + "type": "integer" + }, + "modes": { + "type": "integer" + }, + "submitter": { + "type": "integer" + }, + "thumbnail": { + "type": "integer" + }, + "updated_at": { + "type": "string" + } + } + }, + "PagedResponse-Map": { + "type": "object", + "properties": { + "data": { + "description": "Data contains the actual response payload", + "type": "array", + "items": { + "$ref": "#/definitions/Map" + } + }, + "pagination": { + "description": "Pagination contains information about paging", + "allOf": [ + { + "$ref": "#/definitions/Pagination" + } + ] + } + } + }, + "Pagination": { + "type": "object", + "properties": { + "page": { + "description": "Current page number", + "type": "integer" + }, + "page_size": { + "description": "Number of items per page", + "type": "integer" + } + } + }, + "Response-Map": { + "type": "object", + "properties": { + "data": { + "description": "Data contains the actual response payload", + "allOf": [ + { + "$ref": "#/definitions/Map" + } + ] + } + } + } + }, + "securityDefinitions": { + "ApiKeyAuth": { + "type": "apiKey", + "name": "X-API-Key", + "in": "header" + } + } +}` + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = &swag.Spec{ + Version: "1.0", + Host: "", + BasePath: "/public-api/v1", + Schemes: []string{}, + Title: "StrafesNET Maps API", + Description: "Obtain an api key at https://dev.strafes.net\nRequires Data:Read permission", + InfoInstanceName: "swagger", + SwaggerTemplate: docTemplate, + LeftDelim: "{{", + RightDelim: "}}", +} + +func init() { + swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) +} diff --git a/docs/swagger.json b/docs/swagger.json new file mode 100644 index 0000000..56fe4a8 --- /dev/null +++ b/docs/swagger.json @@ -0,0 +1,217 @@ +{ + "swagger": "2.0", + "info": { + "description": "Obtain an api key at https://dev.strafes.net\nRequires Data:Read permission", + "title": "StrafesNET Maps API", + "contact": {}, + "version": "1.0" + }, + "basePath": "/public-api/v1", + "paths": { + "/map": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get a list of maps", + "produces": [ + "application/json" + ], + "tags": [ + "maps" + ], + "summary": "List maps", + "parameters": [ + { + "maximum": 100, + "minimum": 1, + "type": "integer", + "default": 10, + "description": "Page size (max 100)", + "name": "page_size", + "in": "query" + }, + { + "minimum": 1, + "type": "integer", + "default": 1, + "description": "Page number", + "name": "page_number", + "in": "query" + }, + { + "type": "integer", + "name": "game_id", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/PagedResponse-Map" + } + }, + "default": { + "description": "General error response", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/map/{id}": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get a specific map by its ID", + "produces": [ + "application/json" + ], + "tags": [ + "maps" + ], + "summary": "Get map by ID", + "parameters": [ + { + "type": "integer", + "description": "Map ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/Response-Map" + } + }, + "404": { + "description": "Map not found", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "General error response", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + } + }, + "definitions": { + "Error": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + } + }, + "Map": { + "type": "object", + "properties": { + "asset_version": { + "type": "integer" + }, + "created_at": { + "type": "string" + }, + "creator": { + "type": "string" + }, + "date": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "game_id": { + "type": "integer" + }, + "id": { + "type": "integer" + }, + "load_count": { + "type": "integer" + }, + "modes": { + "type": "integer" + }, + "submitter": { + "type": "integer" + }, + "thumbnail": { + "type": "integer" + }, + "updated_at": { + "type": "string" + } + } + }, + "PagedResponse-Map": { + "type": "object", + "properties": { + "data": { + "description": "Data contains the actual response payload", + "type": "array", + "items": { + "$ref": "#/definitions/Map" + } + }, + "pagination": { + "description": "Pagination contains information about paging", + "allOf": [ + { + "$ref": "#/definitions/Pagination" + } + ] + } + } + }, + "Pagination": { + "type": "object", + "properties": { + "page": { + "description": "Current page number", + "type": "integer" + }, + "page_size": { + "description": "Number of items per page", + "type": "integer" + } + } + }, + "Response-Map": { + "type": "object", + "properties": { + "data": { + "description": "Data contains the actual response payload", + "allOf": [ + { + "$ref": "#/definitions/Map" + } + ] + } + } + } + }, + "securityDefinitions": { + "ApiKeyAuth": { + "type": "apiKey", + "name": "X-API-Key", + "in": "header" + } + } +} \ No newline at end of file diff --git a/docs/swagger.yaml b/docs/swagger.yaml new file mode 100644 index 0000000..4614140 --- /dev/null +++ b/docs/swagger.yaml @@ -0,0 +1,141 @@ +basePath: /public-api/v1 +definitions: + Error: + properties: + error: + type: string + type: object + Map: + properties: + asset_version: + type: integer + created_at: + type: string + creator: + type: string + date: + type: string + display_name: + type: string + game_id: + type: integer + id: + type: integer + load_count: + type: integer + modes: + type: integer + submitter: + type: integer + thumbnail: + type: integer + updated_at: + type: string + type: object + PagedResponse-Map: + properties: + data: + description: Data contains the actual response payload + items: + $ref: '#/definitions/Map' + type: array + pagination: + allOf: + - $ref: '#/definitions/Pagination' + description: Pagination contains information about paging + type: object + Pagination: + properties: + page: + description: Current page number + type: integer + page_size: + description: Number of items per page + type: integer + type: object + Response-Map: + properties: + data: + allOf: + - $ref: '#/definitions/Map' + description: Data contains the actual response payload + type: object +info: + contact: {} + description: |- + Obtain an api key at https://dev.strafes.net + Requires Data:Read permission + title: StrafesNET Maps API + version: "1.0" +paths: + /map: + get: + description: Get a list of maps + parameters: + - default: 10 + description: Page size (max 100) + in: query + maximum: 100 + minimum: 1 + name: page_size + type: integer + - default: 1 + description: Page number + in: query + minimum: 1 + name: page_number + type: integer + - in: query + name: game_id + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/PagedResponse-Map' + default: + description: General error response + schema: + $ref: '#/definitions/Error' + security: + - ApiKeyAuth: [] + summary: List maps + tags: + - maps + /map/{id}: + get: + description: Get a specific map by its ID + parameters: + - description: Map ID + in: path + name: id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/Response-Map' + "404": + description: Map not found + schema: + $ref: '#/definitions/Error' + default: + description: General error response + schema: + $ref: '#/definitions/Error' + security: + - ApiKeyAuth: [] + summary: Get map by ID + tags: + - maps +securityDefinitions: + ApiKeyAuth: + in: header + name: X-API-Key + type: apiKey +swagger: "2.0" diff --git a/generate.go b/generate.go index a27289b..5a37007 100644 --- a/generate.go +++ b/generate.go @@ -1,3 +1,4 @@ package main +//go:generate swag init -g ./cmd/maps-service/service.go //go:generate go run github.com/ogen-go/ogen/cmd/ogen@latest --target pkg/api --clean openapi.yaml diff --git a/go.mod b/go.mod index cc627d2..fcb504d 100644 --- a/go.mod +++ b/go.mod @@ -1,44 +1,80 @@ module git.itzana.me/strafesnet/maps-service -go 1.22 +go 1.24.0 -toolchain go1.23.3 +toolchain go1.24.5 require ( + git.itzana.me/StrafesNET/dev-service v0.0.0-20250628052121-92af8193b5ed git.itzana.me/strafesnet/go-grpc v0.0.0-20250724030029-845bea991815 git.itzana.me/strafesnet/utils v0.0.0-20220716194944-d8ca164052f9 github.com/dchest/siphash v1.2.3 + github.com/gin-gonic/gin v1.10.1 github.com/go-faster/errors v0.7.1 github.com/go-faster/jx v1.1.0 github.com/nats-io/nats.go v1.37.0 github.com/ogen-go/ogen v1.2.1 github.com/sirupsen/logrus v1.9.3 - github.com/urfave/cli/v2 v2.27.5 + github.com/swaggo/files v1.0.1 + github.com/swaggo/gin-swagger v1.6.0 + github.com/swaggo/swag v1.16.6 + github.com/urfave/cli/v2 v2.27.6 go.opentelemetry.io/otel v1.32.0 go.opentelemetry.io/otel/metric v1.32.0 go.opentelemetry.io/otel/trace v1.32.0 google.golang.org/grpc v1.48.0 - gorm.io/driver/postgres v1.5.10 - gorm.io/gorm v1.25.10 + gorm.io/driver/postgres v1.6.0 + gorm.io/gorm v1.25.12 ) require ( + github.com/KyleBanks/depth v1.2.1 // indirect + github.com/PuerkitoBio/purell v1.1.1 // indirect + github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/bytedance/sonic v1.11.6 // indirect + github.com/bytedance/sonic/loader v0.1.1 // indirect + github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/iasm v0.2.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.19.6 // indirect + github.com/go-openapi/spec v0.20.4 // indirect + github.com/go-openapi/swag v0.19.15 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.20.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect - github.com/jackc/pgx/v5 v5.5.5 // indirect - github.com/jackc/puddle/v2 v2.2.1 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.6.0 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.6 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mailru/easyjson v0.7.6 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect github.com/nats-io/nkeys v0.4.7 // indirect github.com/nats-io/nuid v1.0.1 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect - golang.org/x/crypto v0.23.0 // indirect + golang.org/x/arch v0.8.0 // indirect + golang.org/x/crypto v0.32.0 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect - google.golang.org/protobuf v1.28.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) require ( @@ -56,9 +92,9 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc // indirect - golang.org/x/net v0.25.0 // indirect - golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect + golang.org/x/net v0.34.0 // indirect + golang.org/x/sync v0.12.0 // indirect + golang.org/x/sys v0.29.0 // indirect + golang.org/x/text v0.23.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 99b670a..d6354da 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +1,30 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +git.itzana.me/StrafesNET/dev-service v0.0.0-20250628052121-92af8193b5ed h1:eGWIQx2AOrSsLC2dieuSs8MCliRE60tvpZnmxsTBtKc= +git.itzana.me/StrafesNET/dev-service v0.0.0-20250628052121-92af8193b5ed/go.mod h1:KJal0K++M6HEzSry6JJ2iDPZtOQn5zSstNlDbU3X4Jg= git.itzana.me/strafesnet/go-grpc v0.0.0-20250724030029-845bea991815 h1:hkuOnehphRXUq/2z2UYgoqTq5MJj1GsWfshyc7bXda8= git.itzana.me/strafesnet/go-grpc v0.0.0-20250724030029-845bea991815/go.mod h1:X7XTRUScRkBWq8q8bplbeso105RPDlnY7J6Wy1IwBMs= git.itzana.me/strafesnet/utils v0.0.0-20220716194944-d8ca164052f9 h1:7lU6jyR7S7Rhh1dnUp7GyIRHUTBXZagw8F4n4hOyxLw= git.itzana.me/strafesnet/utils v0.0.0-20220716194944-d8ca164052f9/go.mod h1:uyYerSieEt4v0MJCdPLppG0LtJ4Yj035vuTetWGsxjY= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= +github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= +github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= +github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= +github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= @@ -17,6 +33,7 @@ github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -32,8 +49,16 @@ github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go. github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +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/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4= +github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= +github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= github.com/go-faster/errors v0.7.1 h1:MkJTnDoEdi9pDabt1dpWf7AA8/BaSYZqibYyhZ20AYg= github.com/go-faster/errors v0.7.1/go.mod h1:5ySTjWFiphBs07IKuiL69nxdfd5+fzh1u7FPGZP2quo= github.com/go-faster/jx v1.1.0 h1:ZsW3wD+snOdmTDy9eIVgQdjUpXRRV4rqW8NS3t+20bg= @@ -45,6 +70,26 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M= +github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= +github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +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/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -70,6 +115,7 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -77,42 +123,67 @@ github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= -github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= -github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= -github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY= +github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI= github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/nats-io/nats.go v1.37.0 h1:07rauXbVnnJvv1gfIyghFEo6lUcYRY0WXc3x7x0vUxE= github.com/nats-io/nats.go v1.37.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8= github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI= github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/ogen-go/ogen v1.2.1 h1:C5A0lvUMu2wl+eWIxnpXMWnuOJ26a2FyzR1CIC2qG0M= github.com/ogen-go/ogen v1.2.1/go.mod h1:P2zQdEu8UqaVRfD5GEFvl+9q63VjMLvDquq1wVbyInM= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= @@ -121,16 +192,35 @@ github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= -github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= +github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE= +github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg= +github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M= +github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo= +github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI= +github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +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/urfave/cli/v2 v2.27.6 h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g= +github.com/urfave/cli/v2 v2.27.6/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= @@ -144,55 +234,85 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= +golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc h1:O9NuF4s+E/PvMIy+9IUZB9znFwUIXEWSstNjek6VpVg= golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= 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= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= @@ -222,9 +342,11 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -232,12 +354,15 @@ gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/driver/postgres v1.5.10 h1:7Lggqempgy496c0WfHXsYWxk3Th+ZcW66/21QhVFdeE= -gorm.io/driver/postgres v1.5.10/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI= +gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4= +gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo= gorm.io/gorm v1.21.11/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= -gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s= -gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8= +gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/pkg/cmds/api.go b/pkg/cmds/api.go new file mode 100644 index 0000000..717f816 --- /dev/null +++ b/pkg/cmds/api.go @@ -0,0 +1,57 @@ +package cmds + +import ( + "git.itzana.me/strafesnet/maps-service/pkg/public_api" + "github.com/urfave/cli/v2" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +func NewApiCommand() *cli.Command { + return &cli.Command{ + Name: "api", + Usage: "Run api service", + Action: runAPI, + Flags: []cli.Flag{ + &cli.IntFlag{ + Name: "port", + Usage: "Listen port", + EnvVars: []string{"PORT"}, + Value: 8080, + }, + &cli.StringFlag{ + Name: "dev-rpc-host", + Usage: "Host of dev rpc", + EnvVars: []string{"DEV_RPC_HOST"}, + Value: "dev-service:8081", + }, + &cli.StringFlag{ + Name: "maps-rpc-host", + Usage: "Host of maps rpc", + EnvVars: []string{"MAPS_RPC_HOST"}, + Value: "maptest-api:8081", + }, + }, + } +} + +func runAPI(ctx *cli.Context) error { + // Dev service client + devConn, err := grpc.Dial(ctx.String("dev-rpc-host"), grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + return err + } + + // Data service client + mapsConn, err := grpc.Dial(ctx.String("maps-rpc-host"), grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + return err + } + + return api.NewRouter( + api.WithContext(ctx), + api.WithPort(ctx.Int("port")), + api.WithDevClient(devConn), + api.WithMapsClient(mapsConn), + ) +} diff --git a/pkg/public_api/dto/map.go b/pkg/public_api/dto/map.go new file mode 100644 index 0000000..4977c9e --- /dev/null +++ b/pkg/public_api/dto/map.go @@ -0,0 +1,47 @@ +package dto + +import ( + "git.itzana.me/strafesnet/go-grpc/maps_extended" + "time" +) + +type MapFilter struct { + GameID *uint32 `json:"game_id" form:"game_id"` +} // @name MapFilter + +type Map struct { + ID int64 `json:"id"` + DisplayName string `json:"display_name"` + Creator string `json:"creator"` + GameID uint32 `json:"game_id"` + Date time.Time `json:"date"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + Submitter uint64 `json:"submitter"` + Thumbnail uint64 `json:"thumbnail"` + AssetVersion uint64 `json:"asset_version"` + LoadCount uint32 `json:"load_count"` + Modes uint32 `json:"modes"` +} // @name Map + +// FromGRPC converts a maps.MapResponse protobuf message to a Map domain object +func (m *Map) FromGRPC(resp *maps_extended.MapResponse) *Map { + if resp == nil { + return nil + } + + m.ID = resp.ID + m.DisplayName = resp.DisplayName + m.Creator = resp.Creator + m.Date = time.Unix(resp.Date, 0) + m.GameID = resp.GameID + m.CreatedAt = time.Unix(resp.CreatedAt, 0) + m.UpdatedAt = time.Unix(resp.UpdatedAt, 0) + m.Submitter = resp.Submitter + m.Thumbnail = resp.Thumbnail + m.AssetVersion = resp.AssetVersion + m.LoadCount = resp.LoadCount + m.Modes = resp.Modes + + return m +} diff --git a/pkg/public_api/dto/response.go b/pkg/public_api/dto/response.go new file mode 100644 index 0000000..6bb2fa2 --- /dev/null +++ b/pkg/public_api/dto/response.go @@ -0,0 +1,52 @@ +package dto + +// @Description Generic response +type Response[T any] struct { + // Data contains the actual response payload + Data T `json:"data"` +} // @name Response + +type PagedTotalResponse[T any] struct { + // Data contains the actual response payload + Data []T `json:"data"` + + // Pagination contains information about paging + Pagination PaginationWithTotal `json:"pagination"` +} // @name PagedTotalResponse + +// PaginationWithTotal holds information about the current page, total items, etc. +type PaginationWithTotal struct { + // Current page number + Page int `json:"page"` + + // Number of items per page + PageSize int `json:"page_size"` + + // Total number of items across all pages + TotalItems int `json:"total_items"` + + // Total number of pages + TotalPages int `json:"total_pages"` +} // @name PaginationWithTotal + +type PagedResponse[T any] struct { + // Data contains the actual response payload + Data []T `json:"data"` + + // Pagination contains information about paging + Pagination Pagination `json:"pagination"` +} // @name PagedResponse + +// Pagination holds information about the current page. +type Pagination struct { + // Current page number + Page int `json:"page"` + + // Number of items per page + PageSize int `json:"page_size"` +} // @name Pagination + +// Error holds error responses +type Error struct { + Error string `json:"error"` +} // @name Error diff --git a/pkg/public_api/handlers/handler.go b/pkg/public_api/handlers/handler.go new file mode 100644 index 0000000..f5b5a8e --- /dev/null +++ b/pkg/public_api/handlers/handler.go @@ -0,0 +1,98 @@ +package handlers + +import ( + "fmt" + "github.com/gin-gonic/gin" + "google.golang.org/grpc" + "strconv" +) + +const ( + ErrMsgDataClient = "data client is required" +) + +// Handler is a base handler that provides common functionality for all HTTP handlers. +type Handler struct { + mapsClient *grpc.ClientConn +} + +// HandlerOption defines a functional option for configuring a Handler +type HandlerOption func(*Handler) + +// WithMapsClient sets the data client for the Handler +func WithMapsClient(mapsClient *grpc.ClientConn) HandlerOption { + return func(h *Handler) { + h.mapsClient = mapsClient + } +} + +// NewHandler creates a new Handler with the provided options. +// It requires both a datastore and an authentication service to function properly. +func NewHandler(options ...HandlerOption) (*Handler, error) { + handler := &Handler{} + + // Apply all provided options + for _, option := range options { + option(handler) + } + + // Validate required dependencies + if err := handler.validateDependencies(); err != nil { + return nil, err + } + + return handler, nil +} + +// validateDependencies ensures all required dependencies are properly set +func (h *Handler) validateDependencies() error { + if h.mapsClient == nil { + return fmt.Errorf(ErrMsgDataClient) + } + return nil +} + +// validateRange ensures a value is within the specified range, returning defaultValue if outside +func validateRange(value, min, max, defaultValue int) int { + if value < min { + return defaultValue + } + if value > max { + return max + } + return value +} + +// validateMin ensures a value is at least the minimum, returning defaultValue if below +func validateMin(value, min, defaultValue int) int { + if value < min { + return defaultValue + } + return value +} + +// getPagination extracts pagination parameters from query string. +// It applies validation rules to ensure parameters are within acceptable ranges. +func getPagination(ctx *gin.Context, defaultPageSize, minPageSize, maxPageSize int) (pageSize, pageNumber int) { + // Get page size from query string, parse to integer + pageSizeStr := ctx.Query("page_size") + if pageSizeStr != "" { + pageSize, _ = strconv.Atoi(pageSizeStr) + } else { + pageSize = defaultPageSize + } + + // Get page number from query string, parse to integer + pageNumberStr := ctx.Query("page_number") + if pageNumberStr != "" { + pageNumber, _ = strconv.Atoi(pageNumberStr) + } else { + pageNumber = 1 // Default to first page + } + + // Apply validation rules + pageSize = validateRange(pageSize, minPageSize, maxPageSize, defaultPageSize) + pageNumber = validateMin(pageNumber, 1, 1) + + return pageSize, pageNumber +} diff --git a/pkg/public_api/handlers/maps.go b/pkg/public_api/handlers/maps.go new file mode 100644 index 0000000..f4fdbde --- /dev/null +++ b/pkg/public_api/handlers/maps.go @@ -0,0 +1,153 @@ +package handlers + +import ( + "git.itzana.me/strafesnet/go-grpc/maps_extended" + "git.itzana.me/strafesnet/maps-service/pkg/public_api/dto" + "github.com/gin-gonic/gin" + log "github.com/sirupsen/logrus" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "net/http" + "strconv" +) + +// MapHandler handles HTTP requests related to maps. +type MapHandler struct { + *Handler +} + +// NewMapHandler creates a new MapHandler with the provided options. +func NewMapHandler(options ...HandlerOption) (*MapHandler, error) { + baseHandler, err := NewHandler(options...) + if err != nil { + return nil, err + } + return &MapHandler{ + Handler: baseHandler, + }, nil +} + +// @Summary Get map by ID +// @Description Get a specific map by its ID +// @Tags maps +// @Produce json +// @Security ApiKeyAuth +// @Param id path int true "Map ID" +// @Success 200 {object} dto.Response[dto.Map] +// @Failure 404 {object} dto.Error "Map not found" +// @Failure default {object} dto.Error "General error response" +// @Router /map/{id} [get] +func (h *MapHandler) Get(ctx *gin.Context) { + // Extract map ID from path parameter + id := ctx.Param("id") + mapID, err := strconv.ParseInt(id, 10, 64) + if err != nil { + ctx.JSON(http.StatusBadRequest, dto.Error{ + Error: "Invalid map ID format", + }) + return + } + + // Call the gRPC service + mapData, err := maps_extended.NewMapsServiceClient(h.mapsClient).Get(ctx, &maps_extended.MapId{ + ID: mapID, + }) + if err != nil { + statusCode := http.StatusInternalServerError + errorMessage := "Failed to get map" + + // Check if it's a "not found" error + if status.Code(err) == codes.NotFound { + statusCode = http.StatusNotFound + errorMessage = "Map not found" + } + + ctx.JSON(statusCode, dto.Error{ + Error: errorMessage, + }) + log.WithError(err).Error( + "Failed to get map", + ) + return + } + + // Convert gRPC MapResponse object to dto.Map object + var mapDto dto.Map + result := mapDto.FromGRPC(mapData) + + // Return the map data + ctx.JSON(http.StatusOK, dto.Response[dto.Map]{ + Data: *result, + }) +} + +// @Summary List maps +// @Description Get a list of maps +// @Tags maps +// @Produce json +// @Security ApiKeyAuth +// @Param page_size query int false "Page size (max 100)" default(10) minimum(1) maximum(100) +// @Param page_number query int false "Page number" default(1) minimum(1) +// @Param filter query dto.MapFilter false "Map filter parameters" +// @Success 200 {object} dto.PagedResponse[dto.Map] +// @Failure default {object} dto.Error "General error response" +// @Router /map [get] +func (h *MapHandler) List(ctx *gin.Context) { + // Extract and constrain pagination parameters + query := struct { + PageSize int `form:"page_size,default=10" binding:"min=1,max=100"` + PageNumber int `form:"page_number,default=1" binding:"min=1"` + SortBy int `form:"sort_by,default=0" binding:"min=0,max=3"` + }{} + if err := ctx.ShouldBindQuery(&query); err != nil { + ctx.JSON(http.StatusBadRequest, dto.Error{ + Error: err.Error(), + }) + return + } + + // Get list filter + var filter dto.MapFilter + if err := ctx.ShouldBindQuery(&filter); err != nil { + ctx.JSON(http.StatusBadRequest, dto.Error{ + Error: err.Error(), + }) + return + } + + // Call the gRPC service + mapList, err := maps_extended.NewMapsServiceClient(h.mapsClient).List(ctx, &maps_extended.ListRequest{ + Filter: &maps_extended.MapFilter{ + GameID: filter.GameID, + }, + Page: &maps_extended.Pagination{ + Size: uint32(query.PageSize), + Number: uint32(query.PageNumber), + }, + }) + if err != nil { + ctx.JSON(http.StatusInternalServerError, dto.Error{ + Error: "Failed to list maps", + }) + log.WithError(err).Error( + "Failed to list maps", + ) + return + } + + // Convert gRPC MapResponse objects to dto.Map objects + dtoMaps := make([]dto.Map, len(mapList.Maps)) + for i, m := range mapList.Maps { + var mapDto dto.Map + dtoMaps[i] = *mapDto.FromGRPC(m) + } + + // Return the paged response + ctx.JSON(http.StatusOK, dto.PagedResponse[dto.Map]{ + Data: dtoMaps, + Pagination: dto.Pagination{ + Page: query.PageNumber, + PageSize: query.PageSize, + }, + }) +} diff --git a/pkg/public_api/router.go b/pkg/public_api/router.go new file mode 100644 index 0000000..117ca69 --- /dev/null +++ b/pkg/public_api/router.go @@ -0,0 +1,159 @@ +package api + +import ( + "context" + "errors" + "fmt" + "git.itzana.me/StrafesNET/dev-service/pkg/api/middleware" + "git.itzana.me/strafesnet/maps-service/docs" + "git.itzana.me/strafesnet/maps-service/pkg/public_api/handlers" + "github.com/gin-gonic/gin" + log "github.com/sirupsen/logrus" + swaggerfiles "github.com/swaggo/files" + ginSwagger "github.com/swaggo/gin-swagger" + "github.com/urfave/cli/v2" + "google.golang.org/grpc" + "net/http" + "time" +) + +// Option defines a function that configures a Router +type Option func(*RouterConfig) + +// RouterConfig holds all router configuration +type RouterConfig struct { + port int + devClient *grpc.ClientConn + mapsClient *grpc.ClientConn + context *cli.Context + shutdownTimeout time.Duration +} + +// WithPort sets the port for the server£ +func WithPort(port int) Option { + return func(cfg *RouterConfig) { + cfg.port = port + } +} + +// WithContext sets the context for the server +func WithContext(ctx *cli.Context) Option { + return func(cfg *RouterConfig) { + cfg.context = ctx + } +} + +// WithDevClient sets the dev gRPC client +func WithDevClient(conn *grpc.ClientConn) Option { + return func(cfg *RouterConfig) { + cfg.devClient = conn + } +} + +// WithMapsClient sets the data gRPC client +func WithMapsClient(conn *grpc.ClientConn) Option { + return func(cfg *RouterConfig) { + cfg.mapsClient = conn + } +} + +// WithShutdownTimeout sets the graceful shutdown timeout +func WithShutdownTimeout(timeout time.Duration) Option { + return func(cfg *RouterConfig) { + cfg.shutdownTimeout = timeout + } +} + +func setupRoutes(cfg *RouterConfig) (*gin.Engine, error) { + r := gin.Default() + r.ForwardedByClientIP = true + r.Use(gin.Logger()) + r.Use(gin.Recovery()) + + handlerOptions := []handlers.HandlerOption{ + handlers.WithMapsClient(cfg.mapsClient), + } + + // Maps handler + mapsHandler, err := handlers.NewMapHandler(handlerOptions...) + if err != nil { + return nil, err + } + + docs.SwaggerInfo.BasePath = "/public-api/v1" + public_api := r.Group("/public-api") + { + v1 := public_api.Group("/v1") + { + // Auth middleware + v1.Use(middleware.ValidateRequest("Maps", "Read", cfg.devClient)) + + // Maps + v1.GET("/map", mapsHandler.List) + v1.GET("/map/:id", mapsHandler.Get) + } + + // Docs + public_api.GET("/docs/*any", ginSwagger.WrapHandler(swaggerfiles.Handler)) + public_api.GET("/", func(ctx *gin.Context) { + ctx.Redirect(http.StatusPermanentRedirect, "/docs/index.html") + }) + } + + return r, nil +} + +// NewRouter creates a new router with the given options +func NewRouter(options ...Option) error { + // Default configuration + cfg := &RouterConfig{ + port: 8080, // Default port + context: nil, + shutdownTimeout: 5 * time.Second, + } + + // Apply options + for _, option := range options { + option(cfg) + } + + // Validate configuration + if cfg.context == nil { + return errors.New("context is required") + } + + if cfg.devClient == nil { + return errors.New("dev client is required") + } + + routes, err := setupRoutes(cfg) + if err != nil { + return err + } + + log.Info("Starting server") + + return runServer(cfg.context.Context, fmt.Sprint(":", cfg.port), routes, cfg.shutdownTimeout) +} + +func runServer(ctx context.Context, addr string, r *gin.Engine, shutdownTimeout time.Duration) error { + srv := &http.Server{ + Addr: addr, + Handler: r, + } + + // Run the server in a separate goroutine + go func() { + if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { + log.WithError(err).Fatal("web server exit") + } + }() + + // Wait for a shutdown signal + <-ctx.Done() + + // Shutdown server gracefully + ctxShutdown, cancel := context.WithTimeout(context.Background(), shutdownTimeout) + defer cancel() + return srv.Shutdown(ctxShutdown) +} -- 2.49.1 From 5dce0f201700d8fb53b786bf1c15c943bfbc5159 Mon Sep 17 00:00:00 2001 From: Rhys Lloyd Date: Tue, 5 Aug 2025 22:58:37 -0700 Subject: [PATCH 10/18] docker: do not run build-frontend on push --- .drone.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.drone.yml b/.drone.yml index 6c2e8b0..42eb0ab 100644 --- a/.drone.yml +++ b/.drone.yml @@ -42,6 +42,8 @@ steps: branch: - master - staging + event: + - pull_request - name: image-backend image: plugins/docker @@ -80,8 +82,6 @@ steps: from_secret: REGISTRY_PASS dockerfile: web/Containerfile context: web - depends_on: - - build-frontend when: branch: - master @@ -149,6 +149,6 @@ steps: - pull_request --- kind: signature -hmac: c4222688ca348daadee16490168aafe9a3a3dd0c835954296ddb1bddddf0df4b +hmac: cc7f2f8dac4285b5fa1df163bd92115f1a51a92050687cd08169e17803a2de4c ... -- 2.49.1 From 08753d36b452d563c337dcb21710862967206e2c Mon Sep 17 00:00:00 2001 From: Rhys Lloyd Date: Thu, 7 Aug 2025 02:32:22 +0000 Subject: [PATCH 11/18] Cut Down Maps Fields (#257) Remove usages of Creator & Date, add Thumbnail. Reviewed-on: https://git.itzana.me/StrafesNET/maps-service/pulls/257 Co-authored-by: Rhys Lloyd Co-committed-by: Rhys Lloyd --- go.mod | 2 +- go.sum | 4 ++-- pkg/service/maps.go | 19 +++++-------------- 3 files changed, 8 insertions(+), 17 deletions(-) diff --git a/go.mod b/go.mod index fcb504d..4e1235c 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ toolchain go1.24.5 require ( git.itzana.me/StrafesNET/dev-service v0.0.0-20250628052121-92af8193b5ed - git.itzana.me/strafesnet/go-grpc v0.0.0-20250724030029-845bea991815 + git.itzana.me/strafesnet/go-grpc v0.0.0-20250807005013-301d35b914ef git.itzana.me/strafesnet/utils v0.0.0-20220716194944-d8ca164052f9 github.com/dchest/siphash v1.2.3 github.com/gin-gonic/gin v1.10.1 diff --git a/go.sum b/go.sum index d6354da..3e3ccdf 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= git.itzana.me/StrafesNET/dev-service v0.0.0-20250628052121-92af8193b5ed h1:eGWIQx2AOrSsLC2dieuSs8MCliRE60tvpZnmxsTBtKc= git.itzana.me/StrafesNET/dev-service v0.0.0-20250628052121-92af8193b5ed/go.mod h1:KJal0K++M6HEzSry6JJ2iDPZtOQn5zSstNlDbU3X4Jg= -git.itzana.me/strafesnet/go-grpc v0.0.0-20250724030029-845bea991815 h1:hkuOnehphRXUq/2z2UYgoqTq5MJj1GsWfshyc7bXda8= -git.itzana.me/strafesnet/go-grpc v0.0.0-20250724030029-845bea991815/go.mod h1:X7XTRUScRkBWq8q8bplbeso105RPDlnY7J6Wy1IwBMs= +git.itzana.me/strafesnet/go-grpc v0.0.0-20250807005013-301d35b914ef h1:SJi4V4+xzScFnbMRN1gkZxcqR1xKfiT7CaXanLltEzw= +git.itzana.me/strafesnet/go-grpc v0.0.0-20250807005013-301d35b914ef/go.mod h1:X7XTRUScRkBWq8q8bplbeso105RPDlnY7J6Wy1IwBMs= git.itzana.me/strafesnet/utils v0.0.0-20220716194944-d8ca164052f9 h1:7lU6jyR7S7Rhh1dnUp7GyIRHUTBXZagw8F4n4hOyxLw= git.itzana.me/strafesnet/utils v0.0.0-20220716194944-d8ca164052f9/go.mod h1:uyYerSieEt4v0MJCdPLppG0LtJ4Yj035vuTetWGsxjY= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= diff --git a/pkg/service/maps.go b/pkg/service/maps.go index ad29c0f..cdf33b7 100644 --- a/pkg/service/maps.go +++ b/pkg/service/maps.go @@ -46,16 +46,12 @@ func (update MapUpdate) GetDisplayName() (string, bool) { value, ok := datastore.OptionalMap(update).Map()["display_name"].(string) return value, ok } -func (update MapUpdate) GetCreator() (string, bool) { - value, ok := datastore.OptionalMap(update).Map()["creator"].(string) - return value, ok -} func (update MapUpdate) GetGameID() (uint32, bool) { value, ok := datastore.OptionalMap(update).Map()["game_id"].(uint32) return value, ok } -func (update MapUpdate) GetDate() (int64, bool) { - value, ok := datastore.OptionalMap(update).Map()["date"].(int64) +func (update MapUpdate) GetThumbnail() (uint64, bool) { + value, ok := datastore.OptionalMap(update).Map()["thumbnail"].(uint64) return value, ok } @@ -88,14 +84,12 @@ func (svc *Service) CreateMap(ctx context.Context, item model.Map) (int64, error return 0, err } // create map on data-service - date := item.Date.Unix() game_id := int32(item.GameID) _, err = svc.maps.Create(ctx, &maps.MapRequest{ ID: item.ID, DisplayName: &item.DisplayName, - Creator: &item.Creator, GameID: &game_id, - Date: &date, + Thumbnail: &item.Thumbnail, }) if err != nil { return 0, err @@ -136,15 +130,12 @@ func (svc *Service) UpdateMap(ctx context.Context, id int64, pmap MapUpdate) err if display_name, ok := pmap.GetDisplayName(); ok { update.DisplayName = &display_name } - if creator, ok := pmap.GetCreator(); ok { - update.Creator = &creator - } if game_id, ok := pmap.GetGameID(); ok { game_id_int32 := int32(game_id) update.GameID = &game_id_int32 } - if date, ok := pmap.GetDate(); ok { - update.Date = &date + if thumbnail, ok := pmap.GetThumbnail(); ok { + update.Thumbnail = &thumbnail } _, err = svc.maps.Update(ctx, &update) if err != nil { -- 2.49.1 From 8a0cd50b68b71de0132fc1cf94f18e767ee9dacd Mon Sep 17 00:00:00 2001 From: Rhys Lloyd Date: Wed, 6 Aug 2025 20:04:33 -0700 Subject: [PATCH 12/18] fix public api --- cmd/maps-service/service.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/maps-service/service.go b/cmd/maps-service/service.go index 44a9184..e113383 100644 --- a/cmd/maps-service/service.go +++ b/cmd/maps-service/service.go @@ -25,6 +25,7 @@ func main() { app := cmds.NewApp() app.Commands = []*cli.Command{ cmds.NewServeCommand(), + cmds.NewApiCommand(), } if err := app.Run(os.Args); err != nil { -- 2.49.1 From 9999d1ff87f16f6b8640457992f881b4784159a0 Mon Sep 17 00:00:00 2001 From: Rhys Lloyd Date: Wed, 6 Aug 2025 20:15:53 -0700 Subject: [PATCH 13/18] fix docs redirect --- pkg/public_api/router.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/public_api/router.go b/pkg/public_api/router.go index 117ca69..1d889ab 100644 --- a/pkg/public_api/router.go +++ b/pkg/public_api/router.go @@ -96,7 +96,7 @@ func setupRoutes(cfg *RouterConfig) (*gin.Engine, error) { // Docs public_api.GET("/docs/*any", ginSwagger.WrapHandler(swaggerfiles.Handler)) public_api.GET("/", func(ctx *gin.Context) { - ctx.Redirect(http.StatusPermanentRedirect, "/docs/index.html") + ctx.Redirect(http.StatusPermanentRedirect, "/public-api/docs/index.html") }) } -- 2.49.1 From 34bc623ce6acadedb664fc683bedc922eab3858c Mon Sep 17 00:00:00 2001 From: Rhys Lloyd Date: Thu, 7 Aug 2025 16:08:47 -0700 Subject: [PATCH 14/18] make UserInfoHandle.HasRoles public --- pkg/web_api/security.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/web_api/security.go b/pkg/web_api/security.go index fb5bce8..6a2ce11 100644 --- a/pkg/web_api/security.go +++ b/pkg/web_api/security.go @@ -58,7 +58,7 @@ func (usr UserInfoHandle) Validate() (bool, error) { } return validate.Valid, nil } -func (usr UserInfoHandle) hasRoles(wantRoles model.Roles) (bool, error) { +func (usr UserInfoHandle) HasRoles(wantRoles model.Roles) (bool, error) { haveroles, err := usr.GetRoles() if err != nil { return false, err @@ -94,25 +94,25 @@ func (usr UserInfoHandle) GetRoles() (model.Roles, error) { // RoleThumbnail func (usr UserInfoHandle) HasRoleMapfixUpload() (bool, error) { - return usr.hasRoles(model.RolesMapfixUpload) + return usr.HasRoles(model.RolesMapfixUpload) } func (usr UserInfoHandle) HasRoleMapfixReview() (bool, error) { - return usr.hasRoles(model.RolesMapfixReview) + return usr.HasRoles(model.RolesMapfixReview) } func (usr UserInfoHandle) HasRoleMapDownload() (bool, error) { - return usr.hasRoles(model.RolesMapDownload) + return usr.HasRoles(model.RolesMapDownload) } func (usr UserInfoHandle) HasRoleSubmissionRelease() (bool, error) { - return usr.hasRoles(model.RolesSubmissionRelease) + return usr.HasRoles(model.RolesSubmissionRelease) } func (usr UserInfoHandle) HasRoleSubmissionUpload() (bool, error) { - return usr.hasRoles(model.RolesSubmissionUpload) + return usr.HasRoles(model.RolesSubmissionUpload) } func (usr UserInfoHandle) HasRoleSubmissionReview() (bool, error) { - return usr.hasRoles(model.RolesSubmissionReview) + return usr.HasRoles(model.RolesSubmissionReview) } func (usr UserInfoHandle) HasRoleScriptWrite() (bool, error) { - return usr.hasRoles(model.RolesScriptWrite) + return usr.HasRoles(model.RolesScriptWrite) } /// Not implemented func (usr UserInfoHandle) HasRoleMaptest() (bool, error) { -- 2.49.1 From 759ac08aefb2eebee8a92150916eb34a13f09293 Mon Sep 17 00:00:00 2001 From: Rhys Lloyd Date: Thu, 7 Aug 2025 16:13:42 -0700 Subject: [PATCH 15/18] swagger: generate --- docs/docs.go | 2 +- docs/swagger.json | 2 +- docs/swagger.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/docs.go b/docs/docs.go index 4060b38..ecfa982 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -230,7 +230,7 @@ var SwaggerInfo = &swag.Spec{ BasePath: "/public-api/v1", Schemes: []string{}, Title: "StrafesNET Maps API", - Description: "Obtain an api key at https://dev.strafes.net\nRequires Data:Read permission", + Description: "Obtain an api key at https://dev.strafes.net\nRequires Maps:Read permission", InfoInstanceName: "swagger", SwaggerTemplate: docTemplate, LeftDelim: "{{", diff --git a/docs/swagger.json b/docs/swagger.json index 56fe4a8..3465447 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -1,7 +1,7 @@ { "swagger": "2.0", "info": { - "description": "Obtain an api key at https://dev.strafes.net\nRequires Data:Read permission", + "description": "Obtain an api key at https://dev.strafes.net\nRequires Maps:Read permission", "title": "StrafesNET Maps API", "contact": {}, "version": "1.0" diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 4614140..ffc6d9c 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -64,7 +64,7 @@ info: contact: {} description: |- Obtain an api key at https://dev.strafes.net - Requires Data:Read permission + Requires Maps:Read permission title: StrafesNET Maps API version: "1.0" paths: -- 2.49.1 From fe539bf19030b95f1dd1d145b48ac6cf5f5a1e87 Mon Sep 17 00:00:00 2001 From: Rhys Lloyd Date: Thu, 7 Aug 2025 17:37:15 -0700 Subject: [PATCH 16/18] move rust submissions api --- Cargo.toml | 2 +- {validation/api => submissions-api-rs}/Cargo.toml | 0 {validation/api => submissions-api-rs}/LICENSE-APACHE | 0 {validation/api => submissions-api-rs}/LICENSE-MIT | 0 {validation/api => submissions-api-rs}/src/context.rs | 0 {validation/api => submissions-api-rs}/src/external.rs | 0 {validation/api => submissions-api-rs}/src/lib.rs | 0 {validation/api => submissions-api-rs}/src/types.rs | 0 8 files changed, 1 insertion(+), 1 deletion(-) rename {validation/api => submissions-api-rs}/Cargo.toml (100%) rename {validation/api => submissions-api-rs}/LICENSE-APACHE (100%) rename {validation/api => submissions-api-rs}/LICENSE-MIT (100%) rename {validation/api => submissions-api-rs}/src/context.rs (100%) rename {validation/api => submissions-api-rs}/src/external.rs (100%) rename {validation/api => submissions-api-rs}/src/lib.rs (100%) rename {validation/api => submissions-api-rs}/src/types.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 95bfb2f..5689040 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] members = [ "validation", - "validation/api", + "submissions-api-rs", ] resolver = "2" diff --git a/validation/api/Cargo.toml b/submissions-api-rs/Cargo.toml similarity index 100% rename from validation/api/Cargo.toml rename to submissions-api-rs/Cargo.toml diff --git a/validation/api/LICENSE-APACHE b/submissions-api-rs/LICENSE-APACHE similarity index 100% rename from validation/api/LICENSE-APACHE rename to submissions-api-rs/LICENSE-APACHE diff --git a/validation/api/LICENSE-MIT b/submissions-api-rs/LICENSE-MIT similarity index 100% rename from validation/api/LICENSE-MIT rename to submissions-api-rs/LICENSE-MIT diff --git a/validation/api/src/context.rs b/submissions-api-rs/src/context.rs similarity index 100% rename from validation/api/src/context.rs rename to submissions-api-rs/src/context.rs diff --git a/validation/api/src/external.rs b/submissions-api-rs/src/external.rs similarity index 100% rename from validation/api/src/external.rs rename to submissions-api-rs/src/external.rs diff --git a/validation/api/src/lib.rs b/submissions-api-rs/src/lib.rs similarity index 100% rename from validation/api/src/lib.rs rename to submissions-api-rs/src/lib.rs diff --git a/validation/api/src/types.rs b/submissions-api-rs/src/types.rs similarity index 100% rename from validation/api/src/types.rs rename to submissions-api-rs/src/types.rs -- 2.49.1 From 93147060d660b81aa4b0bc5e8c575c9850affff4 Mon Sep 17 00:00:00 2001 From: Rhys Lloyd Date: Thu, 7 Aug 2025 19:22:12 -0700 Subject: [PATCH 17/18] drone: re-sign pipeline after self-inflicted catastrophe --- .drone.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index 42eb0ab..44e4885 100644 --- a/.drone.yml +++ b/.drone.yml @@ -149,6 +149,6 @@ steps: - pull_request --- kind: signature -hmac: cc7f2f8dac4285b5fa1df163bd92115f1a51a92050687cd08169e17803a2de4c +hmac: e33e96d93bf47dbc307273f356a901608737799021264a14fe551eb51d80bea9 ... -- 2.49.1 From 295b1d842bf68cf81d13e7769d30dc1c1f41a7b3 Mon Sep 17 00:00:00 2001 From: Rhys Lloyd Date: Fri, 8 Aug 2025 02:47:53 +0000 Subject: [PATCH 18/18] Sequential Modes Check (#260) Closes #242 Reviewed-on: https://git.itzana.me/StrafesNET/maps-service/pulls/260 Co-authored-by: Rhys Lloyd Co-committed-by: Rhys Lloyd --- validation/src/check.rs | 51 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/validation/src/check.rs b/validation/src/check.rs index bf37214..24db010 100644 --- a/validation/src/check.rs +++ b/validation/src/check.rs @@ -47,12 +47,20 @@ impl From for CheckRequest{ } } -#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq)] +#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq,Ord,PartialOrd)] struct ModeID(u64); impl ModeID{ const MAIN:Self=Self(0); const BONUS:Self=Self(1); } +impl std::fmt::Display for ModeID{ + fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{ + match self{ + &ModeID::MAIN=>write!(f,"Main"), + &ModeID(mode_id)=>write!(f,"Bonus{mode_id}"), + } + } +} enum Zone{ Start, Finish, @@ -446,6 +454,8 @@ struct MapCheck<'a>{ mode_finish_counts:SetDifferenceCheck>>, // Check for dangling MapAnticheat zones (no associated MapStart) mode_anticheat_counts:SetDifferenceCheck>>, + // Check that modes are sequential + modes_sequential:Result<(),Vec>, // Spawn1 must exist spawn1:Result, // Check for dangling Teleport# (no associated Spawn#) @@ -514,6 +524,33 @@ impl<'a> ModelInfo<'a>{ let mode_anticheat_counts=SetDifferenceCheckContextAllowNone::new(self.counts.mode_anticheat_counts) .check(&self.counts.mode_start_counts); + // There must not be non-sequential modes. If Bonus100 exists, Bonuses 1-99 had better also exist. + fn count_sequential(modes:&HashMap>)->usize{ + for mode_id in 0..modes.len(){ + if !modes.contains_key(&ModeID(mode_id as u64)){ + return mode_id; + } + } + return modes.len(); + } + let modes_sequential={ + let sequential=count_sequential(&self.counts.mode_start_counts); + if sequential==self.counts.mode_start_counts.len(){ + Ok(()) + }else{ + let mut non_sequential=Vec::with_capacity(self.counts.mode_start_counts.len()-sequential); + for (&mode_id,_) in &self.counts.mode_start_counts{ + let ModeID(mode_id_u64)=mode_id; + if !(mode_id_u64 ModelInfo<'a>{ mode_start_counts, mode_finish_counts, mode_anticheat_counts, + modes_sequential, spawn1, teleport_counts, spawn_counts, @@ -573,6 +611,7 @@ impl MapCheck<'_>{ mode_start_counts:DuplicateCheck(Ok(())), mode_finish_counts:SetDifferenceCheck(Ok(())), mode_anticheat_counts:SetDifferenceCheck(Ok(())), + modes_sequential:Ok(()), spawn1:Ok(Exists), teleport_counts:SetDifferenceCheck(Ok(())), spawn_counts:DuplicateCheck(Ok(())), @@ -746,6 +785,15 @@ impl MapCheck<'_>{ } } }; + let sequential_modes=match &self.modes_sequential{ + Ok(())=>passed!("SequentialModes"), + Err(context)=>{ + let non_sequential=context.len(); + let plural_non_sequential=if non_sequential==1{"mode"}else{"modes"}; + let comma_separated=Separated::new(", ",||context); + summary_format!("SequentialModes","{non_sequential} {plural_non_sequential} should use a lower ModeID (no gaps): {comma_separated}") + } + }; let spawn1=match &self.spawn1{ Ok(Exists)=>passed!("Spawn1"), Err(Absent)=>summary_format!("Spawn1","Model has no Spawn1"), @@ -824,6 +872,7 @@ impl MapCheck<'_>{ extra_finish, missing_finish, dangling_anticheat, + sequential_modes, spawn1, dangling_teleport, duplicate_spawns, -- 2.49.1