Implement a couple of rpcs
All checks were successful
ci/woodpecker/push/build Pipeline was successful
All checks were successful
ci/woodpecker/push/build Pipeline was successful
Signed-off-by: Nikolai Rodionov <iam@allanger.xyz>
This commit is contained in:
@@ -13,6 +13,7 @@ import (
|
|||||||
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/helpers/postgres"
|
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/helpers/postgres"
|
||||||
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/services"
|
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/services"
|
||||||
accounts "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/accounts/v1"
|
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"
|
test "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/test/v1"
|
||||||
tokens "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/tokens/v1"
|
tokens "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/tokens/v1"
|
||||||
grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap"
|
grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap"
|
||||||
@@ -24,7 +25,6 @@ import (
|
|||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/health"
|
"google.golang.org/grpc/health"
|
||||||
healthgrpc "google.golang.org/grpc/health/grpc_health_v1"
|
healthgrpc "google.golang.org/grpc/health/grpc_health_v1"
|
||||||
healthpb "google.golang.org/grpc/health/grpc_health_v1"
|
|
||||||
"google.golang.org/grpc/reflection"
|
"google.golang.org/grpc/reflection"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -118,6 +118,10 @@ func (cmd *Server) Run(ctx context.Context) error {
|
|||||||
Redis: rdb,
|
Redis: rdb,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
projectsCtrl := &services.ProjectsController{
|
||||||
|
DB: db,
|
||||||
|
}
|
||||||
|
|
||||||
// Services that should be accessible for tokens should go here
|
// Services that should be accessible for tokens should go here
|
||||||
accounts.RegisterAccountsServiceServer(grpcServer, v1.NewAccountServer(accountCtrl, authController))
|
accounts.RegisterAccountsServiceServer(grpcServer, v1.NewAccountServer(accountCtrl, authController))
|
||||||
accounts.RegisterPublicAccountsServiceServer(grpcServer, v1.NewPublicAccountServer(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())
|
test.RegisterPublicTestServiceServer(grpcServer, v1.NewPublicTestServer())
|
||||||
tokens.RegisterTokensServiceServer(grpcServer, v1.NewTokensServer(tokenCtrl, authController))
|
tokens.RegisterTokensServiceServer(grpcServer, v1.NewTokensServer(tokenCtrl, authController))
|
||||||
tokens.RegisterPublicTokensServiceServer(grpcServer, v1.NewPublicTokensServer(tokenCtrl, authController))
|
tokens.RegisterPublicTokensServiceServer(grpcServer, v1.NewPublicTokensServer(tokenCtrl, authController))
|
||||||
|
projects.RegisterProjectsServiceServer(grpcServer, v1.NewProjectsServer(projectsCtrl))
|
||||||
healthcheck := health.NewServer()
|
healthcheck := health.NewServer()
|
||||||
healthgrpc.RegisterHealthServer(grpcServer, healthcheck)
|
healthgrpc.RegisterHealthServer(grpcServer, healthcheck)
|
||||||
|
|
||||||
@@ -140,10 +144,10 @@ func (cmd *Server) Run(ctx context.Context) error {
|
|||||||
dbOK := checkDatabase(db)
|
dbOK := checkDatabase(db)
|
||||||
redisOK := checkRedis(rdb)
|
redisOK := checkRedis(rdb)
|
||||||
|
|
||||||
status := healthpb.HealthCheckResponse_SERVING
|
status := healthgrpc.HealthCheckResponse_SERVING
|
||||||
|
|
||||||
if !dbOK || !redisOK {
|
if !dbOK || !redisOK {
|
||||||
status = healthpb.HealthCheckResponse_NOT_SERVING
|
status = healthgrpc.HealthCheckResponse_NOT_SERVING
|
||||||
}
|
}
|
||||||
|
|
||||||
healthcheck.SetServingStatus(
|
healthcheck.SetServingStatus(
|
||||||
|
|||||||
4
go.mod
4
go.mod
@@ -43,8 +43,8 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260528090010-7bf4ddafe7f0
|
gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260609122010-15a8c9529644
|
||||||
github.com/golang/protobuf v1.5.4 // indirect
|
github.com/golang/protobuf v1.5.4
|
||||||
golang.org/x/net v0.51.0 // indirect
|
golang.org/x/net v0.51.0 // indirect
|
||||||
golang.org/x/sys v0.42.0 // indirect
|
golang.org/x/sys v0.42.0 // indirect
|
||||||
golang.org/x/text v0.34.0 // indirect
|
golang.org/x/text v0.34.0 // indirect
|
||||||
|
|||||||
4
go.sum
4
go.sum
@@ -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 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 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
|
||||||
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
|
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-20260609122010-15a8c9529644 h1:PSLGU8NVSfAi7EZwIbrLZO0AYrx5N8aMAAW3B7kkhRc=
|
||||||
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/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 h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
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=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
|||||||
@@ -3,23 +3,44 @@ package v1
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/services"
|
||||||
projects "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/projects/v1"
|
projects "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/projects/v1"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewProjectsServer() *ProjectsServer {
|
func NewProjectsServer(projectCtrl *services.ProjectsController) *ProjectsServer {
|
||||||
return &ProjectsServer{}
|
return &ProjectsServer{
|
||||||
|
projectCtrl: projectCtrl,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// var _ projects.ProjectsServiceServer = (*ProjectsServer)(nil)
|
// var _ projects.ProjectsServiceServer = (*ProjectsServer)(nil)
|
||||||
|
|
||||||
type ProjectsServer struct {
|
type ProjectsServer struct {
|
||||||
projects.UnimplementedProjectsServiceServer
|
projects.UnimplementedProjectsServiceServer
|
||||||
|
projectCtrl *services.ProjectsController
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateProject implements [v1.ProjectsServiceServer].
|
// CreateProject implements [v1.ProjectsServiceServer].
|
||||||
func (p *ProjectsServer) CreateProject(context.Context, *projects.CreateProjectRequest) (*projects.CreateProjectResponse, error) {
|
func (p *ProjectsServer) CreateProject(ctx context.Context, in *projects.CreateProjectRequest) (*projects.CreateProjectResponse, error) {
|
||||||
panic("unimplemented")
|
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].
|
// GetProject implements [v1.ProjectsServiceServer].
|
||||||
@@ -28,8 +49,27 @@ func (p *ProjectsServer) GetProject(context.Context, *projects.GetProjectRequest
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ListProjects implements [v1.ProjectsServiceServer].
|
// ListProjects implements [v1.ProjectsServiceServer].
|
||||||
func (p *ProjectsServer) ListProjects(*projects.ListProjectsRequest, grpc.ServerStreamingServer[projects.ListProjectsResponse]) error {
|
func (p *ProjectsServer) ListProjects(_in *projects.ListProjectsRequest, stream grpc.ServerStreamingServer[projects.ListProjectsResponse]) error {
|
||||||
panic("unimplemented")
|
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].
|
// UpdateProject implements [v1.ProjectsServiceServer].
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"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"
|
accounts "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/accounts/v1"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewRefreshSessionServer(
|
func NewRefreshSessionServer(
|
||||||
authorizationCtrl *controllers.AuthController,
|
authorizationCtrl *services.AuthController,
|
||||||
) *RefreshSessionService {
|
) *RefreshSessionService {
|
||||||
return &RefreshSessionService{
|
return &RefreshSessionService{
|
||||||
authorizationCtrl: authorizationCtrl,
|
authorizationCtrl: authorizationCtrl,
|
||||||
@@ -21,7 +21,7 @@ func NewRefreshSessionServer(
|
|||||||
|
|
||||||
type RefreshSessionService struct {
|
type RefreshSessionService struct {
|
||||||
accounts.UnimplementedRefreshSessionServiceServer
|
accounts.UnimplementedRefreshSessionServiceServer
|
||||||
authorizationCtrl *controllers.AuthController
|
authorizationCtrl *services.AuthController
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *RefreshSessionService) RefreshSession(ctx context.Context, in *accounts.RefreshSessionRequest) (*accounts.RefreshSessionResponse, error) {
|
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")
|
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")
|
return nil, status.Error(codes.Unauthenticated, "Invalid token")
|
||||||
}
|
}
|
||||||
|
|
||||||
session, err := srv.authorizationCtrl.GetSession(ctx, claims.TokenID)
|
session, err := srv.authorizationCtrl.GetSession(ctx, claims.TokenID)
|
||||||
if err != nil {
|
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.Unauthenticated, "Session doesn't exists")
|
||||||
}
|
}
|
||||||
return nil, status.Error(codes.Internal, "Somethings is broken on our side")
|
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")
|
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,
|
UserID: claims.UserID,
|
||||||
TokenType: controllers.TokenTypeAccess,
|
TokenType: services.TokenTypeAccess,
|
||||||
TokenAud: controllers.TokenAudWeb,
|
TokenAud: services.TokenAudWeb,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Error(codes.Aborted, "Couldn't generate an access token")
|
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,
|
UserID: claims.UserID,
|
||||||
TokenType: controllers.TokenTypeRefresh,
|
TokenType: services.TokenTypeRefresh,
|
||||||
TokenAud: controllers.TokenAudWeb,
|
TokenAud: services.TokenAudWeb,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Error(codes.Aborted, "Couldn't generate an access token")
|
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 {
|
if err := srv.authorizationCtrl.SaveSession(ctx, tokenID, newSession); err != nil {
|
||||||
return nil, status.Error(codes.Aborted, "Couldn't store session")
|
return nil, status.Error(codes.Aborted, "Couldn't store session")
|
||||||
|
|||||||
@@ -60,7 +60,8 @@ func CreateProject(ctx context.Context, db *sql.DB, data *ProjectData) error {
|
|||||||
VALUES
|
VALUES
|
||||||
($1, $2, $3, $4, $5, $6);
|
($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,
|
data.UUID, data.CreatedBy, "owner", "active", data.CreatedBy, data.CreatedAt,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
var pgErr *pgconn.PgError
|
var pgErr *pgconn.PgError
|
||||||
|
|||||||
@@ -23,8 +23,9 @@ type ProjectData struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrProjectExists = errors.New("project exists")
|
ErrProjectExists = errors.New("project exists")
|
||||||
ErrInvalidProject = errors.New("invalid project data")
|
ErrInvalidProject = errors.New("invalid project data")
|
||||||
|
ErrProjectNotFound = errors.New("project not found")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Create a new project
|
// Create a new project
|
||||||
@@ -66,6 +67,24 @@ func (ctrl *ProjectsController) Get(ctx context.Context, projectID string) (data
|
|||||||
}
|
}
|
||||||
|
|
||||||
// List projects available for a user
|
// List projects available for a user
|
||||||
func (ctrl *ProjectsController) List(ctx context.Context) (data []*ProjectData, err error) {
|
func (ctrl *ProjectsController) List(ctx context.Context, userID string) (data []*ProjectData, err error) {
|
||||||
return nil, nil
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user