Implement a couple of rpcs
All checks were successful
ci/woodpecker/push/build Pipeline was successful

Signed-off-by: Nikolai Rodionov <iam@allanger.xyz>
This commit is contained in:
2026-06-09 14:52:06 +02:00
parent 7c55e758fc
commit 3ea6765486
7 changed files with 95 additions and 31 deletions

View File

@@ -13,6 +13,7 @@ import (
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/helpers/postgres"
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/services"
accounts "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/accounts/v1"
projects "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/projects/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"
@@ -24,7 +25,6 @@ import (
"google.golang.org/grpc"
"google.golang.org/grpc/health"
healthgrpc "google.golang.org/grpc/health/grpc_health_v1"
healthpb "google.golang.org/grpc/health/grpc_health_v1"
"google.golang.org/grpc/reflection"
)
@@ -118,6 +118,10 @@ func (cmd *Server) Run(ctx context.Context) error {
Redis: rdb,
}
projectsCtrl := &services.ProjectsController{
DB: db,
}
// Services that should be accessible for tokens should go here
accounts.RegisterAccountsServiceServer(grpcServer, v1.NewAccountServer(accountCtrl, authController))
accounts.RegisterPublicAccountsServiceServer(grpcServer, v1.NewPublicAccountServer(accountCtrl, authController))
@@ -126,7 +130,7 @@ func (cmd *Server) Run(ctx context.Context) error {
test.RegisterPublicTestServiceServer(grpcServer, v1.NewPublicTestServer())
tokens.RegisterTokensServiceServer(grpcServer, v1.NewTokensServer(tokenCtrl, authController))
tokens.RegisterPublicTokensServiceServer(grpcServer, v1.NewPublicTokensServer(tokenCtrl, authController))
projects.RegisterProjectsServiceServer(grpcServer, v1.NewProjectsServer(projectsCtrl))
healthcheck := health.NewServer()
healthgrpc.RegisterHealthServer(grpcServer, healthcheck)
@@ -140,10 +144,10 @@ func (cmd *Server) Run(ctx context.Context) error {
dbOK := checkDatabase(db)
redisOK := checkRedis(rdb)
status := healthpb.HealthCheckResponse_SERVING
status := healthgrpc.HealthCheckResponse_SERVING
if !dbOK || !redisOK {
status = healthpb.HealthCheckResponse_NOT_SERVING
status = healthgrpc.HealthCheckResponse_NOT_SERVING
}
healthcheck.SetServingStatus(

4
go.mod
View File

@@ -43,8 +43,8 @@ require (
)
require (
gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260528090010-7bf4ddafe7f0
github.com/golang/protobuf v1.5.4 // indirect
gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260609122010-15a8c9529644
github.com/golang/protobuf v1.5.4
golang.org/x/net v0.51.0 // indirect
golang.org/x/sys v0.42.0 // indirect
golang.org/x/text v0.34.0 // indirect

4
go.sum
View File

@@ -2,8 +2,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
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=
gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260528090010-7bf4ddafe7f0 h1:CI6EwQndn8cr6ofpc1HbDsphCwK3NOZrdl2PS0BnX+Q=
gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260528090010-7bf4ddafe7f0/go.mod h1:EcQEZ3NN06b3UmKxiRnQnXDDjQ9kmJgoQQBAS+fpRQw=
gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260609122010-15a8c9529644 h1:PSLGU8NVSfAi7EZwIbrLZO0AYrx5N8aMAAW3B7kkhRc=
gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260609122010-15a8c9529644/go.mod h1:EcQEZ3NN06b3UmKxiRnQnXDDjQ9kmJgoQQBAS+fpRQw=
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=

View File

@@ -3,23 +3,44 @@ package v1
import (
"context"
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/services"
projects "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/projects/v1"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
func NewProjectsServer() *ProjectsServer {
return &ProjectsServer{}
func NewProjectsServer(projectCtrl *services.ProjectsController) *ProjectsServer {
return &ProjectsServer{
projectCtrl: projectCtrl,
}
}
// var _ projects.ProjectsServiceServer = (*ProjectsServer)(nil)
type ProjectsServer struct {
projects.UnimplementedProjectsServiceServer
projectCtrl *services.ProjectsController
}
// CreateProject implements [v1.ProjectsServiceServer].
func (p *ProjectsServer) CreateProject(context.Context, *projects.CreateProjectRequest) (*projects.CreateProjectResponse, error) {
panic("unimplemented")
func (p *ProjectsServer) CreateProject(ctx context.Context, in *projects.CreateProjectRequest) (*projects.CreateProjectResponse, error) {
claims, err := services.ClaimsFromContext(ctx)
if err != nil {
return nil, status.Error(codes.Aborted, "Context is invalid")
}
projectData := &services.ProjectData{
Name: in.Name,
Slug: in.Slug,
Description: in.Description,
}
id, err := p.projectCtrl.Create(ctx, projectData, claims.UserID)
if err != nil {
return nil, status.Error(codes.Aborted, "Couldn't create a project")
}
return &projects.CreateProjectResponse{Id: id}, nil
}
// GetProject implements [v1.ProjectsServiceServer].
@@ -28,8 +49,27 @@ func (p *ProjectsServer) GetProject(context.Context, *projects.GetProjectRequest
}
// ListProjects implements [v1.ProjectsServiceServer].
func (p *ProjectsServer) ListProjects(*projects.ListProjectsRequest, grpc.ServerStreamingServer[projects.ListProjectsResponse]) error {
panic("unimplemented")
func (p *ProjectsServer) ListProjects(_in *projects.ListProjectsRequest, stream grpc.ServerStreamingServer[projects.ListProjectsResponse]) error {
claims, err := services.ClaimsFromContext(stream.Context())
if err != nil {
return status.Error(codes.Aborted, "Context is invalid")
}
res, err := p.projectCtrl.List(stream.Context(), claims.UserID)
if err != nil {
return status.Error(codes.Aborted, "Couldn't list projects")
}
for _, project := range res {
payload := &projects.ListProjectsResponse{
Id: project.UUID,
Slug: "dummy",
Name: project.Name,
}
if err := stream.Send(payload); err != nil {
return status.Error(codes.Aborted, "Couldn't send data")
}
}
return nil
}
// UpdateProject implements [v1.ProjectsServiceServer].

View File

@@ -5,14 +5,14 @@ import (
"errors"
"fmt"
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/controllers"
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/services"
accounts "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/accounts/v1"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
func NewRefreshSessionServer(
authorizationCtrl *controllers.AuthController,
authorizationCtrl *services.AuthController,
) *RefreshSessionService {
return &RefreshSessionService{
authorizationCtrl: authorizationCtrl,
@@ -21,7 +21,7 @@ func NewRefreshSessionServer(
type RefreshSessionService struct {
accounts.UnimplementedRefreshSessionServiceServer
authorizationCtrl *controllers.AuthController
authorizationCtrl *services.AuthController
}
func (srv *RefreshSessionService) RefreshSession(ctx context.Context, in *accounts.RefreshSessionRequest) (*accounts.RefreshSessionResponse, error) {
@@ -32,13 +32,13 @@ func (srv *RefreshSessionService) RefreshSession(ctx context.Context, in *accoun
return nil, status.Error(codes.Aborted, "Invalid token is sent")
}
if claims.TokenType != controllers.TokenTypeRefresh {
if claims.TokenType != services.TokenTypeRefresh {
return nil, status.Error(codes.Unauthenticated, "Invalid token")
}
session, err := srv.authorizationCtrl.GetSession(ctx, claims.TokenID)
if err != nil {
if errors.Is(err, controllers.ErrSessionNotFound) {
if errors.Is(err, services.ErrSessionNotFound) {
return nil, status.Error(codes.Unauthenticated, "Session doesn't exists")
}
return nil, status.Error(codes.Internal, "Somethings is broken on our side")
@@ -48,24 +48,24 @@ func (srv *RefreshSessionService) RefreshSession(ctx context.Context, in *accoun
return nil, status.Error(codes.Unauthenticated, "Invalid session")
}
accessToken, _, err := srv.authorizationCtrl.GenerateToken(&controllers.JWTData{
accessToken, _, err := srv.authorizationCtrl.GenerateToken(&services.JWTData{
UserID: claims.UserID,
TokenType: controllers.TokenTypeAccess,
TokenAud: controllers.TokenAudWeb,
TokenType: services.TokenTypeAccess,
TokenAud: services.TokenAudWeb,
})
if err != nil {
return nil, status.Error(codes.Aborted, "Couldn't generate an access token")
}
refreshToken, tokenID, err := srv.authorizationCtrl.GenerateToken(&controllers.JWTData{
refreshToken, tokenID, err := srv.authorizationCtrl.GenerateToken(&services.JWTData{
UserID: claims.UserID,
TokenType: controllers.TokenTypeRefresh,
TokenAud: controllers.TokenAudWeb,
TokenType: services.TokenTypeRefresh,
TokenAud: services.TokenAudWeb,
})
if err != nil {
return nil, status.Error(codes.Aborted, "Couldn't generate an access token")
}
newSession := &controllers.Session{UserID: session.UserID}
newSession := &services.Session{UserID: session.UserID}
if err := srv.authorizationCtrl.SaveSession(ctx, tokenID, newSession); err != nil {
return nil, status.Error(codes.Aborted, "Couldn't store session")

View File

@@ -60,7 +60,8 @@ func CreateProject(ctx context.Context, db *sql.DB, data *ProjectData) error {
VALUES
($1, $2, $3, $4, $5, $6);
`
if _, err := tx.ExecContext(ctx, queryMembership,
if _, err := tx.ExecContext(
ctx, queryMembership,
data.UUID, data.CreatedBy, "owner", "active", data.CreatedBy, data.CreatedAt,
); err != nil {
var pgErr *pgconn.PgError

View File

@@ -23,8 +23,9 @@ type ProjectData struct {
}
var (
ErrProjectExists = errors.New("project exists")
ErrInvalidProject = errors.New("invalid project data")
ErrProjectExists = errors.New("project exists")
ErrInvalidProject = errors.New("invalid project data")
ErrProjectNotFound = errors.New("project not found")
)
// Create a new project
@@ -66,6 +67,24 @@ func (ctrl *ProjectsController) Get(ctx context.Context, projectID string) (data
}
// List projects available for a user
func (ctrl *ProjectsController) List(ctx context.Context) (data []*ProjectData, err error) {
return nil, nil
func (ctrl *ProjectsController) List(ctx context.Context, userID string) (data []*ProjectData, err error) {
log := logger.FromContext(ctx)
log.V(2).Info("Listing projects")
data = []*ProjectData{}
res, err := repository.ListProjects(ctx, ctrl.DB, userID)
if err != nil {
if errors.Is(err, repository.ErrNotFound) {
return nil, ErrProjectNotFound
}
log.Error(err, "Couldn't list projects")
return nil, ErrServerError
}
for _, val := range res {
data = append(data, &ProjectData{
UUID: val.UUID,
Name: val.Name,
})
}
return
}