Merge pull request #28 from PretendoNetwork/feat/level-delete-grpc
Some checks failed
Build and Publish Docker Image / Build and Publish Docker Image (amd64) (push) Has been cancelled
Build and Publish Docker Image / Build and Publish Docker Image (arm64) (push) Has been cancelled

CourseDelete + GRPC server
This commit is contained in:
mrjvs
2026-08-02 18:57:26 +02:00
committed by GitHub
9 changed files with 280 additions and 154 deletions

View File

@@ -60,6 +60,8 @@ All configuration options are handled via environment variables
| `PN_SMM_CONFIG_S3_ACCESS_SECRET` | S3 secret | Yes |
| `PN_SMM_CONFIG_S3_BUCKET` | S3 bucket | Yes |
| `PN_SMM_CONFIG_S3_KEY_BASE` | S3 Key prefix | Yes |
| `PN_SMM_GRPC_SERVER_PORT` | Port for the GRPC server | Yes |
| `PN_SMM_GRPC_API_KEY` | API key for your GRPC server | No (Assumed to be an open gRPC API) |
| `PN_SMM_ACCOUNT_GRPC_HOST` | Host name for your account server gRPC service | Yes |
| `PN_SMM_ACCOUNT_GRPC_PORT` | Port for your account server gRPC service | Yes |
| `PN_SMM_ACCOUNT_GRPC_API_KEY` | API key for your account server gRPC service | No (Assumed to be an open gRPC API) |

View File

@@ -22,3 +22,5 @@ var GRPCAccountClient pb.AccountClient
var GRPCAccountCommonMetadata metadata.MD
var MinIOClient *minio.Client
var Presigner *S3Presigner
var GRPCAPIKey string
var GRPCServerPort int

2
go.mod
View File

@@ -5,7 +5,7 @@ go 1.23.0
toolchain go1.23.5
require (
github.com/PretendoNetwork/grpc/go v0.0.0-20250123103241-5fb484bf544d
github.com/PretendoNetwork/grpc/go v0.0.0-20260801122926-a632ab44588d
github.com/PretendoNetwork/nex-go/v2 v2.1.2
github.com/PretendoNetwork/nex-protocols-common-go/v2 v2.2.2
github.com/PretendoNetwork/nex-protocols-go/v2 v2.2.1

4
go.sum
View File

@@ -1,5 +1,5 @@
github.com/PretendoNetwork/grpc/go v0.0.0-20250123103241-5fb484bf544d h1:i/XyhbTm/j8UfCSnA0Cwrvc0mWOPIftOVrfSYqbh8Xo=
github.com/PretendoNetwork/grpc/go v0.0.0-20250123103241-5fb484bf544d/go.mod h1:L6We4KkcQeiQVPrF7iu8Zax0B1Bm0v4nssR1JOAiRFQ=
github.com/PretendoNetwork/grpc/go v0.0.0-20260801122926-a632ab44588d h1:9tMOif1DXaDNZ3auC+DkTd8+PpX7j3xvH1w2jzdR7w0=
github.com/PretendoNetwork/grpc/go v0.0.0-20260801122926-a632ab44588d/go.mod h1:L6We4KkcQeiQVPrF7iu8Zax0B1Bm0v4nssR1JOAiRFQ=
github.com/PretendoNetwork/nex-go/v2 v2.1.2 h1:OJFAS6U6VNzZ4YzteKqUEZ5aJMwWIHODeRrLwNbN7nw=
github.com/PretendoNetwork/nex-go/v2 v2.1.2/go.mod h1:3LyJzsv3AataJW8D0binp15Q8ZH22MWTYly1VNtXi64=
github.com/PretendoNetwork/nex-protocols-common-go/v2 v2.2.2 h1:rBJNZDJ92pa9fU3Og0sanyizJTWnELPoGR0Tjz8zlws=

View File

@@ -0,0 +1,26 @@
package grpc
import (
"context"
"errors"
"github.com/PretendoNetwork/super-mario-maker/globals"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
)
func apiKeyInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
md, ok := metadata.FromIncomingContext(ctx)
if ok {
apiKeyHeader := md.Get("X-API-Key")
if len(apiKeyHeader) == 0 || apiKeyHeader[0] != globals.GRPCAPIKey {
return nil, errors.New("Missing or invalid API key")
}
}
h, err := handler(ctx, req)
return h, err
}

