forked from ebhomengo/niki
Merge pull request 'mehdikeshavarz/feat/accountDomain And driverDomain #285 #261' (#298) from mehdikeshavarz/driver(agent)/loginorregister into develop
Reviewed-on: ebhomengo/niki#298
This commit is contained in:
commit
850bb3f3e0
|
|
@ -19,7 +19,7 @@ FROM alpine:3.20 AS runtime
|
||||||
# Copy the binary from the builder stage
|
# Copy the binary from the builder stage
|
||||||
COPY --from=builder /niki/niki .
|
COPY --from=builder /niki/niki .
|
||||||
|
|
||||||
# Copy migration files
|
# Copy migrations files
|
||||||
COPY --from=builder /niki/repository/mysql/migration ./repository/mysql/migration
|
COPY --from=builder /niki/repository/mysql/migration ./repository/mysql/migration
|
||||||
|
|
||||||
# Expose application port
|
# Expose application port
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
package accountapp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/accountapp/delivery/grpc"
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/adapter/kavenegar"
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/adapter/redis"
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/domain/account/repository/mysql"
|
||||||
|
redisRepo "git.gocasts.ir/ebhomengo/niki/domain/account/repository/redis"
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/domain/account/service"
|
||||||
|
database "git.gocasts.ir/ebhomengo/niki/pkg/database/mysql"
|
||||||
|
rpcPkg "git.gocasts.ir/ebhomengo/niki/pkg/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Application struct {
|
||||||
|
GrpcServer grpc.Server
|
||||||
|
Config Config
|
||||||
|
accountSvc service.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
func Setup(cfg Config, db *database.DB) Application {
|
||||||
|
redisConn := redis.New(cfg.Redis)
|
||||||
|
otpRepo := redisRepo.NewRepositoryOtp(redisConn)
|
||||||
|
mysqlRepo := mysql.New(db)
|
||||||
|
smsAdapter := kavenegar.New(cfg.Kavenegar)
|
||||||
|
accountSvc := service.NewService(cfg.accountSvc, otpRepo, mysqlRepo, smsAdapter)
|
||||||
|
|
||||||
|
rpcServer := rpcPkg.New(cfg.grpcServerCfg)
|
||||||
|
|
||||||
|
return Application{
|
||||||
|
accountSvc: accountSvc,
|
||||||
|
Config: cfg,
|
||||||
|
GrpcServer: grpc.New(rpcServer, accountSvc),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *Application) Start() {
|
||||||
|
err := app.GrpcServer.Start()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error in serving GRPC server: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
package accountapp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/adapter/kavenegar"
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/adapter/redis"
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/domain/account/service"
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/pkg/database/mysql"
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/pkg/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
accountSvc service.Config `koanf:"service"`
|
||||||
|
Redis redis.Config `koanf:"redis_db"`
|
||||||
|
MysqlDB mysql.Config `koanf:"mysql_db"`
|
||||||
|
Kavenegar kavenegar.Config `koanf:"kavenegar"`
|
||||||
|
grpcServerCfg grpc.Config `koanf:"grpc_server"`
|
||||||
|
grpcClientCfg grpc.Client `koanf:"grpc_client"`
|
||||||
|
PathOfMigration string `koanf:"path_of_migration"`
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
package grpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
pb "git.gocasts.ir/ebhomengo/niki/contract/goprotobuf/account"
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/domain/account/service"
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/pkg/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
server *grpc.RPCServer
|
||||||
|
accountSvc service.Service
|
||||||
|
pb.UnimplementedAccountServiceServer
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(server *grpc.RPCServer, accountSvc service.Service) Server {
|
||||||
|
return Server{
|
||||||
|
server: server,
|
||||||
|
accountSvc: accountSvc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Server) SendOtp(ctx context.Context, req *pb.SendOtpRequest) (*pb.SendOtpResponse, error) {
|
||||||
|
err := s.accountSvc.SendOTP(ctx, req.PhoneNumber)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &pb.SendOtpResponse{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Server) LoginOrRegister(ctx context.Context, req *pb.LoginOrRegisterRequest) (*pb.LoginOrRegisterResponse, error) {
|
||||||
|
res := &pb.LoginOrRegisterResponse{}
|
||||||
|
driver, err := s.accountSvc.LoginOrRegisterDriver(ctx, req.PhoneNumber, req.VerifyCode)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
id := uint64(driver.ID)
|
||||||
|
|
||||||
|
res.Id = id
|
||||||
|
res.PhoneNumber = driver.PhoneNumber
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Server) Start() error {
|
||||||
|
listener, err := net.Listen(s.server.Config.NetworkType, fmt.Sprintf(":%d", s.server.Config.Port))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
accountSvcServer := Server{}
|
||||||
|
|
||||||
|
pb.RegisterAccountServiceServer(s.server.Server, &accountSvcServer)
|
||||||
|
|
||||||
|
if err := s.server.Server.Serve(listener); err != nil {
|
||||||
|
log.Fatalf("failed to serve: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
package account
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
pb "git.gocasts.ir/ebhomengo/niki/contract/goprotobuf/account"
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/driverapp/service"
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/pkg/types"
|
||||||
|
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
Conn *grpc.ClientConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(conn *grpc.ClientConn) *Client {
|
||||||
|
return &Client{
|
||||||
|
Conn: conn,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Client) SendOTP(ctx context.Context, phoneNumber string) error {
|
||||||
|
|
||||||
|
client := pb.NewAccountServiceClient(c.Conn)
|
||||||
|
|
||||||
|
_, err := client.SendOtp(ctx, &pb.SendOtpRequest{
|
||||||
|
PhoneNumber: phoneNumber,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Client) LoginOrRegister(ctx context.Context, req service.LoginOrRegisterRequest) (service.LoginOrRegisterResponse, error) {
|
||||||
|
|
||||||
|
client := pb.NewAccountServiceClient(c.Conn)
|
||||||
|
|
||||||
|
res, err := client.LoginOrRegister(ctx, &pb.LoginOrRegisterRequest{
|
||||||
|
PhoneNumber: req.PhoneNumber,
|
||||||
|
VerifyCode: req.VerifyCode,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return service.LoginOrRegisterResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return service.LoginOrRegisterResponse{
|
||||||
|
ID: types.ID(res.Id),
|
||||||
|
PhoneNumber: res.PhoneNumber,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
package agentapp
|
|
||||||
|
|
||||||
type Application struct {
|
|
||||||
config Config
|
|
||||||
}
|
|
||||||
|
|
||||||
func Setup() {}
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
package agentapp
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
// database config
|
|
||||||
// httpserver config
|
|
||||||
//...
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
package http
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
package http
|
|
||||||
|
|
||||||
type Server struct {
|
|
||||||
// httpServer
|
|
||||||
// handler
|
|
||||||
}
|
|
||||||
|
|
||||||
func New() Server {
|
|
||||||
return Server{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s Server) Serve() {}
|
|
||||||
|
|
||||||
func (s Server) RegisterRoutes() {}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
package repository
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
package service
|
|
||||||
|
|
||||||
type Agent struct {
|
|
||||||
ID uint
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
package service
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
package service
|
|
||||||
|
|
||||||
type Service struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func New() Service {
|
|
||||||
return Service{}
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
package service
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
package command
|
||||||
|
|
||||||
|
import "github.com/spf13/cobra"
|
||||||
|
|
||||||
|
var up bool
|
||||||
|
var down bool
|
||||||
|
|
||||||
|
var migrateCmd = &cobra.Command{
|
||||||
|
Use: "migrate",
|
||||||
|
Short: "Run database migrations",
|
||||||
|
Long: `This command runs the database migrations for the account service.`,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
migrate()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func migrate() {}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
migrateCmd.Flags().BoolVar(&up, "up", false, "Run migrations up")
|
||||||
|
migrateCmd.Flags().BoolVar(&down, "down", false, "Run migrations down")
|
||||||
|
RootCmd.AddCommand(migrateCmd)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
package command
|
||||||
|
|
||||||
|
import "github.com/spf13/cobra"
|
||||||
|
|
||||||
|
var RootCmd = &cobra.Command{
|
||||||
|
Use: "account_service",
|
||||||
|
Short: "A CLI for account Service",
|
||||||
|
Long: `account Service CLI is a tool to manage and run
|
||||||
|
the account service, including migrations and server startup.`,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
package command
|
||||||
|
|
||||||
|
import "github.com/spf13/cobra"
|
||||||
|
|
||||||
|
var serveCmd = &cobra.Command{
|
||||||
|
Use: "serve",
|
||||||
|
Short: "start a account service.",
|
||||||
|
Long: `This command starts the main account service.`,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
serve()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func serve() {}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
package account
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/cmd/account/command"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if err := command.RootCmd.Execute(); err != nil {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
package driverapp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/driverapp"
|
||||||
|
cfgloader "git.gocasts.ir/ebhomengo/niki/pkg/cfg_loader"
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/pkg/migrator"
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/repository/mysql"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var cfg driverapp.Config
|
||||||
|
|
||||||
|
workingDir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error getting current working directory: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
options := cfgloader.Option{
|
||||||
|
Prefix: "DRIVER_",
|
||||||
|
Delimiter: ".",
|
||||||
|
Separator: "__",
|
||||||
|
YamlFilePath: filepath.Join(workingDir, "deploy", "driver", "development", "config.yaml"),
|
||||||
|
CallbackEnv: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
lErr := cfgloader.Load(options, &cfg)
|
||||||
|
if lErr != nil {
|
||||||
|
log.Fatalf("Failed to load driver config: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn := mysql.New(cfg.MysqlDB)
|
||||||
|
|
||||||
|
mgr := migrator.New(cfg.MysqlDB, cfg.PathOfMigration)
|
||||||
|
|
||||||
|
migrate := flag.Bool("migrate", false, "perform database migrations")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if *migrate {
|
||||||
|
fmt.Println("Running migrations")
|
||||||
|
mgr.Up()
|
||||||
|
}
|
||||||
|
|
||||||
|
//dapp := driverapp.Setup(cfg)
|
||||||
|
//dapp.Start()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,281 @@
|
||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.36.11
|
||||||
|
// protoc v3.21.12
|
||||||
|
// source: contract/protobuf/account/account.proto
|
||||||
|
|
||||||
|
package account
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
unsafe "unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
type LoginOrRegisterRequest struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
PhoneNumber string `protobuf:"bytes,1,opt,name=phoneNumber,proto3" json:"phoneNumber,omitempty"`
|
||||||
|
VerifyCode string `protobuf:"bytes,2,opt,name=verifyCode,proto3" json:"verifyCode,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LoginOrRegisterRequest) Reset() {
|
||||||
|
*x = LoginOrRegisterRequest{}
|
||||||
|
mi := &file_contract_protobuf_account_account_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LoginOrRegisterRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*LoginOrRegisterRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *LoginOrRegisterRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_contract_protobuf_account_account_proto_msgTypes[0]
|
||||||
|
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 LoginOrRegisterRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*LoginOrRegisterRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_contract_protobuf_account_account_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LoginOrRegisterRequest) GetPhoneNumber() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.PhoneNumber
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LoginOrRegisterRequest) GetVerifyCode() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.VerifyCode
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoginOrRegisterResponse struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||||
|
PhoneNumber string `protobuf:"bytes,2,opt,name=phoneNumber,proto3" json:"phoneNumber,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LoginOrRegisterResponse) Reset() {
|
||||||
|
*x = LoginOrRegisterResponse{}
|
||||||
|
mi := &file_contract_protobuf_account_account_proto_msgTypes[1]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LoginOrRegisterResponse) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*LoginOrRegisterResponse) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *LoginOrRegisterResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_contract_protobuf_account_account_proto_msgTypes[1]
|
||||||
|
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 LoginOrRegisterResponse.ProtoReflect.Descriptor instead.
|
||||||
|
func (*LoginOrRegisterResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return file_contract_protobuf_account_account_proto_rawDescGZIP(), []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LoginOrRegisterResponse) GetId() uint64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Id
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LoginOrRegisterResponse) GetPhoneNumber() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.PhoneNumber
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type SendOtpRequest struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
PhoneNumber string `protobuf:"bytes,1,opt,name=phoneNumber,proto3" json:"phoneNumber,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SendOtpRequest) Reset() {
|
||||||
|
*x = SendOtpRequest{}
|
||||||
|
mi := &file_contract_protobuf_account_account_proto_msgTypes[2]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SendOtpRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*SendOtpRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *SendOtpRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_contract_protobuf_account_account_proto_msgTypes[2]
|
||||||
|
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 SendOtpRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*SendOtpRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_contract_protobuf_account_account_proto_rawDescGZIP(), []int{2}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SendOtpRequest) GetPhoneNumber() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.PhoneNumber
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type SendOtpResponse struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SendOtpResponse) Reset() {
|
||||||
|
*x = SendOtpResponse{}
|
||||||
|
mi := &file_contract_protobuf_account_account_proto_msgTypes[3]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SendOtpResponse) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*SendOtpResponse) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *SendOtpResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_contract_protobuf_account_account_proto_msgTypes[3]
|
||||||
|
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 SendOtpResponse.ProtoReflect.Descriptor instead.
|
||||||
|
func (*SendOtpResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return file_contract_protobuf_account_account_proto_rawDescGZIP(), []int{3}
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_contract_protobuf_account_account_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
const file_contract_protobuf_account_account_proto_rawDesc = "" +
|
||||||
|
"\n" +
|
||||||
|
"'contract/protobuf/account/account.proto\x12\asendOtp\"Z\n" +
|
||||||
|
"\x16LoginOrRegisterRequest\x12 \n" +
|
||||||
|
"\vphoneNumber\x18\x01 \x01(\tR\vphoneNumber\x12\x1e\n" +
|
||||||
|
"\n" +
|
||||||
|
"verifyCode\x18\x02 \x01(\tR\n" +
|
||||||
|
"verifyCode\"K\n" +
|
||||||
|
"\x17LoginOrRegisterResponse\x12\x0e\n" +
|
||||||
|
"\x02id\x18\x01 \x01(\x04R\x02id\x12 \n" +
|
||||||
|
"\vphoneNumber\x18\x02 \x01(\tR\vphoneNumber\"2\n" +
|
||||||
|
"\x0eSendOtpRequest\x12 \n" +
|
||||||
|
"\vphoneNumber\x18\x01 \x01(\tR\vphoneNumber\"\x11\n" +
|
||||||
|
"\x0fSendOtpResponse2\xa4\x01\n" +
|
||||||
|
"\x0eAccountService\x12<\n" +
|
||||||
|
"\aSendOtp\x12\x17.sendOtp.SendOtpRequest\x1a\x18.sendOtp.SendOtpResponse\x12T\n" +
|
||||||
|
"\x0fLoginOrRegister\x12\x1f.sendOtp.LoginOrRegisterRequest\x1a .sendOtp.LoginOrRegisterResponseB\x1dZ\x1bcontract/goprotobuf/accountb\x06proto3"
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_contract_protobuf_account_account_proto_rawDescOnce sync.Once
|
||||||
|
file_contract_protobuf_account_account_proto_rawDescData []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_contract_protobuf_account_account_proto_rawDescGZIP() []byte {
|
||||||
|
file_contract_protobuf_account_account_proto_rawDescOnce.Do(func() {
|
||||||
|
file_contract_protobuf_account_account_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_contract_protobuf_account_account_proto_rawDesc), len(file_contract_protobuf_account_account_proto_rawDesc)))
|
||||||
|
})
|
||||||
|
return file_contract_protobuf_account_account_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_contract_protobuf_account_account_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
|
||||||
|
var file_contract_protobuf_account_account_proto_goTypes = []any{
|
||||||
|
(*LoginOrRegisterRequest)(nil), // 0: sendOtp.LoginOrRegisterRequest
|
||||||
|
(*LoginOrRegisterResponse)(nil), // 1: sendOtp.LoginOrRegisterResponse
|
||||||
|
(*SendOtpRequest)(nil), // 2: sendOtp.SendOtpRequest
|
||||||
|
(*SendOtpResponse)(nil), // 3: sendOtp.SendOtpResponse
|
||||||
|
}
|
||||||
|
var file_contract_protobuf_account_account_proto_depIdxs = []int32{
|
||||||
|
2, // 0: sendOtp.AccountService.SendOtp:input_type -> sendOtp.SendOtpRequest
|
||||||
|
0, // 1: sendOtp.AccountService.LoginOrRegister:input_type -> sendOtp.LoginOrRegisterRequest
|
||||||
|
3, // 2: sendOtp.AccountService.SendOtp:output_type -> sendOtp.SendOtpResponse
|
||||||
|
1, // 3: sendOtp.AccountService.LoginOrRegister:output_type -> sendOtp.LoginOrRegisterResponse
|
||||||
|
2, // [2:4] is the sub-list for method output_type
|
||||||
|
0, // [0:2] 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
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_contract_protobuf_account_account_proto_init() }
|
||||||
|
func file_contract_protobuf_account_account_proto_init() {
|
||||||
|
if File_contract_protobuf_account_account_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: unsafe.Slice(unsafe.StringData(file_contract_protobuf_account_account_proto_rawDesc), len(file_contract_protobuf_account_account_proto_rawDesc)),
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 4,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 1,
|
||||||
|
},
|
||||||
|
GoTypes: file_contract_protobuf_account_account_proto_goTypes,
|
||||||
|
DependencyIndexes: file_contract_protobuf_account_account_proto_depIdxs,
|
||||||
|
MessageInfos: file_contract_protobuf_account_account_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_contract_protobuf_account_account_proto = out.File
|
||||||
|
file_contract_protobuf_account_account_proto_goTypes = nil
|
||||||
|
file_contract_protobuf_account_account_proto_depIdxs = nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,159 @@
|
||||||
|
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// - protoc-gen-go-grpc v1.6.1
|
||||||
|
// - protoc v3.21.12
|
||||||
|
// source: contract/protobuf/account/account.proto
|
||||||
|
|
||||||
|
package account
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
codes "google.golang.org/grpc/codes"
|
||||||
|
status "google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the grpc package it is being compiled against.
|
||||||
|
// Requires gRPC-Go v1.64.0 or later.
|
||||||
|
const _ = grpc.SupportPackageIsVersion9
|
||||||
|
|
||||||
|
const (
|
||||||
|
AccountService_SendOtp_FullMethodName = "/sendOtp.AccountService/SendOtp"
|
||||||
|
AccountService_LoginOrRegister_FullMethodName = "/sendOtp.AccountService/LoginOrRegister"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AccountServiceClient is the client API for AccountService service.
|
||||||
|
//
|
||||||
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||||
|
type AccountServiceClient interface {
|
||||||
|
SendOtp(ctx context.Context, in *SendOtpRequest, opts ...grpc.CallOption) (*SendOtpResponse, error)
|
||||||
|
LoginOrRegister(ctx context.Context, in *LoginOrRegisterRequest, opts ...grpc.CallOption) (*LoginOrRegisterResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type accountServiceClient struct {
|
||||||
|
cc grpc.ClientConnInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAccountServiceClient(cc grpc.ClientConnInterface) AccountServiceClient {
|
||||||
|
return &accountServiceClient{cc}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *accountServiceClient) SendOtp(ctx context.Context, in *SendOtpRequest, opts ...grpc.CallOption) (*SendOtpResponse, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(SendOtpResponse)
|
||||||
|
err := c.cc.Invoke(ctx, AccountService_SendOtp_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *accountServiceClient) LoginOrRegister(ctx context.Context, in *LoginOrRegisterRequest, opts ...grpc.CallOption) (*LoginOrRegisterResponse, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(LoginOrRegisterResponse)
|
||||||
|
err := c.cc.Invoke(ctx, AccountService_LoginOrRegister_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccountServiceServer is the server API for AccountService service.
|
||||||
|
// All implementations must embed UnimplementedAccountServiceServer
|
||||||
|
// for forward compatibility.
|
||||||
|
type AccountServiceServer interface {
|
||||||
|
SendOtp(context.Context, *SendOtpRequest) (*SendOtpResponse, error)
|
||||||
|
LoginOrRegister(context.Context, *LoginOrRegisterRequest) (*LoginOrRegisterResponse, error)
|
||||||
|
mustEmbedUnimplementedAccountServiceServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnimplementedAccountServiceServer must be embedded to have
|
||||||
|
// forward compatible implementations.
|
||||||
|
//
|
||||||
|
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||||
|
// pointer dereference when methods are called.
|
||||||
|
type UnimplementedAccountServiceServer struct{}
|
||||||
|
|
||||||
|
func (UnimplementedAccountServiceServer) SendOtp(context.Context, *SendOtpRequest) (*SendOtpResponse, error) {
|
||||||
|
return nil, status.Error(codes.Unimplemented, "method SendOtp not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedAccountServiceServer) LoginOrRegister(context.Context, *LoginOrRegisterRequest) (*LoginOrRegisterResponse, error) {
|
||||||
|
return nil, status.Error(codes.Unimplemented, "method LoginOrRegister not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedAccountServiceServer) mustEmbedUnimplementedAccountServiceServer() {}
|
||||||
|
func (UnimplementedAccountServiceServer) testEmbeddedByValue() {}
|
||||||
|
|
||||||
|
// UnsafeAccountServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||||
|
// Use of this interface is not recommended, as added methods to AccountServiceServer will
|
||||||
|
// result in compilation errors.
|
||||||
|
type UnsafeAccountServiceServer interface {
|
||||||
|
mustEmbedUnimplementedAccountServiceServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterAccountServiceServer(s grpc.ServiceRegistrar, srv AccountServiceServer) {
|
||||||
|
// If the following call panics, it indicates UnimplementedAccountServiceServer was
|
||||||
|
// embedded by pointer and is nil. This will cause panics if an
|
||||||
|
// unimplemented method is ever invoked, so we test this at initialization
|
||||||
|
// time to prevent it from happening at runtime later due to I/O.
|
||||||
|
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||||
|
t.testEmbeddedByValue()
|
||||||
|
}
|
||||||
|
s.RegisterService(&AccountService_ServiceDesc, srv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _AccountService_SendOtp_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(SendOtpRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(AccountServiceServer).SendOtp(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: AccountService_SendOtp_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(AccountServiceServer).SendOtp(ctx, req.(*SendOtpRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _AccountService_LoginOrRegister_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(LoginOrRegisterRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(AccountServiceServer).LoginOrRegister(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: AccountService_LoginOrRegister_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(AccountServiceServer).LoginOrRegister(ctx, req.(*LoginOrRegisterRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccountService_ServiceDesc is the grpc.ServiceDesc for AccountService service.
|
||||||
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
|
// and not to be introspected or modified (even as a copy)
|
||||||
|
var AccountService_ServiceDesc = grpc.ServiceDesc{
|
||||||
|
ServiceName: "sendOtp.AccountService",
|
||||||
|
HandlerType: (*AccountServiceServer)(nil),
|
||||||
|
Methods: []grpc.MethodDesc{
|
||||||
|
{
|
||||||
|
MethodName: "SendOtp",
|
||||||
|
Handler: _AccountService_SendOtp_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "LoginOrRegister",
|
||||||
|
Handler: _AccountService_LoginOrRegister_Handler,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Streams: []grpc.StreamDesc{},
|
||||||
|
Metadata: "contract/protobuf/account/account.proto",
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
|
||||||
|
package sendOtp;
|
||||||
|
option go_package = "contract/goprotobuf/account";
|
||||||
|
|
||||||
|
message LoginOrRegisterRequest{
|
||||||
|
string phoneNumber = 1;
|
||||||
|
string verifyCode = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message LoginOrRegisterResponse {
|
||||||
|
uint64 id = 1;
|
||||||
|
string phoneNumber = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
message SendOtpRequest {
|
||||||
|
string phoneNumber = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SendOtpResponse {}
|
||||||
|
|
||||||
|
service AccountService {
|
||||||
|
rpc SendOtp(SendOtpRequest) returns (SendOtpResponse);
|
||||||
|
rpc LoginOrRegister(LoginOrRegisterRequest) returns(LoginOrRegisterResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -8,7 +8,7 @@ import (
|
||||||
func MigrateMariaDB(cfg mysql.Config) func() {
|
func MigrateMariaDB(cfg mysql.Config) func() {
|
||||||
migrations := migrator.New(migrator.Config{
|
migrations := migrator.New(migrator.Config{
|
||||||
MysqlConfig: cfg,
|
MysqlConfig: cfg,
|
||||||
MigrationPath: "../../../repository/mysql/migration",
|
MigrationPath: "../../../repository/mysql/migrations",
|
||||||
MigrationDBName: "gorp_migrations",
|
MigrationDBName: "gorp_migrations",
|
||||||
})
|
})
|
||||||
migrations.Up()
|
migrations.Up()
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
service:
|
||||||
|
length_of_otp_code: 6
|
||||||
|
otp_chars: "0123456789"
|
||||||
|
otp_expire_time: 2
|
||||||
|
redis_db:
|
||||||
|
host:
|
||||||
|
port:
|
||||||
|
password:
|
||||||
|
db:
|
||||||
|
mysql_db:
|
||||||
|
username:
|
||||||
|
password:
|
||||||
|
port:
|
||||||
|
host:
|
||||||
|
db_name:
|
||||||
|
kavenegar:
|
||||||
|
api_key:
|
||||||
|
sender:
|
||||||
|
grpc_server:
|
||||||
|
port:
|
||||||
|
network:
|
||||||
|
grpc_client:
|
||||||
|
host:
|
||||||
|
port:
|
||||||
|
|
||||||
|
|
||||||
|
path_of_migration: ./account/repository/mysql/migration
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
service:
|
||||||
|
length_of_otp_code: 6
|
||||||
|
otp_chars: "0123456789"
|
||||||
|
otp_expire_time: 2
|
||||||
|
redis_db:
|
||||||
|
host:
|
||||||
|
port:
|
||||||
|
password:
|
||||||
|
db:
|
||||||
|
mysql_db:
|
||||||
|
username:
|
||||||
|
password:
|
||||||
|
port:
|
||||||
|
host:
|
||||||
|
db_name:
|
||||||
|
kavenegar:
|
||||||
|
api_key:
|
||||||
|
sender:
|
||||||
|
|
||||||
|
path_of_migration: ./driverapp/repository/mysql/migration
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
services:
|
||||||
|
driver_mariadb:
|
||||||
|
image: bitnami/mariadb:11.1
|
||||||
|
container_name: driver_mariadb
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "3305:3306"
|
||||||
|
volumes:
|
||||||
|
- 'driver-mariadb-data:/bitnami/mariadb'
|
||||||
|
environment:
|
||||||
|
MARIADB_USER: driver_admin
|
||||||
|
MARIADB_PASSWORD: password123
|
||||||
|
MARIADB_DATABASE: driver_db
|
||||||
|
MARIADB_ROOT_PASSWORD: password123
|
||||||
|
driver_redis:
|
||||||
|
image: bitnami/redis:6.2
|
||||||
|
container_name: driver-redis
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- '6380:6379'
|
||||||
|
command: redis-server --loglevel warning --protected-mode no --save "" --appendonly no
|
||||||
|
environment:
|
||||||
|
- ALLOW_EMPTY_PASSWORD=yes
|
||||||
|
volumes:
|
||||||
|
- driver-redis-data:/data
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
driver-mariadb-data:
|
||||||
|
driver-redis-data:
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
package entity
|
||||||
|
|
||||||
|
import "git.gocasts.ir/ebhomengo/niki/pkg/types"
|
||||||
|
|
||||||
|
type Driver struct {
|
||||||
|
ID types.ID
|
||||||
|
PhoneNumber string
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
package mysql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/domain/account/entity"
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/pkg/database/mysql"
|
||||||
|
errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
|
||||||
|
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
|
||||||
|
types "git.gocasts.ir/ebhomengo/niki/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
StatementKeyIsExistDriverByPhoneNumber = iota + 1
|
||||||
|
StatementKeyCreateDriver = iota + 1
|
||||||
|
)
|
||||||
|
|
||||||
|
type AccountRepo struct {
|
||||||
|
db *mysql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(db *mysql.DB) AccountRepo {
|
||||||
|
return AccountRepo{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r AccountRepo) IsExistDriverByPhoneNumber(ctx context.Context, phoneNumber string) (bool, entity.Driver, error) {
|
||||||
|
const op = "Repository.IsExistDriverByPhoneNumber"
|
||||||
|
query := `select * from drivers where phone_number = ?`
|
||||||
|
stmt, err := r.db.PrepareStatement(ctx, StatementKeyIsExistDriverByPhoneNumber, query)
|
||||||
|
if err != nil {
|
||||||
|
return false, entity.Driver{}, richerror.New(op).WithErr(err).WithKind(richerror.KindUnexpected).
|
||||||
|
WithMessage(errmsg.ErrorMsgCantPrepareStatement)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer stmt.Close()
|
||||||
|
|
||||||
|
row := stmt.QueryRowContext(ctx, phoneNumber)
|
||||||
|
d, sErr := DriverScan(row)
|
||||||
|
if sErr != nil {
|
||||||
|
if errors.Is(sErr, sql.ErrNoRows) {
|
||||||
|
return false, entity.Driver{}, richerror.New(op).WithKind(richerror.KindNotFound).
|
||||||
|
WithMessage(errmsg.ErrorMsgNotFound)
|
||||||
|
}
|
||||||
|
return false, entity.Driver{}, richerror.New(op).WithErr(err).
|
||||||
|
WithMessage(errmsg.ErrorMsgCantScanQueryResult).WithKind(richerror.KindUnexpected)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, d, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r AccountRepo) CreateDriver(ctx context.Context, driver entity.Driver) (entity.Driver, error) {
|
||||||
|
const op = "Repository.CreateDriver"
|
||||||
|
query := `insert into drivers(phone_number) values(?)`
|
||||||
|
|
||||||
|
stmt, err := r.db.PrepareStatement(ctx, StatementKeyCreateDriver, query)
|
||||||
|
if err != nil {
|
||||||
|
return entity.Driver{}, richerror.New(op).WithErr(err).WithKind(richerror.KindUnexpected).
|
||||||
|
WithMessage(errmsg.ErrorMsgCantPrepareStatement)
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := stmt.ExecContext(ctx, driver.PhoneNumber)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return entity.Driver{}, richerror.New(op).WithErr(err).WithKind(richerror.KindUnexpected).
|
||||||
|
WithMessage(errmsg.ErrorMsgNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
id, _ := res.LastInsertId()
|
||||||
|
driver.ID = types.ID(id)
|
||||||
|
|
||||||
|
return driver, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DriverScan(scanner mysql.Scanner) (entity.Driver, error) {
|
||||||
|
var createdAt, updatedAt time.Time
|
||||||
|
var driver entity.Driver
|
||||||
|
|
||||||
|
err := scanner.Scan(&driver.ID, &driver.PhoneNumber, &createdAt, &updatedAt)
|
||||||
|
|
||||||
|
return driver, err
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
-- +migrate Up
|
||||||
|
CREATE TABLE `drivers`(
|
||||||
|
`iD` INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
`phone_number` VARCHAR(191) NOT NULL UNIQUE ,
|
||||||
|
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
-- +migrate Down
|
||||||
|
DROP TABLE `drivers`;
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
package redis
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/adapter/redis"
|
||||||
|
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RepositoryOtp struct {
|
||||||
|
conn *redis.Adapter
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRepositoryOtp(conn *redis.Adapter) RepositoryOtp {
|
||||||
|
return RepositoryOtp{conn: conn}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r RepositoryOtp) IsExistPhoneNumber(ctx context.Context, phoneNumber string) (bool, error) {
|
||||||
|
const op = "RepositoryOtp.IsExistPhoneNumber"
|
||||||
|
|
||||||
|
result, err := r.conn.Client().Exists(ctx, phoneNumber).Result()
|
||||||
|
if err != nil {
|
||||||
|
return false, richerror.New(op).WithKind(richerror.KindUnexpected).WithErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if result == 0 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r RepositoryOtp) SaveCodeWithPhoneNumber(ctx context.Context, phoneNumber string, code string, expireTime time.Duration) error {
|
||||||
|
const op = "RepositoryOtp.SaveCodeWithPhoneNumber"
|
||||||
|
|
||||||
|
_, err := r.conn.Client().Set(ctx, phoneNumber, code, expireTime).Result()
|
||||||
|
if err != nil {
|
||||||
|
return richerror.New(op).WithKind(richerror.KindUnexpected).WithErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r RepositoryOtp) GetCodeByPhoneNumber(ctx context.Context, phoneNumber string) (string, error) {
|
||||||
|
const op = "RepositoryOtp.GetCodeByPhoneNumber"
|
||||||
|
|
||||||
|
result, err := r.conn.Client().Get(ctx, phoneNumber).Result()
|
||||||
|
if err != nil {
|
||||||
|
return "", richerror.New(op).WithKind(richerror.KindUnexpected).WithErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r RepositoryOtp) DeleteCodeByPhoneNumber(ctx context.Context, PhoneNumber string) (bool, error) {
|
||||||
|
const op = "RepositoryOtp.DeleteCodeByPhoneNumber"
|
||||||
|
success, err := r.conn.Client().Del(ctx, PhoneNumber).Result()
|
||||||
|
if err != nil {
|
||||||
|
return false, richerror.New(op).WithErr(err).WithKind(richerror.KindUnexpected)
|
||||||
|
}
|
||||||
|
if success != 1 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,115 @@
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
smscontract "git.gocasts.ir/ebhomengo/niki/contract/sms"
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/domain/account/entity"
|
||||||
|
errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
|
||||||
|
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
LengthOfOtpCode int `koanf:"length_of_otp_code"`
|
||||||
|
OtpChars string `koanf:"otp_chars"`
|
||||||
|
OtpExpireTime time.Duration `koanf:"otp_expire_time"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RepositoryOtp interface {
|
||||||
|
IsExistPhoneNumber(ctx context.Context, phoneNumber string) (bool, error)
|
||||||
|
SaveCodeWithPhoneNumber(ctx context.Context, phoneNumber string, code string, expireTime time.Duration) error
|
||||||
|
GetCodeByPhoneNumber(ctx context.Context, phoneNumber string) (string, error)
|
||||||
|
DeleteCodeByPhoneNumber(ctx context.Context, PhoneNumber string) (bool, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Repository interface {
|
||||||
|
IsExistDriverByPhoneNumber(ctx context.Context, phoneNumber string) (bool, entity.Driver, error)
|
||||||
|
CreateDriver(ctx context.Context, driver entity.Driver) (entity.Driver, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
config Config
|
||||||
|
repositoryOtp RepositoryOtp
|
||||||
|
repository Repository
|
||||||
|
smsContract smscontract.SmsAdapter
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewService(cfg Config, repositoryOtp RepositoryOtp, repository Repository, smsContract smscontract.SmsAdapter) Service {
|
||||||
|
return Service{
|
||||||
|
config: cfg,
|
||||||
|
repositoryOtp: repositoryOtp,
|
||||||
|
repository: repository,
|
||||||
|
smsContract: smsContract,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Service) SendOTP(ctx context.Context, phoneNumber string) error {
|
||||||
|
const op = "accountService.SendOTP"
|
||||||
|
|
||||||
|
isExist, iErr := s.repositoryOtp.IsExistPhoneNumber(ctx, phoneNumber)
|
||||||
|
if iErr != nil {
|
||||||
|
return richerror.New(op).WithErr(iErr).WithKind(richerror.KindUnexpected)
|
||||||
|
}
|
||||||
|
|
||||||
|
if isExist {
|
||||||
|
return richerror.New(op).WithMessage(errmsg.ErrorMsgOtpCodeExist).WithKind(richerror.KindForbidden)
|
||||||
|
}
|
||||||
|
|
||||||
|
newCode := s.generateVerificationCode()
|
||||||
|
sErr := s.repositoryOtp.SaveCodeWithPhoneNumber(ctx, phoneNumber, newCode, s.config.OtpExpireTime)
|
||||||
|
if sErr != nil {
|
||||||
|
return richerror.New(op).WithErr(sErr).WithKind(richerror.KindUnexpected)
|
||||||
|
}
|
||||||
|
|
||||||
|
go s.smsContract.Send(phoneNumber, newCode)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Service) LoginOrRegisterDriver(ctx context.Context, phoneNumber string, verifyCode string) (entity.Driver, error) {
|
||||||
|
const op = "accountService.LoginOrRegisterDriver"
|
||||||
|
|
||||||
|
code, gErr := s.repositoryOtp.GetCodeByPhoneNumber(ctx, phoneNumber)
|
||||||
|
if gErr != nil {
|
||||||
|
return entity.Driver{}, richerror.New(op).WithErr(gErr).WithKind(richerror.KindUnexpected)
|
||||||
|
}
|
||||||
|
|
||||||
|
if code == "" || code != verifyCode {
|
||||||
|
return entity.Driver{}, richerror.New(op).WithMessage(errmsg.ErrorMsgOtpCodeIsNotValid).WithKind(richerror.KindForbidden)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, dErr := s.repositoryOtp.DeleteCodeByPhoneNumber(ctx, phoneNumber)
|
||||||
|
if dErr != nil {
|
||||||
|
return entity.Driver{}, richerror.New(op).WithErr(dErr).WithKind(richerror.KindUnexpected)
|
||||||
|
}
|
||||||
|
|
||||||
|
isExist, driver, eErr := s.repository.IsExistDriverByPhoneNumber(ctx, phoneNumber)
|
||||||
|
if eErr != nil {
|
||||||
|
return entity.Driver{}, richerror.New(op).WithErr(eErr).WithKind(richerror.KindUnexpected)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isExist {
|
||||||
|
newDriver, cErr := s.repository.CreateDriver(ctx, entity.Driver{
|
||||||
|
PhoneNumber: phoneNumber,
|
||||||
|
})
|
||||||
|
if cErr != nil {
|
||||||
|
return entity.Driver{}, richerror.New(op).WithErr(cErr).WithKind(richerror.KindUnexpected)
|
||||||
|
}
|
||||||
|
|
||||||
|
driver = newDriver
|
||||||
|
}
|
||||||
|
|
||||||
|
return driver, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Service) generateVerificationCode() string {
|
||||||
|
result := make([]byte, s.config.LengthOfOtpCode)
|
||||||
|
for i := 0; i < s.config.LengthOfOtpCode; i++ {
|
||||||
|
result[i] = s.config.OtpChars[rand.Intn(len(s.config.OtpChars))]
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(result)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
package driverapp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/adapter/account"
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/driverapp/delivery/http"
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/driverapp/service"
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/pkg/http_server"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Application struct {
|
||||||
|
svc service.Service
|
||||||
|
accountClient account.Client
|
||||||
|
handler http.Handler
|
||||||
|
httpServer http.Server
|
||||||
|
config Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func Setup(config Config, conn *grpc.ClientConn) Application {
|
||||||
|
driverValidator := service.NewValidator()
|
||||||
|
accountClient := account.New(conn)
|
||||||
|
driverSvc := service.NewService(config.DriverSvc, accountClient, driverValidator)
|
||||||
|
driverHandler := http.NewHandler(driverSvc)
|
||||||
|
|
||||||
|
httpServer := httpserver.New(config.HttpServer)
|
||||||
|
|
||||||
|
return Application{
|
||||||
|
svc: driverSvc,
|
||||||
|
handler: driverHandler,
|
||||||
|
httpServer: http.New(httpServer, driverHandler),
|
||||||
|
config: config,
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app Application) Start() {
|
||||||
|
app.httpServer.Serve()
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package driverapp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/adapter/kavenegar"
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/adapter/redis"
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/driverapp/service"
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/pkg/http_server"
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/repository/mysql"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
DriverSvc service.Config `koanf:"service"`
|
||||||
|
HttpServer http_server.Config `koanf:"http_server"`
|
||||||
|
Redis redis.Config `koanf:"redis_db"`
|
||||||
|
MysqlDB mysql.Config `koanf:"mysql_db"`
|
||||||
|
Kavenegar kavenegar.Config `koanf:"kavenegar"`
|
||||||
|
PathOfMigration string `koanf:"path_of_migration"`
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/driverapp/service"
|
||||||
|
httpmsg "git.gocasts.ir/ebhomengo/niki/pkg/http_msg"
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Handler struct {
|
||||||
|
DriverSvc service.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHandler(driverSvc service.Service) Handler {
|
||||||
|
return Handler{
|
||||||
|
DriverSvc: driverSvc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Handler) SendOtp(c echo.Context) error {
|
||||||
|
var req service.SendOtpRequest
|
||||||
|
|
||||||
|
if err := c.Bind(&req); err != nil {
|
||||||
|
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := h.DriverSvc.SendOtp(c.Request().Context(), req)
|
||||||
|
if err != nil {
|
||||||
|
msg, code := httpmsg.Error(err)
|
||||||
|
return echo.NewHTTPError(code, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Handler) loginOrRegister(c echo.Context) error {
|
||||||
|
var req service.LoginOrRegisterRequest
|
||||||
|
|
||||||
|
if err := c.Bind(&req); err != nil {
|
||||||
|
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := h.DriverSvc.LoginOrRegister(c.Request().Context(), req)
|
||||||
|
if err != nil {
|
||||||
|
msg, code := httpmsg.Error(err)
|
||||||
|
return echo.NewHTTPError(code, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, res)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s Server) HealthCheck(c echo.Context) error {
|
||||||
|
return c.JSON(http.StatusOK, echo.Map{
|
||||||
|
"message": "everything is good!",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/pkg/http_server"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
HTTPServer http_server.Server
|
||||||
|
Handler Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(server http_server.Server, handler Handler) Server {
|
||||||
|
return Server{
|
||||||
|
HTTPServer: server,
|
||||||
|
Handler: handler,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Server) Serve() {
|
||||||
|
s.RegisterRoutes()
|
||||||
|
|
||||||
|
if err := s.HTTPServer.Start(); err != nil {
|
||||||
|
fmt.Println("router start error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Server) RegisterRoutes() {
|
||||||
|
v1 := s.HTTPServer.Router.Group("/v1")
|
||||||
|
|
||||||
|
v1.GET("/health_check", s.HealthCheck)
|
||||||
|
v1.POST("/send_otp", s.Handler.SendOtp)
|
||||||
|
v1.POST("/login_or_register", s.Handler.loginOrRegister)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package entity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Driver struct {
|
||||||
|
ID types.ID
|
||||||
|
FirstName string
|
||||||
|
LastName string
|
||||||
|
PhoneNumber string
|
||||||
|
NationalCode string
|
||||||
|
LicenseNumber string
|
||||||
|
BirthDate time.Time
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package service
|
||||||
|
|
||||||
|
import "git.gocasts.ir/ebhomengo/niki/pkg/types"
|
||||||
|
|
||||||
|
type LoginOrRegisterRequest struct {
|
||||||
|
PhoneNumber string `json:"phone_number"`
|
||||||
|
VerifyCode string `json:"verify_code"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoginOrRegisterResponse struct {
|
||||||
|
ID types.ID `json:"id"`
|
||||||
|
PhoneNumber string `json:"phone_number"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Token struct {
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
RefreshToken string `json:"refresh_token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SendOtpRequest struct {
|
||||||
|
PhoneNumber string `json:"phone_number"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SendOtpResponse struct {
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
LengthOfOtpCode int `koanf:"length_of_otp_code"`
|
||||||
|
OtpChars string `koanf:"otp_chars"`
|
||||||
|
OtpExpireTime time.Duration `koanf:"otp_expire_time"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AccountClient interface {
|
||||||
|
SendOTP(ctx context.Context, phoneNumber string) error
|
||||||
|
LoginOrRegister(ctx context.Context, req LoginOrRegisterRequest) (LoginOrRegisterResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type AuthClient interface {
|
||||||
|
CreateAccessToken()
|
||||||
|
CreateRefreshToken()
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
config Config
|
||||||
|
accountClient AccountClient
|
||||||
|
validator Validator
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewService(cfg Config,
|
||||||
|
accountClient AccountClient,
|
||||||
|
validator Validator) Service {
|
||||||
|
return Service{
|
||||||
|
config: cfg,
|
||||||
|
accountClient: accountClient,
|
||||||
|
validator: validator,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Service) SendOtp(ctx context.Context, req SendOtpRequest) (SendOtpResponse, error) {
|
||||||
|
const op = "driverService.SendOtp"
|
||||||
|
err := s.validator.ValidateSendOtpRequest(req)
|
||||||
|
if err != nil {
|
||||||
|
return SendOtpResponse{}, richerror.New(op).WithErr(err).WithMessage(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
sErr := s.accountClient.SendOTP(ctx, req.PhoneNumber)
|
||||||
|
if sErr != nil {
|
||||||
|
return SendOtpResponse{}, richerror.New(op).WithErr(sErr).WithMessage(sErr.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return SendOtpResponse{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Service) LoginOrRegister(ctx context.Context, req LoginOrRegisterRequest) (LoginOrRegisterResponse, error) {
|
||||||
|
const op = "driverService.LoginOrRegister"
|
||||||
|
|
||||||
|
err := s.validator.ValidateLoginOrRegisterRequest(req)
|
||||||
|
if err != nil {
|
||||||
|
return LoginOrRegisterResponse{}, richerror.New(op).WithErr(err).WithMessage(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, lErr := s.accountClient.LoginOrRegister(ctx, req)
|
||||||
|
if lErr != nil {
|
||||||
|
return LoginOrRegisterResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("res:", resp)
|
||||||
|
|
||||||
|
// TODO : CreateAccessToken and create CreateRefreshToken
|
||||||
|
|
||||||
|
return LoginOrRegisterResponse{}, nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
|
||||||
|
validation "github.com/go-ozzo/ozzo-validation"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PhoneNumberRegex = "^(0|0098|\\+98)9(0[1-5]|[1 3]\\d|2[0-2]|98)\\d{7}$"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Validator struct{}
|
||||||
|
|
||||||
|
func NewValidator() Validator {
|
||||||
|
return Validator{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Validator) ValidateSendOtpRequest(req SendOtpRequest) error {
|
||||||
|
err := validation.ValidateStruct(&req,
|
||||||
|
validation.Field(req.PhoneNumber,
|
||||||
|
validation.Required,
|
||||||
|
validation.Match(regexp.MustCompile(PhoneNumberRegex)).Error(errmsg.ErrorMsgPhoneNumberIsNotValid),
|
||||||
|
))
|
||||||
|
|
||||||
|
return err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Validator) ValidateLoginOrRegisterRequest(req LoginOrRegisterRequest) error {
|
||||||
|
err := validation.ValidateStruct(&req,
|
||||||
|
validation.Field(req.PhoneNumber,
|
||||||
|
validation.Required,
|
||||||
|
validation.Match(regexp.MustCompile(PhoneNumberRegex)).Error(errmsg.ErrorMsgPhoneNumberIsNotValid)),
|
||||||
|
validation.Field(req.VerifyCode,
|
||||||
|
validation.Required))
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
3
go.mod
3
go.mod
|
|
@ -61,6 +61,7 @@ require (
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
|
github.com/labstack/gommon v0.4.2 // indirect
|
||||||
github.com/mailru/easyjson v0.7.7 // indirect
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
|
@ -94,7 +95,7 @@ require (
|
||||||
golang.org/x/text v0.35.0 // indirect
|
golang.org/x/text v0.35.0 // indirect
|
||||||
golang.org/x/time v0.14.0 // indirect
|
golang.org/x/time v0.14.0 // indirect
|
||||||
golang.org/x/tools v0.42.0 // indirect
|
golang.org/x/tools v0.42.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260420184626-e10c466a9529 // indirect
|
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
2
main.go
2
main.go
|
|
@ -48,7 +48,7 @@ func MariaDB(cfg config.Config) *mysql.DB {
|
||||||
if *migrate {
|
if *migrate {
|
||||||
migrator.New(migrator.Config{
|
migrator.New(migrator.Config{
|
||||||
MysqlConfig: cfg.Mysql,
|
MysqlConfig: cfg.Mysql,
|
||||||
MigrationPath: "./repository/mysql/migration",
|
MigrationPath: "./repository/mysql/migrations",
|
||||||
MigrationDBName: "gorp_migrations",
|
MigrationDBName: "gorp_migrations",
|
||||||
}).Up()
|
}).Up()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
package migrator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/repository/mysql"
|
||||||
|
migrate "github.com/rubenv/sql-migrate"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Migrator struct {
|
||||||
|
dialect string
|
||||||
|
dbConfig mysql.Config
|
||||||
|
migrations *migrate.FileMigrationSource
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(dbConfig mysql.Config, path string) Migrator {
|
||||||
|
migrations := &migrate.FileMigrationSource{
|
||||||
|
Dir: path,
|
||||||
|
}
|
||||||
|
|
||||||
|
return Migrator{
|
||||||
|
dialect: "mysql",
|
||||||
|
dbConfig: dbConfig,
|
||||||
|
migrations: migrations,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Migrator) Up() {
|
||||||
|
db, err := sql.Open(m.dialect, fmt.Sprintf("%s:%s@(%s:%d)/%s?parseTime=true",
|
||||||
|
m.dbConfig.Username,
|
||||||
|
m.dbConfig.Password,
|
||||||
|
m.dbConfig.Host,
|
||||||
|
m.dbConfig.Port,
|
||||||
|
m.dbConfig.DBName))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
panic(fmt.Errorf("can't open db : %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := migrate.Exec(db, m.dialect, m.migrations, migrate.Up)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("can't apply migrations: %v", err))
|
||||||
|
}
|
||||||
|
fmt.Printf("Applied %d migrations!\n", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Migrator) Down() {
|
||||||
|
db, err := sql.Open(m.dialect, fmt.Sprintf("%s:%s@(%s:%d)/%s?parseTime=true",
|
||||||
|
m.dbConfig.Username,
|
||||||
|
m.dbConfig.Password,
|
||||||
|
m.dbConfig.Host,
|
||||||
|
m.dbConfig.Port,
|
||||||
|
m.dbConfig.DBName))
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("can't open db connection: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := migrate.Exec(db, m.dialect, m.migrations, migrate.Down)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("can't rollback migrations: %v", err))
|
||||||
|
}
|
||||||
|
fmt.Printf("Rollback %d migrations!\n", n)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
type ID uint64
|
||||||
|
|
@ -20,7 +20,7 @@ type Migrator struct {
|
||||||
migrations *migrate.FileMigrationSource
|
migrations *migrate.FileMigrationSource
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO - set migration table name
|
// TODO - set migrations table name
|
||||||
// TODO - add limit to Up and Down method
|
// TODO - add limit to Up and Down method
|
||||||
|
|
||||||
func New(cfg Config) Migrator {
|
func New(cfg Config) Migrator {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
production:
|
production:
|
||||||
dialect: mysql
|
dialect: mysql
|
||||||
datasource: niki:nikiappt0lk2o20@(localhost:3306)/niki_db?parseTime=true
|
datasource: niki:nikiappt0lk2o20@(localhost:3306)/niki_db?parseTime=true
|
||||||
dir: repository/mysql/migration
|
dir: repository/mysql/migrations
|
||||||
table: gorp_migrations
|
table: gorp_migrations
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue