From 6be414248703eae0e0b378c617d63601bcb75889 Mon Sep 17 00:00:00 2001 From: Fedorov Vladimir Date: Mon, 6 Jul 2026 15:56:10 +0700 Subject: [PATCH] add full auth --- .vscode/settings.json | 3 +- api/main.proto | 88 +- .../options/annotations.proto | 51 ++ .../options/openapiv2.proto | 762 ++++++++++++++++++ cmd/evening_detective_server/main.go | 16 +- .../main.swagger.json | 114 ++- internal/app/server.go | 36 + internal/modules/processor_jwt/interceptor.go | 45 ++ internal/repos/user.go | 3 + internal/repos/users_repo/repo.go | 38 + internal/services/users_service/service.go | 11 + proto/main.pb.go | 297 ++++++- proto/main.pb.gw.go | 60 ++ proto/main_grpc.pb.go | 38 + 14 files changed, 1512 insertions(+), 50 deletions(-) create mode 100644 api/protoc-gen-openapiv2/options/annotations.proto create mode 100644 api/protoc-gen-openapiv2/options/openapiv2.proto create mode 100644 internal/modules/processor_jwt/interceptor.go diff --git a/.vscode/settings.json b/.vscode/settings.json index 2869530..0c17c4c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "cSpell.words": [ - "pgxpool" + "pgxpool", + "timestamppb" ] } \ No newline at end of file diff --git a/api/main.proto b/api/main.proto index 94ec482..435889c 100644 --- a/api/main.proto +++ b/api/main.proto @@ -3,19 +3,57 @@ syntax = "proto3"; package crabs.evening_detective_server; import "google/api/annotations.proto"; +import "google/protobuf/timestamp.proto"; +import "protoc-gen-openapiv2/options/annotations.proto"; option go_package = "pkg/proto"; +option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { + info: { + title : "Документация сервиса \"Вечерний детектив\""; + version: "v0.1.0"; + }; + + security_definitions: { + security: { + key : "BearerAuth"; + value: { + type : TYPE_API_KEY; + in : IN_HEADER; + name : "Authorization"; + description: "Authentication token, prefixed by Bearer: Bearer "; + } + } + } + security: { + security_requirement: { + key : "BearerAuth"; + value: {}; + } + } +}; + service EveningDetectiveServer { rpc Ping(PingReq) returns (PingRsp) { option (google.api.http) = { get: "/api/ping" }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + security: { + security_requirement: {} + } + }; } + rpc Echo(EchoReq) returns (EchoRsp) { option (google.api.http) = { post: "/api/echo" }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + security: { + security_requirement: {} + } + }; } rpc Signup(SignupReq) returns (SignupRsp) { @@ -23,6 +61,11 @@ service EveningDetectiveServer { post: "/api/signup" body: "*" }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + security: { + security_requirement: {} + } + }; } rpc RefreshPassword(RefreshPasswordReq) returns (RefreshPasswordRsp) { @@ -30,6 +73,11 @@ service EveningDetectiveServer { post: "/api/refresh-password" body: "*" }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + security: { + security_requirement: {} + } + }; } rpc Login(LoginReq) returns (LoginRsp) { @@ -37,6 +85,11 @@ service EveningDetectiveServer { post: "/api/login" body: "*" }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + security: { + security_requirement: {} + } + }; } rpc Refresh(RefreshReq) returns (RefreshRsp) { @@ -44,6 +97,17 @@ service EveningDetectiveServer { post: "/api/refresh" body: "*" }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + security: { + security_requirement: {} + } + }; + } + + rpc GetUsers(GetUsersReq) returns (GetUsersRsp) { + option (google.api.http) = { + get: "/api/users" + }; } } @@ -61,7 +125,7 @@ message EchoRsp { message SignupReq { string username = 1; - string email = 2; + string email = 2; } message SignupRsp { @@ -77,12 +141,12 @@ message RefreshPasswordRsp { } message LoginReq { - string email = 1; + string email = 1; string password = 2; } message LoginRsp { - string error = 1; + string error = 1; string accessToken = 2; string refreshToken = 3; } @@ -92,7 +156,23 @@ message RefreshReq { } message RefreshRsp { - string error = 1; + string error = 1; string accessToken = 2; string refreshToken = 3; } + +message GetUsersReq {} + +message GetUsersRsp { + string error = 1; + repeated User users = 2; +} + +message User { + int32 id = 1; + string username = 2; + string email = 3; + repeated string roles = 4; + bool is_active = 5; + google.protobuf.Timestamp created_at = 6; +} diff --git a/api/protoc-gen-openapiv2/options/annotations.proto b/api/protoc-gen-openapiv2/options/annotations.proto new file mode 100644 index 0000000..aecc5e7 --- /dev/null +++ b/api/protoc-gen-openapiv2/options/annotations.proto @@ -0,0 +1,51 @@ +syntax = "proto3"; + +package grpc.gateway.protoc_gen_openapiv2.options; + +import "google/protobuf/descriptor.proto"; +import "protoc-gen-openapiv2/options/openapiv2.proto"; + +option go_package = "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options"; + +extend google.protobuf.FileOptions { + // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project. + // + // All IDs are the same, as assigned. It is okay that they are the same, as they extend + // different descriptor messages. + Swagger openapiv2_swagger = 1042; +} +extend google.protobuf.MethodOptions { + // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project. + // + // All IDs are the same, as assigned. It is okay that they are the same, as they extend + // different descriptor messages. + Operation openapiv2_operation = 1042; +} +extend google.protobuf.MessageOptions { + // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project. + // + // All IDs are the same, as assigned. It is okay that they are the same, as they extend + // different descriptor messages. + Schema openapiv2_schema = 1042; +} +extend google.protobuf.EnumOptions { + // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project. + // + // All IDs are the same, as assigned. It is okay that they are the same, as they extend + // different descriptor messages. + EnumSchema openapiv2_enum = 1042; +} +extend google.protobuf.ServiceOptions { + // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project. + // + // All IDs are the same, as assigned. It is okay that they are the same, as they extend + // different descriptor messages. + Tag openapiv2_tag = 1042; +} +extend google.protobuf.FieldOptions { + // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project. + // + // All IDs are the same, as assigned. It is okay that they are the same, as they extend + // different descriptor messages. + JSONSchema openapiv2_field = 1042; +} diff --git a/api/protoc-gen-openapiv2/options/openapiv2.proto b/api/protoc-gen-openapiv2/options/openapiv2.proto new file mode 100644 index 0000000..444a568 --- /dev/null +++ b/api/protoc-gen-openapiv2/options/openapiv2.proto @@ -0,0 +1,762 @@ +syntax = "proto3"; + +package grpc.gateway.protoc_gen_openapiv2.options; + +import "google/protobuf/struct.proto"; + +option go_package = "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options"; + +// Scheme describes the schemes supported by the OpenAPI Swagger +// and Operation objects. +enum Scheme { + UNKNOWN = 0; + HTTP = 1; + HTTPS = 2; + WS = 3; + WSS = 4; +} + +// `Swagger` is a representation of OpenAPI v2 specification's Swagger object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#swaggerObject +// +// Example: +// +// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { +// info: { +// title: "Echo API"; +// version: "1.0"; +// description: ""; +// contact: { +// name: "gRPC-Gateway project"; +// url: "https://github.com/grpc-ecosystem/grpc-gateway"; +// email: "none@example.com"; +// }; +// license: { +// name: "BSD 3-Clause License"; +// url: "https://github.com/grpc-ecosystem/grpc-gateway/blob/main/LICENSE"; +// }; +// }; +// schemes: HTTPS; +// consumes: "application/json"; +// produces: "application/json"; +// }; +// +message Swagger { + // Specifies the OpenAPI Specification version being used. It can be + // used by the OpenAPI UI and other clients to interpret the API listing. The + // value MUST be "2.0". + string swagger = 1; + // Provides metadata about the API. The metadata can be used by the + // clients if needed. + Info info = 2; + // The host (name or ip) serving the API. This MUST be the host only and does + // not include the scheme nor sub-paths. It MAY include a port. If the host is + // not included, the host serving the documentation is to be used (including + // the port). The host does not support path templating. + string host = 3; + // The base path on which the API is served, which is relative to the host. If + // it is not included, the API is served directly under the host. The value + // MUST start with a leading slash (/). The basePath does not support path + // templating. + // Note that using `base_path` does not change the endpoint paths that are + // generated in the resulting OpenAPI file. If you wish to use `base_path` + // with relatively generated OpenAPI paths, the `base_path` prefix must be + // manually removed from your `google.api.http` paths and your code changed to + // serve the API from the `base_path`. + string base_path = 4; + // The transfer protocol of the API. Values MUST be from the list: "http", + // "https", "ws", "wss". If the schemes is not included, the default scheme to + // be used is the one used to access the OpenAPI definition itself. + repeated Scheme schemes = 5; + // A list of MIME types the APIs can consume. This is global to all APIs but + // can be overridden on specific API calls. Value MUST be as described under + // Mime Types. + repeated string consumes = 6; + // A list of MIME types the APIs can produce. This is global to all APIs but + // can be overridden on specific API calls. Value MUST be as described under + // Mime Types. + repeated string produces = 7; + // field 8 is reserved for 'paths'. + reserved 8; + // field 9 is reserved for 'definitions', which at this time are already + // exposed as and customizable as proto messages. + reserved 9; + // An object to hold responses that can be used across operations. This + // property does not define global responses for all operations. + map responses = 10; + // Security scheme definitions that can be used across the specification. + SecurityDefinitions security_definitions = 11; + // A declaration of which security schemes are applied for the API as a whole. + // The list of values describes alternative security schemes that can be used + // (that is, there is a logical OR between the security requirements). + // Individual operations can override this definition. + repeated SecurityRequirement security = 12; + // A list of tags for API documentation control. Tags can be used for logical + // grouping of operations by resources or any other qualifier. + repeated Tag tags = 13; + // Additional external documentation. + ExternalDocumentation external_docs = 14; + // Custom properties that start with "x-" such as "x-foo" used to describe + // extra functionality that is not covered by the standard OpenAPI Specification. + // See: https://swagger.io/docs/specification/2-0/swagger-extensions/ + map extensions = 15; +} + +// `Operation` is a representation of OpenAPI v2 specification's Operation object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#operationObject +// +// Example: +// +// service EchoService { +// rpc Echo(SimpleMessage) returns (SimpleMessage) { +// option (google.api.http) = { +// get: "/v1/example/echo/{id}" +// }; +// +// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { +// summary: "Get a message."; +// operation_id: "getMessage"; +// tags: "echo"; +// responses: { +// key: "200" +// value: { +// description: "OK"; +// } +// } +// }; +// } +// } +message Operation { + // A list of tags for API documentation control. Tags can be used for logical + // grouping of operations by resources or any other qualifier. + repeated string tags = 1; + // A short summary of what the operation does. For maximum readability in the + // swagger-ui, this field SHOULD be less than 120 characters. + string summary = 2; + // A verbose explanation of the operation behavior. GFM syntax can be used for + // rich text representation. + string description = 3; + // Additional external documentation for this operation. + ExternalDocumentation external_docs = 4; + // Unique string used to identify the operation. The id MUST be unique among + // all operations described in the API. Tools and libraries MAY use the + // operationId to uniquely identify an operation, therefore, it is recommended + // to follow common programming naming conventions. + string operation_id = 5; + // A list of MIME types the operation can consume. This overrides the consumes + // definition at the OpenAPI Object. An empty value MAY be used to clear the + // global definition. Value MUST be as described under Mime Types. + repeated string consumes = 6; + // A list of MIME types the operation can produce. This overrides the produces + // definition at the OpenAPI Object. An empty value MAY be used to clear the + // global definition. Value MUST be as described under Mime Types. + repeated string produces = 7; + // field 8 is reserved for 'parameters'. + reserved 8; + // The list of possible responses as they are returned from executing this + // operation. + map responses = 9; + // The transfer protocol for the operation. Values MUST be from the list: + // "http", "https", "ws", "wss". The value overrides the OpenAPI Object + // schemes definition. + repeated Scheme schemes = 10; + // Declares this operation to be deprecated. Usage of the declared operation + // should be refrained. Default value is false. + bool deprecated = 11; + // A declaration of which security schemes are applied for this operation. The + // list of values describes alternative security schemes that can be used + // (that is, there is a logical OR between the security requirements). This + // definition overrides any declared top-level security. To remove a top-level + // security declaration, an empty array can be used. + repeated SecurityRequirement security = 12; + // Custom properties that start with "x-" such as "x-foo" used to describe + // extra functionality that is not covered by the standard OpenAPI Specification. + // See: https://swagger.io/docs/specification/2-0/swagger-extensions/ + map extensions = 13; + // Custom parameters such as HTTP request headers. + // See: https://swagger.io/docs/specification/2-0/describing-parameters/ + // and https://swagger.io/specification/v2/#parameter-object. + Parameters parameters = 14; +} + +// `Parameters` is a representation of OpenAPI v2 specification's parameters object. +// Note: This technically breaks compatibility with the OpenAPI 2 definition structure as we only +// allow header parameters to be set here since we do not want users specifying custom non-header +// parameters beyond those inferred from the Protobuf schema. +// See: https://swagger.io/specification/v2/#parameter-object +message Parameters { + // `Headers` is one or more HTTP header parameter. + // See: https://swagger.io/docs/specification/2-0/describing-parameters/#header-parameters + repeated HeaderParameter headers = 1; +} + +// `HeaderParameter` a HTTP header parameter. +// See: https://swagger.io/specification/v2/#parameter-object +message HeaderParameter { + // `Type` is a supported HTTP header type. + // See https://swagger.io/specification/v2/#parameterType. + enum Type { + UNKNOWN = 0; + STRING = 1; + NUMBER = 2; + INTEGER = 3; + BOOLEAN = 4; + } + + // `Name` is the header name. + string name = 1; + // `Description` is a short description of the header. + string description = 2; + // `Type` is the type of the object. The value MUST be one of "string", "number", "integer", or "boolean". The "array" type is not supported. + // See: https://swagger.io/specification/v2/#parameterType. + Type type = 3; + // `Format` The extending format for the previously mentioned type. + string format = 4; + // `Required` indicates if the header is optional + bool required = 5; + // field 6 is reserved for 'items', but in OpenAPI-specific way. + reserved 6; + // field 7 is reserved `Collection Format`. Determines the format of the array if type array is used. + reserved 7; +} + +// `Header` is a representation of OpenAPI v2 specification's Header object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#headerObject +// +message Header { + // `Description` is a short description of the header. + string description = 1; + // The type of the object. The value MUST be one of "string", "number", "integer", or "boolean". The "array" type is not supported. + string type = 2; + // `Format` The extending format for the previously mentioned type. + string format = 3; + // field 4 is reserved for 'items', but in OpenAPI-specific way. + reserved 4; + // field 5 is reserved `Collection Format` Determines the format of the array if type array is used. + reserved 5; + // `Default` Declares the value of the header that the server will use if none is provided. + // See: https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-6.2. + // Unlike JSON Schema this value MUST conform to the defined type for the header. + string default = 6; + // field 7 is reserved for 'maximum'. + reserved 7; + // field 8 is reserved for 'exclusiveMaximum'. + reserved 8; + // field 9 is reserved for 'minimum'. + reserved 9; + // field 10 is reserved for 'exclusiveMinimum'. + reserved 10; + // field 11 is reserved for 'maxLength'. + reserved 11; + // field 12 is reserved for 'minLength'. + reserved 12; + // 'Pattern' See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.2.3. + string pattern = 13; + // field 14 is reserved for 'maxItems'. + reserved 14; + // field 15 is reserved for 'minItems'. + reserved 15; + // field 16 is reserved for 'uniqueItems'. + reserved 16; + // field 17 is reserved for 'enum'. + reserved 17; + // field 18 is reserved for 'multipleOf'. + reserved 18; +} + +// `Response` is a representation of OpenAPI v2 specification's Response object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#responseObject +// +message Response { + // `Description` is a short description of the response. + // GFM syntax can be used for rich text representation. + string description = 1; + // `Schema` optionally defines the structure of the response. + // If `Schema` is not provided, it means there is no content to the response. + Schema schema = 2; + // `Headers` A list of headers that are sent with the response. + // `Header` name is expected to be a string in the canonical format of the MIME header key + // See: https://golang.org/pkg/net/textproto/#CanonicalMIMEHeaderKey + map headers = 3; + // `Examples` gives per-mimetype response examples. + // See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#example-object + map examples = 4; + // Custom properties that start with "x-" such as "x-foo" used to describe + // extra functionality that is not covered by the standard OpenAPI Specification. + // See: https://swagger.io/docs/specification/2-0/swagger-extensions/ + map extensions = 5; +} + +// `Info` is a representation of OpenAPI v2 specification's Info object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#infoObject +// +// Example: +// +// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { +// info: { +// title: "Echo API"; +// version: "1.0"; +// description: ""; +// contact: { +// name: "gRPC-Gateway project"; +// url: "https://github.com/grpc-ecosystem/grpc-gateway"; +// email: "none@example.com"; +// }; +// license: { +// name: "BSD 3-Clause License"; +// url: "https://github.com/grpc-ecosystem/grpc-gateway/blob/main/LICENSE"; +// }; +// }; +// ... +// }; +// +message Info { + // The title of the application. + string title = 1; + // A short description of the application. GFM syntax can be used for rich + // text representation. + string description = 2; + // The Terms of Service for the API. + string terms_of_service = 3; + // The contact information for the exposed API. + Contact contact = 4; + // The license information for the exposed API. + License license = 5; + // Provides the version of the application API (not to be confused + // with the specification version). + string version = 6; + // Custom properties that start with "x-" such as "x-foo" used to describe + // extra functionality that is not covered by the standard OpenAPI Specification. + // See: https://swagger.io/docs/specification/2-0/swagger-extensions/ + map extensions = 7; +} + +// `Contact` is a representation of OpenAPI v2 specification's Contact object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#contactObject +// +// Example: +// +// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { +// info: { +// ... +// contact: { +// name: "gRPC-Gateway project"; +// url: "https://github.com/grpc-ecosystem/grpc-gateway"; +// email: "none@example.com"; +// }; +// ... +// }; +// ... +// }; +// +message Contact { + // The identifying name of the contact person/organization. + string name = 1; + // The URL pointing to the contact information. MUST be in the format of a + // URL. + string url = 2; + // The email address of the contact person/organization. MUST be in the format + // of an email address. + string email = 3; +} + +// `License` is a representation of OpenAPI v2 specification's License object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#licenseObject +// +// Example: +// +// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { +// info: { +// ... +// license: { +// name: "BSD 3-Clause License"; +// url: "https://github.com/grpc-ecosystem/grpc-gateway/blob/main/LICENSE"; +// }; +// ... +// }; +// ... +// }; +// +message License { + // The license name used for the API. + string name = 1; + // A URL to the license used for the API. MUST be in the format of a URL. + string url = 2; +} + +// `ExternalDocumentation` is a representation of OpenAPI v2 specification's +// ExternalDocumentation object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#externalDocumentationObject +// +// Example: +// +// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { +// ... +// external_docs: { +// description: "More about gRPC-Gateway"; +// url: "https://github.com/grpc-ecosystem/grpc-gateway"; +// } +// ... +// }; +// +message ExternalDocumentation { + // A short description of the target documentation. GFM syntax can be used for + // rich text representation. + string description = 1; + // The URL for the target documentation. Value MUST be in the format + // of a URL. + string url = 2; +} + +// `Schema` is a representation of OpenAPI v2 specification's Schema object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#schemaObject +// +message Schema { + JSONSchema json_schema = 1; + // Adds support for polymorphism. The discriminator is the schema property + // name that is used to differentiate between other schema that inherit this + // schema. The property name used MUST be defined at this schema and it MUST + // be in the required property list. When used, the value MUST be the name of + // this schema or any schema that inherits it. + string discriminator = 2; + // Relevant only for Schema "properties" definitions. Declares the property as + // "read only". This means that it MAY be sent as part of a response but MUST + // NOT be sent as part of the request. Properties marked as readOnly being + // true SHOULD NOT be in the required list of the defined schema. Default + // value is false. + bool read_only = 3; + // field 4 is reserved for 'xml'. + reserved 4; + // Additional external documentation for this schema. + ExternalDocumentation external_docs = 5; + // A free-form property to include an example of an instance for this schema in JSON. + // This is copied verbatim to the output. + string example = 6; +} + +// `EnumSchema` is subset of fields from the OpenAPI v2 specification's Schema object. +// Only fields that are applicable to Enums are included +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#schemaObject +// +// Example: +// +// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_enum) = { +// ... +// title: "MyEnum"; +// description:"This is my nice enum"; +// example: "ZERO"; +// required: true; +// ... +// }; +// +message EnumSchema { + // A short description of the schema. + string description = 1; + string default = 2; + // The title of the schema. + string title = 3; + bool required = 4; + bool read_only = 5; + // Additional external documentation for this schema. + ExternalDocumentation external_docs = 6; + string example = 7; + // Ref is used to define an external reference to include in the message. + // This could be a fully qualified proto message reference, and that type must + // be imported into the protofile. If no message is identified, the Ref will + // be used verbatim in the output. + // For example: + // `ref: ".google.protobuf.Timestamp"`. + string ref = 8; + // Custom properties that start with "x-" such as "x-foo" used to describe + // extra functionality that is not covered by the standard OpenAPI Specification. + // See: https://swagger.io/docs/specification/2-0/swagger-extensions/ + map extensions = 9; +} + +// `JSONSchema` represents properties from JSON Schema taken, and as used, in +// the OpenAPI v2 spec. +// +// This includes changes made by OpenAPI v2. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#schemaObject +// +// See also: https://cswr.github.io/JsonSchema/spec/basic_types/, +// https://github.com/json-schema-org/json-schema-spec/blob/master/schema.json +// +// Example: +// +// message SimpleMessage { +// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = { +// json_schema: { +// title: "SimpleMessage" +// description: "A simple message." +// required: ["id"] +// } +// }; +// +// // Id represents the message identifier. +// string id = 1; [ +// (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { +// description: "The unique identifier of the simple message." +// }]; +// } +// +message JSONSchema { + // field 1 is reserved for '$id', omitted from OpenAPI v2. + reserved 1; + // field 2 is reserved for '$schema', omitted from OpenAPI v2. + reserved 2; + // Ref is used to define an external reference to include in the message. + // This could be a fully qualified proto message reference, and that type must + // be imported into the protofile. If no message is identified, the Ref will + // be used verbatim in the output. + // For example: + // `ref: ".google.protobuf.Timestamp"`. + string ref = 3; + // field 4 is reserved for '$comment', omitted from OpenAPI v2. + reserved 4; + // The title of the schema. + string title = 5; + // A short description of the schema. + string description = 6; + string default = 7; + bool read_only = 8; + // A free-form property to include a JSON example of this field. This is copied + // verbatim to the output swagger.json. Quotes must be escaped. + // This property is the same for 2.0 and 3.0.0 https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/3.0.0.md#schemaObject https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#schemaObject + string example = 9; + double multiple_of = 10; + // Maximum represents an inclusive upper limit for a numeric instance. The + // value of MUST be a number, + double maximum = 11; + bool exclusive_maximum = 12; + // minimum represents an inclusive lower limit for a numeric instance. The + // value of MUST be a number, + double minimum = 13; + bool exclusive_minimum = 14; + uint64 max_length = 15; + uint64 min_length = 16; + string pattern = 17; + // field 18 is reserved for 'additionalItems', omitted from OpenAPI v2. + reserved 18; + // field 19 is reserved for 'items', but in OpenAPI-specific way. + // TODO(ivucica): add 'items'? + reserved 19; + uint64 max_items = 20; + uint64 min_items = 21; + bool unique_items = 22; + // field 23 is reserved for 'contains', omitted from OpenAPI v2. + reserved 23; + uint64 max_properties = 24; + uint64 min_properties = 25; + repeated string required = 26; + // field 27 is reserved for 'additionalProperties', but in OpenAPI-specific + // way. TODO(ivucica): add 'additionalProperties'? + reserved 27; + // field 28 is reserved for 'definitions', omitted from OpenAPI v2. + reserved 28; + // field 29 is reserved for 'properties', but in OpenAPI-specific way. + // TODO(ivucica): add 'additionalProperties'? + reserved 29; + // following fields are reserved, as the properties have been omitted from + // OpenAPI v2: + // patternProperties, dependencies, propertyNames, const + reserved 30 to 33; + // Items in 'array' must be unique. + repeated string array = 34; + + enum JSONSchemaSimpleTypes { + UNKNOWN = 0; + ARRAY = 1; + BOOLEAN = 2; + INTEGER = 3; + NULL = 4; + NUMBER = 5; + OBJECT = 6; + STRING = 7; + } + + repeated JSONSchemaSimpleTypes type = 35; + // `Format` + string format = 36; + // following fields are reserved, as the properties have been omitted from + // OpenAPI v2: contentMediaType, contentEncoding, if, then, else + reserved 37 to 41; + // field 42 is reserved for 'allOf', but in OpenAPI-specific way. + // TODO(ivucica): add 'allOf'? + reserved 42; + // following fields are reserved, as the properties have been omitted from + // OpenAPI v2: + // anyOf, oneOf, not + reserved 43 to 45; + // Items in `enum` must be unique https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.5.1 + repeated string enum = 46; + + // Additional field level properties used when generating the OpenAPI v2 file. + FieldConfiguration field_configuration = 1001; + + // 'FieldConfiguration' provides additional field level properties used when generating the OpenAPI v2 file. + // These properties are not defined by OpenAPIv2, but they are used to control the generation. + message FieldConfiguration { + // Alternative parameter name when used as path parameter. If set, this will + // be used as the complete parameter name when this field is used as a path + // parameter. Use this to avoid having auto generated path parameter names + // for overlapping paths. + string path_param_name = 47; + // Declares this field to be deprecated. Allows for the generated OpenAPI + // parameter to be marked as deprecated without affecting the proto field. + bool deprecated = 49; + } + // Custom properties that start with "x-" such as "x-foo" used to describe + // extra functionality that is not covered by the standard OpenAPI Specification. + // See: https://swagger.io/docs/specification/2-0/swagger-extensions/ + map extensions = 48; +} + +// `Tag` is a representation of OpenAPI v2 specification's Tag object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#tagObject +// +message Tag { + // The name of the tag. Use it to allow override of the name of a + // global Tag object, then use that name to reference the tag throughout the + // OpenAPI file. + string name = 1; + // A short description for the tag. GFM syntax can be used for rich text + // representation. + string description = 2; + // Additional external documentation for this tag. + ExternalDocumentation external_docs = 3; + // Custom properties that start with "x-" such as "x-foo" used to describe + // extra functionality that is not covered by the standard OpenAPI Specification. + // See: https://swagger.io/docs/specification/2-0/swagger-extensions/ + map extensions = 4; +} + +// `SecurityDefinitions` is a representation of OpenAPI v2 specification's +// Security Definitions object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#securityDefinitionsObject +// +// A declaration of the security schemes available to be used in the +// specification. This does not enforce the security schemes on the operations +// and only serves to provide the relevant details for each scheme. +message SecurityDefinitions { + // A single security scheme definition, mapping a "name" to the scheme it + // defines. + map security = 1; +} + +// `SecurityScheme` is a representation of OpenAPI v2 specification's +// Security Scheme object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#securitySchemeObject +// +// Allows the definition of a security scheme that can be used by the +// operations. Supported schemes are basic authentication, an API key (either as +// a header or as a query parameter) and OAuth2's common flows (implicit, +// password, application and access code). +message SecurityScheme { + // The type of the security scheme. Valid values are "basic", + // "apiKey" or "oauth2". + enum Type { + TYPE_INVALID = 0; + TYPE_BASIC = 1; + TYPE_API_KEY = 2; + TYPE_OAUTH2 = 3; + } + + // The location of the API key. Valid values are "query" or "header". + enum In { + IN_INVALID = 0; + IN_QUERY = 1; + IN_HEADER = 2; + } + + // The flow used by the OAuth2 security scheme. Valid values are + // "implicit", "password", "application" or "accessCode". + enum Flow { + FLOW_INVALID = 0; + FLOW_IMPLICIT = 1; + FLOW_PASSWORD = 2; + FLOW_APPLICATION = 3; + FLOW_ACCESS_CODE = 4; + } + + // The type of the security scheme. Valid values are "basic", + // "apiKey" or "oauth2". + Type type = 1; + // A short description for security scheme. + string description = 2; + // The name of the header or query parameter to be used. + // Valid for apiKey. + string name = 3; + // The location of the API key. Valid values are "query" or + // "header". + // Valid for apiKey. + In in = 4; + // The flow used by the OAuth2 security scheme. Valid values are + // "implicit", "password", "application" or "accessCode". + // Valid for oauth2. + Flow flow = 5; + // The authorization URL to be used for this flow. This SHOULD be in + // the form of a URL. + // Valid for oauth2/implicit and oauth2/accessCode. + string authorization_url = 6; + // The token URL to be used for this flow. This SHOULD be in the + // form of a URL. + // Valid for oauth2/password, oauth2/application and oauth2/accessCode. + string token_url = 7; + // The available scopes for the OAuth2 security scheme. + // Valid for oauth2. + Scopes scopes = 8; + // Custom properties that start with "x-" such as "x-foo" used to describe + // extra functionality that is not covered by the standard OpenAPI Specification. + // See: https://swagger.io/docs/specification/2-0/swagger-extensions/ + map extensions = 9; +} + +// `SecurityRequirement` is a representation of OpenAPI v2 specification's +// Security Requirement object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#securityRequirementObject +// +// Lists the required security schemes to execute this operation. The object can +// have multiple security schemes declared in it which are all required (that +// is, there is a logical AND between the schemes). +// +// The name used for each property MUST correspond to a security scheme +// declared in the Security Definitions. +message SecurityRequirement { + // If the security scheme is of type "oauth2", then the value is a list of + // scope names required for the execution. For other security scheme types, + // the array MUST be empty. + message SecurityRequirementValue { + repeated string scope = 1; + } + // Each name must correspond to a security scheme which is declared in + // the Security Definitions. If the security scheme is of type "oauth2", + // then the value is a list of scope names required for the execution. + // For other security scheme types, the array MUST be empty. + map security_requirement = 1; +} + +// `Scopes` is a representation of OpenAPI v2 specification's Scopes object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#scopesObject +// +// Lists the available scopes for an OAuth2 security scheme. +message Scopes { + // Maps between a name of a scope to a short description of it (as the value + // of the property). + map scope = 1; +} diff --git a/cmd/evening_detective_server/main.go b/cmd/evening_detective_server/main.go index b60b769..86d81b1 100644 --- a/cmd/evening_detective_server/main.go +++ b/cmd/evening_detective_server/main.go @@ -62,7 +62,21 @@ func main() { } // Create a gRPC server object - s := grpc.NewServer() + s := grpc.NewServer( + grpc.UnaryInterceptor( + processor_jwt.NewAuthorizationInterceptor( + map[string]bool{ + "/crabs.evening_detective_server.EveningDetectiveServer/Ping": true, + "/crabs.evening_detective_server.EveningDetectiveServer/Echo": true, + "/crabs.evening_detective_server.EveningDetectiveServer/Signup": true, + "/crabs.evening_detective_server.EveningDetectiveServer/RefreshPassword": true, + "/crabs.evening_detective_server.EveningDetectiveServer/Login": true, + "/crabs.evening_detective_server.EveningDetectiveServer/Refresh": true, + }, + processorJWT, + ), + ), + ) // Attach the Greeter service to the server proto.RegisterEveningDetectiveServerServer( s, diff --git a/cmd/evening_detective_server/main.swagger.json b/cmd/evening_detective_server/main.swagger.json index a6d1394..9a8664a 100644 --- a/cmd/evening_detective_server/main.swagger.json +++ b/cmd/evening_detective_server/main.swagger.json @@ -1,8 +1,8 @@ { "swagger": "2.0", "info": { - "title": "main.proto", - "version": "version not set" + "title": "Документация сервиса \"Вечерний детектив\"", + "version": "v0.1.0" }, "tags": [ { @@ -43,6 +43,11 @@ ], "tags": [ "EveningDetectiveServer" + ], + "security": [ + { + "": [] + } ] } }, @@ -75,6 +80,11 @@ ], "tags": [ "EveningDetectiveServer" + ], + "security": [ + { + "": [] + } ] } }, @@ -97,6 +107,11 @@ }, "tags": [ "EveningDetectiveServer" + ], + "security": [ + { + "": [] + } ] } }, @@ -129,6 +144,11 @@ ], "tags": [ "EveningDetectiveServer" + ], + "security": [ + { + "": [] + } ] } }, @@ -161,6 +181,11 @@ ], "tags": [ "EveningDetectiveServer" + ], + "security": [ + { + "": [] + } ] } }, @@ -191,6 +216,33 @@ } } ], + "tags": [ + "EveningDetectiveServer" + ], + "security": [ + { + "": [] + } + ] + } + }, + "/api/users": { + "get": { + "operationId": "EveningDetectiveServer_GetUsers", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/evening_detective_serverGetUsersRsp" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, "tags": [ "EveningDetectiveServer" ] @@ -206,6 +258,21 @@ } } }, + "evening_detective_serverGetUsersRsp": { + "type": "object", + "properties": { + "error": { + "type": "string" + }, + "users": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/evening_detective_serverUser" + } + } + } + }, "evening_detective_serverLoginReq": { "type": "object", "properties": { @@ -291,6 +358,34 @@ } } }, + "evening_detective_serverUser": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "username": { + "type": "string" + }, + "email": { + "type": "string" + }, + "roles": { + "type": "array", + "items": { + "type": "string" + } + }, + "isActive": { + "type": "boolean" + }, + "createdAt": { + "type": "string", + "format": "date-time" + } + } + }, "protobufAny": { "type": "object", "properties": { @@ -319,5 +414,18 @@ } } } - } + }, + "securityDefinitions": { + "BearerAuth": { + "type": "apiKey", + "description": "Authentication token, prefixed by Bearer: Bearer \u003ctoken\u003e", + "name": "Authorization", + "in": "header" + } + }, + "security": [ + { + "BearerAuth": [] + } + ] } diff --git a/internal/app/server.go b/internal/app/server.go index b0ec964..44353c4 100644 --- a/internal/app/server.go +++ b/internal/app/server.go @@ -2,8 +2,13 @@ package app import ( "context" + "evening_detective_server/internal/modules/processor_jwt" "evening_detective_server/internal/services/users_service" proto "evening_detective_server/proto" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/timestamppb" ) type server struct { @@ -73,3 +78,34 @@ func (s *server) Refresh(ctx context.Context, req *proto.RefreshReq) (*proto.Ref RefreshToken: refreshToken, }, nil } + +func (s *server) GetUsers(ctx context.Context, req *proto.GetUsersReq) (*proto.GetUsersRsp, error) { + claims := ctx.Value("claims").(*processor_jwt.JWTClaims) + if !claims.HasRole("admin") { + return nil, status.Errorf(codes.PermissionDenied, "permission denied") + } + users, err := s.usersService.GetUsers(ctx) + if err != nil { + return &proto.GetUsersRsp{ + Error: err.Error(), + }, nil + } + + resUsers := make([]*proto.User, 0, len(users)) + for _, user := range users { + resUsers = append( + resUsers, + &proto.User{ + Id: int32(user.ID), + Username: user.Username, + Email: user.Email, + Roles: user.Roles, + IsActive: user.IsActive, + CreatedAt: timestamppb.New(user.CreatedAt), + }, + ) + } + return &proto.GetUsersRsp{ + Users: resUsers, + }, nil +} diff --git a/internal/modules/processor_jwt/interceptor.go b/internal/modules/processor_jwt/interceptor.go new file mode 100644 index 0000000..0e1cfd7 --- /dev/null +++ b/internal/modules/processor_jwt/interceptor.go @@ -0,0 +1,45 @@ +package processor_jwt + +import ( + "context" + "strings" + + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" +) + +func NewAuthorizationInterceptor( + publicMethods map[string]bool, + processorJWT IProcessorJWT, +) grpc.UnaryServerInterceptor { + return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) { + if publicMethods[info.FullMethod] { + return handler(ctx, req) + } + + md, ok := metadata.FromIncomingContext(ctx) + if !ok { + return nil, status.Errorf(codes.Unauthenticated, "metadata is not provided") + } + + values := md.Get("authorization") + if len(values) == 0 { + return nil, status.Errorf(codes.Unauthenticated, "authorization token is not provided") + } + + token := strings.TrimPrefix(values[0], "Bearer ") + if token == "" { + return nil, status.Errorf(codes.Unauthenticated, "invalid token format") + } + + claims, err := processorJWT.GetClaims(token) + if err != nil { + return nil, status.Errorf(codes.Unauthenticated, "invalid token") + } + + ctx = context.WithValue(ctx, "claims", claims) + return handler(ctx, req) + } +} diff --git a/internal/repos/user.go b/internal/repos/user.go index 38ba16a..3219e07 100644 --- a/internal/repos/user.go +++ b/internal/repos/user.go @@ -1,5 +1,7 @@ package repos +import "time" + type User struct { ID int Username string @@ -7,4 +9,5 @@ type User struct { PasswordHash string Roles []string IsActive bool + CreatedAt time.Time } diff --git a/internal/repos/users_repo/repo.go b/internal/repos/users_repo/repo.go index 19b4311..8be8220 100644 --- a/internal/repos/users_repo/repo.go +++ b/internal/repos/users_repo/repo.go @@ -137,3 +137,41 @@ func (s *UsersRepo) GetUserByID( return user, nil } + +func (s *UsersRepo) GetUsers( + ctx context.Context, +) ([]*repos.User, error) { + rows, err := s.pool.Query( + ctx, + `SELECT id, username, email, roles, is_active, created_at + FROM users + ORDER BY created_at DESC`, + ) + if err != nil { + return nil, err + } + defer rows.Close() + + var users []*repos.User + for rows.Next() { + user := &repos.User{} + err := rows.Scan( + &user.ID, + &user.Username, + &user.Email, + &user.Roles, + &user.IsActive, + &user.CreatedAt, + ) + if err != nil { + return nil, err + } + users = append(users, user) + } + + if err := rows.Err(); err != nil { + return nil, err + } + + return users, nil +} diff --git a/internal/services/users_service/service.go b/internal/services/users_service/service.go index d573d12..705f04b 100644 --- a/internal/services/users_service/service.go +++ b/internal/services/users_service/service.go @@ -6,6 +6,7 @@ import ( "evening_detective_server/internal/modules/email_sender" "evening_detective_server/internal/modules/password_generator" "evening_detective_server/internal/modules/processor_jwt" + "evening_detective_server/internal/repos" "evening_detective_server/internal/repos/refresh_tokens_repo" "evening_detective_server/internal/repos/users_repo" "fmt" @@ -186,3 +187,13 @@ func (s *UsersService) Refresh( return accessToken, refreshToken, nil } + +func (s *UsersService) GetUsers( + ctx context.Context, +) ([]*repos.User, error) { + users, err := s.usersRepo.GetUsers(ctx) + if err != nil { + return nil, err + } + return users, nil +} diff --git a/proto/main.pb.go b/proto/main.pb.go index 19a3197..2d2e455 100644 --- a/proto/main.pb.go +++ b/proto/main.pb.go @@ -7,9 +7,11 @@ package proto import ( + _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" _ "google.golang.org/genproto/googleapis/api/annotations" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" sync "sync" unsafe "unsafe" @@ -582,12 +584,184 @@ func (x *RefreshRsp) GetRefreshToken() string { return "" } +type GetUsersReq struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetUsersReq) Reset() { + *x = GetUsersReq{} + mi := &file_main_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetUsersReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetUsersReq) ProtoMessage() {} + +func (x *GetUsersReq) ProtoReflect() protoreflect.Message { + mi := &file_main_proto_msgTypes[12] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetUsersReq.ProtoReflect.Descriptor instead. +func (*GetUsersReq) Descriptor() ([]byte, []int) { + return file_main_proto_rawDescGZIP(), []int{12} +} + +type GetUsersRsp struct { + state protoimpl.MessageState `protogen:"open.v1"` + Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` + Users []*User `protobuf:"bytes,2,rep,name=users,proto3" json:"users,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetUsersRsp) Reset() { + *x = GetUsersRsp{} + mi := &file_main_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetUsersRsp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetUsersRsp) ProtoMessage() {} + +func (x *GetUsersRsp) ProtoReflect() protoreflect.Message { + mi := &file_main_proto_msgTypes[13] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetUsersRsp.ProtoReflect.Descriptor instead. +func (*GetUsersRsp) Descriptor() ([]byte, []int) { + return file_main_proto_rawDescGZIP(), []int{13} +} + +func (x *GetUsersRsp) GetError() string { + if x != nil { + return x.Error + } + return "" +} + +func (x *GetUsersRsp) GetUsers() []*User { + if x != nil { + return x.Users + } + return nil +} + +type User struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"` + Email string `protobuf:"bytes,3,opt,name=email,proto3" json:"email,omitempty"` + Roles []string `protobuf:"bytes,4,rep,name=roles,proto3" json:"roles,omitempty"` + IsActive bool `protobuf:"varint,5,opt,name=is_active,json=isActive,proto3" json:"is_active,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *User) Reset() { + *x = User{} + mi := &file_main_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *User) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*User) ProtoMessage() {} + +func (x *User) ProtoReflect() protoreflect.Message { + mi := &file_main_proto_msgTypes[14] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use User.ProtoReflect.Descriptor instead. +func (*User) Descriptor() ([]byte, []int) { + return file_main_proto_rawDescGZIP(), []int{14} +} + +func (x *User) GetId() int32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *User) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *User) GetEmail() string { + if x != nil { + return x.Email + } + return "" +} + +func (x *User) GetRoles() []string { + if x != nil { + return x.Roles + } + return nil +} + +func (x *User) GetIsActive() bool { + if x != nil { + return x.IsActive + } + return false +} + +func (x *User) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + var File_main_proto protoreflect.FileDescriptor const file_main_proto_rawDesc = "" + "\n" + "\n" + - "main.proto\x12\x1ecrabs.evening_detective_server\x1a\x1cgoogle/api/annotations.proto\"\t\n" + + "main.proto\x12\x1ecrabs.evening_detective_server\x1a\x1cgoogle/api/annotations.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a.protoc-gen-openapiv2/options/annotations.proto\"\t\n" + "\aPingReq\"\t\n" + "\aPingRsp\"\x1d\n" + "\aEchoReq\x12\x12\n" + @@ -617,15 +791,48 @@ const file_main_proto_rawDesc = "" + "RefreshRsp\x12\x14\n" + "\x05error\x18\x01 \x01(\tR\x05error\x12 \n" + "\vaccessToken\x18\x02 \x01(\tR\vaccessToken\x12\"\n" + - "\frefreshToken\x18\x03 \x01(\tR\frefreshToken2\xf8\x05\n" + - "\x16EveningDetectiveServer\x12k\n" + - "\x04Ping\x12'.crabs.evening_detective_server.PingReq\x1a'.crabs.evening_detective_server.PingRsp\"\x11\x82\xd3\xe4\x93\x02\v\x12\t/api/ping\x12k\n" + - "\x04Echo\x12'.crabs.evening_detective_server.EchoReq\x1a'.crabs.evening_detective_server.EchoRsp\"\x11\x82\xd3\xe4\x93\x02\v\"\t/api/echo\x12v\n" + - "\x06Signup\x12).crabs.evening_detective_server.SignupReq\x1a).crabs.evening_detective_server.SignupRsp\"\x16\x82\xd3\xe4\x93\x02\x10:\x01*\"\v/api/signup\x12\x9b\x01\n" + - "\x0fRefreshPassword\x122.crabs.evening_detective_server.RefreshPasswordReq\x1a2.crabs.evening_detective_server.RefreshPasswordRsp\" \x82\xd3\xe4\x93\x02\x1a:\x01*\"\x15/api/refresh-password\x12r\n" + - "\x05Login\x12(.crabs.evening_detective_server.LoginReq\x1a(.crabs.evening_detective_server.LoginRsp\"\x15\x82\xd3\xe4\x93\x02\x0f:\x01*\"\n" + - "/api/login\x12z\n" + - "\aRefresh\x12*.crabs.evening_detective_server.RefreshReq\x1a*.crabs.evening_detective_server.RefreshRsp\"\x17\x82\xd3\xe4\x93\x02\x11:\x01*\"\f/api/refreshB\vZ\tpkg/protob\x06proto3" + "\frefreshToken\x18\x03 \x01(\tR\frefreshToken\"\r\n" + + "\vGetUsersReq\"_\n" + + "\vGetUsersRsp\x12\x14\n" + + "\x05error\x18\x01 \x01(\tR\x05error\x12:\n" + + "\x05users\x18\x02 \x03(\v2$.crabs.evening_detective_server.UserR\x05users\"\xb6\x01\n" + + "\x04User\x12\x0e\n" + + "\x02id\x18\x01 \x01(\x05R\x02id\x12\x1a\n" + + "\busername\x18\x02 \x01(\tR\busername\x12\x14\n" + + "\x05email\x18\x03 \x01(\tR\x05email\x12\x14\n" + + "\x05roles\x18\x04 \x03(\tR\x05roles\x12\x1b\n" + + "\tis_active\x18\x05 \x01(\bR\bisActive\x129\n" + + "\n" + + "created_at\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt2\xb6\a\n" + + "\x16EveningDetectiveServer\x12v\n" + + "\x04Ping\x12'.crabs.evening_detective_server.PingReq\x1a'.crabs.evening_detective_server.PingRsp\"\x1c\x92A\bb\x06\n" + + "\x04\n" + + "\x00\x12\x00\x82\xd3\xe4\x93\x02\v\x12\t/api/ping\x12v\n" + + "\x04Echo\x12'.crabs.evening_detective_server.EchoReq\x1a'.crabs.evening_detective_server.EchoRsp\"\x1c\x92A\bb\x06\n" + + "\x04\n" + + "\x00\x12\x00\x82\xd3\xe4\x93\x02\v\"\t/api/echo\x12\x81\x01\n" + + "\x06Signup\x12).crabs.evening_detective_server.SignupReq\x1a).crabs.evening_detective_server.SignupRsp\"!\x92A\bb\x06\n" + + "\x04\n" + + "\x00\x12\x00\x82\xd3\xe4\x93\x02\x10:\x01*\"\v/api/signup\x12\xa6\x01\n" + + "\x0fRefreshPassword\x122.crabs.evening_detective_server.RefreshPasswordReq\x1a2.crabs.evening_detective_server.RefreshPasswordRsp\"+\x92A\bb\x06\n" + + "\x04\n" + + "\x00\x12\x00\x82\xd3\xe4\x93\x02\x1a:\x01*\"\x15/api/refresh-password\x12}\n" + + "\x05Login\x12(.crabs.evening_detective_server.LoginReq\x1a(.crabs.evening_detective_server.LoginRsp\" \x92A\bb\x06\n" + + "\x04\n" + + "\x00\x12\x00\x82\xd3\xe4\x93\x02\x0f:\x01*\"\n" + + "/api/login\x12\x85\x01\n" + + "\aRefresh\x12*.crabs.evening_detective_server.RefreshReq\x1a*.crabs.evening_detective_server.RefreshRsp\"\"\x92A\bb\x06\n" + + "\x04\n" + + "\x00\x12\x00\x82\xd3\xe4\x93\x02\x11:\x01*\"\f/api/refresh\x12x\n" + + "\bGetUsers\x12+.crabs.evening_detective_server.GetUsersReq\x1a+.crabs.evening_detective_server.GetUsersRsp\"\x12\x82\xd3\xe4\x93\x02\f\x12\n" + + "/api/usersB\xd7\x01\x92A\xc8\x01\x12U\n" + + "KДокументация сервиса \"Вечерний детектив\"2\x06v0.1.0Z]\n" + + "[\n" + + "\n" + + "BearerAuth\x12M\b\x02\x128Authentication token, prefixed by Bearer: Bearer \x1a\rAuthorization \x02b\x10\n" + + "\x0e\n" + + "\n" + + "BearerAuth\x12\x00Z\tpkg/protob\x06proto3" var ( file_main_proto_rawDescOnce sync.Once @@ -639,39 +846,47 @@ func file_main_proto_rawDescGZIP() []byte { return file_main_proto_rawDescData } -var file_main_proto_msgTypes = make([]protoimpl.MessageInfo, 12) +var file_main_proto_msgTypes = make([]protoimpl.MessageInfo, 15) var file_main_proto_goTypes = []any{ - (*PingReq)(nil), // 0: crabs.evening_detective_server.PingReq - (*PingRsp)(nil), // 1: crabs.evening_detective_server.PingRsp - (*EchoReq)(nil), // 2: crabs.evening_detective_server.EchoReq - (*EchoRsp)(nil), // 3: crabs.evening_detective_server.EchoRsp - (*SignupReq)(nil), // 4: crabs.evening_detective_server.SignupReq - (*SignupRsp)(nil), // 5: crabs.evening_detective_server.SignupRsp - (*RefreshPasswordReq)(nil), // 6: crabs.evening_detective_server.RefreshPasswordReq - (*RefreshPasswordRsp)(nil), // 7: crabs.evening_detective_server.RefreshPasswordRsp - (*LoginReq)(nil), // 8: crabs.evening_detective_server.LoginReq - (*LoginRsp)(nil), // 9: crabs.evening_detective_server.LoginRsp - (*RefreshReq)(nil), // 10: crabs.evening_detective_server.RefreshReq - (*RefreshRsp)(nil), // 11: crabs.evening_detective_server.RefreshRsp + (*PingReq)(nil), // 0: crabs.evening_detective_server.PingReq + (*PingRsp)(nil), // 1: crabs.evening_detective_server.PingRsp + (*EchoReq)(nil), // 2: crabs.evening_detective_server.EchoReq + (*EchoRsp)(nil), // 3: crabs.evening_detective_server.EchoRsp + (*SignupReq)(nil), // 4: crabs.evening_detective_server.SignupReq + (*SignupRsp)(nil), // 5: crabs.evening_detective_server.SignupRsp + (*RefreshPasswordReq)(nil), // 6: crabs.evening_detective_server.RefreshPasswordReq + (*RefreshPasswordRsp)(nil), // 7: crabs.evening_detective_server.RefreshPasswordRsp + (*LoginReq)(nil), // 8: crabs.evening_detective_server.LoginReq + (*LoginRsp)(nil), // 9: crabs.evening_detective_server.LoginRsp + (*RefreshReq)(nil), // 10: crabs.evening_detective_server.RefreshReq + (*RefreshRsp)(nil), // 11: crabs.evening_detective_server.RefreshRsp + (*GetUsersReq)(nil), // 12: crabs.evening_detective_server.GetUsersReq + (*GetUsersRsp)(nil), // 13: crabs.evening_detective_server.GetUsersRsp + (*User)(nil), // 14: crabs.evening_detective_server.User + (*timestamppb.Timestamp)(nil), // 15: google.protobuf.Timestamp } var file_main_proto_depIdxs = []int32{ - 0, // 0: crabs.evening_detective_server.EveningDetectiveServer.Ping:input_type -> crabs.evening_detective_server.PingReq - 2, // 1: crabs.evening_detective_server.EveningDetectiveServer.Echo:input_type -> crabs.evening_detective_server.EchoReq - 4, // 2: crabs.evening_detective_server.EveningDetectiveServer.Signup:input_type -> crabs.evening_detective_server.SignupReq - 6, // 3: crabs.evening_detective_server.EveningDetectiveServer.RefreshPassword:input_type -> crabs.evening_detective_server.RefreshPasswordReq - 8, // 4: crabs.evening_detective_server.EveningDetectiveServer.Login:input_type -> crabs.evening_detective_server.LoginReq - 10, // 5: crabs.evening_detective_server.EveningDetectiveServer.Refresh:input_type -> crabs.evening_detective_server.RefreshReq - 1, // 6: crabs.evening_detective_server.EveningDetectiveServer.Ping:output_type -> crabs.evening_detective_server.PingRsp - 3, // 7: crabs.evening_detective_server.EveningDetectiveServer.Echo:output_type -> crabs.evening_detective_server.EchoRsp - 5, // 8: crabs.evening_detective_server.EveningDetectiveServer.Signup:output_type -> crabs.evening_detective_server.SignupRsp - 7, // 9: crabs.evening_detective_server.EveningDetectiveServer.RefreshPassword:output_type -> crabs.evening_detective_server.RefreshPasswordRsp - 9, // 10: crabs.evening_detective_server.EveningDetectiveServer.Login:output_type -> crabs.evening_detective_server.LoginRsp - 11, // 11: crabs.evening_detective_server.EveningDetectiveServer.Refresh:output_type -> crabs.evening_detective_server.RefreshRsp - 6, // [6:12] is the sub-list for method output_type - 0, // [0:6] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name + 14, // 0: crabs.evening_detective_server.GetUsersRsp.users:type_name -> crabs.evening_detective_server.User + 15, // 1: crabs.evening_detective_server.User.created_at:type_name -> google.protobuf.Timestamp + 0, // 2: crabs.evening_detective_server.EveningDetectiveServer.Ping:input_type -> crabs.evening_detective_server.PingReq + 2, // 3: crabs.evening_detective_server.EveningDetectiveServer.Echo:input_type -> crabs.evening_detective_server.EchoReq + 4, // 4: crabs.evening_detective_server.EveningDetectiveServer.Signup:input_type -> crabs.evening_detective_server.SignupReq + 6, // 5: crabs.evening_detective_server.EveningDetectiveServer.RefreshPassword:input_type -> crabs.evening_detective_server.RefreshPasswordReq + 8, // 6: crabs.evening_detective_server.EveningDetectiveServer.Login:input_type -> crabs.evening_detective_server.LoginReq + 10, // 7: crabs.evening_detective_server.EveningDetectiveServer.Refresh:input_type -> crabs.evening_detective_server.RefreshReq + 12, // 8: crabs.evening_detective_server.EveningDetectiveServer.GetUsers:input_type -> crabs.evening_detective_server.GetUsersReq + 1, // 9: crabs.evening_detective_server.EveningDetectiveServer.Ping:output_type -> crabs.evening_detective_server.PingRsp + 3, // 10: crabs.evening_detective_server.EveningDetectiveServer.Echo:output_type -> crabs.evening_detective_server.EchoRsp + 5, // 11: crabs.evening_detective_server.EveningDetectiveServer.Signup:output_type -> crabs.evening_detective_server.SignupRsp + 7, // 12: crabs.evening_detective_server.EveningDetectiveServer.RefreshPassword:output_type -> crabs.evening_detective_server.RefreshPasswordRsp + 9, // 13: crabs.evening_detective_server.EveningDetectiveServer.Login:output_type -> crabs.evening_detective_server.LoginRsp + 11, // 14: crabs.evening_detective_server.EveningDetectiveServer.Refresh:output_type -> crabs.evening_detective_server.RefreshRsp + 13, // 15: crabs.evening_detective_server.EveningDetectiveServer.GetUsers:output_type -> crabs.evening_detective_server.GetUsersRsp + 9, // [9:16] is the sub-list for method output_type + 2, // [2:9] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name } func init() { file_main_proto_init() } @@ -685,7 +900,7 @@ func file_main_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_main_proto_rawDesc), len(file_main_proto_rawDesc)), NumEnums: 0, - NumMessages: 12, + NumMessages: 15, NumExtensions: 0, NumServices: 1, }, diff --git a/proto/main.pb.gw.go b/proto/main.pb.gw.go index 4af4d16..eb64ba9 100644 --- a/proto/main.pb.gw.go +++ b/proto/main.pb.gw.go @@ -199,6 +199,27 @@ func local_request_EveningDetectiveServer_Refresh_0(ctx context.Context, marshal return msg, metadata, err } +func request_EveningDetectiveServer_GetUsers_0(ctx context.Context, marshaler runtime.Marshaler, client EveningDetectiveServerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq GetUsersReq + metadata runtime.ServerMetadata + ) + if req.Body != nil { + _, _ = io.Copy(io.Discard, req.Body) + } + msg, err := client.GetUsers(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_EveningDetectiveServer_GetUsers_0(ctx context.Context, marshaler runtime.Marshaler, server EveningDetectiveServerServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq GetUsersReq + metadata runtime.ServerMetadata + ) + msg, err := server.GetUsers(ctx, &protoReq) + return msg, metadata, err +} + // RegisterEveningDetectiveServerHandlerServer registers the http handlers for service EveningDetectiveServer to "mux". // UnaryRPC :call EveningDetectiveServerServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -325,6 +346,26 @@ func RegisterEveningDetectiveServerHandlerServer(ctx context.Context, mux *runti } forward_EveningDetectiveServer_Refresh_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) + mux.Handle(http.MethodGet, pattern_EveningDetectiveServer_GetUsers_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/crabs.evening_detective_server.EveningDetectiveServer/GetUsers", runtime.WithHTTPPathPattern("/api/users")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_EveningDetectiveServer_GetUsers_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_EveningDetectiveServer_GetUsers_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) return nil } @@ -467,6 +508,23 @@ func RegisterEveningDetectiveServerHandlerClient(ctx context.Context, mux *runti } forward_EveningDetectiveServer_Refresh_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) + mux.Handle(http.MethodGet, pattern_EveningDetectiveServer_GetUsers_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/crabs.evening_detective_server.EveningDetectiveServer/GetUsers", runtime.WithHTTPPathPattern("/api/users")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_EveningDetectiveServer_GetUsers_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_EveningDetectiveServer_GetUsers_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) return nil } @@ -477,6 +535,7 @@ var ( pattern_EveningDetectiveServer_RefreshPassword_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"api", "refresh-password"}, "")) pattern_EveningDetectiveServer_Login_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"api", "login"}, "")) pattern_EveningDetectiveServer_Refresh_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"api", "refresh"}, "")) + pattern_EveningDetectiveServer_GetUsers_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"api", "users"}, "")) ) var ( @@ -486,4 +545,5 @@ var ( forward_EveningDetectiveServer_RefreshPassword_0 = runtime.ForwardResponseMessage forward_EveningDetectiveServer_Login_0 = runtime.ForwardResponseMessage forward_EveningDetectiveServer_Refresh_0 = runtime.ForwardResponseMessage + forward_EveningDetectiveServer_GetUsers_0 = runtime.ForwardResponseMessage ) diff --git a/proto/main_grpc.pb.go b/proto/main_grpc.pb.go index 2e31ac0..2e4706d 100644 --- a/proto/main_grpc.pb.go +++ b/proto/main_grpc.pb.go @@ -25,6 +25,7 @@ const ( EveningDetectiveServer_RefreshPassword_FullMethodName = "/crabs.evening_detective_server.EveningDetectiveServer/RefreshPassword" EveningDetectiveServer_Login_FullMethodName = "/crabs.evening_detective_server.EveningDetectiveServer/Login" EveningDetectiveServer_Refresh_FullMethodName = "/crabs.evening_detective_server.EveningDetectiveServer/Refresh" + EveningDetectiveServer_GetUsers_FullMethodName = "/crabs.evening_detective_server.EveningDetectiveServer/GetUsers" ) // EveningDetectiveServerClient is the client API for EveningDetectiveServer service. @@ -37,6 +38,7 @@ type EveningDetectiveServerClient interface { RefreshPassword(ctx context.Context, in *RefreshPasswordReq, opts ...grpc.CallOption) (*RefreshPasswordRsp, error) Login(ctx context.Context, in *LoginReq, opts ...grpc.CallOption) (*LoginRsp, error) Refresh(ctx context.Context, in *RefreshReq, opts ...grpc.CallOption) (*RefreshRsp, error) + GetUsers(ctx context.Context, in *GetUsersReq, opts ...grpc.CallOption) (*GetUsersRsp, error) } type eveningDetectiveServerClient struct { @@ -107,6 +109,16 @@ func (c *eveningDetectiveServerClient) Refresh(ctx context.Context, in *RefreshR return out, nil } +func (c *eveningDetectiveServerClient) GetUsers(ctx context.Context, in *GetUsersReq, opts ...grpc.CallOption) (*GetUsersRsp, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetUsersRsp) + err := c.cc.Invoke(ctx, EveningDetectiveServer_GetUsers_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + // EveningDetectiveServerServer is the server API for EveningDetectiveServer service. // All implementations must embed UnimplementedEveningDetectiveServerServer // for forward compatibility. @@ -117,6 +129,7 @@ type EveningDetectiveServerServer interface { RefreshPassword(context.Context, *RefreshPasswordReq) (*RefreshPasswordRsp, error) Login(context.Context, *LoginReq) (*LoginRsp, error) Refresh(context.Context, *RefreshReq) (*RefreshRsp, error) + GetUsers(context.Context, *GetUsersReq) (*GetUsersRsp, error) mustEmbedUnimplementedEveningDetectiveServerServer() } @@ -145,6 +158,9 @@ func (UnimplementedEveningDetectiveServerServer) Login(context.Context, *LoginRe func (UnimplementedEveningDetectiveServerServer) Refresh(context.Context, *RefreshReq) (*RefreshRsp, error) { return nil, status.Error(codes.Unimplemented, "method Refresh not implemented") } +func (UnimplementedEveningDetectiveServerServer) GetUsers(context.Context, *GetUsersReq) (*GetUsersRsp, error) { + return nil, status.Error(codes.Unimplemented, "method GetUsers not implemented") +} func (UnimplementedEveningDetectiveServerServer) mustEmbedUnimplementedEveningDetectiveServerServer() { } func (UnimplementedEveningDetectiveServerServer) testEmbeddedByValue() {} @@ -275,6 +291,24 @@ func _EveningDetectiveServer_Refresh_Handler(srv interface{}, ctx context.Contex return interceptor(ctx, in, info, handler) } +func _EveningDetectiveServer_GetUsers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetUsersReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(EveningDetectiveServerServer).GetUsers(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: EveningDetectiveServer_GetUsers_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(EveningDetectiveServerServer).GetUsers(ctx, req.(*GetUsersReq)) + } + return interceptor(ctx, in, info, handler) +} + // EveningDetectiveServer_ServiceDesc is the grpc.ServiceDesc for EveningDetectiveServer service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -306,6 +340,10 @@ var EveningDetectiveServer_ServiceDesc = grpc.ServiceDesc{ MethodName: "Refresh", Handler: _EveningDetectiveServer_Refresh_Handler, }, + { + MethodName: "GetUsers", + Handler: _EveningDetectiveServer_GetUsers_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "main.proto",