44
grpc/delete_course.go Normal file
View File

@@ -0,0 +1,44 @@
package grpc
import (
"context"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
pb "github.com/PretendoNetwork/grpc/go/smm/v1"
"github.com/PretendoNetwork/nex-go/v2/types"
datastore_db "github.com/PretendoNetwork/super-mario-maker/database/datastore"
"github.com/PretendoNetwork/super-mario-maker/globals"
)
func (s *gRPCSMMServer) DeleteCourse(ctx context.Context, in *pb.DeleteCourseRequest) (*pb.DeleteCourseResponse, error) {
dataID := types.NewUInt64(in.DataId)
err := datastore_db.IsObjectAvailable(dataID)
if err != nil {
return &pb.DeleteCourseResponse{
Success: false,
}, status.Errorf(codes.NotFound, "Course not found")
}
metaInfo, err := datastore_db.GetObjectInfoByDataID(dataID)
if err != nil {
globals.Logger.Critical(err.Error())
return &pb.DeleteCourseResponse{
Success: false,
}, status.Errorf(codes.Internal, "internal server error")
}
err = datastore_db.DeleteObjectByDataID(dataID)
if err != nil {
globals.Logger.Critical(err.Error())
return &pb.DeleteCourseResponse{
Success: false,
}, status.Errorf(codes.Internal, "internal server error")
}
return &pb.DeleteCourseResponse{
CourseName: string(metaInfo.Name),
Success: true,
}, nil
}

34
grpc/grpc_server.go Normal file
View File

@@ -0,0 +1,34 @@
package grpc
import (
"fmt"
"log"
"net"
pb "github.com/PretendoNetwork/grpc/go/smm/v1"
"github.com/PretendoNetwork/super-mario-maker/globals"
"google.golang.org/grpc"
)
type gRPCSMMServer struct {
pb.UnimplementedSMMServiceServer
}
func StartGRPCServer() {
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", globals.GRPCServerPort))
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
server := grpc.NewServer(
grpc.UnaryInterceptor(apiKeyInterceptor),
)
pb.RegisterSMMServiceServer(server, &gRPCSMMServer{})
log.Printf("server listening at %v", listener.Addr())
if err := server.Serve(listener); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}

317
init.go
View File

