106 lines
4.9 KiB
Go
106 lines
4.9 KiB
Go
package models
|
|
|
|
import (
|
|
"encoding/json"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
type Group struct {
|
|
ID uuid.UUID `json:"id" db:"id"`
|
|
Name string `json:"name" db:"name"`
|
|
Description string `json:"description" db:"description"`
|
|
Type string `json:"type" db:"type"` // 'geo', 'social', 'public_geo', 'private_capsule'
|
|
Privacy string `json:"privacy" db:"privacy"` // 'public' or 'private'
|
|
LocationCenter any `json:"location_center,omitempty" db:"location_center"`
|
|
Lat *float64 `json:"lat,omitempty"`
|
|
Long *float64 `json:"long,omitempty"`
|
|
RadiusMeters int `json:"radius_meters" db:"radius_meters"`
|
|
AvatarURL *string `json:"avatar_url" db:"avatar_url"`
|
|
CreatedBy *uuid.UUID `json:"created_by" db:"created_by"`
|
|
MemberCount int `json:"member_count" db:"member_count"`
|
|
IsActive bool `json:"is_active" db:"is_active"`
|
|
IsEncrypted bool `json:"is_encrypted" db:"is_encrypted"`
|
|
PublicKey *string `json:"public_key,omitempty" db:"public_key"`
|
|
Settings json.RawMessage `json:"settings,omitempty" db:"settings"`
|
|
InviteCode *string `json:"invite_code,omitempty" db:"invite_code"`
|
|
Category string `json:"category" db:"category"` // general, hobby, sports, professional, local_business, support, education
|
|
KeyVersion int `json:"key_version" db:"key_version"`
|
|
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
|
|
}
|
|
|
|
// IsCapsule returns true if this group uses E2EE
|
|
func (g *Group) IsCapsule() bool {
|
|
return g.Type == "private_capsule" && g.IsEncrypted
|
|
}
|
|
|
|
type GroupMember struct {
|
|
ID uuid.UUID `json:"id" db:"id"`
|
|
GroupID uuid.UUID `json:"group_id" db:"group_id"`
|
|
UserID uuid.UUID `json:"user_id" db:"user_id"`
|
|
Role string `json:"role" db:"role"` // owner, admin, moderator, member
|
|
EncryptedGroupKey *string `json:"encrypted_group_key,omitempty" db:"encrypted_group_key"`
|
|
KeyVersion int `json:"key_version" db:"key_version"`
|
|
JoinedAt time.Time `json:"joined_at" db:"joined_at"`
|
|
}
|
|
|
|
// CapsuleEntry holds E2EE content — the server NEVER decrypts this
|
|
type CapsuleEntry struct {
|
|
ID uuid.UUID `json:"id" db:"id"`
|
|
GroupID uuid.UUID `json:"group_id" db:"group_id"`
|
|
AuthorID uuid.UUID `json:"author_id" db:"author_id"`
|
|
IV string `json:"iv" db:"iv"`
|
|
EncryptedPayload string `json:"encrypted_payload" db:"encrypted_payload"`
|
|
DataType string `json:"data_type" db:"data_type"` // chat, forum_post, document, image
|
|
ReplyToID *uuid.UUID `json:"reply_to_id,omitempty" db:"reply_to_id"`
|
|
KeyVersion int `json:"key_version" db:"key_version"`
|
|
IsDeleted bool `json:"is_deleted" db:"is_deleted"`
|
|
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
|
|
|
|
// Joined fields (not encrypted — public metadata)
|
|
AuthorHandle string `json:"author_handle,omitempty" db:"author_handle"`
|
|
AuthorDisplayName string `json:"author_display_name,omitempty" db:"author_display_name"`
|
|
AuthorAvatarURL string `json:"author_avatar_url,omitempty" db:"author_avatar_url"`
|
|
}
|
|
|
|
// GroupWithDistance is returned by nearest-geo-group queries
|
|
type GroupWithDistance struct {
|
|
Group
|
|
DistanceMeters float64 `json:"distance_meters" db:"distance_meters"`
|
|
}
|
|
|
|
// GroupSettings defines what features are enabled for a group
|
|
type GroupSettings struct {
|
|
Chat bool `json:"chat"`
|
|
Forum bool `json:"forum"`
|
|
Files bool `json:"files"`
|
|
}
|
|
|
|
// CapsuleKey stores a per-user encrypted copy of a group's symmetric key.
|
|
// Security: Backend MUST only return rows WHERE user_id = authenticated user.
|
|
type CapsuleKey struct {
|
|
ID uuid.UUID `json:"id" db:"id"`
|
|
UserID uuid.UUID `json:"user_id" db:"user_id"`
|
|
GroupID uuid.UUID `json:"group_id" db:"group_id"`
|
|
EncryptedKeyBlob string `json:"encrypted_key_blob" db:"encrypted_key_blob"`
|
|
KeyVersion int `json:"key_version" db:"key_version"`
|
|
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
|
|
}
|
|
|
|
// CapsuleKeyBackup stores a PIN-encrypted backup of the user's private key.
|
|
// Zero-Knowledge: the server sees ONLY ciphertext — it cannot derive the PIN or key.
|
|
type CapsuleKeyBackup struct {
|
|
ID uuid.UUID `json:"id" db:"id"`
|
|
UserID uuid.UUID `json:"user_id" db:"user_id"`
|
|
Salt string `json:"salt" db:"salt"`
|
|
IV string `json:"iv" db:"iv"`
|
|
Payload string `json:"payload" db:"payload"`
|
|
PublicKey string `json:"pub" db:"public_key"`
|
|
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
|
|
}
|