Merge pull request 'implement-account-services' (#4) from implement-account-services into main
Reviewed-on: softplayer/softplayer-backend#4
This commit was merged in pull request #4.
This commit is contained in:
@@ -10,4 +10,5 @@ RUN CGO_ENABLED=0 GOOS=linux go build -o backend
|
||||
FROM scratch
|
||||
COPY --from=0 /app/backend /app
|
||||
COPY --from=0 /etc/ssl /etc/ssl
|
||||
COPY migrations /migrations
|
||||
ENTRYPOINT ["/app"]
|
||||
|
||||
40
Taskfile.yml
Normal file
40
Taskfile.yml
Normal file
@@ -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
|
||||
@@ -1,68 +0,0 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/controllers"
|
||||
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/helpers/email"
|
||||
"gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/accounts/v1"
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
func NewAccountRPCImpl(db *sql.DB, hashCost int16, email *email.EmailConf, devMode bool) *AccountsServer {
|
||||
return &AccountsServer{
|
||||
Params: &controllers.AccountParams{
|
||||
HashCost: hashCost,
|
||||
},
|
||||
SQLDriver: db,
|
||||
emailConfig: *email,
|
||||
devMode: devMode,
|
||||
}
|
||||
}
|
||||
|
||||
type AccountsServer struct {
|
||||
accounts.UnimplementedAccountsServer
|
||||
Params *controllers.AccountParams
|
||||
SQLDriver *sql.DB
|
||||
emailConfig email.EmailConf
|
||||
// When dev mode is enabled, email won't be sent, instead the code will be returned in metadata
|
||||
devMode bool
|
||||
}
|
||||
|
||||
func (a *AccountsServer) SignUp(ctx context.Context, in *accounts.AccountWithPassword) (*accounts.AccountFullWithToken, error) {
|
||||
accountCtrl := controllers.AccountController{
|
||||
Params: controllers.AccountParams{
|
||||
HashCost: a.Params.HashCost,
|
||||
},
|
||||
DB: a.SQLDriver,
|
||||
DevMode: a.devMode,
|
||||
}
|
||||
|
||||
if err := accountCtrl.Create(ctx, &controllers.AccountData{
|
||||
Username: in.Data.GetName(),
|
||||
Password: in.Password.GetPassword(),
|
||||
Email: in.Data.GetEmail(),
|
||||
}); err != nil {
|
||||
return nil, status.Error(codes.Aborted, "Couldn't create a user")
|
||||
}
|
||||
return &accounts.AccountFullWithToken{
|
||||
Id: &accounts.AccountId{},
|
||||
Data: &accounts.AccountData{},
|
||||
Token: "",
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *AccountsServer) SignIn(ctx context.Context, in *accounts.AccountWithPassword) (*accounts.AccountFullWithToken, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "Endpoint is not implemented")
|
||||
}
|
||||
|
||||
func (a *AccountsServer) ResetPassword(ctx context.Context, in *accounts.AccountData) (*empty.Empty, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "Endpoint is not implemented")
|
||||
}
|
||||
|
||||
func (acc *AccountsServer) NewPassword(ctx context.Context, in *accounts.AccountWithPasswordAndCode) (*empty.Empty, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "Endpoint is not implemented")
|
||||
}
|
||||
59
api/v1/accounts_auth.go
Normal file
59
api/v1/accounts_auth.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/controllers"
|
||||
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/tools/logger"
|
||||
accounts "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/accounts/v1"
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
|
||||
func NewAccountAuthRPCImpl(ctrl *controllers.AccountController) *AccountsAuthServer {
|
||||
return &AccountsAuthServer{
|
||||
ctrl: ctrl,
|
||||
}
|
||||
}
|
||||
|
||||
type AccountsAuthServer struct {
|
||||
accounts.UnimplementedAccountsAuthServiceServer
|
||||
ctrl *controllers.AccountController
|
||||
}
|
||||
|
||||
func (a *AccountsAuthServer) RefreshToken(ctx context.Context, in *empty.Empty) (*empty.Empty, error) {
|
||||
tokenID := ctx.Value("token_id").(string)
|
||||
userID := ctx.Value("user_id").(string)
|
||||
log := logger.FromContext(ctx)
|
||||
uuid, err := a.ctrl.ValidateRefreshToken(ctx, tokenID, userID)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Unauthenticated, "refresh token is invalid")
|
||||
}
|
||||
accessToken, err := a.ctrl.GenerateAccessToken(uuid)
|
||||
if err != nil {
|
||||
log.Error(err, "Couldn't generate an access token")
|
||||
return nil, status.Error(codes.Aborted, "Couldn't generate Access Token")
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
header := metadata.Pairs(
|
||||
"access-token", accessToken,
|
||||
"refreshToken", refreshToken,
|
||||
)
|
||||
|
||||
if err := grpc.SetHeader(ctx, header); err != nil {
|
||||
log.Error(err, "Couldn't set headers")
|
||||
return nil, status.Error(codes.Unknown, "Couldn't set headers")
|
||||
}
|
||||
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
73
api/v1/accounts_no_auth.go
Normal file
73
api/v1/accounts_no_auth.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/controllers"
|
||||
accounts "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/accounts/v1"
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
"golang.org/x/oauth2"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
|
||||
func NewAccountNoAuthRPCImpl(ctrl *controllers.AccountController) *AccountsNoAuthServer {
|
||||
return &AccountsNoAuthServer{
|
||||
ctrl: ctrl,
|
||||
}
|
||||
}
|
||||
|
||||
type AccountsNoAuthServer struct {
|
||||
accounts.UnimplementedAccountsNoAuthServiceServer
|
||||
ctrl *controllers.AccountController
|
||||
}
|
||||
|
||||
func (a *AccountsNoAuthServer) SignIn(ctx context.Context, in *accounts.SignInRequest) (*empty.Empty, error) {
|
||||
provider, err := oidc.NewProvider(ctx, "https://authentik.badhouseplants.net")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Configure an OpenID Connect aware OAuth2 client.
|
||||
oauth2Config := oauth2.Config{
|
||||
ClientID: "softplayer-localhost",
|
||||
ClientSecret: "pRpe3scGUE2jNH6t5rqI9R4OROeQHs4eO6ku957mYjDumKhQGX8QJcO0BMJ2FG4sUpvFrqccEqWgc3wKMp94tC8LyvTnkPF0Tg0CaldAEHuoQQdNKAzXVxwrHE6kNyBC",
|
||||
RedirectURL: "http://localhost:8080/#/auth/callback",
|
||||
|
||||
// Discovery returns the OAuth2 endpoints.
|
||||
Endpoint: provider.Endpoint(),
|
||||
|
||||
// "openid" is a required scope for OpenID Connect flows.
|
||||
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
|
||||
}
|
||||
verifier := provider.Verifier(&oidc.Config{ClientID: "softplayer-localhost"})
|
||||
|
||||
oauth2Token, err := oauth2Config.Exchange(ctx, in.Code)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Extract the ID Token from OAuth2 token.
|
||||
rawIDToken, ok := oauth2Token.Extra("id_token").(string)
|
||||
if !ok {
|
||||
return nil, status.Error(codes.Unauthenticated, "Couldn't parse oauth token")
|
||||
}
|
||||
|
||||
// Parse and verify ID Token payload.
|
||||
idToken, err := verifier.Verify(ctx, rawIDToken)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Unauthenticated, "Couldn't verify oauth token")
|
||||
}
|
||||
|
||||
// Extract custom claims
|
||||
var claims struct {
|
||||
Email string `json:"email"`
|
||||
Verified bool `json:"email_verified"`
|
||||
}
|
||||
if err := idToken.Claims(&claims); err != nil {
|
||||
// handle error
|
||||
}
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/controllers"
|
||||
proto "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/applications/v1"
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
)
|
||||
|
||||
func NewApplicationsGrpcImpl(controller ctrl.Manager, log logr.Logger) *ApplicationServer {
|
||||
return &ApplicationServer{
|
||||
controller: controller,
|
||||
logInstance: log,
|
||||
}
|
||||
}
|
||||
|
||||
type ApplicationServer struct {
|
||||
proto.UnimplementedApplicationsServer
|
||||
controller ctrl.Manager
|
||||
logInstance logr.Logger
|
||||
}
|
||||
|
||||
// Create an environment
|
||||
func (app *ApplicationServer) Create(ctx context.Context, in *proto.CreateOptions) (*proto.ApplicationFull, error) {
|
||||
log := app.logInstance
|
||||
log.WithValues("user_id", in.GetOwnerId().GetUuid(), "environment_id", in.GetSpec().GetEnvironemntId(), "app_name", in.GetSpec().GetApplication())
|
||||
ctx = logr.NewContext(ctx, log)
|
||||
|
||||
data := &controllers.ApplicationData{
|
||||
Name: in.Metadata.Name,
|
||||
Application: in.Spec.Application,
|
||||
Version: in.Spec.Version,
|
||||
Environemnt: in.Spec.EnvironemntId,
|
||||
Config: in.Spec.Config,
|
||||
RawConfig: "",
|
||||
}
|
||||
|
||||
application := &controllers.Application{
|
||||
UserID: in.GetOwnerId().GetUuid(),
|
||||
Controller: app.controller,
|
||||
Data: data,
|
||||
Token: in.GetToken().GetToken(),
|
||||
}
|
||||
|
||||
err := application.Create(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &proto.ApplicationFull{
|
||||
Metadata: in.GetMetadata(),
|
||||
Id: &proto.ApplicationId{
|
||||
Uuid: application.Data.UUID,
|
||||
},
|
||||
Spec: in.GetSpec(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (app *ApplicationServer) Delete(ctx context.Context, in *proto.DeleteOptions) (*empty.Empty, error) {
|
||||
log := app.logInstance
|
||||
log.WithValues("user_id", in.GetOwnerId().GetUuid(), "app_id", in.GetId().GetUuid())
|
||||
ctx = logr.NewContext(ctx, log)
|
||||
|
||||
data := &controllers.ApplicationData{
|
||||
Name: in.Metadata.Name,
|
||||
UUID: in.GetId().GetUuid(),
|
||||
}
|
||||
|
||||
application := &controllers.Application{
|
||||
UserID: in.GetOwnerId().GetUuid(),
|
||||
Controller: app.controller,
|
||||
Data: data,
|
||||
Token: in.GetToken().GetToken(),
|
||||
}
|
||||
|
||||
err := application.Delete(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/controllers"
|
||||
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/helpers/email"
|
||||
proto_email "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/email/v1"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
)
|
||||
|
||||
type EmailServer struct {
|
||||
proto_email.UnimplementedEmailValidationServer
|
||||
emailConfig email.EmailConf
|
||||
controller ctrl.Manager
|
||||
// When dev mode is enabled, email won't be sent, instead the code will be returned in metadata
|
||||
devMode bool
|
||||
}
|
||||
|
||||
func InitEmailServer(controller ctrl.Manager, emailConfig *email.EmailConf, devMode bool) *EmailServer {
|
||||
return &EmailServer{
|
||||
controller: controller,
|
||||
emailConfig: *emailConfig,
|
||||
devMode: devMode,
|
||||
}
|
||||
}
|
||||
|
||||
// Send the validation code to email
|
||||
func (c *EmailServer) SendRequest(ctx context.Context, in *proto_email.RequestValidation) (*emptypb.Empty, error) {
|
||||
// Validation
|
||||
if len(in.GetUserId()) == 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "user id must not be empty")
|
||||
}
|
||||
|
||||
// Body
|
||||
emailSvc := controllers.EmailSvc{
|
||||
Data: controllers.EmailData{
|
||||
UserID: in.GetUserId(),
|
||||
},
|
||||
EmailConfig: c.emailConfig,
|
||||
Controller: c.controller,
|
||||
DevMode: c.devMode,
|
||||
}
|
||||
err := emailSvc.SendVerification(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if c.devMode {
|
||||
header := metadata.Pairs("code", emailSvc.Data.Code)
|
||||
if err := grpc.SendHeader(ctx, header); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
func (c *EmailServer) ValidateEmail(ctx context.Context, in *proto_email.ConfirmValidation) (*emptypb.Empty, error) {
|
||||
// Validation
|
||||
if len(in.GetUserId()) == 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "user id must not be empty")
|
||||
}
|
||||
if in.GetCode() == 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "code must not be empty")
|
||||
}
|
||||
|
||||
// Body
|
||||
emailSvc := controllers.EmailSvc{
|
||||
Data: controllers.EmailData{
|
||||
UserID: in.GetUserId(),
|
||||
Code: fmt.Sprintf("%d", in.GetCode()),
|
||||
},
|
||||
EmailConfig: c.emailConfig,
|
||||
Controller: c.controller,
|
||||
}
|
||||
err := emailSvc.ConfirmVerification(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
@@ -1,261 +0,0 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/controllers"
|
||||
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/providers/infra"
|
||||
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/providers/kubernetes"
|
||||
proto "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/environments/v1"
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
)
|
||||
|
||||
func NewapiGrpcImpl(controller ctrl.Manager, log logr.Logger) *EnvironmentsServer {
|
||||
return &EnvironmentsServer{
|
||||
controller: controller,
|
||||
logInstance: log,
|
||||
}
|
||||
}
|
||||
|
||||
type EnvironmentsServer struct {
|
||||
proto.UnimplementedEnvironmentsServer
|
||||
controller ctrl.Manager
|
||||
logInstance logr.Logger
|
||||
}
|
||||
|
||||
// Create an environment
|
||||
func (e *EnvironmentsServer) Create(ctx context.Context, in *proto.CreateOptions) (*proto.EnvironmentFull, error) {
|
||||
log := e.logInstance
|
||||
log.WithValues("user_id", in.GetOwnerId().GetUuid())
|
||||
ctx = logr.NewContext(ctx, log)
|
||||
|
||||
provider, err := infra.GetProvider(in.GetSpec().GetProvider().String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
k8s, err := kubernetes.GetKubernetes(in.GetSpec().GetKubernetes().String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
location, err := provider.GetServerLocation(in.GetSpec().GetServerLocation().String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
serverType, err := provider.GetServerType(in.Spec.ServerType.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data := &controllers.EnvironemntData{
|
||||
Name: in.GetMetadata().GetName(),
|
||||
Description: in.GetMetadata().GetDescription(),
|
||||
Provider: provider.GetProviderName(),
|
||||
Kubernetes: k8s.GetKubernetesName(),
|
||||
Location: location,
|
||||
ServerType: serverType,
|
||||
DiskSize: int(in.GetSpec().GetDiskSize()),
|
||||
}
|
||||
|
||||
environment := &controllers.Environemnt{
|
||||
UserID: in.GetOwnerId().GetUuid(),
|
||||
Config: ctrl.GetConfigOrDie(),
|
||||
Controller: e.controller,
|
||||
Data: data,
|
||||
Token: in.GetToken().GetToken(),
|
||||
}
|
||||
|
||||
err = environment.Create(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &proto.EnvironmentFull{
|
||||
Metadata: in.GetMetadata(),
|
||||
Id: &proto.EnvironmentId{
|
||||
Uuid: environment.Data.UUID,
|
||||
},
|
||||
Spec: in.GetSpec(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (e *EnvironmentsServer) Update(ctx context.Context, in *proto.UpdateOptions) (*proto.EnvironmentFull, error) {
|
||||
log := e.logInstance
|
||||
log.WithValues("user_id", in.GetOwnerId().GetUuid())
|
||||
ctx = logr.NewContext(ctx, log)
|
||||
|
||||
provider, err := infra.GetProvider(in.GetSpec().GetProvider().String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
k8s, err := kubernetes.GetKubernetes(in.GetSpec().GetKubernetes().String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
location, err := provider.GetServerLocation(in.GetSpec().GetServerLocation().String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
serverType, err := provider.GetServerType(in.Spec.ServerType.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data := &controllers.EnvironemntData{
|
||||
Name: in.GetMetadata().GetName(),
|
||||
UUID: in.GetId().GetUuid(),
|
||||
Description: in.GetMetadata().GetDescription(),
|
||||
Provider: provider.GetProviderName(),
|
||||
Kubernetes: k8s.GetKubernetesName(),
|
||||
Location: location,
|
||||
ServerType: serverType,
|
||||
DiskSize: int(in.GetSpec().GetDiskSize()),
|
||||
}
|
||||
|
||||
environment := &controllers.Environemnt{
|
||||
Config: ctrl.GetConfigOrDie(),
|
||||
UserID: in.GetOwnerId().GetUuid(),
|
||||
Controller: e.controller,
|
||||
Data: data,
|
||||
Token: in.GetToken().GetToken(),
|
||||
}
|
||||
|
||||
err = environment.Update(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &proto.EnvironmentFull{
|
||||
Metadata: in.GetMetadata(),
|
||||
Id: in.GetId(),
|
||||
Spec: in.GetSpec(),
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func (e *EnvironmentsServer) Delete(ctx context.Context, in *proto.DeleteOptions) (*empty.Empty, error) {
|
||||
log := e.logInstance
|
||||
log.WithValues("user_id", in.GetOwnerId().GetUuid())
|
||||
ctx = logr.NewContext(ctx, log)
|
||||
|
||||
data := &controllers.EnvironemntData{
|
||||
Name: in.GetMetadata().GetName(),
|
||||
UUID: in.GetId().GetUuid(),
|
||||
}
|
||||
|
||||
environment := &controllers.Environemnt{
|
||||
Config: ctrl.GetConfigOrDie(),
|
||||
UserID: in.GetOwnerId().GetUuid(),
|
||||
Controller: e.controller,
|
||||
Data: data,
|
||||
Token: in.GetToken().GetToken(),
|
||||
}
|
||||
err := environment.Delete(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (e *EnvironmentsServer) Get(ctx context.Context, in *proto.GetOptions) (*proto.EnvironmentFull, error) {
|
||||
log := e.logInstance
|
||||
log.WithValues("user_id", in.GetOwnerId().GetUuid())
|
||||
ctx = logr.NewContext(ctx, log)
|
||||
|
||||
data := &controllers.EnvironemntData{
|
||||
UUID: in.GetId().GetUuid(),
|
||||
}
|
||||
|
||||
environment := &controllers.Environemnt{
|
||||
Config: ctrl.GetConfigOrDie(),
|
||||
UserID: in.GetOwnerId().GetUuid(),
|
||||
Controller: e.controller,
|
||||
Data: data,
|
||||
Token: in.GetToken().GetToken(),
|
||||
}
|
||||
|
||||
if err := environment.Get(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
provider, err := infra.GetProvider(environment.Data.Provider)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
k8s, err := kubernetes.GetKubernetes(environment.Data.Kubernetes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &proto.EnvironmentFull{
|
||||
Spec: &proto.EnvironmentSpec{
|
||||
Provider: proto.Provider(proto.Provider_value[provider.RawProviderName()]),
|
||||
Kubernetes: proto.Kubernetes(proto.Kubernetes_value[k8s.RawKubernetesName()]),
|
||||
ServerLocation: proto.Location(proto.Location_value[provider.RawServerLocation(environment.Data.Location)]),
|
||||
ServerType: proto.ServerType(proto.ServerType_value[provider.RawServerType(environment.Data.ServerType)]),
|
||||
DiskSize: int32(environment.Data.DiskSize),
|
||||
},
|
||||
Id: in.GetId(),
|
||||
Metadata: &proto.EnvironmentMetadata{
|
||||
Name: environment.Data.Name,
|
||||
Description: environment.Data.Description,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (e *EnvironmentsServer) List(in *proto.ListOptions, stream proto.Environments_ListServer) error {
|
||||
logInstance := e.logInstance
|
||||
log := logInstance.WithValues("user_id", in.GetOwnerId().GetUuid())
|
||||
ctx := logr.NewContext(stream.Context(), log)
|
||||
|
||||
environment := &controllers.Environemnt{
|
||||
Config: ctrl.GetConfigOrDie(),
|
||||
UserID: in.GetOwnerId().GetUuid(),
|
||||
Controller: e.controller,
|
||||
Token: in.GetToken().GetToken(),
|
||||
}
|
||||
|
||||
envs, err := environment.List(ctx, in.GetSearchString())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, env := range envs {
|
||||
provider, err := infra.GetProvider(env.Data.Provider)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
k8s, err := kubernetes.GetKubernetes(env.Data.Kubernetes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := stream.Send(&proto.EnvironmentFull{
|
||||
Metadata: &proto.EnvironmentMetadata{
|
||||
Name: env.Data.Name,
|
||||
Description: env.Data.Description,
|
||||
},
|
||||
Id: &proto.EnvironmentId{
|
||||
Uuid: env.Data.UUID,
|
||||
},
|
||||
Spec: &proto.EnvironmentSpec{
|
||||
Provider: proto.Provider(proto.Provider_value[provider.RawProviderName()]),
|
||||
Kubernetes: proto.Kubernetes(proto.Kubernetes_value[k8s.RawKubernetesName()]),
|
||||
ServerLocation: proto.Location(proto.Location_value[provider.RawServerLocation(env.Data.Location)]),
|
||||
ServerType: proto.ServerType(proto.ServerType_value[provider.RawServerType(env.Data.ServerType)]),
|
||||
DiskSize: int32(env.Data.DiskSize),
|
||||
},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
22
api/v1/test_auth.go
Normal file
22
api/v1/test_auth.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/tools/logger"
|
||||
test "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/test/v1"
|
||||
)
|
||||
|
||||
func NewTestAuthRPCImpl() *TestAuthServer {
|
||||
return &TestAuthServer{}
|
||||
}
|
||||
|
||||
type TestAuthServer struct {
|
||||
test.UnimplementedTestAuthServiceServer
|
||||
}
|
||||
|
||||
func (t *TestAuthServer) Pong(ctx context.Context, in *test.PongRequest) (*test.PongResponse, error) {
|
||||
log := logger.FromContext(ctx)
|
||||
log.Info("Pong")
|
||||
return &test.PongResponse{}, nil
|
||||
}
|
||||
22
api/v1/test_no_auth.go
Normal file
22
api/v1/test_no_auth.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/tools/logger"
|
||||
test "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/test/v1"
|
||||
)
|
||||
|
||||
func NewTestNoAuthRPCImpl() *TestNoAuthServer {
|
||||
return &TestNoAuthServer{}
|
||||
}
|
||||
|
||||
type TestNoAuthServer struct {
|
||||
test.UnimplementedTestNoAuthServiceServer
|
||||
}
|
||||
|
||||
func (t *TestNoAuthServer) Ping(ctx context.Context, in *test.PingRequest) (*test.PingResponse, error) {
|
||||
log := logger.FromContext(ctx)
|
||||
log.Info("Ping")
|
||||
return &test.PingResponse{}, nil
|
||||
}
|
||||
11
go.mod
11
go.mod
@@ -5,17 +5,22 @@ go 1.25.9
|
||||
require (
|
||||
github.com/alecthomas/assert/v2 v2.11.0
|
||||
github.com/alecthomas/kong v1.15.0
|
||||
github.com/coreos/go-oidc/v3 v3.18.0
|
||||
github.com/go-logr/logr v1.4.3
|
||||
github.com/go-logr/zapr v1.3.0
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2
|
||||
github.com/golang-migrate/migrate/v4 v4.19.1
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/mattn/go-colorable v0.1.13
|
||||
github.com/redis/go-redis/v9 v9.18.0
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
go.uber.org/zap v1.27.0
|
||||
golang.org/x/crypto v0.47.0
|
||||
golang.org/x/oauth2 v0.36.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
helm.sh/helm/v3 v3.20.2
|
||||
k8s.io/api v0.35.1
|
||||
@@ -45,6 +50,7 @@ require (
|
||||
github.com/containerd/platforms v0.2.1 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.6.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
|
||||
github.com/evanphx/json-patch v5.9.11+incompatible // indirect
|
||||
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
|
||||
@@ -54,6 +60,7 @@ require (
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/go-errors/errors v1.4.2 // indirect
|
||||
github.com/go-gorp/gorp/v3 v3.1.0 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.1.4 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
@@ -104,10 +111,10 @@ require (
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xlab/treeprint v1.2.0 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/oauth2 v0.34.0 // indirect
|
||||
golang.org/x/sync v0.19.0 // indirect
|
||||
golang.org/x/term v0.39.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
@@ -133,7 +140,7 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260423180238-53a8a976b540
|
||||
gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260430152421-88c087f0cea0
|
||||
github.com/golang/protobuf v1.5.4
|
||||
golang.org/x/net v0.49.0 // indirect
|
||||
golang.org/x/sys v0.40.0 // indirect
|
||||
|
||||
33
go.sum
33
go.sum
@@ -1,10 +1,13 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.121.6 h1:waZiuajrI28iAf40cWgycWNgaXPO06dupuS+sgibK6c=
|
||||
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
|
||||
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
|
||||
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
||||
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260423180238-53a8a976b540 h1:DX/lMTetxHz4ezEyBI4bKaJwxhO0uXUyGv9pQTV9NYI=
|
||||
gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260423180238-53a8a976b540/go.mod h1:zgX1KfGcHue8TjuXWN0onGpg3pQPek/lKMfdT6S7TQM=
|
||||
gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260430152421-88c087f0cea0 h1:2UggBAWgOJ1MYgkk+RTaWhfTGtzAZ0B9MriZMoHqnq4=
|
||||
gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260430152421-88c087f0cea0/go.mod h1:AgOh1lkPHyRgBf3/s1btKcAqke/33LbKYarTD13qeAg=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
|
||||
@@ -40,6 +43,10 @@ github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM
|
||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||
github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70=
|
||||
github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
@@ -59,6 +66,8 @@ github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
|
||||
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
|
||||
github.com/coreos/go-oidc/v3 v3.18.0 h1:V9orjXynvu5wiC9SemFTWnG4F45v403aIcjWo0d41+A=
|
||||
github.com/coreos/go-oidc/v3 v3.18.0/go.mod h1:DYCf24+ncYi+XkIH97GY1+dqoRlbaSI26KVTCI9SrY4=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
@@ -120,6 +129,8 @@ github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxI
|
||||
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||
github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs=
|
||||
github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw=
|
||||
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
|
||||
github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
@@ -143,6 +154,8 @@ github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-migrate/migrate/v4 v4.19.1 h1:OCyb44lFuQfYXYLx1SCxPZQGU7mcaZ7gH9yH4jSFbBA=
|
||||
github.com/golang-migrate/migrate/v4 v4.19.1/go.mod h1:CTcgfjxhaUtsLipnLoQRWCrjYXycRz/g5+RWDuYgPrE=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
@@ -176,6 +189,8 @@ github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJr
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0 h1:FbSCl+KggFl+Ocym490i/EyXF4lPgLoUtcSWquBM0Rs=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0/go.mod h1:qOchhhIlmRcqk/O9uCo/puJlyo07YINaIqdZfZG3Jkc=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
@@ -205,6 +220,8 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
@@ -294,8 +311,8 @@ github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 h1:EaDatTxkdHG+U3Bk4EUr+DZ7fO
|
||||
github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5/go.mod h1:fyalQWdtzDBECAQFBJuQe5bzQ02jGd5Qcbgb97Flm7U=
|
||||
github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 h1:EfpWLLCyXw8PSM2/XNJLjI3Pb27yVE+gIAfeqp8LUCc=
|
||||
github.com/redis/go-redis/extra/redisotel/v9 v9.0.5/go.mod h1:WZjPDy7VNzn77AAfnAfVjZNvfJTYfPetfZk5yoSTLaQ=
|
||||
github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
|
||||
github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
|
||||
github.com/redis/go-redis/v9 v9.18.0 h1:pMkxYPkEbMPwRdenAzUNyFNrDgHx9U+DrBabWNfSRQs=
|
||||
github.com/redis/go-redis/v9 v9.18.0/go.mod h1:k3ufPphLU5YXwNTUcCRXGxUoF1fqxnhFQmscfkCoDA0=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/rubenv/sql-migrate v1.8.1 h1:EPNwCvjAowHI3TnZ+4fQu3a915OpnQoPAjTXCGOy2U0=
|
||||
@@ -335,6 +352,8 @@ github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ=
|
||||
github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
|
||||
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/bridges/prometheus v0.57.0 h1:UW0+QyeyBVhn+COBec3nGhfnFe5lwB0ic1JBVjzhk0w=
|
||||
@@ -382,6 +401,8 @@ go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4Etq
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
@@ -418,8 +439,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
|
||||
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
|
||||
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
|
||||
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
|
||||
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
environments:
|
||||
default:
|
||||
kubeContext: kind-kind
|
||||
values:
|
||||
- databases:
|
||||
postgres:
|
||||
@@ -19,7 +20,7 @@ repositories:
|
||||
|
||||
releases:
|
||||
- name: postgres-instance
|
||||
namespace: databases
|
||||
namespace: softplayer
|
||||
chart: cloudpirates/postgres
|
||||
version: 0.19.1
|
||||
installed: true
|
||||
@@ -35,7 +36,7 @@ releases:
|
||||
enabled: true
|
||||
whenDeleted: Delete
|
||||
- name: dragonfly
|
||||
namespace: databases
|
||||
namespace: softplayer
|
||||
chart: dragonfly/dragonfly
|
||||
version: v1.38.0
|
||||
installed: true
|
||||
@@ -55,20 +56,18 @@ releases:
|
||||
readOnlyRootFilesystem: true
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1000
|
||||
redis:
|
||||
# -- Enable redis cluster with docker container.
|
||||
enable: true
|
||||
image:
|
||||
repository: bitnamilegacy/redis
|
||||
# -- Cluster domain.
|
||||
clusterDomain: 'cluster.local'
|
||||
auth:
|
||||
# -- Enable password authentication.
|
||||
enabled: true
|
||||
# -- Redis password.
|
||||
password: dragonfly
|
||||
master:
|
||||
service:
|
||||
ports:
|
||||
# -- Redis master service port.
|
||||
redis: 6379
|
||||
service:
|
||||
port: 30379
|
||||
type: NodePort
|
||||
strategicMergePatches:
|
||||
- apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: dragonfly
|
||||
namespace: softplayer
|
||||
spec:
|
||||
ports:
|
||||
- name: dragonfly
|
||||
port: 30379
|
||||
protocol: TCP
|
||||
nodePort: 30379
|
||||
|
||||
@@ -3,21 +3,34 @@ package controllers
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/helpers/hash"
|
||||
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/tools/logger"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/google/uuid"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
type AccountController struct {
|
||||
Params AccountParams
|
||||
DB *sql.DB
|
||||
DevMode bool
|
||||
DB *sql.DB
|
||||
Redis *redis.Client
|
||||
DevMode bool
|
||||
HashCost int16
|
||||
AccessTokenTTL time.Duration
|
||||
RefreshTokenTTL time.Duration
|
||||
JWTSecret []byte
|
||||
}
|
||||
|
||||
type AccountParams struct {
|
||||
HashCost int16
|
||||
type JWT struct {
|
||||
RefreshToken string
|
||||
AccessToken string
|
||||
}
|
||||
|
||||
type AccountParams struct{}
|
||||
|
||||
type AccountData struct {
|
||||
Username string
|
||||
Password string
|
||||
@@ -25,16 +38,67 @@ type AccountData struct {
|
||||
UUID string
|
||||
}
|
||||
|
||||
func (c *AccountController) Create(ctx context.Context, data *AccountData) error {
|
||||
func (c *AccountController) Create(ctx context.Context, data *AccountData) (string, error) {
|
||||
data.UUID = uuid.New().String()
|
||||
|
||||
passwordHash, err := hash.HashPassword(data.Password, int(c.Params.HashCost))
|
||||
passwordHash, err := hash.HashPassword(data.Password, int(c.HashCost))
|
||||
if err != nil {
|
||||
return nil
|
||||
return "", nil
|
||||
}
|
||||
|
||||
query := "INSERT INTO users (uuid, username, email, password_hash) VALUES ($1, $2, $3, $4)"
|
||||
if _, err := c.DB.Query(query, data.UUID, data.Username, data.Email, passwordHash); err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
return nil
|
||||
|
||||
return data.UUID, nil
|
||||
}
|
||||
|
||||
func (c *AccountController) GenerateAccessToken(userID string) (string, error) {
|
||||
claims := jwt.MapClaims{
|
||||
"user_id": userID,
|
||||
"type": "access",
|
||||
"exp": time.Now().Add(c.AccessTokenTTL).Unix(),
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
return token.SignedString(c.JWTSecret)
|
||||
}
|
||||
|
||||
func redisKey(id string) string {
|
||||
return fmt.Sprintf("refresh:%s", id)
|
||||
}
|
||||
|
||||
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.RefreshTokenTTL).Unix(),
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
if err := c.Redis.Set(ctx, redisKey(tokenID), userID, c.RefreshTokenTTL).Err(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return token.SignedString(c.JWTSecret)
|
||||
}
|
||||
|
||||
// It must validate the refresh token
|
||||
// Get it's id from the content
|
||||
// Find a corresponding token in redis, and if it's found, remove it and create a new one
|
||||
func (c *AccountController) ValidateRefreshToken(ctx context.Context, tokenID, userID string) (string, error) {
|
||||
log := logger.FromContext(ctx)
|
||||
userIDRedis := c.Redis.Get(ctx, redisKey(tokenID)).Val()
|
||||
if err := c.Redis.Del(ctx, redisKey(tokenID)).Err(); err != nil {
|
||||
log.Error(err, "Couldn't delete redis entry")
|
||||
return "", err
|
||||
}
|
||||
log.Info(userIDRedis)
|
||||
log.Info(userID)
|
||||
if userID != userIDRedis {
|
||||
return "", errors.New("user id doesn't match")
|
||||
}
|
||||
return userIDRedis, nil
|
||||
}
|
||||
|
||||
64
internal/interceptors/authjwt.go
Normal file
64
internal/interceptors/authjwt.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package interceptors
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/tools/logger"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type JWTVerifier struct {
|
||||
secret []byte
|
||||
serverCtx context.Context
|
||||
}
|
||||
|
||||
func NewJWTVerifier(ctx context.Context, secret []byte) *JWTVerifier {
|
||||
return &JWTVerifier{
|
||||
serverCtx: ctx,
|
||||
secret: secret,
|
||||
}
|
||||
}
|
||||
|
||||
// This is an interceptors that should verify that a user is authorized
|
||||
func (v *JWTVerifier) JWTAuthInterceptor(
|
||||
ctx context.Context,
|
||||
req interface{},
|
||||
info *grpc.UnaryServerInfo,
|
||||
handler grpc.UnaryHandler,
|
||||
) (interface{}, error) {
|
||||
log := logger.FromContext(v.serverCtx).WithValues("method", info.FullMethod)
|
||||
if !strings.Contains(info.FullMethod, "NoAuth") {
|
||||
log.Info("Checking the JWT token")
|
||||
md, ok := metadata.FromIncomingContext(ctx)
|
||||
if !ok {
|
||||
return nil, status.Error(codes.Unauthenticated, "User is not authorized")
|
||||
}
|
||||
|
||||
tokenString := md.Get("token")[0]
|
||||
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 v.secret, nil
|
||||
}, jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Alg()}))
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Unauthenticated, "User is not authorized")
|
||||
}
|
||||
|
||||
if claims, ok := token.Claims.(jwt.MapClaims); ok {
|
||||
fmt.Println(claims["userID"])
|
||||
} else {
|
||||
fmt.Println(err)
|
||||
}
|
||||
// Get the token from the metadata
|
||||
// Validate the token
|
||||
// Get the user id from the token
|
||||
} else {
|
||||
log.Info("Auth is not required for this request")
|
||||
}
|
||||
return handler(ctx, req)
|
||||
}
|
||||
@@ -5,5 +5,5 @@ nodes:
|
||||
extraPortMappings:
|
||||
- containerPort: 30432
|
||||
hostPort: 30432
|
||||
- containerPort: 30431
|
||||
hostPort: 30431
|
||||
- containerPort: 30379
|
||||
hostPort: 30379
|
||||
|
||||
181
main.go
181
main.go
@@ -6,42 +6,62 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
v1 "gitea.badhouseplants.net/softplayer/softplayer-backend/api/v1"
|
||||
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/helpers/email"
|
||||
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/controllers"
|
||||
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/tools/logger"
|
||||
"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"
|
||||
accounts "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/accounts/v1"
|
||||
test "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/test/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/grpc-ecosystem/go-grpc-middleware/v2/interceptors"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/selector"
|
||||
_ "github.com/lib/pq"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/reflection"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"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"`
|
||||
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() {
|
||||
@@ -52,15 +72,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")
|
||||
@@ -69,7 +99,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")
|
||||
@@ -77,7 +107,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")
|
||||
@@ -90,67 +120,102 @@ func migrateDB(ctx context.Context, db *sql.DB) error {
|
||||
return 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", "postgres://softplayer:qwertyu9@localhost:30432/softplayer?sslmode=disable")
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// jwtVerifier := interceptors.NewJWTVerifier(ctx, []byte(params.JWTSecret))
|
||||
|
||||
authFn := func(ctx context.Context) (context.Context, error) {
|
||||
tokenString, err := auth.AuthFromMD(ctx, "bearer")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) {
|
||||
return []byte(params.JWTSecret), nil
|
||||
}, jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Alg()}))
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Unauthenticated, "User is not authorized")
|
||||
}
|
||||
|
||||
if claims, ok := token.Claims.(jwt.MapClaims); ok {
|
||||
ctx = context.WithValue(ctx, "token_id", claims["token_id"].(string))
|
||||
ctx = context.WithValue(ctx, "user_id", claims["user_id"].(string))
|
||||
} else {
|
||||
return ctx, errors.New("claims are missing int the token")
|
||||
}
|
||||
return ctx, nil
|
||||
}
|
||||
authReqServices := func(ctx context.Context, callMeta interceptors.CallMeta) bool {
|
||||
return !strings.Contains(callMeta.Service, "NoAuth")
|
||||
}
|
||||
grpcServer := grpc.NewServer(
|
||||
grpc.UnaryInterceptor(grpc_zap.UnaryServerInterceptor(logger.SetupLogger("info"))),
|
||||
grpc.ChainUnaryInterceptor(
|
||||
grpc_zap.UnaryServerInterceptor(logger.SetupLogger("info")),
|
||||
// jwtVerifier.JWTAuthInterceptor,
|
||||
selector.UnaryServerInterceptor(auth.UnaryServerInterceptor(authFn), selector.MatchFunc(authReqServices)),
|
||||
),
|
||||
grpc.StreamInterceptor(grpc_zap.StreamServerInterceptor(logger.SetupLogger("info"))),
|
||||
)
|
||||
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: params.RedisHost,
|
||||
})
|
||||
if params.Reflection {
|
||||
reflection.Register(grpcServer)
|
||||
}
|
||||
|
||||
environments.RegisterEnvironmentsServer(grpcServer, v1.NewapiGrpcImpl(controller, log))
|
||||
accounts.RegisterAccountsServer(grpcServer, v1.NewAccountRPCImpl(db, params.HashCost, &emailConfig, params.DevMode))
|
||||
email_proto.RegisterEmailValidationServer(grpcServer, v1.InitEmailServer(controller, &emailConfig, params.DevMode))
|
||||
applications_proto.RegisterApplicationsServer(grpcServer, v1.NewApplicationsGrpcImpl(controller, log))
|
||||
|
||||
accountCtrl := &controllers.AccountController{
|
||||
HashCost: params.HashCost,
|
||||
DB: db,
|
||||
DevMode: params.DevMode,
|
||||
RefreshTokenTTL: params.RefrestTokenTTL,
|
||||
AccessTokenTTL: params.AccessTokenTTL,
|
||||
JWTSecret: []byte(params.JWTSecret),
|
||||
Redis: rdb,
|
||||
}
|
||||
accounts.RegisterAccountsNoAuthServiceServer(grpcServer, v1.NewAccountNoAuthRPCImpl(accountCtrl))
|
||||
accounts.RegisterAccountsAuthServiceServer(grpcServer, v1.NewAccountAuthRPCImpl(accountCtrl))
|
||||
test.RegisterTestAuthServiceServer(grpcServer, v1.NewTestAuthRPCImpl())
|
||||
test.RegisterTestNoAuthServiceServer(grpcServer, v1.NewTestNoAuthRPCImpl())
|
||||
if err := grpcServer.Serve(lis); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user