@@ -1,150 +1,167 @@
package main
import (
"crypto/rand"
"fmt"
"os"
"strconv"
"strings"
pb "github.com/PretendoNetwork/grpc/go/account"
"github.com/PretendoNetwork/plogger-go"
"github.com/PretendoNetwork/super-mario-maker/database"
"github.com/PretendoNetwork/super-mario-maker/globals"
"github.com/joho/godotenv"
"github.com/PretendoNetwork/nex-go/v2"
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/metadata"
)
func init() {
globals.Logger = plogger.NewLogger()
var err error
err = godotenv.Load()
if err != nil {
globals.Logger.Warning("Error loading .env file")
}
s3Endpoint := os.Getenv("PN_SMM_CONFIG_S3_ENDPOINT")
s3AccessKey := os.Getenv("PN_SMM_CONFIG_S3_ACCESS_KEY")
s3AccessSecret := os.Getenv("PN_SMM_CONFIG_S3_ACCESS_SECRET")
s3SecureEnv := os.Getenv("PN_SMM_CONFIG_S3_SECURE")
postgresURI := os.Getenv("PN_SMM_POSTGRES_URI")
authenticationServerPort := os.Getenv("PN_SMM_AUTHENTICATION_SERVER_PORT")
secureServerHost := os.Getenv("PN_SMM_SECURE_SERVER_HOST")
secureServerPort := os.Getenv("PN_SMM_SECURE_SERVER_PORT")
accountGRPCHost := os.Getenv("PN_SMM_ACCOUNT_GRPC_HOST")
accountGRPCPort := os.Getenv("PN_SMM_ACCOUNT_GRPC_PORT")
accountGRPCAPIKey := os.Getenv("PN_SMM_ACCOUNT_GRPC_API_KEY")
if strings.TrimSpace(postgresURI) == "" {
globals.Logger.Error("PN_SMM_POSTGRES_URI environment variable not set")
os.Exit(0)
}
kerberosPassword := make([]byte, 0x10)
_, err = rand.Read(kerberosPassword)
if err != nil {
globals.Logger.Error("Error generating Kerberos password")
os.Exit(0)
}
globals.KerberosPassword = string(kerberosPassword)
globals.AuthenticationServerAccount = nex.NewAccount(1, "Quazal Authentication", globals.KerberosPassword)
globals.SecureServerAccount = nex.NewAccount(2, "Quazal Rendez-Vous", globals.KerberosPassword)
if strings.TrimSpace(authenticationServerPort) == "" {
globals.Logger.Error("PN_SMM_AUTHENTICATION_SERVER_PORT environment variable not set")
os.Exit(0)
}
if port, err := strconv.Atoi(authenticationServerPort); err != nil {
globals.Logger.Errorf("PN_SMM_AUTHENTICATION_SERVER_PORT is not a valid port. Expected 0-65535, got %s", authenticationServerPort)
os.Exit(0)
} else if port < 0 || port > 65535 {
globals.Logger.Errorf("PN_SMM_AUTHENTICATION_SERVER_PORT is not a valid port. Expected 0-65535, got %s", authenticationServerPort)
os.Exit(0)
}
if strings.TrimSpace(secureServerHost) == "" {
globals.Logger.Error("PN_SMM_SECURE_SERVER_HOST environment variable not set")
os.Exit(0)
}
if strings.TrimSpace(secureServerPort) == "" {
globals.Logger.Error("PN_SMM_SECURE_SERVER_PORT environment variable not set")
os.Exit(0)
}
if port, err := strconv.Atoi(secureServerPort); err != nil {
globals.Logger.Errorf("PN_SMM_SECURE_SERVER_PORT is not a valid port. Expected 0-65535, got %s", secureServerPort)
os.Exit(0)
} else if port < 0 || port > 65535 {
globals.Logger.Errorf("PN_SMM_SECURE_SERVER_PORT is not a valid port. Expected 0-65535, got %s", secureServerPort)
os.Exit(0)
}
if strings.TrimSpace(accountGRPCHost) == "" {
globals.Logger.Error("PN_SMM_ACCOUNT_GRPC_HOST environment variable not set")
os.Exit(0)
}
if strings.TrimSpace(accountGRPCPort) == "" {
globals.Logger.Error("PN_SMM_ACCOUNT_GRPC_PORT environment variable not set")
os.Exit(0)
}
if port, err := strconv.Atoi(accountGRPCPort); err != nil {
globals.Logger.Errorf("PN_SMM_ACCOUNT_GRPC_PORT is not a valid port. Expected 0-65535, got %s", accountGRPCPort)
os.Exit(0)
} else if port < 0 || port > 65535 {
globals.Logger.Errorf("PN_SMM_ACCOUNT_GRPC_PORT is not a valid port. Expected 0-65535, got %s", accountGRPCPort)
os.Exit(0)
}
if strings.TrimSpace(accountGRPCAPIKey) == "" {
globals.Logger.Warning("Insecure gRPC server detected. PN_SMM_ACCOUNT_GRPC_API_KEY environment variable not set")
}
globals.GRPCAccountClientConnection, err = grpc.NewClient(fmt.Sprintf("%s:%s", accountGRPCHost, accountGRPCPort), grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
globals.Logger.Criticalf("Failed to connect to account gRPC server: %v", err)
os.Exit(0)
}
globals.GRPCAccountClient = pb.NewAccountClient(globals.GRPCAccountClientConnection)
globals.GRPCAccountCommonMetadata = metadata.Pairs(
"X-API-Key", accountGRPCAPIKey,
)
staticCredentials := credentials.NewStaticV4(s3AccessKey, s3AccessSecret, "")
s3Secure, err := strconv.ParseBool(s3SecureEnv)
if err != nil {
globals.Logger.Warningf("PN_SMM_CONFIG_S3_SECURE environment variable not set. Using default value: %t", true)
s3Secure = true
}
minIOClient, err := minio.New(s3Endpoint, &minio.Options{
Creds: staticCredentials,
Secure: s3Secure,
})
if err != nil {
panic(err)
}
globals.MinIOClient = minIOClient
globals.Presigner = globals.NewS3Presigner(globals.MinIOClient)
// * Connect to and setup databases
database.ConnectPostgres()
}
package main
import (
"crypto/rand"
"fmt"
"os"
"strconv"
"strings"
pb "github.com/PretendoNetwork/grpc/go/account"
"github.com/PretendoNetwork/plogger-go"
"github.com/PretendoNetwork/super-mario-maker/database"
"github.com/PretendoNetwork/super-mario-maker/globals"
"github.com/joho/godotenv"
"github.com/PretendoNetwork/nex-go/v2"
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/metadata"
)
func init() {
globals.Logger = plogger.NewLogger()
var err error
err = godotenv.Load()
if err != nil {
globals.Logger.Warning("Error loading .env file")
}
s3Endpoint := os.Getenv("PN_SMM_CONFIG_S3_ENDPOINT")
s3AccessKey := os.Getenv("PN_SMM_CONFIG_S3_ACCESS_KEY")
s3AccessSecret := os.Getenv("PN_SMM_CONFIG_S3_ACCESS_SECRET")
s3SecureEnv := os.Getenv("PN_SMM_CONFIG_S3_SECURE")
postgresURI := os.Getenv("PN_SMM_POSTGRES_URI")
authenticationServerPort := os.Getenv("PN_SMM_AUTHENTICATION_SERVER_PORT")
secureServerHost := os.Getenv("PN_SMM_SECURE_SERVER_HOST")
secureServerPort := os.Getenv("PN_SMM_SECURE_SERVER_PORT")
gRPCServerPort := os.Getenv("PN_SMM_GRPC_SERVER_PORT")
gRPCAPIKey := os.Getenv("PN_SMM_GRPC_API_KEY")
accountGRPCHost := os.Getenv("PN_SMM_ACCOUNT_GRPC_HOST")
accountGRPCPort := os.Getenv("PN_SMM_ACCOUNT_GRPC_PORT")
accountGRPCAPIKey := os.Getenv("PN_SMM_ACCOUNT_GRPC_API_KEY")
if strings.TrimSpace(postgresURI) == "" {
globals.Logger.Error("PN_SMM_POSTGRES_URI environment variable not set")
os.Exit(0)
}
kerberosPassword := make([]byte, 0x10)
_, err = rand.Read(kerberosPassword)
if err != nil {
globals.Logger.Error("Error generating Kerberos password")
os.Exit(0)
}
globals.KerberosPassword = string(kerberosPassword)
globals.AuthenticationServerAccount = nex.NewAccount(1, "Quazal Authentication", globals.KerberosPassword)
globals.SecureServerAccount = nex.NewAccount(2, "Quazal Rendez-Vous", globals.KerberosPassword)
if strings.TrimSpace(authenticationServerPort) == "" {
globals.Logger.Error("PN_SMM_AUTHENTICATION_SERVER_PORT environment variable not set")
os.Exit(0)
}
if port, err := strconv.Atoi(authenticationServerPort); err != nil {
globals.Logger.Errorf("PN_SMM_AUTHENTICATION_SERVER_PORT is not a valid port. Expected 0-65535, got %s", authenticationServerPort)
os.Exit(0)
} else if port < 0 || port > 65535 {
globals.Logger.Errorf("PN_SMM_AUTHENTICATION_SERVER_PORT is not a valid port. Expected 0-65535, got %s", authenticationServerPort)
os.Exit(0)
}
if strings.TrimSpace(secureServerHost) == "" {
globals.Logger.Error("PN_SMM_SECURE_SERVER_HOST environment variable not set")
os.Exit(0)
}
if strings.TrimSpace(secureServerPort) == "" {
globals.Logger.Error("PN_SMM_SECURE_SERVER_PORT environment variable not set")
os.Exit(0)
}
if port, err := strconv.Atoi(secureServerPort); err != nil {
globals.Logger.Errorf("PN_SMM_SECURE_SERVER_PORT is not a valid port. Expected 0-65535, got %s", secureServerPort)
os.Exit(0)
} else if port < 0 || port > 65535 {
globals.Logger.Errorf("PN_SMM_SECURE_SERVER_PORT is not a valid port. Expected 0-65535, got %s", secureServerPort)
os.Exit(0)
}
gRPCPort, err := strconv.Atoi(gRPCServerPort)
if err != nil {
globals.Logger.Errorf("PN_SMM_GRPC_SERVER_PORT is not a valid port. Expected 0-65535, got %s", gRPCServerPort)
os.Exit(0)
} else if gRPCPort < 0 || gRPCPort > 65535 {
globals.Logger.Errorf("PN_SMM_GRPC_SERVER_PORT is not a valid port. Expected 0-65535, got %s", gRPCServerPort)
os.Exit(0)
}
globals.GRPCServerPort = gRPCPort
if strings.TrimSpace(gRPCAPIKey) == "" {
globals.Logger.Warning("Insecure gRPC server detected. PN_SMM_GRPC_API_KEY environment variable not set")
}
globals.GRPCAPIKey = gRPCAPIKey
if strings.TrimSpace(accountGRPCHost) == "" {
globals.Logger.Error("PN_SMM_ACCOUNT_GRPC_HOST environment variable not set")
os.Exit(0)
}
if strings.TrimSpace(accountGRPCPort) == "" {
globals.Logger.Error("PN_SMM_ACCOUNT_GRPC_PORT environment variable not set")
os.Exit(0)
}
if port, err := strconv.Atoi(accountGRPCPort); err != nil {
globals.Logger.Errorf("PN_SMM_ACCOUNT_GRPC_PORT is not a valid port. Expected 0-65535, got %s", accountGRPCPort)
os.Exit(0)
} else if port < 0 || port > 65535 {
globals.Logger.Errorf("PN_SMM_ACCOUNT_GRPC_PORT is not a valid port. Expected 0-65535, got %s", accountGRPCPort)
os.Exit(0)
}
if strings.TrimSpace(accountGRPCAPIKey) == "" {
globals.Logger.Warning("Insecure gRPC server detected. PN_SMM_ACCOUNT_GRPC_API_KEY environment variable not set")
}
globals.GRPCAccountClientConnection, err = grpc.NewClient(fmt.Sprintf("%s:%s", accountGRPCHost, accountGRPCPort), grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
globals.Logger.Criticalf("Failed to connect to account gRPC server: %v", err)
os.Exit(0)
}
globals.GRPCAccountClient = pb.NewAccountClient(globals.GRPCAccountClientConnection)
globals.GRPCAccountCommonMetadata = metadata.Pairs(
"X-API-Key", accountGRPCAPIKey,
)
staticCredentials := credentials.NewStaticV4(s3AccessKey, s3AccessSecret, "")
s3Secure, err := strconv.ParseBool(s3SecureEnv)
if err != nil {
globals.Logger.Warningf("PN_SMM_CONFIG_S3_SECURE environment variable not set. Using default value: %t", true)
s3Secure = true
}
minIOClient, err := minio.New(s3Endpoint, &minio.Options{
Creds: staticCredentials,
Secure: s3Secure,
})
if err != nil {
panic(err)
}
globals.MinIOClient = minIOClient
globals.Presigner = globals.NewS3Presigner(globals.MinIOClient)
// * Connect to and setup databases
database.ConnectPostgres()
}

View File

@@ -3,6 +3,7 @@ package main
import (
"sync"
"github.com/PretendoNetwork/super-mario-maker/grpc"
"github.com/PretendoNetwork/super-mario-maker/nex"
)
@@ -11,7 +12,7 @@ var wg sync.WaitGroup
func main() {
wg.Add(2)
// TODO - Add gRPC server
go grpc.StartGRPCServer()
go nex.StartAuthenticationServer()
go nex.StartSecureServer()