Files
notely/backend/internal/application/dto/dto.go
domrichardson df40cc57e1 first commit
2026-03-24 16:03:04 +00:00

441 lines
15 KiB
Go

package dto
import (
"github.com/noteapp/backend/internal/domain/entities"
)
// ========== AUTH DTOs ==========
// RegisterRequest represents a registration request
type RegisterRequest struct {
Email string `json:"email" validate:"required,email"`
Username string `json:"username" validate:"required,min=3,max=20"`
Password string `json:"password" validate:"required,min=8"`
PasswordConfirm string `json:"password_confirm" validate:"required,eqfield=Password"`
FirstName string `json:"first_name" validate:"max=50"`
LastName string `json:"last_name" validate:"max=50"`
}
// LoginRequest represents a login request
type LoginRequest struct {
Email string `json:"email" validate:"required,email"`
Password string `json:"password" validate:"required"`
}
// LoginResponse represents a login response
type LoginResponse struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token,omitempty"`
User *UserDTO `json:"user"`
ExpiresIn int `json:"expires_in"`
}
// AuthProviderDTO represents an OAuth/OIDC provider in API responses.
type AuthProviderDTO struct {
ID string `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
AuthorizationURL string `json:"authorization_url,omitempty"`
TokenURL string `json:"token_url,omitempty"`
UserInfoURL string `json:"userinfo_url,omitempty"`
Scopes []string `json:"scopes"`
IDTokenClaim string `json:"id_token_claim,omitempty"`
IsActive bool `json:"is_active"`
}
// CreateAuthProviderRequest represents an OAuth/OIDC provider creation request.
type CreateAuthProviderRequest struct {
Name string `json:"name"`
Type string `json:"type"`
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret"`
AuthorizationURL string `json:"authorization_url"`
TokenURL string `json:"token_url"`
UserInfoURL string `json:"userinfo_url"`
Scopes []string `json:"scopes"`
IDTokenClaim string `json:"id_token_claim,omitempty"`
IsActive bool `json:"is_active"`
}
// FeatureFlagsDTO represents app-wide feature flags in API responses.
type FeatureFlagsDTO struct {
RegistrationEnabled bool `json:"registration_enabled"`
ProviderLoginEnabled bool `json:"provider_login_enabled"`
PublicSharingEnabled bool `json:"public_sharing_enabled"`
}
// UpdateFeatureFlagsRequest represents admin payload for feature flag updates.
type UpdateFeatureFlagsRequest struct {
RegistrationEnabled bool `json:"registration_enabled"`
ProviderLoginEnabled bool `json:"provider_login_enabled"`
PublicSharingEnabled bool `json:"public_sharing_enabled"`
}
// UserDTO represents a user in API responses
type UserDTO struct {
ID string `json:"id"`
Email string `json:"email"`
Username string `json:"username"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Avatar string `json:"avatar,omitempty"`
GroupIDs []string `json:"group_ids,omitempty"`
Permissions []string `json:"permissions,omitempty"`
EmailVerified bool `json:"email_verified"`
}
// NewUserDTO creates a DTO from a user entity
func NewUserDTO(user *entities.User) *UserDTO {
groupIDs := make([]string, 0, len(user.GroupIDs))
for _, groupID := range user.GroupIDs {
groupIDs = append(groupIDs, groupID.Hex())
}
return &UserDTO{
ID: user.ID.Hex(),
Email: user.Email,
Username: user.Username,
FirstName: user.FirstName,
LastName: user.LastName,
Avatar: user.Avatar,
GroupIDs: groupIDs,
Permissions: user.Permissions,
EmailVerified: user.EmailVerified,
}
}
// AdminUserDTO extends UserDTO with admin-visible fields
type AdminUserDTO struct {
*UserDTO
IsActive bool `json:"is_active"`
CreatedAt string `json:"created_at"`
}
// PermissionGroupDTO represents a permission group in API responses.
type PermissionGroupDTO struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Permissions []string `json:"permissions"`
IsSystem bool `json:"is_system"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
// CreatePermissionGroupRequest represents group creation input.
type CreatePermissionGroupRequest struct {
Name string `json:"name"`
Description string `json:"description"`
Permissions []string `json:"permissions"`
}
// UpdatePermissionGroupRequest represents group update input.
type UpdatePermissionGroupRequest struct {
Name string `json:"name"`
Description string `json:"description"`
Permissions []string `json:"permissions"`
}
// UpdateUserGroupsRequest represents user group assignment input.
type UpdateUserGroupsRequest struct {
GroupIDs []string `json:"group_ids"`
}
// NewAdminUserDTO creates an admin DTO from a user entity
func NewAdminUserDTO(user *entities.User) *AdminUserDTO {
return &AdminUserDTO{
UserDTO: NewUserDTO(user),
IsActive: user.IsActive,
CreatedAt: user.CreatedAt.Format("2006-01-02T15:04:05Z"),
}
}
// NewPermissionGroupDTO creates a DTO from a permission group entity.
func NewPermissionGroupDTO(group *entities.PermissionGroup) *PermissionGroupDTO {
return &PermissionGroupDTO{
ID: group.ID.Hex(),
Name: group.Name,
Description: group.Description,
Permissions: group.Permissions,
IsSystem: group.IsSystem,
CreatedAt: group.CreatedAt.Format("2006-01-02T15:04:05Z"),
UpdatedAt: group.UpdatedAt.Format("2006-01-02T15:04:05Z"),
}
}
// AddSpaceMemberRequest represents a request to add a member to a space
type AddSpaceMemberRequest struct {
UserID string `json:"user_id"`
}
// SpaceMemberDTO represents a member in a space
type SpaceMemberDTO struct {
UserID string `json:"user_id"`
Username string `json:"username"`
JoinedAt string `json:"joined_at"`
}
// UserOptionDTO is a lightweight user object for dropdowns
type UserOptionDTO struct {
ID string `json:"id"`
Username string `json:"username"`
}
// NewAuthProviderDTO creates a DTO from an auth provider entity.
func NewAuthProviderDTO(provider *entities.AuthProvider) *AuthProviderDTO {
return &AuthProviderDTO{
ID: provider.ID.Hex(),
Name: provider.Name,
Type: provider.Type,
AuthorizationURL: provider.AuthorizationURL,
TokenURL: provider.TokenURL,
UserInfoURL: provider.UserInfoURL,
Scopes: provider.Scopes,
IDTokenClaim: provider.IDTokenClaim,
IsActive: provider.IsActive,
}
}
// NewFeatureFlagsDTO creates a DTO from feature flags entity.
func NewFeatureFlagsDTO(flags *entities.FeatureFlags) *FeatureFlagsDTO {
if flags == nil {
flags = entities.NewDefaultFeatureFlags()
}
return &FeatureFlagsDTO{
RegistrationEnabled: flags.RegistrationEnabled,
ProviderLoginEnabled: flags.ProviderLoginEnabled,
PublicSharingEnabled: flags.PublicSharingEnabled,
}
}
// ========== SPACE DTOs ==========
// CreateSpaceRequest represents a space creation request
type CreateSpaceRequest struct {
Name string `json:"name" validate:"required,min=1,max=100"`
Description string `json:"description" validate:"max=500"`
Icon string `json:"icon,omitempty" validate:"max=20"`
IsPublic bool `json:"is_public"`
}
// SpaceDTO represents a space in API responses
type SpaceDTO struct {
ID string `json:"id"`
Name string `json:"name"`
PermissionKey string `json:"permission_key"`
Description string `json:"description"`
Icon string `json:"icon,omitempty"`
OwnerID string `json:"owner_id"`
IsPublic bool `json:"is_public"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
// NewSpaceDTO creates a DTO from a space entity
func NewSpaceDTO(space *entities.Space) *SpaceDTO {
dto := &SpaceDTO{
ID: space.ID.Hex(),
Name: space.Name,
PermissionKey: entities.SpacePermissionToken(space.Name),
Description: space.Description,
Icon: space.Icon,
OwnerID: space.OwnerID.Hex(),
IsPublic: space.IsPublic,
CreatedAt: space.CreatedAt.Format("2006-01-02T15:04:05Z"),
UpdatedAt: space.UpdatedAt.Format("2006-01-02T15:04:05Z"),
}
return dto
}
// ========== NOTE DTOs ==========
// CreateNoteRequest represents a note creation request
type CreateNoteRequest struct {
Title string `json:"title" validate:"required,min=1,max=255"`
Description string `json:"description" validate:"max=500"`
Content string `json:"content"`
NotePassword string `json:"note_password,omitempty" validate:"omitempty,min=4,max=128"`
Tags []string `json:"tags"`
CategoryID *string `json:"category_id,omitempty"`
IsPinned bool `json:"is_pinned"`
IsFavorite bool `json:"is_favorite"`
IsPublic bool `json:"is_public"`
}
// UpdateNoteRequest represents a note update request
type UpdateNoteRequest struct {
Title string `json:"title" validate:"min=1,max=255"`
Description *string `json:"description,omitempty" validate:"omitempty,max=500"`
Content string `json:"content"`
NotePassword *string `json:"note_password,omitempty" validate:"omitempty,max=128"`
Tags []string `json:"tags"`
CategoryID *string `json:"category_id,omitempty"`
IsPinned *bool `json:"is_pinned"`
IsFavorite *bool `json:"is_favorite"`
IsPublic *bool `json:"is_public,omitempty"`
}
// UnlockNoteRequest represents a password unlock request for protected notes
type UnlockNoteRequest struct {
Password string `json:"password" validate:"required,min=1,max=128"`
}
// NoteDTO represents a note in API responses
type NoteDTO struct {
ID string `json:"id"`
SpaceID string `json:"space_id"`
CategoryID *string `json:"category_id,omitempty"`
Title string `json:"title"`
Description string `json:"description"`
Content string `json:"content"`
Tags []string `json:"tags"`
IsPinned bool `json:"is_pinned"`
IsFavorite bool `json:"is_favorite"`
IsPublic bool `json:"is_public"`
IsPasswordProtected bool `json:"is_password_protected"`
CreatedBy string `json:"created_by"`
UpdatedBy string `json:"updated_by"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
// NoteListItemDTO represents a lightweight note payload for list/tree endpoints
type NoteListItemDTO struct {
ID string `json:"id"`
SpaceID string `json:"space_id"`
CategoryID *string `json:"category_id,omitempty"`
Title string `json:"title"`
Description string `json:"description"`
IsPinned bool `json:"is_pinned"`
IsFavorite bool `json:"is_favorite"`
IsPublic bool `json:"is_public"`
IsPasswordProtected bool `json:"is_password_protected"`
UpdatedAt string `json:"updated_at"`
}
// NewNoteDTO creates a DTO from a note entity
func NewNoteDTO(note *entities.Note) *NoteDTO {
var categoryID *string
if note.CategoryID != nil {
id := note.CategoryID.Hex()
categoryID = &id
}
return &NoteDTO{
ID: note.ID.Hex(),
SpaceID: note.SpaceID.Hex(),
CategoryID: categoryID,
Title: note.Title,
Description: note.Description,
Content: note.Content,
Tags: note.Tags,
IsPinned: note.IsPinned,
IsFavorite: note.IsFavorite,
IsPublic: note.IsPublic,
IsPasswordProtected: note.IsPasswordProtected,
CreatedBy: note.CreatedBy.Hex(),
UpdatedBy: note.UpdatedBy.Hex(),
CreatedAt: note.CreatedAt.Format("2006-01-02T15:04:05Z"),
UpdatedAt: note.UpdatedAt.Format("2006-01-02T15:04:05Z"),
}
}
// NewNoteListItemDTO creates a lightweight DTO from a note entity
func NewNoteListItemDTO(note *entities.Note) *NoteListItemDTO {
var categoryID *string
if note.CategoryID != nil {
id := note.CategoryID.Hex()
categoryID = &id
}
return &NoteListItemDTO{
ID: note.ID.Hex(),
SpaceID: note.SpaceID.Hex(),
CategoryID: categoryID,
Title: note.Title,
Description: note.Description,
IsPinned: note.IsPinned,
IsFavorite: note.IsFavorite,
IsPublic: note.IsPublic,
IsPasswordProtected: note.IsPasswordProtected,
UpdatedAt: note.UpdatedAt.Format("2006-01-02T15:04:05Z"),
}
}
// ========== CATEGORY DTOs ==========
// CreateCategoryRequest represents a category creation request
type CreateCategoryRequest struct {
Name string `json:"name" validate:"required,min=1,max=100"`
Description string `json:"description" validate:"max=500"`
ParentID *string `json:"parent_id,omitempty"`
Icon string `json:"icon,omitempty" validate:"max=20"`
}
// UpdateCategoryRequest represents a category update request
type UpdateCategoryRequest struct {
Name string `json:"name" validate:"min=1,max=100"`
Description string `json:"description" validate:"max=500"`
Icon string `json:"icon,omitempty" validate:"max=20"`
}
// CategoryDTO represents a category in API responses
type CategoryDTO struct {
ID string `json:"id"`
SpaceID string `json:"space_id"`
Name string `json:"name"`
Description string `json:"description"`
ParentID *string `json:"parent_id,omitempty"`
Icon string `json:"icon,omitempty"`
Order int `json:"order"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
// CategoryTreeDTO represents a category with its subcategories and notes
type CategoryTreeDTO struct {
*CategoryDTO
Subcategories []*CategoryTreeDTO `json:"subcategories"`
Notes []*NoteListItemDTO `json:"notes"`
}
// NewCategoryDTO creates a DTO from a category entity
func NewCategoryDTO(category *entities.Category) *CategoryDTO {
var parentID *string
if category.ParentID != nil {
id := category.ParentID.Hex()
parentID = &id
}
return &CategoryDTO{
ID: category.ID.Hex(),
SpaceID: category.SpaceID.Hex(),
Name: category.Name,
Description: category.Description,
ParentID: parentID,
Icon: category.Icon,
Order: category.Order,
CreatedAt: category.CreatedAt.Format("2006-01-02T15:04:05Z"),
UpdatedAt: category.UpdatedAt.Format("2006-01-02T15:04:05Z"),
}
}
// ========== ERROR DTOs ==========
// ErrorResponse represents an error response
type ErrorResponse struct {
Error string `json:"error"`
Message string `json:"message"`
Code int `json:"code"`
}
// ValidationError represents a validation error
type ValidationError struct {
Field string `json:"field"`
Message string `json:"message"`
}
// ValidationErrorResponse represents multiple validation errors
type ValidationErrorResponse struct {
Errors []ValidationError `json:"errors"`
}