All checks were successful
ci/woodpecker/push/build Pipeline was successful
Signed-off-by: Nikolai Rodionov <iam@allanger.xyz>
156 lines
4.7 KiB
Go
156 lines
4.7 KiB
Go
package cmd
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"net"
|
|
"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/logger"
|
|
accounts "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/accounts/v1"
|
|
test "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/test/v1"
|
|
tokens "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/tokens/v1"
|
|
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/redis/go-redis/v9"
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/reflection"
|
|
)
|
|
|
|
type Server 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"`
|
|
}
|
|
|
|
// Run the grpc backend server
|
|
func (cmd *Server) Run(ctx context.Context) error {
|
|
// Make sure the download dir exists
|
|
log := logger.FromContext(ctx)
|
|
|
|
log.Info("Opening a database connection")
|
|
db, err := sql.Open("postgres", cmd.DBConnectionString)
|
|
if err != nil {
|
|
log.Error(err, "Couldn't start a database driver")
|
|
return err
|
|
}
|
|
|
|
address := fmt.Sprintf("%s:%d", cmd.Host, cmd.Port)
|
|
lis, err := net.Listen("tcp", address)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
rdb := redis.NewClient(&redis.Options{
|
|
Addr: cmd.RedisHost,
|
|
})
|
|
|
|
authInterceptor := controllers.NewAuthController(
|
|
[]byte(cmd.JWTSecret),
|
|
cmd.AccessTokenTTL,
|
|
cmd.RefrestTokenTTL,
|
|
rdb,
|
|
)
|
|
|
|
grpcServer := grpc.NewServer(
|
|
grpc.ChainUnaryInterceptor(
|
|
grpc_zap.UnaryServerInterceptor(logger.SetupLogger("info")),
|
|
// jwtVerifier.JWTAuthInterceptor,
|
|
selector.UnaryServerInterceptor(
|
|
auth.UnaryServerInterceptor(authInterceptor.AuthInterceptorFN),
|
|
selector.MatchFunc(selectorRequireAuth),
|
|
),
|
|
),
|
|
grpc.ChainStreamInterceptor(
|
|
grpc_zap.StreamServerInterceptor(logger.SetupLogger("info")),
|
|
selector.StreamServerInterceptor(
|
|
auth.StreamServerInterceptor(authInterceptor.AuthInterceptorFN),
|
|
selector.MatchFunc(selectorRequireAuth),
|
|
),
|
|
),
|
|
)
|
|
|
|
if cmd.Reflection {
|
|
reflection.Register(grpcServer)
|
|
}
|
|
|
|
tokenCtrl := &controllers.TokenController{
|
|
DB: db,
|
|
HashCost: cmd.HashCost,
|
|
Redis: rdb,
|
|
}
|
|
|
|
accountCtrl := &controllers.AccountController{
|
|
HashCost: cmd.HashCost,
|
|
DB: db,
|
|
DevMode: cmd.DevMode,
|
|
RefreshTokenTTL: cmd.RefrestTokenTTL,
|
|
AccessTokenTTL: cmd.AccessTokenTTL,
|
|
JWTSecret: []byte(cmd.JWTSecret),
|
|
Redis: rdb,
|
|
}
|
|
|
|
// Services that should be accessible for tokens should go here
|
|
accounts.RegisterAccountsServiceServer(grpcServer, v1.NewAccountServer(accountCtrl, authInterceptor))
|
|
test.RegisterTestServiceServer(grpcServer, v1.NewTestServer())
|
|
test.RegisterPublicTestServiceServer(grpcServer, v1.NewPublicTestServer())
|
|
tokens.RegisterTokensServiceServer(grpcServer, v1.NewTokensServer(tokenCtrl, authInterceptor))
|
|
accounts.RegisterPublicAccountsServiceServer(grpcServer, v1.NewPublicAccountServer(accountCtrl, authInterceptor))
|
|
|
|
info := grpcServer.GetServiceInfo()
|
|
tokenCtrl.SetGRPCInfo(info)
|
|
tokenCtrl.SetRules()
|
|
|
|
if err := grpcServer.Serve(lis); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Parse the service name and check whether it's public or not
|
|
func selectorRequireAuth(ctx context.Context, callMeta interceptors.CallMeta) bool {
|
|
serviceParts := strings.Split(callMeta.Service, ".")
|
|
if len(serviceParts) == 0 {
|
|
return false
|
|
}
|
|
serviceName := serviceParts[len(serviceParts)-1]
|
|
fmt.Println(serviceName)
|
|
|
|
if strings.HasPrefix(serviceName, "Public") {
|
|
return false
|
|
}
|
|
|
|
if strings.Contains(serviceName, "ServerReflection") {
|
|
return false
|
|
}
|
|
|
|
if strings.Contains(callMeta.Method, "AuthenticateWithToken") {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|