add auth #16
@ -58,9 +58,8 @@ message User {
 | 
			
		||||
 | 
			
		||||
message CreateCategoryReq {
 | 
			
		||||
  string name = 1;
 | 
			
		||||
  int32 user_id = 2;
 | 
			
		||||
  bool favorite = 3;
 | 
			
		||||
  int32 monthly_limit = 4;
 | 
			
		||||
  bool favorite = 2;
 | 
			
		||||
  int32 monthly_limit = 3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message UpdateCategoryReq {
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,7 @@ import (
 | 
			
		||||
	"github.com/jackc/pgx/v5/pgxpool"
 | 
			
		||||
	"google.golang.org/grpc"
 | 
			
		||||
	"google.golang.org/grpc/credentials/insecure"
 | 
			
		||||
	"google.golang.org/grpc/metadata"
 | 
			
		||||
 | 
			
		||||
	proto "git.3crabs.ru/save_my_money/smm_core/proto"
 | 
			
		||||
)
 | 
			
		||||
@ -56,7 +57,12 @@ func main() {
 | 
			
		||||
		log.Fatalln("Failed to dial server:", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mux := runtime.NewServeMux()
 | 
			
		||||
	mux := runtime.NewServeMux(
 | 
			
		||||
		runtime.WithMetadata(func(ctx context.Context, request *http.Request) metadata.MD {
 | 
			
		||||
			header := request.Header.Get("User-Id")
 | 
			
		||||
			return metadata.Pairs("user-id", header)
 | 
			
		||||
		}),
 | 
			
		||||
	)
 | 
			
		||||
	// Register Greeter
 | 
			
		||||
	err = proto.RegisterSmmCoreHandler(context.Background(), mux, conn)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,7 @@ import (
 | 
			
		||||
	"errors"
 | 
			
		||||
 | 
			
		||||
	"git.3crabs.ru/save_my_money/smm_core/internal/services/category"
 | 
			
		||||
	"git.3crabs.ru/save_my_money/smm_core/internal/services/context_utils"
 | 
			
		||||
	"git.3crabs.ru/save_my_money/smm_core/internal/services/user"
 | 
			
		||||
	proto "git.3crabs.ru/save_my_money/smm_core/proto"
 | 
			
		||||
	"google.golang.org/grpc/codes"
 | 
			
		||||
@ -55,12 +56,17 @@ func (s *Server) AddCategory(ctx context.Context, req *proto.CreateCategoryReq)
 | 
			
		||||
		ctx,
 | 
			
		||||
		&category.CategoryEntity{
 | 
			
		||||
			Name:         req.Name,
 | 
			
		||||
			UserId:       int(req.UserId),
 | 
			
		||||
			Favorite:     req.Favorite,
 | 
			
		||||
			MonthlyLimit: int(req.MonthlyLimit),
 | 
			
		||||
		},
 | 
			
		||||
	)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if errors.Is(err, &context_utils.UnauthorizedErr{}) {
 | 
			
		||||
			return nil, status.Error(codes.Unauthenticated, "Клиент не авторизован")
 | 
			
		||||
		}
 | 
			
		||||
		if errors.Is(err, &category.CategoryAlreadyExistsErr{}) {
 | 
			
		||||
			return nil, status.Error(codes.AlreadyExists, "Категория с таким именеи уже существует")
 | 
			
		||||
		}
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &proto.Category{
 | 
			
		||||
 | 
			
		||||
@ -2,9 +2,12 @@ package category
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"git.3crabs.ru/save_my_money/smm_core/internal/services/context_utils"
 | 
			
		||||
	"github.com/jackc/pgx/v5"
 | 
			
		||||
	"github.com/jackc/pgx/v5/pgconn"
 | 
			
		||||
	"github.com/jackc/pgx/v5/pgxpool"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -16,6 +19,12 @@ type CategoryEntity struct {
 | 
			
		||||
	MonthlyLimit int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type CategoryAlreadyExistsErr struct{}
 | 
			
		||||
 | 
			
		||||
func (e *CategoryAlreadyExistsErr) Error() string {
 | 
			
		||||
	return "category already exists error"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type CategoryService struct {
 | 
			
		||||
	db *pgxpool.Pool
 | 
			
		||||
}
 | 
			
		||||
@ -29,15 +38,24 @@ func NewCategoryService(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *CategoryService) AddCategory(ctx context.Context, category *CategoryEntity) (*CategoryEntity, error) {
 | 
			
		||||
	query := `INSERT INTO categories (name, user_id, favorite, monthly_limit) VALUES (@name, @user_id, @favorite, @monthly_limit)`
 | 
			
		||||
	userId, err := context_utils.GetUserId(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	query := `INSERT INTO categories (name, user_id, favorite, monthly_limit) VALUES (@name, @user_id, @favorite, @monthly_limit) RETURNING id`
 | 
			
		||||
	args := pgx.NamedArgs{
 | 
			
		||||
		"name":          category.Name,
 | 
			
		||||
		"user_id":       1,
 | 
			
		||||
		"user_id":       userId,
 | 
			
		||||
		"favorite":      category.Favorite,
 | 
			
		||||
		"monthly_limit": category.MonthlyLimit,
 | 
			
		||||
	}
 | 
			
		||||
	_, err := s.db.Exec(ctx, query, args)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
	if err := s.db.QueryRow(ctx, query, args).Scan(&category.Id); err != nil {
 | 
			
		||||
		var pgErr *pgconn.PgError
 | 
			
		||||
		if errors.As(err, &pgErr) {
 | 
			
		||||
			if pgErr.Code == "23505" && pgErr.ConstraintName == "categories_user_id_name_key" { // unique_violation
 | 
			
		||||
				return nil, &CategoryAlreadyExistsErr{}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return nil, fmt.Errorf("unable to insert row: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	return category, nil
 | 
			
		||||
 | 
			
		||||
@ -1 +0,0 @@
 | 
			
		||||
[{"kind":1,"language":"markdown","value":"# Добавление категории","outputs":[]},{"kind":2,"language":"rest-book","value":"POST http://localhost:8090/categories\n\n{\n    \"name\": \"Продукты питания\",\n    \"favorite\": true,\n    \"monthlyLimit\": 5000\n}","outputs":[{"mime":"x-application/rest-book","value":{"status":200,"statusText":"OK","headers":{"Date":"Tue, 19 Nov 2024 15:43:01 GMT","Content-Type":"application/json","Content-Length":"88"},"config":{"timeout":10000,"xsrfCookieName":"XSRF-TOKEN","xsrfHeaderName":"X-XSRF-TOKEN","headers":{"Accept":"application/json, text/plain, */*","Content-Type":"application/json","User-Agent":"rest-book","Content-Length":78}},"request":{"method":"POST","httpVersion":"1.1","responseUrl":"http://localhost:8090/categories","timeout":10000,"headers":{"User-Agent":"rest-book"},"data":{"name":"Продукты питания","favorite":true,"monthlyLimit":5000}},"data":{"id":0,"name":"Продукты питания","favorite":true,"monthlyLimit":5000}}},{"mime":"text/x-json","value":{"status":200,"statusText":"OK","headers":{"Date":"Tue, 19 Nov 2024 15:43:01 GMT","Content-Type":"application/json","Content-Length":"88"},"config":{"timeout":10000,"xsrfCookieName":"XSRF-TOKEN","xsrfHeaderName":"X-XSRF-TOKEN","headers":{"Accept":"application/json, text/plain, */*","Content-Type":"application/json","User-Agent":"rest-book","Content-Length":78}},"request":{"method":"POST","httpVersion":"1.1","responseUrl":"http://localhost:8090/categories","timeout":10000,"headers":{"User-Agent":"rest-book"},"data":{"name":"Продукты питания","favorite":true,"monthlyLimit":5000}},"data":{"id":0,"name":"Продукты питания","favorite":true,"monthlyLimit":5000}}},{"mime":"text/html","value":"[object Object]"}]}]
 | 
			
		||||
							
								
								
									
										26
									
								
								internal/services/context_utils/get_user_id.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								internal/services/context_utils/get_user_id.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
			
		||||
package context_utils
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"strconv"
 | 
			
		||||
 | 
			
		||||
	"google.golang.org/grpc/metadata"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type UnauthorizedErr struct{}
 | 
			
		||||
 | 
			
		||||
func (e *UnauthorizedErr) Error() string {
 | 
			
		||||
	return "user not autorize"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetUserId(ctx context.Context) (int, error) {
 | 
			
		||||
	md, ok := metadata.FromIncomingContext(ctx)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return 0, &UnauthorizedErr{}
 | 
			
		||||
	}
 | 
			
		||||
	id, err := strconv.Atoi(md["user-id"][0])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, &UnauthorizedErr{}
 | 
			
		||||
	}
 | 
			
		||||
	return id, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								internal/services/requests.restbook
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								internal/services/requests.restbook
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
[{"kind":1,"language":"markdown","value":"# Добавление пользователя","outputs":[]},{"kind":2,"language":"rest-book","value":"POST http://localhost:8090/users\n\n{\n    \"username\": \"foo\"\n}","outputs":[{"mime":"x-application/rest-book","value":{"status":200,"statusText":"OK","headers":{"Date":"Wed, 20 Nov 2024 16:14:45 GMT","Content-Type":"application/json","Content-Length":"25"},"config":{"timeout":10000,"xsrfCookieName":"XSRF-TOKEN","xsrfHeaderName":"X-XSRF-TOKEN","headers":{"Accept":"application/json, text/plain, */*","Content-Type":"application/json","User-Agent":"rest-book","Content-Length":18}},"request":{"method":"POST","httpVersion":"1.1","responseUrl":"http://localhost:8090/users","timeout":10000,"headers":{"User-Agent":"rest-book"},"data":{"username":"foo"}},"data":{"id":1,"username":"foo"}}},{"mime":"text/x-json","value":{"status":200,"statusText":"OK","headers":{"Date":"Wed, 20 Nov 2024 16:14:45 GMT","Content-Type":"application/json","Content-Length":"25"},"config":{"timeout":10000,"xsrfCookieName":"XSRF-TOKEN","xsrfHeaderName":"X-XSRF-TOKEN","headers":{"Accept":"application/json, text/plain, */*","Content-Type":"application/json","User-Agent":"rest-book","Content-Length":18}},"request":{"method":"POST","httpVersion":"1.1","responseUrl":"http://localhost:8090/users","timeout":10000,"headers":{"User-Agent":"rest-book"},"data":{"username":"foo"}},"data":{"id":1,"username":"foo"}}},{"mime":"text/html","value":"[object Object]"}]},{"kind":1,"language":"markdown","value":"# Добавление категории","outputs":[]},{"kind":2,"language":"rest-book","value":"POST http://localhost:8090/categories\nUser-Id: 1\n\n{\n    \"name\": \"Продукты питания\"\n}","outputs":[{"mime":"x-application/rest-book","value":{"status":500,"statusText":"Internal Server Error","headers":{"Date":"Wed, 20 Nov 2024 16:25:26 GMT","Content-Type":"application/json","Content-Length":"184"},"config":{"timeout":10000,"xsrfCookieName":"XSRF-TOKEN","xsrfHeaderName":"X-XSRF-TOKEN","headers":{"Accept":"application/json, text/plain, */*","Content-Type":"application/json","User-Id":"2","User-Agent":"axios/0.21.4","Content-Length":42}},"request":{"method":"POST","httpVersion":"1.1","responseUrl":"http://localhost:8090/categories","timeout":10000,"headers":{"User-Id":"2"},"data":{"name":"Продукты питания"}},"data":{"code":2,"message":"unable to insert row: ERROR: insert or update on table \"categories\" violates foreign key constraint \"categories_user_id_fkey\" (SQLSTATE 23503)","details":[]}}},{"mime":"text/x-json","value":{"status":500,"statusText":"Internal Server Error","headers":{"Date":"Wed, 20 Nov 2024 16:25:26 GMT","Content-Type":"application/json","Content-Length":"184"},"config":{"timeout":10000,"xsrfCookieName":"XSRF-TOKEN","xsrfHeaderName":"X-XSRF-TOKEN","headers":{"Accept":"application/json, text/plain, */*","Content-Type":"application/json","User-Id":"2","User-Agent":"axios/0.21.4","Content-Length":42}},"request":{"method":"POST","httpVersion":"1.1","responseUrl":"http://localhost:8090/categories","timeout":10000,"headers":{"User-Id":"2"},"data":{"name":"Продукты питания"}},"data":{"code":2,"message":"unable to insert row: ERROR: insert or update on table \"categories\" violates foreign key constraint \"categories_user_id_fkey\" (SQLSTATE 23503)","details":[]}}},{"mime":"text/html","value":"[object Object]"}]}]
 | 
			
		||||
@ -1 +0,0 @@
 | 
			
		||||
[{"kind":1,"language":"markdown","value":"# Добавление пользователя","outputs":[]},{"kind":2,"language":"rest-book","value":"POST http://localhost:8090/users\n\n{\n    \"username\": \"foo\"\n}","outputs":[{"mime":"x-application/rest-book","value":{"status":409,"statusText":"Conflict","headers":{"Date":"Wed, 20 Nov 2024 08:30:32 GMT","Content-Type":"application/json","Content-Length":"111"},"config":{"timeout":10000,"xsrfCookieName":"XSRF-TOKEN","xsrfHeaderName":"X-XSRF-TOKEN","headers":{"Accept":"application/json, text/plain, */*","Content-Type":"application/json","User-Agent":"rest-book","Content-Length":18}},"request":{"method":"POST","httpVersion":"1.1","responseUrl":"http://localhost:8090/users","timeout":10000,"headers":{"User-Agent":"rest-book"},"data":{"username":"bar"}},"data":{"code":6,"message":"Пользователь с таким username уже существует","details":[]}}},{"mime":"text/x-json","value":{"status":409,"statusText":"Conflict","headers":{"Date":"Wed, 20 Nov 2024 08:30:32 GMT","Content-Type":"application/json","Content-Length":"111"},"config":{"timeout":10000,"xsrfCookieName":"XSRF-TOKEN","xsrfHeaderName":"X-XSRF-TOKEN","headers":{"Accept":"application/json, text/plain, */*","Content-Type":"application/json","User-Agent":"rest-book","Content-Length":18}},"request":{"method":"POST","httpVersion":"1.1","responseUrl":"http://localhost:8090/users","timeout":10000,"headers":{"User-Agent":"rest-book"},"data":{"username":"bar"}},"data":{"code":6,"message":"Пользователь с таким username уже существует","details":[]}}},{"mime":"text/html","value":"[object Object]"}]}]
 | 
			
		||||
@ -5,7 +5,8 @@ CREATE TABLE IF NOT EXISTS categories (
 | 
			
		||||
    name TEXT NOT NULL,
 | 
			
		||||
    user_id INT REFERENCES users(id),
 | 
			
		||||
    favorite BOOLEAN DEFAULT FALSE,
 | 
			
		||||
    monthly_limit INT DEFAULT 0
 | 
			
		||||
    monthly_limit INT DEFAULT 0,
 | 
			
		||||
    UNIQUE (user_id, name)
 | 
			
		||||
| 
					
	
	
	
	
	
	
	
	 | 
			||||
);
 | 
			
		||||
-- +goose StatementEnd
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -198,9 +198,8 @@ type CreateCategoryReq struct {
 | 
			
		||||
	unknownFields protoimpl.UnknownFields
 | 
			
		||||
 | 
			
		||||
	Name         string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
 | 
			
		||||
	UserId       int32  `protobuf:"varint,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
 | 
			
		||||
	Favorite     bool   `protobuf:"varint,3,opt,name=favorite,proto3" json:"favorite,omitempty"`
 | 
			
		||||
	MonthlyLimit int32  `protobuf:"varint,4,opt,name=monthly_limit,json=monthlyLimit,proto3" json:"monthly_limit,omitempty"`
 | 
			
		||||
	Favorite     bool   `protobuf:"varint,2,opt,name=favorite,proto3" json:"favorite,omitempty"`
 | 
			
		||||
	MonthlyLimit int32  `protobuf:"varint,3,opt,name=monthly_limit,json=monthlyLimit,proto3" json:"monthly_limit,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *CreateCategoryReq) Reset() {
 | 
			
		||||
@ -240,13 +239,6 @@ func (x *CreateCategoryReq) GetName() string {
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *CreateCategoryReq) GetUserId() int32 {
 | 
			
		||||
	if x != nil {
 | 
			
		||||
		return x.UserId
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *CreateCategoryReq) GetFavorite() bool {
 | 
			
		||||
	if x != nil {
 | 
			
		||||
		return x.Favorite
 | 
			
		||||
@ -506,68 +498,67 @@ var file_smm_core_proto_rawDesc = []byte{
 | 
			
		||||
	0x65, 0x22, 0x32, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18,
 | 
			
		||||
	0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65,
 | 
			
		||||
	0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65,
 | 
			
		||||
	0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x81, 0x01, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
 | 
			
		||||
	0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x6e,
 | 
			
		||||
	0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
 | 
			
		||||
	0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05,
 | 
			
		||||
	0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x61, 0x76, 0x6f,
 | 
			
		||||
	0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x68, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43,
 | 
			
		||||
	0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
 | 
			
		||||
	0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a,
 | 
			
		||||
	0x0a, 0x08, 0x66, 0x61, 0x76, 0x6f, 0x72, 0x69, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08,
 | 
			
		||||
	0x52, 0x08, 0x66, 0x61, 0x76, 0x6f, 0x72, 0x69, 0x74, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x6f,
 | 
			
		||||
	0x6e, 0x74, 0x68, 0x6c, 0x79, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28,
 | 
			
		||||
	0x05, 0x52, 0x0c, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x6c, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x22,
 | 
			
		||||
	0x78, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72,
 | 
			
		||||
	0x79, 0x52, 0x65, 0x71, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05,
 | 
			
		||||
	0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01,
 | 
			
		||||
	0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x61, 0x76, 0x6f,
 | 
			
		||||
	0x72, 0x69, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x66, 0x61, 0x76, 0x6f,
 | 
			
		||||
	0x72, 0x69, 0x74, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x6c, 0x79, 0x5f,
 | 
			
		||||
	0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6d, 0x6f, 0x6e,
 | 
			
		||||
	0x74, 0x68, 0x6c, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0x78, 0x0a, 0x11, 0x55, 0x70, 0x64,
 | 
			
		||||
	0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x12, 0x0e,
 | 
			
		||||
	0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12,
 | 
			
		||||
	0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
 | 
			
		||||
	0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x61, 0x76, 0x6f, 0x72, 0x69, 0x74, 0x65, 0x18, 0x03,
 | 
			
		||||
	0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x66, 0x61, 0x76, 0x6f, 0x72, 0x69, 0x74, 0x65, 0x12, 0x23,
 | 
			
		||||
	0x0a, 0x0d, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x6c, 0x79, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18,
 | 
			
		||||
	0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x6c, 0x79, 0x4c, 0x69,
 | 
			
		||||
	0x6d, 0x69, 0x74, 0x22, 0x6f, 0x0a, 0x08, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x12,
 | 
			
		||||
	0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12,
 | 
			
		||||
	0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e,
 | 
			
		||||
	0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x61, 0x76, 0x6f, 0x72, 0x69, 0x74, 0x65, 0x18,
 | 
			
		||||
	0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x66, 0x61, 0x76, 0x6f, 0x72, 0x69, 0x74, 0x65, 0x12,
 | 
			
		||||
	0x23, 0x0a, 0x0d, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x6c, 0x79, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74,
 | 
			
		||||
	0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x6c, 0x79, 0x4c,
 | 
			
		||||
	0x69, 0x6d, 0x69, 0x74, 0x22, 0x2f, 0x0a, 0x11, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79,
 | 
			
		||||
	0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x61, 0x76,
 | 
			
		||||
	0x6f, 0x72, 0x69, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x66, 0x61, 0x76,
 | 
			
		||||
	0x6f, 0x72, 0x69, 0x74, 0x65, 0x22, 0x46, 0x0a, 0x0a, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72,
 | 
			
		||||
	0x69, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65,
 | 
			
		||||
	0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x72, 0x61, 0x62, 0x73, 0x2e,
 | 
			
		||||
	0x73, 0x6d, 0x6d, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72,
 | 
			
		||||
	0x79, 0x52, 0x0a, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x32, 0xd7, 0x03,
 | 
			
		||||
	0x0a, 0x07, 0x53, 0x6d, 0x6d, 0x43, 0x6f, 0x72, 0x65, 0x12, 0x47, 0x0a, 0x04, 0x50, 0x69, 0x6e,
 | 
			
		||||
	0x67, 0x12, 0x17, 0x2e, 0x63, 0x72, 0x61, 0x62, 0x73, 0x2e, 0x73, 0x6d, 0x6d, 0x5f, 0x63, 0x6f,
 | 
			
		||||
	0x72, 0x65, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x1a, 0x17, 0x2e, 0x63, 0x72, 0x61,
 | 
			
		||||
	0x62, 0x73, 0x2e, 0x73, 0x6d, 0x6d, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x50, 0x69, 0x6e, 0x67,
 | 
			
		||||
	0x52, 0x73, 0x70, 0x22, 0x0d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x07, 0x12, 0x05, 0x2f, 0x70, 0x69,
 | 
			
		||||
	0x6e, 0x67, 0x12, 0x51, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1d, 0x2e,
 | 
			
		||||
	0x74, 0x68, 0x6c, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0x6f, 0x0a, 0x08, 0x43, 0x61, 0x74,
 | 
			
		||||
	0x65, 0x67, 0x6f, 0x72, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
 | 
			
		||||
	0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20,
 | 
			
		||||
	0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x61, 0x76,
 | 
			
		||||
	0x6f, 0x72, 0x69, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x66, 0x61, 0x76,
 | 
			
		||||
	0x6f, 0x72, 0x69, 0x74, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x6c, 0x79,
 | 
			
		||||
	0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6d, 0x6f,
 | 
			
		||||
	0x6e, 0x74, 0x68, 0x6c, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0x2f, 0x0a, 0x11, 0x43, 0x61,
 | 
			
		||||
	0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x12,
 | 
			
		||||
	0x1a, 0x0a, 0x08, 0x66, 0x61, 0x76, 0x6f, 0x72, 0x69, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
 | 
			
		||||
	0x08, 0x52, 0x08, 0x66, 0x61, 0x76, 0x6f, 0x72, 0x69, 0x74, 0x65, 0x22, 0x46, 0x0a, 0x0a, 0x43,
 | 
			
		||||
	0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x0a, 0x63, 0x61, 0x74,
 | 
			
		||||
	0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e,
 | 
			
		||||
	0x63, 0x72, 0x61, 0x62, 0x73, 0x2e, 0x73, 0x6d, 0x6d, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x43,
 | 
			
		||||
	0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x14, 0x2e, 0x63,
 | 
			
		||||
	0x72, 0x61, 0x62, 0x73, 0x2e, 0x73, 0x6d, 0x6d, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x55, 0x73,
 | 
			
		||||
	0x65, 0x72, 0x22, 0x11, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0b, 0x3a, 0x01, 0x2a, 0x22, 0x06, 0x2f,
 | 
			
		||||
	0x75, 0x73, 0x65, 0x72, 0x73, 0x12, 0x62, 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x43, 0x61, 0x74, 0x65,
 | 
			
		||||
	0x67, 0x6f, 0x72, 0x79, 0x12, 0x21, 0x2e, 0x63, 0x72, 0x61, 0x62, 0x73, 0x2e, 0x73, 0x6d, 0x6d,
 | 
			
		||||
	0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x65,
 | 
			
		||||
	0x67, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x18, 0x2e, 0x63, 0x72, 0x61, 0x62, 0x73, 0x2e,
 | 
			
		||||
	0x73, 0x6d, 0x6d, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72,
 | 
			
		||||
	0x79, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x3a, 0x01, 0x2a, 0x22, 0x0b, 0x2f, 0x63,
 | 
			
		||||
	0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x12, 0x67, 0x0a, 0x0e, 0x55, 0x70, 0x64,
 | 
			
		||||
	0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x12, 0x21, 0x2e, 0x63, 0x72,
 | 
			
		||||
	0x61, 0x62, 0x73, 0x2e, 0x73, 0x6d, 0x6d, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x55, 0x70, 0x64,
 | 
			
		||||
	0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x18,
 | 
			
		||||
	0x2e, 0x63, 0x72, 0x61, 0x62, 0x73, 0x2e, 0x73, 0x6d, 0x6d, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e,
 | 
			
		||||
	0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12,
 | 
			
		||||
	0x1a, 0x10, 0x2f, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x7b, 0x69,
 | 
			
		||||
	0x64, 0x7d, 0x12, 0x63, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72,
 | 
			
		||||
	0x69, 0x65, 0x73, 0x12, 0x21, 0x2e, 0x63, 0x72, 0x61, 0x62, 0x73, 0x2e, 0x73, 0x6d, 0x6d, 0x5f,
 | 
			
		||||
	0x63, 0x6f, 0x72, 0x65, 0x2e, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x46, 0x69, 0x6c,
 | 
			
		||||
	0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x1a, 0x2e, 0x63, 0x72, 0x61, 0x62, 0x73, 0x2e, 0x73,
 | 
			
		||||
	0x6d, 0x6d, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69,
 | 
			
		||||
	0x65, 0x73, 0x22, 0x13, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, 0x12, 0x0b, 0x2f, 0x63, 0x61, 0x74,
 | 
			
		||||
	0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x42, 0x0e, 0x92, 0x41, 0x00, 0x5a, 0x09, 0x70, 0x6b,
 | 
			
		||||
	0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 | 
			
		||||
	0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x52, 0x0a, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72,
 | 
			
		||||
	0x69, 0x65, 0x73, 0x32, 0xd7, 0x03, 0x0a, 0x07, 0x53, 0x6d, 0x6d, 0x43, 0x6f, 0x72, 0x65, 0x12,
 | 
			
		||||
	0x47, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x17, 0x2e, 0x63, 0x72, 0x61, 0x62, 0x73, 0x2e,
 | 
			
		||||
	0x73, 0x6d, 0x6d, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71,
 | 
			
		||||
	0x1a, 0x17, 0x2e, 0x63, 0x72, 0x61, 0x62, 0x73, 0x2e, 0x73, 0x6d, 0x6d, 0x5f, 0x63, 0x6f, 0x72,
 | 
			
		||||
	0x65, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x73, 0x70, 0x22, 0x0d, 0x82, 0xd3, 0xe4, 0x93, 0x02,
 | 
			
		||||
	0x07, 0x12, 0x05, 0x2f, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x51, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x55,
 | 
			
		||||
	0x73, 0x65, 0x72, 0x12, 0x1d, 0x2e, 0x63, 0x72, 0x61, 0x62, 0x73, 0x2e, 0x73, 0x6d, 0x6d, 0x5f,
 | 
			
		||||
	0x63, 0x6f, 0x72, 0x65, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52,
 | 
			
		||||
	0x65, 0x71, 0x1a, 0x14, 0x2e, 0x63, 0x72, 0x61, 0x62, 0x73, 0x2e, 0x73, 0x6d, 0x6d, 0x5f, 0x63,
 | 
			
		||||
	0x6f, 0x72, 0x65, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x22, 0x11, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0b,
 | 
			
		||||
	0x3a, 0x01, 0x2a, 0x22, 0x06, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x12, 0x62, 0x0a, 0x0b, 0x41,
 | 
			
		||||
	0x64, 0x64, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x12, 0x21, 0x2e, 0x63, 0x72, 0x61,
 | 
			
		||||
	0x62, 0x73, 0x2e, 0x73, 0x6d, 0x6d, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x43, 0x72, 0x65, 0x61,
 | 
			
		||||
	0x74, 0x65, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x18, 0x2e,
 | 
			
		||||
	0x63, 0x72, 0x61, 0x62, 0x73, 0x2e, 0x73, 0x6d, 0x6d, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x43,
 | 
			
		||||
	0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x3a,
 | 
			
		||||
	0x01, 0x2a, 0x22, 0x0b, 0x2f, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x12,
 | 
			
		||||
	0x67, 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72,
 | 
			
		||||
	0x79, 0x12, 0x21, 0x2e, 0x63, 0x72, 0x61, 0x62, 0x73, 0x2e, 0x73, 0x6d, 0x6d, 0x5f, 0x63, 0x6f,
 | 
			
		||||
	0x72, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72,
 | 
			
		||||
	0x79, 0x52, 0x65, 0x71, 0x1a, 0x18, 0x2e, 0x63, 0x72, 0x61, 0x62, 0x73, 0x2e, 0x73, 0x6d, 0x6d,
 | 
			
		||||
	0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x22, 0x18,
 | 
			
		||||
	0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x1a, 0x10, 0x2f, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72,
 | 
			
		||||
	0x69, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x63, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x43,
 | 
			
		||||
	0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x12, 0x21, 0x2e, 0x63, 0x72, 0x61, 0x62,
 | 
			
		||||
	0x73, 0x2e, 0x73, 0x6d, 0x6d, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x43, 0x61, 0x74, 0x65, 0x67,
 | 
			
		||||
	0x6f, 0x72, 0x79, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x1a, 0x2e, 0x63,
 | 
			
		||||
	0x72, 0x61, 0x62, 0x73, 0x2e, 0x73, 0x6d, 0x6d, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x43, 0x61,
 | 
			
		||||
	0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x22, 0x13, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d,
 | 
			
		||||
	0x12, 0x0b, 0x2f, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x42, 0x0e, 0x92,
 | 
			
		||||
	0x41, 0x00, 0x5a, 0x09, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70,
 | 
			
		||||
	0x72, 0x6f, 0x74, 0x6f, 0x33,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
 | 
			
		||||
@ -249,10 +249,6 @@
 | 
			
		||||
        "name": {
 | 
			
		||||
          "type": "string"
 | 
			
		||||
        },
 | 
			
		||||
        "userId": {
 | 
			
		||||
          "type": "integer",
 | 
			
		||||
          "format": "int32"
 | 
			
		||||
        },
 | 
			
		||||
        "favorite": {
 | 
			
		||||
          "type": "boolean"
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	
неправильно ты как-то миграциями пользуешься, она же на проде не применится