feat(epic2): Implement core authentication and authorization services
- Implement Audit Service (2.5) - gRPC server with Record and Query operations - Database persistence with audit schema - Service registry integration - Entry point: cmd/audit-service - Implement Identity Service (2.2) - User CRUD operations - Password hashing with argon2id - Email verification and password reset flows - Entry point: cmd/identity-service - Fix package naming conflicts in user_service.go - Implement Auth Service (2.1) - JWT token generation and validation - Login, RefreshToken, ValidateToken, Logout RPCs - Integration with Identity Service - Entry point: cmd/auth-service - Note: RefreshToken entity needs Ent generation - Implement Authz Service (2.3, 2.4) - Permission checking and authorization - User roles and permissions retrieval - RBAC-based authorization - Entry point: cmd/authz-service - Implement gRPC clients for all services - Auth, Identity, Authz, and Audit clients - Service discovery integration - Full gRPC communication - Add service configurations to config/default.yaml - Create SUMMARY.md with implementation details and testing instructions - Fix compilation errors in Identity Service (password package conflicts) - All services build successfully and tests pass
This commit is contained in:
235
cmd/auth-service/main.go
Normal file
235
cmd/auth-service/main.go
Normal file
@@ -0,0 +1,235 @@
|
||||
// Package main provides the entry point for the Auth Service.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"git.dcentral.systems/toolz/goplt/internal/client"
|
||||
"git.dcentral.systems/toolz/goplt/internal/di"
|
||||
healthpkg "git.dcentral.systems/toolz/goplt/internal/health"
|
||||
"git.dcentral.systems/toolz/goplt/internal/infra/database"
|
||||
"git.dcentral.systems/toolz/goplt/pkg/config"
|
||||
"git.dcentral.systems/toolz/goplt/pkg/logger"
|
||||
"git.dcentral.systems/toolz/goplt/pkg/registry"
|
||||
"git.dcentral.systems/toolz/goplt/pkg/services"
|
||||
"go.uber.org/fx"
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// grpcServerWrapper wraps the gRPC server for lifecycle management.
|
||||
type grpcServerWrapper struct {
|
||||
server *grpc.Server
|
||||
listener net.Listener
|
||||
port int
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
func (s *grpcServerWrapper) Start() error {
|
||||
s.logger.Info("Starting Auth Service gRPC server",
|
||||
zap.Int("port", s.port),
|
||||
zap.String("addr", s.listener.Addr().String()),
|
||||
)
|
||||
|
||||
errChan := make(chan error, 1)
|
||||
go func() {
|
||||
if err := s.server.Serve(s.listener); err != nil {
|
||||
errChan <- err
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-errChan:
|
||||
return fmt.Errorf("gRPC server failed to start: %w", err)
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
s.logger.Info("Auth Service gRPC server started successfully",
|
||||
zap.Int("port", s.port),
|
||||
)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *grpcServerWrapper) Stop(ctx context.Context) error {
|
||||
s.logger.Info("Stopping Auth Service gRPC server")
|
||||
|
||||
stopped := make(chan struct{})
|
||||
go func() {
|
||||
s.server.GracefulStop()
|
||||
close(stopped)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-stopped:
|
||||
s.logger.Info("Auth Service gRPC server stopped gracefully")
|
||||
return nil
|
||||
case <-ctx.Done():
|
||||
s.logger.Warn("Auth Service gRPC server stop timeout, forcing stop")
|
||||
s.server.Stop()
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *grpcServerWrapper) Port() int {
|
||||
return s.port
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Create DI container
|
||||
container := di.NewContainer(
|
||||
// Core kernel services
|
||||
di.CoreModule(),
|
||||
|
||||
// Database for auth service (auth schema)
|
||||
fx.Provide(func(cfg config.ConfigProvider, log logger.Logger) (*database.Client, error) {
|
||||
dsn := cfg.GetString("database.dsn")
|
||||
if dsn == "" {
|
||||
return nil, fmt.Errorf("database.dsn is required")
|
||||
}
|
||||
client, err := database.NewClientWithSchema(dsn, "auth")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Run migrations
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := client.Migrate(ctx); err != nil {
|
||||
log.Warn("Failed to run migrations",
|
||||
zap.Error(err),
|
||||
)
|
||||
} else {
|
||||
log.Info("Database migrations completed for auth service")
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}),
|
||||
|
||||
// Health registry with database checker
|
||||
fx.Provide(func(db *database.Client, log logger.Logger) (*healthpkg.Registry, error) {
|
||||
registry := healthpkg.NewRegistry()
|
||||
registry.Register("database", healthpkg.NewDatabaseChecker(db))
|
||||
return registry, nil
|
||||
}),
|
||||
|
||||
// Identity Service client
|
||||
fx.Provide(func(factory *client.ServiceClientFactory) (services.IdentityServiceClient, error) {
|
||||
return factory.GetIdentityClient()
|
||||
}),
|
||||
|
||||
// Provide auth service and gRPC server (defined in auth_service_fx.go)
|
||||
provideAuthService(),
|
||||
|
||||
// Lifecycle hooks
|
||||
fx.Invoke(registerLifecycle),
|
||||
)
|
||||
|
||||
// Create root context
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// Handle signals
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
|
||||
|
||||
// Start the application
|
||||
if err := container.Start(ctx); err != nil {
|
||||
log := logger.GetGlobalLogger()
|
||||
if log != nil {
|
||||
log.Error("Failed to start Auth Service",
|
||||
logger.Error(err),
|
||||
)
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "Failed to start Auth Service: %v\n", err)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Wait for interrupt signal
|
||||
<-sigChan
|
||||
fmt.Println("\nShutting down Auth Service...")
|
||||
|
||||
// Create shutdown context with timeout
|
||||
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer shutdownCancel()
|
||||
|
||||
// Stop the application
|
||||
if err := container.Stop(shutdownCtx); err != nil {
|
||||
log := logger.GetGlobalLogger()
|
||||
if log != nil {
|
||||
log.Error("Error during Auth Service shutdown",
|
||||
logger.Error(err),
|
||||
)
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "Error during shutdown: %v\n", err)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Println("Auth Service stopped successfully")
|
||||
}
|
||||
|
||||
// registerLifecycle registers lifecycle hooks for the service.
|
||||
func registerLifecycle(
|
||||
lc fx.Lifecycle,
|
||||
grpcServer *grpcServerWrapper,
|
||||
serviceRegistry registry.ServiceRegistry,
|
||||
cfg config.ConfigProvider,
|
||||
log logger.Logger,
|
||||
) {
|
||||
lc.Append(fx.Hook{
|
||||
OnStart: func(ctx context.Context) error {
|
||||
// Start gRPC server
|
||||
if err := grpcServer.Start(); err != nil {
|
||||
return fmt.Errorf("failed to start gRPC server: %w", err)
|
||||
}
|
||||
|
||||
// Register with service registry
|
||||
serviceID := fmt.Sprintf("auth-service-%d", time.Now().Unix())
|
||||
host := cfg.GetString("services.auth.host")
|
||||
if host == "" {
|
||||
host = "localhost"
|
||||
}
|
||||
port := grpcServer.Port()
|
||||
|
||||
instance := ®istry.ServiceInstance{
|
||||
ID: serviceID,
|
||||
Name: "auth-service",
|
||||
Address: host,
|
||||
Port: port,
|
||||
Tags: []string{"grpc", "auth"},
|
||||
Metadata: map[string]string{
|
||||
"version": "1.0.0",
|
||||
"protocol": "grpc",
|
||||
},
|
||||
}
|
||||
|
||||
if err := serviceRegistry.Register(ctx, instance); err != nil {
|
||||
log.Warn("Failed to register with service registry",
|
||||
zap.Error(err),
|
||||
)
|
||||
} else {
|
||||
log.Info("Registered Auth Service with service registry",
|
||||
zap.String("service_id", serviceID),
|
||||
zap.String("name", instance.Name),
|
||||
zap.Int("port", port),
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
OnStop: func(ctx context.Context) error {
|
||||
// Stop gRPC server
|
||||
if err := grpcServer.Stop(ctx); err != nil {
|
||||
return fmt.Errorf("failed to stop gRPC server: %w", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user