From 874d762efa607b4e844a10ca4e9114379d463db8 Mon Sep 17 00:00:00 2001 From: Nikolai Rodionov Date: Tue, 28 Apr 2026 10:33:20 +0200 Subject: [PATCH] A couple of updates Signed-off-by: Nikolai Rodionov --- .env.dev | 2 + Taskfile.yml | 40 +++++++ api/v1/accounts.go | 2 +- helmfile.yaml.gotmpl | 1 + internal/controllers/accounts.go | 11 +- main.go | 178 +++++++++++-------------------- 6 files changed, 117 insertions(+), 117 deletions(-) create mode 100644 .env.dev create mode 100644 Taskfile.yml diff --git a/.env.dev b/.env.dev new file mode 100644 index 0000000..ba6aa41 --- /dev/null +++ b/.env.dev @@ -0,0 +1,2 @@ +export SOFTPLAYER_DB_CONNECTION_STRING=postgres://softplayer:qwertyu9@localhost:30432/softplayer?sslmode=disable +export SOFYTPLAYER_REDIS_HOST=localhost:30379 diff --git a/Taskfile.yml b/Taskfile.yml new file mode 100644 index 0000000..d9bfa02 --- /dev/null +++ b/Taskfile.yml @@ -0,0 +1,40 @@ +# yaml-language-server: $schema=https://taskfile.dev/schema.json + +version: "3" + +tasks: + build: + desc: Build go code + cmd: go build + silent: true + + run-migrations-dev: + desc: Execute database migrations + env: + SOFTPLAYER_DB_CONNECTION_STRING: postgres://softplayer:qwertyu9@localhost:30432/softplayer?sslmode=disable + cmd: go run main.go migrate --migrations-path=file://migrations + + run-server-dev: + desc: Run the local dev server + env: + SOFTPLAYER_DB_CONNECTION_STRING: postgres://softplayer:qwertyu9@localhost:30432/softplayer?sslmode=disable + SOFTPLAYER_REDIS_HOST: localhost:30379 + cmd: go run main.go serve --dev-mode --reflection + + deploy-local-env: + desc: Run a kind cluster and deploy deps + deps: + - kind-cluster + - helmfile-deploy + + kind-cluster: + desc: Run a kind cluster + cmd: kind create cluster --config ./kind-config.yaml + + kind-cluster-remove: + desc: Remove the kind cluster + cmd: kind delete cluster + + helmfile-deploy: + desc: Deploy the helmfile for the local dev + cmd: helmfile apply diff --git a/api/v1/accounts.go b/api/v1/accounts.go index 3bb33b9..be597e9 100644 --- a/api/v1/accounts.go +++ b/api/v1/accounts.go @@ -46,7 +46,7 @@ func (a *AccountsNoAuthServer) SignUp(ctx context.Context, in *accounts.SignUpRe return nil, status.Error(codes.Aborted, "Couldn't generate Access Token") } - refreshToken, err := a.ctrl.GenerateRefreshToken(uuid) + refreshToken, err := a.ctrl.GenerateRefreshToken(ctx, uuid) if err != nil { log.Error(err, "Couldn't generate a refresh token") return nil, status.Error(codes.Aborted, "Couldn't generate Access Token") diff --git a/helmfile.yaml.gotmpl b/helmfile.yaml.gotmpl index a404f97..fe66f6d 100644 --- a/helmfile.yaml.gotmpl +++ b/helmfile.yaml.gotmpl @@ -1,5 +1,6 @@ environments: default: + kubeContext: kind-kind values: - databases: postgres: diff --git a/internal/controllers/accounts.go b/internal/controllers/accounts.go index d4757bf..e11d151 100644 --- a/internal/controllers/accounts.go +++ b/internal/controllers/accounts.go @@ -3,6 +3,7 @@ package controllers import ( "context" "database/sql" + "fmt" "time" "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/helpers/hash" @@ -17,7 +18,7 @@ type AccountController struct { DevMode bool HashCost int16 AccessTokenTTL time.Duration - RefrestTokenTTL time.Duration + RefreshTokenTTL time.Duration JWTSecret []byte } @@ -62,15 +63,19 @@ func (c *AccountController) GenerateAccessToken(userID string) (string, error) { return token.SignedString(c.JWTSecret) } -func (c *AccountController) GenerateRefreshToken(userID string) (string, error) { +func (c *AccountController) GenerateRefreshToken(ctx context.Context, userID string) (string, error) { tokenID := uuid.New().String() claims := jwt.MapClaims{ "user_id": userID, "token_id": tokenID, "type": "refresh", - "exp": time.Now().Add(c.RefrestTokenTTL).Unix(), + "exp": time.Now().Add(c.RefreshTokenTTL).Unix(), } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + redisKey := fmt.Sprintf("refresh_token:%s", tokenID) + if err := c.Redis.Set(ctx, redisKey, userID, c.RefreshTokenTTL).Err(); err != nil { + return "", err + } return token.SignedString(c.JWTSecret) } diff --git a/main.go b/main.go index ea96cc8..98d4f7d 100644 --- a/main.go +++ b/main.go @@ -6,52 +6,54 @@ import ( "errors" "fmt" "net" - "os" - "strings" "time" v1 "gitea.badhouseplants.net/softplayer/softplayer-backend/api/v1" "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/controllers" - "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/helpers/email" "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/tools/logger" accounts "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/accounts/v1" - applications_proto "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/applications/v1" - email_proto "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/email/v1" - "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/environments/v1" "github.com/alecthomas/kong" - "github.com/golang-jwt/jwt/v5" "github.com/golang-migrate/migrate/v4" "github.com/golang-migrate/migrate/v4/database/postgres" _ "github.com/golang-migrate/migrate/v4/source/file" grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap" _ "github.com/lib/pq" - "github.com/redis/go-redis/v9" "google.golang.org/grpc" - "google.golang.org/grpc/metadata" "google.golang.org/grpc/reflection" - ctrl "sigs.k8s.io/controller-runtime" + + "github.com/redis/go-redis/v9" ) -type Serve struct { - Port int64 `short:"p" env:"SOFTPLAYER_PORT" default:"4020"` - Host string `env:"SOFTPLAYER_HOST" default:"0.0.0.0"` - HashCost int16 `env:"SOFTPLAYER_HASH_COST" default:"1"` - Reflection bool `env:"SOFTPLAYER_REFLECTION" default:"false"` - DevMode bool `env:"SOFTPLAYER_DEV_MODE" default:"false"` - SmtpHost string `env:"SOFTPLAYER_SMTP_HOST"` - SmtpPort string `env:"SOFTPLAYER_SMTP_PORT" default:"587"` - SmtpFrom string `env:"SOFTPLAYER_SMTP_FROM" default:"overlord@badhouseplants.net"` - SmtpPassword string `env:"SOFTPLAYER_SMTP_PASSWORD"` - DownloadDir string `env:"SOFTPLAYER_DOWNLOAD_DIR" default:"/tmp/softplayer"` - DBConnectionString string `env:"SOFTPLAYER_DB_CONNECTION_STRING"` - RedisHost string `env:"SOFTPLAYER_REDIS_HOST"` - RefrestTokenTTL time.Duration `default:"8h"` - AccessTokenTTL time.Duration `default:"15m"` - JWTSecret string `default:"qwertyu9"` +var CLI struct { + Serve Serve `cmd:"" help:"Start the grpc server"` + Migrate Migrate `cmd:"" help:"Run the database migrations"` } -var CLI struct { - Serve Serve `cmd:"" help:"Start the grpc server"` +type Serve struct { + // Service related + Port int64 `short:"p" env:"SOFTPLAYER_PORT" default:"4020"` + Host string `env:"SOFTPLAYER_HOST" default:"0.0.0.0"` + HashCost int16 `env:"SOFTPLAYER_HASH_COST" default:"1"` + // SMTP Config + SMTPHost string `env:"SOFTPLAYER_SMTP_HOST"` + SMTPPort string `env:"SOFTPLAYER_SMTP_PORT" default:"587"` + SMTPFrom string `env:"SOFTPLAYER_SMTP_FROM" default:"overlord@badhouseplants.net"` + SMTPPassword string `env:"SOFTPLAYER_SMTP_PASSWORD"` + // Database and redis + DBConnectionString string `env:"SOFTPLAYER_DB_CONNECTION_STRING"` + RedisHost string `env:"SOFTPLAYER_REDIS_HOST"` + // JWT parameters + RefrestTokenTTL time.Duration `default:"8h"` + AccessTokenTTL time.Duration `default:"15m"` + JWTSecret string `default:"qwertyu9"` + // Dev and logging + Reflection bool `env:"SOFTPLAYER_REFLECTION" default:"false"` + DevMode bool `env:"SOFTPLAYER_DEV_MODE" default:"false"` +} + +type Migrate struct { + DBConnectionString string `env:"SOFTPLAYER_DB_CONNECTION_STRING"` + MigrationsPath string `env:"SOFTPLAYER_DB_MIGRATIOON_PATH" default:"file://migrations"` } func main() { @@ -62,15 +64,25 @@ func main() { if err := server(ctx, CLI.Serve); err != nil { panic(err) } + case "migrate": + if err := migrateDB(ctx, CLI.Migrate); err != nil { + panic(err) + } default: panic(kongCtx.Command()) } } -func migrateDB(ctx context.Context, db *sql.DB) error { +// Migrate the database to the latest version +func migrateDB(ctx context.Context, params Migrate) error { log := logger.FromContext(ctx) - log.Info("Starting a database migration driver") + db, err := sql.Open("postgres", params.DBConnectionString) + if err != nil { + log.Error(err, "Couldn't start a database driver") + return err + } + driver, err := postgres.WithInstance(db, &postgres.Config{}) if err != nil { log.Error(err, "Couldn't start a database migration driver") @@ -79,7 +91,7 @@ func migrateDB(ctx context.Context, db *sql.DB) error { log.Info("Preparing database migrations") m, err := migrate.NewWithDatabaseInstance( - "file://migrations", + params.MigrationsPath, "postgres", driver) if err != nil { log.Error(err, "Couldn't perform database migrations") @@ -87,7 +99,7 @@ func migrateDB(ctx context.Context, db *sql.DB) error { } log.Info("Starting database migrations") - err = m.Up() // or m.Steps(2) if you want to explicitly set the number of migrations to r + err = m.Up() if err != nil { if errors.Is(err, migrate.ErrNoChange) { log.Info("Database is already up-to-date") @@ -100,93 +112,40 @@ func migrateDB(ctx context.Context, db *sql.DB) error { return nil } -func extractToken(ctx context.Context) (string, error) { - md, ok := metadata.FromIncomingContext(ctx) - if !ok { - return "", fmt.Errorf("missing metadata") - } - - auth := md.Get("authorization") - if len(auth) == 0 { - return "", fmt.Errorf("missing auth token") - } - - token := strings.TrimPrefix(auth[0], "Bearer ") - return token, nil -} - -func AuthInterceptor( - ctx context.Context, - req interface{}, - info *grpc.UnaryServerInfo, - handler grpc.UnaryHandler, -) (interface{}, error) { - if !strings.Contains(info.FullMethod, "NoAuth") { - tokenString, err := extractToken(ctx) - if err != nil { - return nil, err - } - - token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) { - // hmacSampleSecret is a []byte containing your secret, e.g. []byte("my_secret_key") - return []byte("qwertyu9"), nil - }, jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Alg()})) - if err != nil { - return nil, err - } - - if claims, ok := token.Claims.(jwt.MapClaims); ok { - fmt.Println(claims["foo"], claims["nbf"]) - } else { - fmt.Println(err) - } - - return handler(ctx, req) - } - return nil, nil -} - +// Run the grpc backend server func server(ctx context.Context, params Serve) error { // Make sure the download dir exists log := logger.FromContext(ctx) - if err := os.MkdirAll(params.DownloadDir, 0o777); err != nil { - log.Error(err, "Coulnd't create a download dir") - return err - } - // Init a logger - log.Info("Starting a kubernetes manager") - controller, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{}) - if err != nil { - log.Error(err, "Coulnd't start a kube manager") - return err - } + //log.Info("Starting a kubernetes manager") + //controller, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{}) + //if err != nil { + // log.Error(err, "Coulnd't start a kube manager") + // return err + //} // TODO: Handle the error - go func() { - if err := controller.Start(ctx); err != nil { - panic(err) - } - }() + //go func() { + // if err := controller.Start(ctx); err != nil { + // panic(err) + // } + //}() log.Info("Opening a database connection") + db, err := sql.Open("postgres", params.DBConnectionString) if err != nil { log.Error(err, "Couldn't start a database driver") return err } - if err := migrateDB(ctx, db); err != nil { - log.Error(err, "Error occured while executing migrations") - return err - } - emailConfig := email.EmailConf{ - From: params.SmtpFrom, - Password: params.SmtpPassword, - SmtpHost: params.SmtpHost, - SmtpPort: params.SmtpPort, - } + //emailConfig := email.EmailConf{ + // From: params.SmtpFrom, + // Password: params.SmtpPassword, + // SmtpHost: params.SmtpHost, + // SmtpPort: params.SmtpPort, + //} address := fmt.Sprintf("%s:%d", params.Host, params.Port) lis, err := net.Listen("tcp", address) @@ -197,17 +156,12 @@ func server(ctx context.Context, params Serve) error { grpc.UnaryInterceptor( grpc_zap.UnaryServerInterceptor(logger.SetupLogger("info")), ), - grpc.UnaryInterceptor(AuthInterceptor), grpc.StreamInterceptor(grpc_zap.StreamServerInterceptor(logger.SetupLogger("info"))), ) rdb := redis.NewClient(&redis.Options{ Addr: params.RedisHost, }) - err = rdb.Set(ctx, "key", "value", 0).Err() - if err != nil { - panic(err) - } if params.Reflection { reflection.Register(grpcServer) } @@ -216,14 +170,12 @@ func server(ctx context.Context, params Serve) error { HashCost: params.HashCost, DB: db, DevMode: params.DevMode, - RefrestTokenTTL: params.RefrestTokenTTL, + RefreshTokenTTL: params.RefrestTokenTTL, AccessTokenTTL: params.AccessTokenTTL, JWTSecret: []byte(params.JWTSecret), + Redis: rdb, } - environments.RegisterEnvironmentsServer(grpcServer, v1.NewapiGrpcImpl(controller, log)) accounts.RegisterAccountsNoAuthServiceServer(grpcServer, v1.NewAccountRPCImpl(accountCtrl)) - email_proto.RegisterEmailValidationServer(grpcServer, v1.InitEmailServer(controller, &emailConfig, params.DevMode)) - applications_proto.RegisterApplicationsServer(grpcServer, v1.NewApplicationsGrpcImpl(controller, log)) if err := grpcServer.Serve(lis); err != nil { return err