package graph // This file will be automatically regenerated based on the schema, any resolver // implementations // will be copied through when generating and any unknown code will be moved to the end. // Code generated by github.com/99designs/gqlgen version v0.17.87 import ( "context" "errors" "fmt" "time" "gogs.dmsc.dev/arp/auth" "gogs.dmsc.dev/arp/graph/model" "gogs.dmsc.dev/arp/logging" "gogs.dmsc.dev/arp/models" ) // Login is the resolver for the login field. func (r *mutationResolver) Login(ctx context.Context, email string, password string) (*model.AuthPayload, error) { var user models.User if err := r.DB.Preload("Roles.Permissions").Where("email = ?", email).First(&user).Error; err != nil { return nil, errors.New("invalid credentials") } // Check password if !auth.CheckPassword(password, user.Password) { return nil, errors.New("invalid credentials") } token, err := auth.GenerateToken(user) if err != nil { return nil, fmt.Errorf("failed to generate token: %w", err) } return &model.AuthPayload{ Token: token, User: convertUser(user), }, nil } // CreateUser is the resolver for the createUser field. func (r *mutationResolver) CreateUser(ctx context.Context, input model.NewUser) (*model.User, error) { // Auth check if !auth.IsAuthenticated(ctx) { return nil, errors.New("unauthorized: authentication required") } roles := make([]models.Role, len(input.Roles)) for i, roleIDStr := range input.Roles { roleID, err := toID(roleIDStr) if err != nil { return nil, fmt.Errorf("invalid role ID: %w", err) } var role models.Role if err := r.DB.First(&role, roleID).Error; err != nil { return nil, fmt.Errorf("role not found: %w", err) } roles[i] = role } // Hash the password before storing hashedPassword, err := auth.HashPassword(input.Password) if err != nil { return nil, fmt.Errorf("failed to hash password: %w", err) } user := models.User{ Email: input.Email, Password: hashedPassword, Roles: roles, } if err := r.DB.Create(&user).Error; err != nil { return nil, fmt.Errorf("failed to create user: %w", err) } logging.LogMutation(ctx, "CREATE", "USER", user.Email) return convertUser(user), nil } // UpdateUser is the resolver for the updateUser field. func (r *mutationResolver) UpdateUser(ctx context.Context, id string, input model.UpdateUserInput) (*model.User, error) { // Auth check if !auth.IsAuthenticated(ctx) { return nil, errors.New("unauthorized: authentication required") } if !auth.HasPermission(ctx, "user:update") { return nil, errors.New("unauthorized: missing user:update permission") } userID, err := toID(id) if err != nil { return nil, fmt.Errorf("invalid user ID: %w", err) } var existing models.User if err := r.DB.First(&existing, userID).Error; err != nil { return nil, fmt.Errorf("user not found: %w", err) } if input.Email != nil { existing.Email = *input.Email } if input.Password != nil { // Hash the new password hashedPassword, err := auth.HashPassword(*input.Password) if err != nil { return nil, fmt.Errorf("failed to hash password: %w", err) } existing.Password = hashedPassword } if len(input.Roles) > 0 { roles := make([]models.Role, len(input.Roles)) for i, roleIDStr := range input.Roles { roleID, err := toID(roleIDStr) if err != nil { return nil, fmt.Errorf("invalid role ID: %w", err) } var role models.Role if err := r.DB.First(&role, roleID).Error; err != nil { return nil, fmt.Errorf("role not found: %w", err) } roles[i] = role } existing.Roles = roles } if err := r.DB.Save(&existing).Error; err != nil { return nil, fmt.Errorf("failed to update user: %w", err) } logging.LogMutation(ctx, "UPDATE", "USER", existing.Email) return convertUser(existing), nil } // DeleteUser is the resolver for the deleteUser field. func (r *mutationResolver) DeleteUser(ctx context.Context, id string) (bool, error) { // Auth check if !auth.IsAuthenticated(ctx) { return false, errors.New("unauthorized: authentication required") } if !auth.HasPermission(ctx, "user:delete") { return false, errors.New("unauthorized: missing user:delete permission") } userID, err := toID(id) if err != nil { return false, fmt.Errorf("invalid user ID: %w", err) } result := r.DB.Delete(&models.User{}, userID) if result.Error != nil { return false, fmt.Errorf("failed to delete user: %w", result.Error) } logging.LogMutation(ctx, "DELETE", "USER", id) return result.RowsAffected > 0, nil } // CreateNote is the resolver for the createNote field. func (r *mutationResolver) CreateNote(ctx context.Context, input model.NewNote) (*model.Note, error) { // Auth check if !auth.IsAuthenticated(ctx) { return nil, errors.New("unauthorized: authentication required") } userID, err := toID(input.UserID) if err != nil { return nil, fmt.Errorf("invalid user ID: %w", err) } serviceID, err := toID(input.ServiceID) if err != nil { return nil, fmt.Errorf("invalid service ID: %w", err) } note := models.Note{ Title: input.Title, Content: input.Content, UserID: userID, ServiceID: serviceID, } if err := r.DB.Create(¬e).Error; err != nil { return nil, fmt.Errorf("failed to create note: %w", err) } logging.LogMutation(ctx, "CREATE", "NOTE", note.Title) return convertNote(note), nil } // UpdateNote is the resolver for the updateNote field. func (r *mutationResolver) UpdateNote(ctx context.Context, id string, input model.UpdateNoteInput) (*model.Note, error) { // Auth check if !auth.IsAuthenticated(ctx) { return nil, errors.New("unauthorized: authentication required") } if !auth.HasPermission(ctx, "note:update") { return nil, errors.New("unauthorized: missing note:update permission") } noteID, err := toID(id) if err != nil { return nil, fmt.Errorf("invalid note ID: %w", err) } var existing models.Note if err := r.DB.First(&existing, noteID).Error; err != nil { return nil, fmt.Errorf("note not found: %w", err) } if input.Title != nil { existing.Title = *input.Title } if input.Content != nil { existing.Content = *input.Content } if input.UserID != nil { userID, err := toID(*input.UserID) if err != nil { return nil, fmt.Errorf("invalid user ID: %w", err) } existing.UserID = userID } if input.ServiceID != nil { serviceID, err := toID(*input.ServiceID) if err != nil { return nil, fmt.Errorf("invalid service ID: %w", err) } existing.ServiceID = serviceID } if err := r.DB.Save(&existing).Error; err != nil { return nil, fmt.Errorf("failed to update note: %w", err) } logging.LogMutation(ctx, "UPDATE", "NOTE", existing.Title) return convertNote(existing), nil } // DeleteNote is the resolver for the deleteNote field. func (r *mutationResolver) DeleteNote(ctx context.Context, id string) (bool, error) { // Auth check if !auth.IsAuthenticated(ctx) { return false, errors.New("unauthorized: authentication required") } if !auth.HasPermission(ctx, "note:delete") { return false, errors.New("unauthorized: missing note:delete permission") } noteID, err := toID(id) if err != nil { return false, fmt.Errorf("invalid note ID: %w", err) } result := r.DB.Delete(&models.Note{}, noteID) if result.Error != nil { return false, fmt.Errorf("failed to delete note: %w", result.Error) } logging.LogMutation(ctx, "DELETE", "NOTE", id) return result.RowsAffected > 0, nil } // CreateRole is the resolver for the createRole field. func (r *mutationResolver) CreateRole(ctx context.Context, input model.NewRole) (*model.Role, error) { // Auth check if !auth.IsAuthenticated(ctx) { return nil, errors.New("unauthorized: authentication required") } permissions := make([]models.Permission, len(input.Permissions)) for i, permIDStr := range input.Permissions { permID, err := toID(permIDStr) if err != nil { return nil, fmt.Errorf("invalid permission ID: %w", err) } var perm models.Permission if err := r.DB.First(&perm, permID).Error; err != nil { return nil, fmt.Errorf("permission not found: %w", err) } permissions[i] = perm } role := models.Role{ Name: input.Name, Description: input.Description, Permissions: permissions, } if err := r.DB.Create(&role).Error; err != nil { return nil, fmt.Errorf("failed to create role: %w", err) } logging.LogMutation(ctx, "CREATE", "ROLE", role.Name) return convertRole(role), nil } // UpdateRole is the resolver for the updateRole field. func (r *mutationResolver) UpdateRole(ctx context.Context, id string, input model.UpdateRoleInput) (*model.Role, error) { // Auth check if !auth.IsAuthenticated(ctx) { return nil, errors.New("unauthorized: authentication required") } if !auth.HasPermission(ctx, "role:update") { return nil, errors.New("unauthorized: missing role:update permission") } roleID, err := toID(id) if err != nil { return nil, fmt.Errorf("invalid role ID: %w", err) } var existing models.Role if err := r.DB.First(&existing, roleID).Error; err != nil { return nil, fmt.Errorf("role not found: %w", err) } if input.Name != nil { existing.Name = *input.Name } if input.Description != nil { existing.Description = *input.Description } if len(input.Permissions) > 0 { permissions := make([]models.Permission, len(input.Permissions)) for i, permIDStr := range input.Permissions { permID, err := toID(permIDStr) if err != nil { return nil, fmt.Errorf("invalid permission ID: %w", err) } var perm models.Permission if err := r.DB.First(&perm, permID).Error; err != nil { return nil, fmt.Errorf("permission not found: %w", err) } permissions[i] = perm } existing.Permissions = permissions } if err := r.DB.Save(&existing).Error; err != nil { return nil, fmt.Errorf("failed to update role: %w", err) } logging.LogMutation(ctx, "UPDATE", "ROLE", existing.Name) return convertRole(existing), nil } // DeleteRole is the resolver for the deleteRole field. func (r *mutationResolver) DeleteRole(ctx context.Context, id string) (bool, error) { // Auth check if !auth.IsAuthenticated(ctx) { return false, errors.New("unauthorized: authentication required") } if !auth.HasPermission(ctx, "role:delete") { return false, errors.New("unauthorized: missing role:delete permission") } roleID, err := toID(id) if err != nil { return false, fmt.Errorf("invalid role ID: %w", err) } result := r.DB.Delete(&models.Role{}, roleID) if result.Error != nil { return false, fmt.Errorf("failed to delete role: %w", result.Error) } logging.LogMutation(ctx, "DELETE", "ROLE", id) return result.RowsAffected > 0, nil } // CreatePermission is the resolver for the createPermission field. func (r *mutationResolver) CreatePermission(ctx context.Context, input model.NewPermission) (*model.Permission, error) { // Auth check if !auth.IsAuthenticated(ctx) { return nil, errors.New("unauthorized: authentication required") } permission := models.Permission{ Code: input.Code, Description: input.Description, } if err := r.DB.Create(&permission).Error; err != nil { return nil, fmt.Errorf("failed to create permission: %w", err) } logging.LogMutation(ctx, "CREATE", "PERMISSION", permission.Code) return convertPermission(permission), nil } // UpdatePermission is the resolver for the updatePermission field. func (r *mutationResolver) UpdatePermission(ctx context.Context, id string, input model.UpdatePermissionInput) (*model.Permission, error) { // Auth check if !auth.IsAuthenticated(ctx) { return nil, errors.New("unauthorized: authentication required") } if !auth.HasPermission(ctx, "permission:update") { return nil, errors.New("unauthorized: missing permission:update permission") } permID, err := toID(id) if err != nil { return nil, fmt.Errorf("invalid permission ID: %w", err) } var existing models.Permission if err := r.DB.First(&existing, permID).Error; err != nil { return nil, fmt.Errorf("permission not found: %w", err) } if input.Code != nil { existing.Code = *input.Code } if input.Description != nil { existing.Description = *input.Description } if err := r.DB.Save(&existing).Error; err != nil { return nil, fmt.Errorf("failed to update permission: %w", err) } logging.LogMutation(ctx, "UPDATE", "PERMISSION", existing.Code) return convertPermission(existing), nil } // DeletePermission is the resolver for the deletePermission field. func (r *mutationResolver) DeletePermission(ctx context.Context, id string) (bool, error) { // Auth check if !auth.IsAuthenticated(ctx) { return false, errors.New("unauthorized: authentication required") } if !auth.HasPermission(ctx, "permission:delete") { return false, errors.New("unauthorized: missing permission:delete permission") } permID, err := toID(id) if err != nil { return false, fmt.Errorf("invalid permission ID: %w", err) } result := r.DB.Delete(&models.Permission{}, permID) if result.Error != nil { return false, fmt.Errorf("failed to delete permission: %w", result.Error) } logging.LogMutation(ctx, "DELETE", "PERMISSION", id) return result.RowsAffected > 0, nil } // CreateService is the resolver for the createService field. func (r *mutationResolver) CreateService(ctx context.Context, input model.NewService) (*model.Service, error) { // Auth check if !auth.IsAuthenticated(ctx) { return nil, errors.New("unauthorized: authentication required") } createdByID, err := toID(input.CreatedByID) if err != nil { return nil, fmt.Errorf("invalid created by ID: %w", err) } service := models.Service{ Name: input.Name, CreatedByID: createdByID, } if input.Description != nil { service.Description = *input.Description } // Add participants for _, participantIDStr := range input.Participants { participantID, err := toID(participantIDStr) if err != nil { return nil, fmt.Errorf("invalid participant ID: %w", err) } var user models.User if err := r.DB.First(&user, participantID).Error; err != nil { return nil, fmt.Errorf("participant not found: %w", err) } service.Participants = append(service.Participants, user) } if err := r.DB.Create(&service).Error; err != nil { return nil, fmt.Errorf("failed to create service: %w", err) } // Reload with associations r.DB.Preload("CreatedBy").Preload("Participants").Preload("Tasks").First(&service, service.ID) logging.LogMutation(ctx, "CREATE", "SERVICE", service.Name) return convertService(service), nil } // UpdateService is the resolver for the updateService field. func (r *mutationResolver) UpdateService(ctx context.Context, id string, input model.UpdateServiceInput) (*model.Service, error) { // Auth check if !auth.IsAuthenticated(ctx) { return nil, errors.New("unauthorized: authentication required") } if !auth.HasPermission(ctx, "service:update") { return nil, errors.New("unauthorized: missing service:update permission") } serviceID, err := toID(id) if err != nil { return nil, fmt.Errorf("invalid service ID: %w", err) } var existing models.Service if err := r.DB.Preload("Participants").First(&existing, serviceID).Error; err != nil { return nil, fmt.Errorf("service not found: %w", err) } if input.Name != nil { existing.Name = *input.Name } if input.Description != nil { existing.Description = *input.Description } if len(input.Participants) > 0 { participants := []models.User{} for _, participantIDStr := range input.Participants { participantID, err := toID(participantIDStr) if err != nil { return nil, fmt.Errorf("invalid participant ID: %w", err) } var user models.User if err := r.DB.First(&user, participantID).Error; err != nil { return nil, fmt.Errorf("participant not found: %w", err) } participants = append(participants, user) } existing.Participants = participants } if err := r.DB.Save(&existing).Error; err != nil { return nil, fmt.Errorf("failed to update service: %w", err) } // Reload with associations for response r.DB.Preload("CreatedBy").Preload("Participants").Preload("Tasks").First(&existing, existing.ID) logging.LogMutation(ctx, "UPDATE", "SERVICE", existing.Name) return convertService(existing), nil } // DeleteService is the resolver for the deleteService field. func (r *mutationResolver) DeleteService(ctx context.Context, id string) (bool, error) { // Auth check if !auth.IsAuthenticated(ctx) { return false, errors.New("unauthorized: authentication required") } if !auth.HasPermission(ctx, "service:delete") { return false, errors.New("unauthorized: missing service:delete permission") } serviceID, err := toID(id) if err != nil { return false, fmt.Errorf("invalid service ID: %w", err) } result := r.DB.Delete(&models.Service{}, serviceID) if result.Error != nil { return false, fmt.Errorf("failed to delete service: %w", result.Error) } logging.LogMutation(ctx, "DELETE", "SERVICE", id) return result.RowsAffected > 0, nil } // CreateTask is the resolver for the createTask field. func (r *mutationResolver) CreateTask(ctx context.Context, input model.NewTask) (*model.Task, error) { // Auth check if !auth.IsAuthenticated(ctx) { return nil, errors.New("unauthorized: authentication required") } createdByID, err := toID(input.CreatedByID) if err != nil { return nil, fmt.Errorf("invalid created by ID: %w", err) } task := models.Task{ Title: input.Title, Content: input.Content, CreatedByID: createdByID, Priority: input.Priority, } if input.AssigneeID != nil { assigneeID, err := toID(*input.AssigneeID) if err != nil { return nil, fmt.Errorf("invalid assignee ID: %w", err) } task.AssigneeID = &assigneeID } if input.StatusID != nil { statusID, err := toID(*input.StatusID) if err != nil { return nil, fmt.Errorf("invalid status ID: %w", err) } task.StatusID = statusID } if input.DueDate != nil { parsedTime, parseErr := time.Parse(time.RFC3339, *input.DueDate) if parseErr != nil { return nil, fmt.Errorf("invalid due date format: %w", parseErr) } task.DueDate = &parsedTime } if err := r.DB.Create(&task).Error; err != nil { return nil, fmt.Errorf("failed to create task: %w", err) } // Reload with associations r.DB.Preload("CreatedBy").Preload("Assignee").Preload("Status").First(&task, task.ID) // Publish task created event to assignee graphqlTask := convertTask(task) r.PublishTaskEvent(graphqlTask, task.AssigneeID, "created") logging.LogMutation(ctx, "CREATE", "TASK", task.Title) return graphqlTask, nil } // UpdateTask is the resolver for the updateTask field. func (r *mutationResolver) UpdateTask(ctx context.Context, id string, input model.UpdateTaskInput) (*model.Task, error) { // Auth check if !auth.IsAuthenticated(ctx) { return nil, errors.New("unauthorized: authentication required") } if !auth.HasPermission(ctx, "task:update") { return nil, errors.New("unauthorized: missing task:update permission") } // Get current user for UpdatedBy currentUser, err := auth.CurrentUser(ctx) if err != nil { return nil, fmt.Errorf("failed to get current user: %w", err) } taskID, err := toID(id) if err != nil { return nil, fmt.Errorf("invalid task ID: %w", err) } var existing models.Task if err := r.DB.Preload("CreatedBy").Preload("Assignee").First(&existing, taskID).Error; err != nil { return nil, fmt.Errorf("task not found: %w", err) } if input.Title != nil { existing.Title = *input.Title } if input.Content != nil { existing.Content = *input.Content } if input.AssigneeID != nil { if *input.AssigneeID == "" { existing.AssigneeID = nil } else { assigneeID, err := toID(*input.AssigneeID) if err != nil { return nil, fmt.Errorf("invalid assignee ID: %w", err) } existing.AssigneeID = &assigneeID } } if input.StatusID != nil { if *input.StatusID == "" { existing.StatusID = 0 } else { statusID, err := toID(*input.StatusID) if err != nil { return nil, fmt.Errorf("invalid status ID: %w", err) } existing.StatusID = statusID } } if input.DueDate != nil { if *input.DueDate == "" { existing.DueDate = nil } else { parsedTime, parseErr := time.Parse(time.RFC3339, *input.DueDate) if parseErr != nil { return nil, fmt.Errorf("invalid due date format: %w", parseErr) } existing.DueDate = &parsedTime } } if input.Priority != nil { existing.Priority = *input.Priority } // Set UpdatedByID to current user existing.UpdatedByID = currentUser.ID if err := r.DB.Save(&existing).Error; err != nil { return nil, fmt.Errorf("failed to update task: %w", err) } // Reload with associations for response r.DB.Preload("CreatedBy").Preload("UpdatedBy").Preload("Assignee").Preload("Status").First(&existing, existing.ID) // Publish task updated event to assignee graphqlTask := convertTask(existing) r.PublishTaskEvent(graphqlTask, existing.AssigneeID, "updated") logging.LogMutation(ctx, "UPDATE", "TASK", existing.Title) return graphqlTask, nil } // DeleteTask is the resolver for the deleteTask field. func (r *mutationResolver) DeleteTask(ctx context.Context, id string) (bool, error) { // Auth check if !auth.IsAuthenticated(ctx) { return false, errors.New("unauthorized: authentication required") } if !auth.HasPermission(ctx, "task:delete") { return false, errors.New("unauthorized: missing task:delete permission") } taskID, err := toID(id) if err != nil { return false, fmt.Errorf("invalid task ID: %w", err) } result := r.DB.Delete(&models.Task{}, taskID) if result.Error != nil { return false, fmt.Errorf("failed to delete task: %w", result.Error) } logging.LogMutation(ctx, "DELETE", "TASK", id) return result.RowsAffected > 0, nil } // CreateTaskStatus is the resolver for the createTaskStatus field. func (r *mutationResolver) CreateTaskStatus(ctx context.Context, input model.NewTaskStatus) (*model.TaskStatus, error) { // Auth check if !auth.IsAuthenticated(ctx) { return nil, errors.New("unauthorized: authentication required") } taskStatus := models.TaskStatus{ Code: input.Code, Label: input.Label, } if err := r.DB.Create(&taskStatus).Error; err != nil { return nil, fmt.Errorf("failed to create task status: %w", err) } logging.LogMutation(ctx, "CREATE", "TASKSTATUS", taskStatus.Code) return convertTaskStatus(taskStatus), nil } // UpdateTaskStatus is the resolver for the updateTaskStatus field. func (r *mutationResolver) UpdateTaskStatus(ctx context.Context, id string, input model.UpdateTaskStatusInput) (*model.TaskStatus, error) { // Auth check if !auth.IsAuthenticated(ctx) { return nil, errors.New("unauthorized: authentication required") } if !auth.HasPermission(ctx, "taskstatus:update") { return nil, errors.New("unauthorized: missing taskstatus:update permission") } statusID, err := toID(id) if err != nil { return nil, fmt.Errorf("invalid task status ID: %w", err) } var existing models.TaskStatus if err := r.DB.First(&existing, statusID).Error; err != nil { return nil, fmt.Errorf("task status not found: %w", err) } if input.Code != nil { existing.Code = *input.Code } if input.Label != nil { existing.Label = *input.Label } if err := r.DB.Save(&existing).Error; err != nil { return nil, fmt.Errorf("failed to update task status: %w", err) } // Reload with tasks for response r.DB.Preload("Tasks").First(&existing, existing.ID) logging.LogMutation(ctx, "UPDATE", "TASKSTATUS", existing.Code) return convertTaskStatus(existing), nil } // DeleteTaskStatus is the resolver for the deleteTaskStatus field. func (r *mutationResolver) DeleteTaskStatus(ctx context.Context, id string) (bool, error) { // Auth check if !auth.IsAuthenticated(ctx) { return false, errors.New("unauthorized: authentication required") } if !auth.HasPermission(ctx, "taskstatus:delete") { return false, errors.New("unauthorized: missing taskstatus:delete permission") } statusID, err := toID(id) if err != nil { return false, fmt.Errorf("invalid task status ID: %w", err) } result := r.DB.Delete(&models.TaskStatus{}, statusID) if result.Error != nil { return false, fmt.Errorf("failed to delete task status: %w", result.Error) } logging.LogMutation(ctx, "DELETE", "TASKSTATUS", id) return result.RowsAffected > 0, nil } // CreateMessage is the resolver for the createMessage field. func (r *mutationResolver) CreateMessage(ctx context.Context, input model.NewMessage) (*model.Message, error) { // Auth check if !auth.IsAuthenticated(ctx) { return nil, errors.New("unauthorized: authentication required") } // Get sender from authenticated user currentUser, err := auth.CurrentUser(ctx) if err != nil { return nil, fmt.Errorf("failed to get current user: %w", err) } // Build receivers list receivers := make([]models.User, 0, len(input.Receivers)) receiverIDs := make([]uint, 0, len(input.Receivers)) for _, receiverIDStr := range input.Receivers { receiverID, err := toID(receiverIDStr) if err != nil { return nil, fmt.Errorf("invalid receiver ID: %w", err) } var user models.User if err := r.DB.First(&user, receiverID).Error; err != nil { return nil, fmt.Errorf("receiver not found: %w", err) } receivers = append(receivers, user) receiverIDs = append(receiverIDs, receiverID) } message := models.Message{ SenderID: currentUser.ID, Content: input.Content, Receivers: receivers, } if err := r.DB.Create(&message).Error; err != nil { return nil, fmt.Errorf("failed to create message: %w", err) } // Reload with associations r.DB.Preload("Sender").Preload("Receivers").First(&message, message.ID) // Publish message event to receivers (excluding the sender to prevent notification loops) notifyReceiverIDs := make([]uint, 0, len(receiverIDs)) for _, receiverID := range receiverIDs { if receiverID != currentUser.ID { notifyReceiverIDs = append(notifyReceiverIDs, receiverID) } } graphqlMessage := convertMessage(message) r.PublishMessageEvent(graphqlMessage, notifyReceiverIDs) logging.LogMutation(ctx, "CREATE", "MESSAGE", fmt.Sprintf("id=%d content=%s", message.ID, message.Content)) return graphqlMessage, nil } // UpdateMessage is the resolver for the updateMessage field. func (r *mutationResolver) UpdateMessage(ctx context.Context, id string, input model.UpdateMessageInput) (*model.Message, error) { // Auth check if !auth.IsAuthenticated(ctx) { return nil, errors.New("unauthorized: authentication required") } if !auth.HasPermission(ctx, "message:update") { return nil, errors.New("unauthorized: missing message:update permission") } messageID, err := toID(id) if err != nil { return nil, fmt.Errorf("invalid message ID: %w", err) } var existing models.Message if err := r.DB.Preload("Sender").Preload("Receivers").First(&existing, messageID).Error; err != nil { return nil, fmt.Errorf("message not found: %w", err) } if input.Content != nil { existing.Content = *input.Content } if len(input.Receivers) > 0 { receivers := make([]models.User, 0, len(input.Receivers)) for _, receiverIDStr := range input.Receivers { receiverID, err := toID(receiverIDStr) if err != nil { return nil, fmt.Errorf("invalid receiver ID: %w", err) } var user models.User if err := r.DB.First(&user, receiverID).Error; err != nil { return nil, fmt.Errorf("receiver not found: %w", err) } receivers = append(receivers, user) } existing.Receivers = receivers } if err := r.DB.Save(&existing).Error; err != nil { return nil, fmt.Errorf("failed to update message: %w", err) } // Reload with associations r.DB.Preload("Sender").Preload("Receivers").First(&existing, existing.ID) logging.LogMutation(ctx, "UPDATE", "MESSAGE", id) return convertMessage(existing), nil } // DeleteMessage is the resolver for the deleteMessage field. func (r *mutationResolver) DeleteMessage(ctx context.Context, id string) (bool, error) { // Auth check if !auth.IsAuthenticated(ctx) { return false, errors.New("unauthorized: authentication required") } if !auth.HasPermission(ctx, "message:delete") { return false, errors.New("unauthorized: missing message:delete permission") } messageID, err := toID(id) if err != nil { return false, fmt.Errorf("invalid message ID: %w", err) } result := r.DB.Delete(&models.Message{}, messageID) if result.Error != nil { return false, fmt.Errorf("failed to delete message: %w", result.Error) } logging.LogMutation(ctx, "DELETE", "MESSAGE", id) return result.RowsAffected > 0, nil } // Users is the resolver for the users field. func (r *queryResolver) Users(ctx context.Context) ([]*model.User, error) { // Auth check if !auth.IsAuthenticated(ctx) { return nil, errors.New("unauthorized: authentication required") } var users []models.User if err := r.DB.Find(&users).Error; err != nil { return nil, fmt.Errorf("failed to fetch users: %w", err) } logging.LogQuery(ctx, "USERS", "all") return convertUsers(users), nil } // User is the resolver for the user field. func (r *queryResolver) User(ctx context.Context, id string) (*model.User, error) { // Auth check if !auth.IsAuthenticated(ctx) { return nil, errors.New("unauthorized: authentication required") } userID, err := toID(id) if err != nil { return nil, fmt.Errorf("invalid user ID: %w", err) } var user models.User if err := r.DB.Preload("Roles.Permissions").First(&user, userID).Error; err != nil { return nil, fmt.Errorf("user not found: %w", err) } logging.LogQuery(ctx, "USER", id) return convertUser(user), nil } // Notes is the resolver for the notes field. func (r *queryResolver) Notes(ctx context.Context) ([]*model.Note, error) { // Auth check if !auth.IsAuthenticated(ctx) { return nil, errors.New("unauthorized: authentication required") } var notes []models.Note if err := r.DB.Preload("User").Preload("Service").Find(¬es).Error; err != nil { return nil, fmt.Errorf("failed to fetch notes: %w", err) } logging.LogQuery(ctx, "NOTES", "all") return convertNotes(notes), nil } // Note is the resolver for the note field. func (r *queryResolver) Note(ctx context.Context, id string) (*model.Note, error) { // Auth check if !auth.IsAuthenticated(ctx) { return nil, errors.New("unauthorized: authentication required") } noteID, err := toID(id) if err != nil { return nil, fmt.Errorf("invalid note ID: %w", err) } var note models.Note if err := r.DB.Preload("User").Preload("Service").First(¬e, noteID).Error; err != nil { return nil, fmt.Errorf("note not found: %w", err) } logging.LogQuery(ctx, "NOTE", id) return convertNote(note), nil } // Roles is the resolver for the roles field. func (r *queryResolver) Roles(ctx context.Context) ([]*model.Role, error) { // Auth check if !auth.IsAuthenticated(ctx) { return nil, errors.New("unauthorized: authentication required") } var roles []models.Role if err := r.DB.Preload("Permissions").Find(&roles).Error; err != nil { return nil, fmt.Errorf("failed to fetch roles: %w", err) } logging.LogQuery(ctx, "ROLES", "all") return convertRoles(roles), nil } // Role is the resolver for the role field. func (r *queryResolver) Role(ctx context.Context, id string) (*model.Role, error) { // Auth check if !auth.IsAuthenticated(ctx) { return nil, errors.New("unauthorized: authentication required") } roleID, err := toID(id) if err != nil { return nil, fmt.Errorf("invalid role ID: %w", err) } var role models.Role if err := r.DB.Preload("Permissions").First(&role, roleID).Error; err != nil { return nil, fmt.Errorf("role not found: %w", err) } logging.LogQuery(ctx, "ROLE", id) return convertRole(role), nil } // Permissions is the resolver for the permissions field. func (r *queryResolver) Permissions(ctx context.Context) ([]*model.Permission, error) { // Auth check if !auth.IsAuthenticated(ctx) { return nil, errors.New("unauthorized: authentication required") } var perms []models.Permission if err := r.DB.Find(&perms).Error; err != nil { return nil, fmt.Errorf("failed to fetch permissions: %w", err) } logging.LogQuery(ctx, "PERMISSIONS", "all") return convertPermissions(perms), nil } // Permission is the resolver for the permission field. func (r *queryResolver) Permission(ctx context.Context, id string) (*model.Permission, error) { // Auth check if !auth.IsAuthenticated(ctx) { return nil, errors.New("unauthorized: authentication required") } permID, err := toID(id) if err != nil { return nil, fmt.Errorf("invalid permission ID: %w", err) } var perm models.Permission if err := r.DB.First(&perm, permID).Error; err != nil { return nil, fmt.Errorf("permission not found: %w", err) } logging.LogQuery(ctx, "PERMISSION", id) return convertPermission(perm), nil } // Services is the resolver for the services field. func (r *queryResolver) Services(ctx context.Context) ([]*model.Service, error) { // Auth check if !auth.IsAuthenticated(ctx) { return nil, errors.New("unauthorized: authentication required") } var services []models.Service if err := r.DB.Preload("CreatedBy").Preload("Participants").Preload("Tasks").Find(&services).Error; err != nil { return nil, fmt.Errorf("failed to fetch services: %w", err) } logging.LogQuery(ctx, "SERVICES", "all") return convertServices(services), nil } // Service is the resolver for the service field. func (r *queryResolver) Service(ctx context.Context, id string) (*model.Service, error) { // Auth check if !auth.IsAuthenticated(ctx) { return nil, errors.New("unauthorized: authentication required") } serviceID, err := toID(id) if err != nil { return nil, fmt.Errorf("invalid service ID: %w", err) } var service models.Service if err := r.DB.Preload("CreatedBy").Preload("Participants").Preload("Tasks").First(&service, serviceID).Error; err != nil { return nil, fmt.Errorf("service not found: %w", err) } logging.LogQuery(ctx, "SERVICE", id) return convertService(service), nil } // Tasks is the resolver for the tasks field. func (r *queryResolver) Tasks(ctx context.Context) ([]*model.Task, error) { // Auth check if !auth.IsAuthenticated(ctx) { return nil, errors.New("unauthorized: authentication required") } var tasks []models.Task if err := r.DB.Preload("CreatedBy").Preload("UpdatedBy").Preload("Assignee").Preload("Status").Find(&tasks).Error; err != nil { return nil, fmt.Errorf("failed to fetch tasks: %w", err) } logging.LogQuery(ctx, "TASKS", "all") return convertTasks(tasks), nil } // Task is the resolver for the task field. func (r *queryResolver) Task(ctx context.Context, id string) (*model.Task, error) { // Auth check if !auth.IsAuthenticated(ctx) { return nil, errors.New("unauthorized: authentication required") } taskID, err := toID(id) if err != nil { return nil, fmt.Errorf("invalid task ID: %w", err) } var task models.Task if err := r.DB.Preload("CreatedBy").Preload("UpdatedBy").Preload("Assignee").Preload("Status").First(&task, taskID).Error; err != nil { return nil, fmt.Errorf("task not found: %w", err) } logging.LogQuery(ctx, "TASK", id) return convertTask(task), nil } // TaskStatuses is the resolver for the taskStatuses field. func (r *queryResolver) TaskStatuses(ctx context.Context) ([]*model.TaskStatus, error) { // Auth check if !auth.IsAuthenticated(ctx) { return nil, errors.New("unauthorized: authentication required") } var statuses []models.TaskStatus if err := r.DB.Preload("Tasks").Find(&statuses).Error; err != nil { return nil, fmt.Errorf("failed to fetch task statuses: %w", err) } logging.LogQuery(ctx, "TASKSTATUSES", "all") return convertTaskStatuses(statuses), nil } // TaskStatus func (r *queryResolver) TaskStatus(ctx context.Context, id string) (*model.TaskStatus, error) { // Auth check if !auth.IsAuthenticated(ctx) { return nil, errors.New("unauthorized: authentication required") } statusID, err := toID(id) if err != nil { return nil, fmt.Errorf("invalid task status ID: %w", err) } var status models.TaskStatus if err := r.DB.Preload("Tasks").First(&status, statusID).Error; err != nil { return nil, fmt.Errorf("task status not found: %w", err) } logging.LogQuery(ctx, "TASKSTATUS", id) return convertTaskStatus(status), nil } // Messages is the resolver for the messages field. func (r *queryResolver) Messages(ctx context.Context) ([]*model.Message, error) { // Auth check if !auth.IsAuthenticated(ctx) { return nil, errors.New("unauthorized: authentication required") } var messages []models.Message if err := r.DB.Preload("Sender").Find(&messages).Error; err != nil { return nil, fmt.Errorf("failed to fetch messages: %w", err) } logging.LogQuery(ctx, "MESSAGES", "all") return convertMessages(messages), nil } // Message is the resolver for the message field. func (r *queryResolver) Message(ctx context.Context, id string) (*model.Message, error) { // Auth check if !auth.IsAuthenticated(ctx) { return nil, errors.New("unauthorized: authentication required") } messageID, err := toID(id) if err != nil { return nil, fmt.Errorf("invalid message ID: %w", err) } var message models.Message if err := r.DB.Preload("Sender").First(&message, messageID).Error; err != nil { return nil, fmt.Errorf("message not found: %w", err) } logging.LogQuery(ctx, "MESSAGE", id) return convertMessage(message), nil } // TaskCreated is the resolver for the taskCreated field. // Users only receive events for tasks where they are the assignee. func (r *subscriptionResolver) TaskCreated(ctx context.Context) (<-chan *model.Task, error) { // Get current user user, err := auth.CurrentUser(ctx) if err != nil { return nil, errors.New("unauthorized: authentication required") } // Subscribe to task events eventChan := r.SubscribeToTasks(user.ID) // Create output channel outputChan := make(chan *model.Task, 10) // Start goroutine to filter and forward events go func() { defer close(outputChan) for { select { case <-ctx.Done(): return case event, ok := <-eventChan: if !ok { return } // Only forward "created" events if event.EventType == "created" && event.Task != nil { select { case outputChan <- event.Task: default: // Channel full, skip } } } } }() return outputChan, nil } // TaskUpdated is the resolver for the taskUpdated field. // Users only receive events for tasks where they are the assignee. func (r *subscriptionResolver) TaskUpdated(ctx context.Context) (<-chan *model.Task, error) { // Get current user user, err := auth.CurrentUser(ctx) if err != nil { return nil, errors.New("unauthorized: authentication required") } // Subscribe to task events eventChan := r.SubscribeToTasks(user.ID) // Create output channel outputChan := make(chan *model.Task, 10) // Start goroutine to filter and forward events go func() { defer close(outputChan) for { select { case <-ctx.Done(): return case event, ok := <-eventChan: if !ok { return } // Only forward "updated" events if event.EventType == "updated" && event.Task != nil { select { case outputChan <- event.Task: default: // Channel full, skip } } } } }() return outputChan, nil } // TaskDeleted is the resolver for the taskDeleted field. // Users only receive events for tasks where they are the assignee. func (r *subscriptionResolver) TaskDeleted(ctx context.Context) (<-chan *model.Task, error) { // Get current user user, err := auth.CurrentUser(ctx) if err != nil { return nil, errors.New("unauthorized: authentication required") } // Subscribe to task events eventChan := r.SubscribeToTasks(user.ID) // Create output channel outputChan := make(chan *model.Task, 10) // Start goroutine to filter and forward events go func() { defer close(outputChan) for { select { case <-ctx.Done(): return case event, ok := <-eventChan: if !ok { return } // Only forward "deleted" events if event.EventType == "deleted" && event.Task != nil { select { case outputChan <- event.Task: default: // Channel full, skip } } } } }() return outputChan, nil } // MessageAdded is the resolver for the messageAdded field. // Users only receive events for messages where they are in the receivers list. func (r *subscriptionResolver) MessageAdded(ctx context.Context) (<-chan *model.Message, error) { // Get current user user, err := auth.CurrentUser(ctx) if err != nil { return nil, errors.New("unauthorized: authentication required") } // Subscribe to message events eventChan := r.SubscribeToMessages(user.ID) // Create output channel outputChan := make(chan *model.Message, 10) // Start goroutine to filter and forward events go func() { defer close(outputChan) for { select { case <-ctx.Done(): return case event, ok := <-eventChan: if !ok { return } // Check if user is in the receiver list isReceiver := false for _, receiverID := range event.ReceiverIDs { if receiverID == user.ID { isReceiver = true break } } if isReceiver && event.Message != nil { select { case outputChan <- event.Message: default: // Channel full, skip } } } } }() return outputChan, nil } // Mutation returns MutationResolver implementation. func (r *Resolver) Mutation() MutationResolver { return &mutationResolver{r} } // Query returns QueryResolver implementation. func (r *Resolver) Query() QueryResolver { return &queryResolver{r} } // Subscription returns SubscriptionResolver implementation. func (r *Resolver) Subscription() SubscriptionResolver { return &subscriptionResolver{r} } type mutationResolver struct{ *Resolver } type queryResolver struct{ *Resolver } type subscriptionResolver struct{ *Resolver }