init bootstrap
This commit is contained in:
34
local/service/api.go
Normal file
34
local/service/api.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"omega-server/local/repository"
|
||||
)
|
||||
|
||||
// ApiService provides API-related business logic
|
||||
type ApiService struct {
|
||||
// Add repository dependencies as needed
|
||||
repo *repository.BaseRepository[any, any] // Placeholder
|
||||
}
|
||||
|
||||
// NewApiService creates a new ApiService
|
||||
func NewApiService() *ApiService {
|
||||
return &ApiService{
|
||||
// Initialize with required dependencies
|
||||
}
|
||||
}
|
||||
|
||||
// SetServerService sets the server service reference (for cross-service communication)
|
||||
func (s *ApiService) SetServerService(serverService interface{}) {
|
||||
// TODO: Implement when ServerService is available
|
||||
}
|
||||
|
||||
// GetApiInfo returns basic API information
|
||||
func (s *ApiService) GetApiInfo() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"name": "Omega Server API",
|
||||
"version": "1.0.0",
|
||||
"status": "active",
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add other API service methods as needed
|
||||
307
local/service/membership.go
Normal file
307
local/service/membership.go
Normal file
@@ -0,0 +1,307 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"omega-server/local/model"
|
||||
"omega-server/local/repository"
|
||||
"omega-server/local/utl/jwt"
|
||||
"omega-server/local/utl/logging"
|
||||
"os"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// CacheInvalidator interface for cache invalidation
|
||||
type CacheInvalidator interface {
|
||||
InvalidateUserPermissions(userID string)
|
||||
InvalidateAllUserPermissions()
|
||||
}
|
||||
|
||||
// MembershipService provides business logic for membership-related operations.
|
||||
type MembershipService struct {
|
||||
repo *repository.MembershipRepository
|
||||
cacheInvalidator CacheInvalidator
|
||||
}
|
||||
|
||||
// NewMembershipService creates a new MembershipService.
|
||||
func NewMembershipService(repo *repository.MembershipRepository) *MembershipService {
|
||||
return &MembershipService{
|
||||
repo: repo,
|
||||
cacheInvalidator: nil, // Will be set later via SetCacheInvalidator
|
||||
}
|
||||
}
|
||||
|
||||
// SetCacheInvalidator sets the cache invalidator after service initialization
|
||||
func (s *MembershipService) SetCacheInvalidator(invalidator CacheInvalidator) {
|
||||
s.cacheInvalidator = invalidator
|
||||
}
|
||||
|
||||
// Login authenticates a user and returns a JWT.
|
||||
func (s *MembershipService) Login(ctx context.Context, username, password string) (string, error) {
|
||||
user, err := s.repo.FindUserByUsername(ctx, username)
|
||||
if err != nil {
|
||||
return "", errors.New("invalid credentials")
|
||||
}
|
||||
|
||||
// Use secure password verification with constant-time comparison
|
||||
if !user.VerifyPassword(password) {
|
||||
return "", errors.New("invalid credentials")
|
||||
}
|
||||
|
||||
// Extract role names for JWT
|
||||
roleNames := make([]string, len(user.Roles))
|
||||
for i, role := range user.Roles {
|
||||
roleNames[i] = role.Name
|
||||
}
|
||||
|
||||
return jwt.GenerateToken(user.ID, user.Email, user.Username, roleNames)
|
||||
}
|
||||
|
||||
// CreateUser creates a new user.
|
||||
func (s *MembershipService) CreateUser(ctx context.Context, username, password, roleName string) (*model.User, error) {
|
||||
|
||||
role, err := s.repo.FindRoleByName(ctx, roleName)
|
||||
if err != nil {
|
||||
logging.Error("Failed to find role by name: %v", err)
|
||||
return nil, errors.New("role not found")
|
||||
}
|
||||
|
||||
user := &model.User{
|
||||
Username: username,
|
||||
Email: username + "@example.com", // You may want to accept email as parameter
|
||||
Name: username,
|
||||
}
|
||||
|
||||
// Set password using the model's SetPassword method
|
||||
if err := user.SetPassword(password); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Assign roles
|
||||
user.Roles = []model.Role{*role}
|
||||
|
||||
if err := s.repo.CreateUser(ctx, user); err != nil {
|
||||
logging.Error("Failed to create user: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logging.InfoOperation("USER_CREATE", "Created user: "+user.Username+" (ID: "+user.ID+", Role: "+roleName+")")
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// ListUsers retrieves all users.
|
||||
func (s *MembershipService) ListUsers(ctx context.Context) ([]*model.User, error) {
|
||||
return s.repo.ListUsers(ctx)
|
||||
}
|
||||
|
||||
// GetUser retrieves a single user by ID.
|
||||
func (s *MembershipService) GetUser(ctx context.Context, userID uuid.UUID) (*model.User, error) {
|
||||
return s.repo.FindUserByID(ctx, userID)
|
||||
}
|
||||
|
||||
// GetUserWithPermissions retrieves a single user by ID with their role and permissions.
|
||||
func (s *MembershipService) GetUserWithPermissions(ctx context.Context, userID string) (*model.User, error) {
|
||||
return s.repo.FindUserByIDWithPermissions(ctx, userID)
|
||||
}
|
||||
|
||||
// UpdateUserRequest defines the request body for updating a user.
|
||||
type UpdateUserRequest struct {
|
||||
Username *string `json:"username"`
|
||||
Password *string `json:"password"`
|
||||
RoleID *string `json:"roleId"`
|
||||
}
|
||||
|
||||
// DeleteUser deletes a user with validation to prevent Super Admin deletion.
|
||||
func (s *MembershipService) DeleteUser(ctx context.Context, userID uuid.UUID) error {
|
||||
// Get user with role information
|
||||
user, err := s.repo.FindUserByID(ctx, userID)
|
||||
if err != nil {
|
||||
return errors.New("user not found")
|
||||
}
|
||||
|
||||
// Prevent deletion of Super Admin users
|
||||
for _, role := range user.Roles {
|
||||
if role.Name == "Super Admin" {
|
||||
return errors.New("cannot delete Super Admin user")
|
||||
}
|
||||
}
|
||||
|
||||
err = s.repo.DeleteUser(ctx, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Invalidate cache for deleted user
|
||||
if s.cacheInvalidator != nil {
|
||||
s.cacheInvalidator.InvalidateUserPermissions(userID.String())
|
||||
}
|
||||
|
||||
logging.InfoOperation("USER_DELETE", "Deleted user: "+userID.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateUser updates a user's details.
|
||||
func (s *MembershipService) UpdateUser(ctx context.Context, userID uuid.UUID, req UpdateUserRequest) (*model.User, error) {
|
||||
user, err := s.repo.FindUserByID(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.New("user not found")
|
||||
}
|
||||
|
||||
if req.Username != nil {
|
||||
user.Username = *req.Username
|
||||
}
|
||||
|
||||
if req.Password != nil && *req.Password != "" {
|
||||
// Use the model's SetPassword method to hash the password
|
||||
if err := user.SetPassword(*req.Password); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if req.RoleID != nil {
|
||||
// Check if role exists
|
||||
roleUUID, err := uuid.Parse(*req.RoleID)
|
||||
if err != nil {
|
||||
return nil, errors.New("invalid role ID format")
|
||||
}
|
||||
role, err := s.repo.FindRoleByID(ctx, roleUUID)
|
||||
if err != nil {
|
||||
return nil, errors.New("role not found")
|
||||
}
|
||||
user.Roles = []model.Role{*role}
|
||||
}
|
||||
|
||||
if err := s.repo.UpdateUser(ctx, user); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Invalidate cache if role was changed
|
||||
if req.RoleID != nil && s.cacheInvalidator != nil {
|
||||
s.cacheInvalidator.InvalidateUserPermissions(userID.String())
|
||||
}
|
||||
|
||||
logging.InfoOperation("USER_UPDATE", "Updated user: "+user.Username+" (ID: "+user.ID+")")
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// HasPermission checks if a user has a specific permission.
|
||||
func (s *MembershipService) HasPermission(ctx context.Context, userID string, permissionName string) (bool, error) {
|
||||
user, err := s.repo.FindUserByIDWithPermissions(ctx, userID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Check all user roles for permissions
|
||||
for _, role := range user.Roles {
|
||||
// Super admin and Admin have all permissions
|
||||
if role.Name == "Super Admin" || role.Name == "Admin" {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
for _, p := range role.Permissions {
|
||||
if p.Name == permissionName {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// SetupInitialData creates the initial roles and permissions.
|
||||
func (s *MembershipService) SetupInitialData(ctx context.Context) error {
|
||||
// Define all permissions
|
||||
permissions := model.AllPermissions
|
||||
|
||||
createdPermissions := make([]model.Permission, 0)
|
||||
for _, pName := range permissions {
|
||||
perm, err := s.repo.FindPermissionByName(ctx, pName)
|
||||
if err != nil { // Assuming error means not found
|
||||
perm = &model.Permission{Name: pName}
|
||||
if err := s.repo.CreatePermission(ctx, perm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
createdPermissions = append(createdPermissions, *perm)
|
||||
}
|
||||
|
||||
// Create Super Admin role with all permissions
|
||||
superAdminRole, err := s.repo.FindRoleByName(ctx, "Super Admin")
|
||||
if err != nil {
|
||||
superAdminRole = &model.Role{Name: "Super Admin"}
|
||||
if err := s.repo.CreateRole(ctx, superAdminRole); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := s.repo.AssignPermissionsToRole(ctx, superAdminRole, createdPermissions); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create Admin role with same permissions as Super Admin
|
||||
adminRole, err := s.repo.FindRoleByName(ctx, "Admin")
|
||||
if err != nil {
|
||||
adminRole = &model.Role{Name: "Admin"}
|
||||
if err := s.repo.CreateRole(ctx, adminRole); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := s.repo.AssignPermissionsToRole(ctx, adminRole, createdPermissions); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create Manager role with limited permissions (excluding membership, role, user, server create/delete)
|
||||
managerRole, err := s.repo.FindRoleByName(ctx, "Manager")
|
||||
if err != nil {
|
||||
managerRole = &model.Role{Name: "Manager"}
|
||||
if err := s.repo.CreateRole(ctx, managerRole); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Define manager permissions (limited set)
|
||||
managerPermissionNames := []string{
|
||||
model.ServerView,
|
||||
model.ServerUpdate,
|
||||
model.ServerStart,
|
||||
model.ServerStop,
|
||||
model.ConfigView,
|
||||
model.ConfigUpdate,
|
||||
}
|
||||
|
||||
managerPermissions := make([]model.Permission, 0)
|
||||
for _, permName := range managerPermissionNames {
|
||||
for _, perm := range createdPermissions {
|
||||
if perm.Name == permName {
|
||||
managerPermissions = append(managerPermissions, perm)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.repo.AssignPermissionsToRole(ctx, managerRole, managerPermissions); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Invalidate all caches after role setup changes
|
||||
if s.cacheInvalidator != nil {
|
||||
s.cacheInvalidator.InvalidateAllUserPermissions()
|
||||
}
|
||||
|
||||
// Create a default admin user if one doesn't exist
|
||||
_, err = s.repo.FindUserByUsername(ctx, "admin")
|
||||
if err != nil {
|
||||
logging.Debug("Creating default admin user")
|
||||
_, err = s.CreateUser(ctx, "admin", os.Getenv("PASSWORD"), "Super Admin") // Default password, should be changed
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAllRoles retrieves all roles for dropdown selection.
|
||||
func (s *MembershipService) GetAllRoles(ctx context.Context) ([]*model.Role, error) {
|
||||
return s.repo.ListRoles(ctx)
|
||||
}
|
||||
28
local/service/membership_interface.go
Normal file
28
local/service/membership_interface.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"omega-server/local/model"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// MembershipServiceInterface defines the interface for membership-related operations
|
||||
type MembershipServiceInterface interface {
|
||||
// Authentication and Authorization
|
||||
Login(ctx context.Context, username, password string) (string, error)
|
||||
HasPermission(ctx context.Context, userID string, permissionName string) (bool, error)
|
||||
GetUserWithPermissions(ctx context.Context, userID string) (*model.User, error)
|
||||
SetCacheInvalidator(invalidator CacheInvalidator)
|
||||
|
||||
// User Management
|
||||
CreateUser(ctx context.Context, username, password, roleName string) (*model.User, error)
|
||||
ListUsers(ctx context.Context) ([]*model.User, error)
|
||||
GetUser(ctx context.Context, userID uuid.UUID) (*model.User, error)
|
||||
DeleteUser(ctx context.Context, userID uuid.UUID) error
|
||||
UpdateUser(ctx context.Context, userID uuid.UUID, req UpdateUserRequest) (*model.User, error)
|
||||
|
||||
// Role Management
|
||||
GetAllRoles(ctx context.Context) ([]*model.Role, error)
|
||||
SetupInitialData(ctx context.Context) error
|
||||
}
|
||||
24
local/service/service.go
Normal file
24
local/service/service.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"omega-server/local/repository"
|
||||
"omega-server/local/utl/logging"
|
||||
|
||||
"go.uber.org/dig"
|
||||
)
|
||||
|
||||
// InitializeServices
|
||||
// Initializes Dependency Injection modules for services
|
||||
//
|
||||
// Args:
|
||||
// *dig.Container: Dig Container
|
||||
func InitializeServices(c *dig.Container) {
|
||||
logging.Debug("Initializing repositories")
|
||||
repository.InitializeRepositories(c)
|
||||
|
||||
logging.Debug("Registering services")
|
||||
// Provide services
|
||||
c.Provide(NewMembershipService)
|
||||
|
||||
logging.Debug("Completed service initialization")
|
||||
}
|
||||
Reference in New Issue
Block a user