1e5ef02bdf
Messages between matched users are now encrypted client-side with each user's own key pair before they ever reach the server; the backend only ever stores/relays opaque ciphertext + nonce and cannot read message content, including for the last-message preview on the matches list. - User gains a PublicKey field, registered/rotated via PUT /profile/public-key - Message gains CipherText/Nonce; legacy plaintext Text is kept as-is for messages sent before this change - MatchView exposes the partner's public key and the last message's ciphertext/nonce so the client can decrypt previews itself - Removed the now-dead plaintext SanitizeMessageText utility — the server can no longer sanitize message content since it's opaque ciphertext Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
77 lines
3.2 KiB
Go
77 lines
3.2 KiB
Go
package models
|
|
|
|
import "time"
|
|
|
|
// Photo یکی از عکسهای پروفایل کاربر
|
|
type Photo struct {
|
|
ID uint `gorm:"primaryKey" json:"id"`
|
|
UserID uint `gorm:"not null;index" json:"user_id"`
|
|
URL string `gorm:"size:500;not null" json:"url"`
|
|
SortOrder int `gorm:"default:0;not null" json:"sort_order"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
}
|
|
|
|
func (Photo) TableName() string {
|
|
return "photos"
|
|
}
|
|
|
|
// SwipeAction نوع تصمیمی که کاربر روی یک پروفایل دیگه گرفته
|
|
type SwipeAction string
|
|
|
|
const (
|
|
SwipeLike SwipeAction = "like"
|
|
SwipeSuperlike SwipeAction = "superlike"
|
|
SwipePass SwipeAction = "pass"
|
|
)
|
|
|
|
// Swipe ثبت تصمیم یک کاربر (لایک/ردّ) روی یک کاربر دیگه.
|
|
// هر جفت (SwiperID, SwipedID) فقط یک بار میتونه ثبت بشه؛ توی discovery feed
|
|
// کسایی که قبلاً روشون تصمیم گرفته شده دیگه نشون داده نمیشن.
|
|
type Swipe struct {
|
|
ID uint `gorm:"primaryKey" json:"id"`
|
|
SwiperID uint `gorm:"not null;uniqueIndex:idx_swiper_swiped" json:"swiper_id"`
|
|
SwipedID uint `gorm:"not null;uniqueIndex:idx_swiper_swiped;index" json:"swiped_id"`
|
|
Action SwipeAction `gorm:"size:10;not null" json:"action"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
}
|
|
|
|
func (Swipe) TableName() string {
|
|
return "swipes"
|
|
}
|
|
|
|
// Match وقتی ساخته میشه که دو کاربر متقابلاً همدیگه رو لایک کرده باشن.
|
|
// UserOneID همیشه شناسهی کوچیکتره (برای جلوگیری از رکورد تکراری در دو جهت).
|
|
type Match struct {
|
|
ID uint `gorm:"primaryKey" json:"id"`
|
|
UserOneID uint `gorm:"not null;uniqueIndex:idx_match_pair" json:"user_one_id"`
|
|
UserTwoID uint `gorm:"not null;uniqueIndex:idx_match_pair" json:"user_two_id"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
|
|
UserOne *User `gorm:"foreignKey:UserOneID" json:"-"`
|
|
UserTwo *User `gorm:"foreignKey:UserTwoID" json:"-"`
|
|
}
|
|
|
|
func (Match) TableName() string {
|
|
return "matches"
|
|
}
|
|
|
|
// Message یک پیام داخل یک مچ. از رمزنگاری سرتاسری (E2EE) استفاده میشه:
|
|
// سرور فقط CipherText+Nonce (خروجی رمزنگاریشدهی سمت کلاینت با NaCl box،
|
|
// X25519-XSalsa20-Poly1305) رو ذخیره میکنه و هیچوقت متن خام پیام رو نمیبینه.
|
|
// Text قدیمی (قبل از اضافه شدن E2EE) برای نمایش پیامهای تاریخی نگه داشته میشه؛
|
|
// پیامهای جدید Text خالی دارن و بهجاش CipherText/Nonce پر میشه.
|
|
type Message struct {
|
|
ID uint `gorm:"primaryKey" json:"id"`
|
|
MatchID uint `gorm:"not null;index" json:"match_id"`
|
|
SenderID uint `gorm:"not null" json:"sender_id"`
|
|
Text string `gorm:"size:2000;not null" json:"text"`
|
|
CipherText *string `gorm:"size:12000" json:"cipher_text,omitempty"`
|
|
Nonce *string `gorm:"size:64" json:"nonce,omitempty"`
|
|
ReadAt *time.Time `json:"read_at,omitempty"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
}
|
|
|
|
func (Message) TableName() string {
|
|
return "messages"
|
|
}
|