Initial commit: Эфир мессенджер

This commit is contained in:
2026-04-06 14:57:36 +03:00
commit ff93679b6d
50 changed files with 5642 additions and 0 deletions

View File

@@ -0,0 +1,31 @@
package models
import (
"time"
)
type Attachment struct {
ID int64 `json:"id"`
MessageID *int64 `json:"message_id,omitempty"`
FileName string `json:"file_name"`
FileSize int64 `json:"file_size"`
StoragePath string `json:"-"` // never send to client
MimeType string `json:"mime_type"`
UploadedAt time.Time `json:"uploaded_at"`
}
// AttachmentResponse используется для отправки информации о файле клиенту
type AttachmentResponse struct {
ID int64 `json:"id"`
FileName string `json:"file_name"`
FileSize int64 `json:"file_size"`
MimeType string `json:"mime_type"`
UploadURL string `json:"upload_url"` // URL для скачивания
UploadedAt time.Time `json:"uploaded_at"`
}
// UploadFileRequest DTO для загрузки файла
type UploadFileRequest struct {
ChatID int64 `json:"chat_id"`
// файл будет в multipart form
}

70
internal/models/chat.go Normal file
View File

@@ -0,0 +1,70 @@
package models
import (
"time"
)
type ChatType string
const (
ChatTypePrivate ChatType = "private"
ChatTypeGroup ChatType = "group"
)
type MemberRole string
const (
MemberRoleMember MemberRole = "member"
MemberRoleAdmin MemberRole = "admin"
)
type Chat struct {
ID int64 `json:"id"`
Type ChatType `json:"type"`
Title *string `json:"title,omitempty"` // только для групп
CreatedAt time.Time `json:"created_at"`
}
type ChatMember struct {
ChatID int64 `json:"chat_id"`
UserID int64 `json:"user_id"`
Role MemberRole `json:"role"`
JoinedAt time.Time `json:"joined_at"`
}
// ChatWithDetails используется для возврата чата с дополнительной информацией
type ChatWithDetails struct {
ID int64 `json:"id"`
Type ChatType `json:"type"`
Title *string `json:"title,omitempty"`
CreatedAt time.Time `json:"created_at"`
LastMessage *Message `json:"last_message,omitempty"`
UnreadCount int `json:"unread_count,omitempty"`
ParticipantIDs []int64 `json:"participant_ids,omitempty"`
}
// CreatePrivateChatRequest DTO для создания личного чата
type CreatePrivateChatRequest struct {
TargetLogin string `json:"target_login"`
}
// CreateGroupChatRequest DTO для создания группового чата
type CreateGroupChatRequest struct {
Title string `json:"title"`
MemberLogins []string `json:"member_logins"`
}
// AddMembersRequest DTO для добавления участников в группу
type AddMembersRequest struct {
UserLogins []string `json:"user_logins"`
}
// UpdateMemberRoleRequest DTO для обновления роли участника
type UpdateMemberRoleRequest struct {
Role MemberRole `json:"role"`
}
// UpdateChatTitleRequest DTO для обновления названия чата
type UpdateChatTitleRequest struct {
Title string `json:"title"`
}

View File

@@ -0,0 +1,45 @@
package models
import (
"time"
)
type Message struct {
ID int64 `json:"id"`
ChatID int64 `json:"chat_id"`
SenderID int64 `json:"sender_id"`
EncryptedBody []byte `json:"-"` // never send raw encrypted body
Plaintext string `json:"plaintext,omitempty"` // используется только после расшифровки
AttachmentID *int64 `json:"attachment_id,omitempty"`
IsRead bool `json:"is_read"`
CreatedAt time.Time `json:"created_at"`
}
// MessageResponse используется для отправки сообщений клиенту
type MessageResponse struct {
ID int64 `json:"id"`
ChatID int64 `json:"chat_id"`
SenderID int64 `json:"sender_id"`
Plaintext string `json:"plaintext"` // расшифрованный текст
Attachment *Attachment `json:"attachment,omitempty"`
IsRead bool `json:"is_read"`
CreatedAt time.Time `json:"created_at"`
}
// SendMessageRequest DTO для отправки сообщения
type SendMessageRequest struct {
ChatID int64 `json:"chat_id"`
Plaintext string `json:"plaintext"`
AttachmentID *int64 `json:"attachment_id,omitempty"`
}
// EditMessageRequest DTO для редактирования сообщения
type EditMessageRequest struct {
Plaintext string `json:"plaintext"`
}
// GetMessagesRequest DTO для запроса истории
type GetMessagesRequest struct {
Limit int `json:"limit"` // количество сообщений
Before string `json:"before"` // timestamp (RFC3339)
}

View File

@@ -0,0 +1,16 @@
package models
type Profile struct {
UserID int64 `json:"user_id"`
DisplayName *string `json:"display_name,omitempty"`
Bio *string `json:"bio,omitempty"`
AvatarURL *string `json:"avatar_url,omitempty"`
}
// ProfileWithUser объединяет профиль с безопасными данными пользователя
type ProfileWithUser struct {
User *SafeUser `json:"user"`
DisplayName *string `json:"display_name,omitempty"`
Bio *string `json:"bio,omitempty"`
AvatarURL *string `json:"avatar_url,omitempty"`
}

45
internal/models/user.go Normal file
View File

@@ -0,0 +1,45 @@
package models
import (
"time"
)
type UserRole string
const (
RoleUser UserRole = "user"
RoleGlobalAdmin UserRole = "global_admin"
)
type User struct {
ID int64 `json:"id"`
Login string `json:"login"`
PasswordHash string `json:"-"` // never send to client
Role UserRole `json:"role"`
LastSeen *time.Time `json:"last_seen,omitempty"`
CreatedAt time.Time `json:"created_at"`
}
// SafeUser возвращает пользователя без чувствительных данных
type SafeUser struct {
ID int64 `json:"id"`
Login string `json:"login"`
Role UserRole `json:"role"`
LastSeen *time.Time `json:"last_seen,omitempty"`
CreatedAt time.Time `json:"created_at"`
}
func (u *User) ToSafe() *SafeUser {
return &SafeUser{
ID: u.ID,
Login: u.Login,
Role: u.Role,
LastSeen: u.LastSeen,
CreatedAt: u.CreatedAt,
}
}
// IsGlobalAdmin проверяет, является ли пользователь глобальным админом
func (u *User) IsGlobalAdmin() bool {
return u.Role == RoleGlobalAdmin
}