Files
Tomas Dvorak 4c812e376d feat(messages): implement integrated chat with voice/calls and tidy root go module
Add Discord-like messaging APIs, websocket realtime, smart suggestions, password vault flows, semantic indexing integration, and new /app/messages UI.

Add typing indicators, advanced message search filters, voice notes, browser-local optional transcription, and WebRTC call signaling (offer/answer/ice/hangup).

Clean root go.mod via go mod tidy and remove stale root go.sum.
2026-02-26 10:54:19 +01:00

201 lines
8.8 KiB
Go

package models
import (
"time"
"gorm.io/gorm"
)
// ConversationType represents the type of a conversation.
type ConversationType string
const (
ConversationTypeGlobal ConversationType = "global"
ConversationTypeTeam ConversationType = "team"
ConversationTypeGroup ConversationType = "group"
ConversationTypeDM ConversationType = "dm"
ConversationTypeSelf ConversationType = "self"
ConversationTypePasswordVault ConversationType = "password_vault"
)
// ConversationMemberRole represents the role of a user in a conversation.
type ConversationMemberRole string
const (
ConversationMemberRoleOwner ConversationMemberRole = "owner"
ConversationMemberRoleAdmin ConversationMemberRole = "admin"
ConversationMemberRoleMember ConversationMemberRole = "member"
ConversationMemberRoleViewer ConversationMemberRole = "viewer"
)
// SuggestionStatus is the lifecycle state of a message suggestion.
type SuggestionStatus string
const (
SuggestionStatusPending SuggestionStatus = "pending"
SuggestionStatusAccepted SuggestionStatus = "accepted"
SuggestionStatusDismissed SuggestionStatus = "dismissed"
)
// Conversation is a user-to-user chat space (global/team/group/dm/self/password).
type Conversation struct {
ID uint `json:"id" gorm:"primaryKey"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
Type ConversationType `json:"type" gorm:"not null;index"`
Name string `json:"name" gorm:"not null"`
Topic string `json:"topic"`
TeamID *uint `json:"team_id,omitempty" gorm:"index"`
Team *Team `json:"team,omitempty" gorm:"foreignKey:TeamID"`
CreatedBy uint `json:"created_by" gorm:"not null;index"`
Creator User `json:"creator,omitempty" gorm:"foreignKey:CreatedBy"`
IsDefault bool `json:"is_default" gorm:"default:false;index"`
IsArchived bool `json:"is_archived" gorm:"default:false;index"`
LastMessageAt *time.Time `json:"last_message_at"`
Members []ConversationMember `json:"members,omitempty" gorm:"foreignKey:ConversationID"`
Messages []Message `json:"messages,omitempty" gorm:"foreignKey:ConversationID"`
}
// ConversationMember links users to conversations.
type ConversationMember struct {
ID uint `json:"id" gorm:"primaryKey"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
ConversationID uint `json:"conversation_id" gorm:"not null;index:idx_conv_member,unique"`
UserID uint `json:"user_id" gorm:"not null;index:idx_conv_member,unique"`
Role ConversationMemberRole `json:"role" gorm:"not null;default:member"`
JoinedAt time.Time `json:"joined_at"`
LastReadMessageID *uint `json:"last_read_message_id,omitempty" gorm:"index"`
LastReadAt *time.Time `json:"last_read_at,omitempty"`
MutedUntil *time.Time `json:"muted_until,omitempty"`
IsHidden bool `json:"is_hidden" gorm:"default:false"`
Conversation Conversation `json:"conversation,omitempty" gorm:"foreignKey:ConversationID"`
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
}
// Message is a single chat message in a conversation.
type Message struct {
ID uint `json:"id" gorm:"primaryKey"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
ConversationID uint `json:"conversation_id" gorm:"not null;index"`
Conversation Conversation `json:"conversation,omitempty" gorm:"foreignKey:ConversationID"`
SenderID uint `json:"sender_id" gorm:"not null;index"`
Sender User `json:"sender,omitempty" gorm:"foreignKey:SenderID"`
Body string `json:"body" gorm:"type:text"`
IsSensitive bool `json:"is_sensitive" gorm:"default:false"`
EditedAt *time.Time `json:"edited_at,omitempty"`
DeletedAt *time.Time `json:"deleted_at,omitempty" gorm:"index"`
MetadataJSON string `json:"metadata_json" gorm:"type:text"`
Attachments []MessageAttachment `json:"attachments,omitempty" gorm:"foreignKey:MessageID"`
References []MessageReference `json:"references,omitempty" gorm:"foreignKey:MessageID"`
Suggestions []MessageSuggestion `json:"suggestions,omitempty" gorm:"foreignKey:MessageID"`
Reactions []MessageReaction `json:"reactions,omitempty" gorm:"foreignKey:MessageID"`
}
// MessageAttachment represents file/link-style message attachments.
type MessageAttachment struct {
ID uint `json:"id" gorm:"primaryKey"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
MessageID uint `json:"message_id" gorm:"not null;index"`
Message Message `json:"message,omitempty" gorm:"foreignKey:MessageID"`
Kind string `json:"kind" gorm:"not null;index"` // file,image,youtube,github,website,bookmark,task,event,calendar,activity,learning_path,saved_search,voice_note
FileID *uint `json:"file_id,omitempty" gorm:"index"`
URL string `json:"url"`
Title string `json:"title"`
PreviewJSON string `json:"preview_json" gorm:"type:text"`
}
// MessageReference maps chat messages to Trackeep entities.
type MessageReference struct {
ID uint `json:"id" gorm:"primaryKey"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
MessageID uint `json:"message_id" gorm:"not null;index"`
Message Message `json:"message,omitempty" gorm:"foreignKey:MessageID"`
EntityType string `json:"entity_type" gorm:"not null;index"`
EntityID uint `json:"entity_id" gorm:"not null;index"`
DeepLink string `json:"deep_link" gorm:"not null"`
}
// MessageSuggestion stores non-blocking smart suggestions triggered by message text.
type MessageSuggestion struct {
ID uint `json:"id" gorm:"primaryKey"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
MessageID uint `json:"message_id" gorm:"not null;index"`
Message Message `json:"message,omitempty" gorm:"foreignKey:MessageID"`
Type string `json:"type" gorm:"not null;index"` // create_task, create_event, save_bookmark, ...
PayloadJSON string `json:"payload_json" gorm:"type:text"`
Status SuggestionStatus `json:"status" gorm:"not null;default:pending;index"`
}
// MessageReaction stores emoji reactions.
type MessageReaction struct {
ID uint `json:"id" gorm:"primaryKey"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
MessageID uint `json:"message_id" gorm:"not null;index:idx_message_reaction,unique"`
Message Message `json:"message,omitempty" gorm:"foreignKey:MessageID"`
UserID uint `json:"user_id" gorm:"not null;index:idx_message_reaction,unique"`
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
Emoji string `json:"emoji" gorm:"not null;index:idx_message_reaction,unique"`
}
// PasswordVaultItem is encrypted secret data owned by a user.
type PasswordVaultItem struct {
ID uint `json:"id" gorm:"primaryKey"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
OwnerUserID uint `json:"owner_user_id" gorm:"not null;index"`
OwnerUser User `json:"owner_user,omitempty" gorm:"foreignKey:OwnerUserID"`
Label string `json:"label" gorm:"not null"`
EncryptedSecret string `json:"-" gorm:"type:text;not null"`
EncryptedNotes string `json:"-" gorm:"type:text"`
SourceMessageID *uint `json:"source_message_id,omitempty" gorm:"index"`
SourceMessage *Message `json:"source_message,omitempty" gorm:"foreignKey:SourceMessageID"`
CreatedBy uint `json:"created_by" gorm:"not null;index"`
LastAccessedAt *time.Time `json:"last_accessed_at,omitempty"`
Shares []PasswordVaultShare `json:"shares,omitempty" gorm:"foreignKey:VaultItemID"`
}
// PasswordVaultShare controls explicit sharing of vault items.
type PasswordVaultShare struct {
ID uint `json:"id" gorm:"primaryKey"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
VaultItemID uint `json:"vault_item_id" gorm:"not null;index"`
VaultItem PasswordVaultItem `json:"vault_item,omitempty" gorm:"foreignKey:VaultItemID"`
SharedByUserID uint `json:"shared_by_user_id" gorm:"not null;index"`
SharedByUser User `json:"shared_by_user,omitempty" gorm:"foreignKey:SharedByUserID"`
TargetConversationID uint `json:"target_conversation_id" gorm:"not null;index"`
TargetConversation Conversation `json:"target_conversation,omitempty" gorm:"foreignKey:TargetConversationID"`
ExpiresAt *time.Time `json:"expires_at,omitempty"`
AllowReveal bool `json:"allow_reveal" gorm:"default:false"`
}