Files
softplayer-backend/cmd/server.go
Nikolai Rodionov efe9042bdc
All checks were successful
ci/woodpecker/push/build Pipeline was successful
Start adding token auth
Signed-off-by: Nikolai Rodionov <iam@allanger.xyz>
2026-05-14 19:54:55 +02:00

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
}