iykyka/core-backend/models.go

146 lines
4.4 KiB
Go

package main
import "time"
type SenderMode string
const (
SenderHuman SenderMode = "human"
SenderTwin SenderMode = "twin"
)
type AutonomyLevel string
const (
AutonomyL0 AutonomyLevel = "L0"
AutonomyL1 AutonomyLevel = "L1"
AutonomyL2 AutonomyLevel = "L2"
)
type User struct {
ID uint `gorm:"primaryKey"`
InviteCode string `gorm:"uniqueIndex;not null"`
DisplayName string `gorm:"not null"`
CreatedAt time.Time
}
// Session is a bearer token issued at signup/login (roadmap A2).
// v1 beta: opaque random token, no refresh rotation yet.
type Session struct {
ID uint `gorm:"primaryKey"`
Token string `gorm:"uniqueIndex;not null"`
UserID uint `gorm:"not null;index"`
CreatedAt time.Time
ExpiresAt time.Time `gorm:"not null;index"`
}
// InviteCode is a pre-minted, single-use code (roadmap.md Phase 1 §2.6
// "초대 기반 베타 가입 플로우") -- signup validates against this table instead
// of just deduping User.InviteCode, so joining actually requires a code
// someone handed out, not any never-used string.
type InviteCode struct {
ID uint `gorm:"primaryKey"`
Code string `gorm:"uniqueIndex;not null"`
CreatedAt time.Time
UsedAt *time.Time
UsedByUserID *uint
}
type Contact struct {
ID uint `gorm:"primaryKey"`
OwnerUserID uint `gorm:"not null;index"`
ContactUserID *uint
DisplayName string `gorm:"not null"`
RelationshipNote string
CreatedAt time.Time
}
// TwinDisabledByPeer is the veto flag (tech-design.md §4: "대화방 단위
// 플래그(twin_disabled_by_peer)") -- per conversation, not per contact, so
// it lives here rather than on Contact. Set by POST /conversations/:id/veto
// when the counterpart asks to talk to the human only; checked before any
// twin auto-send in that conversation (main.go).
type Conversation struct {
ID uint `gorm:"primaryKey"`
IsGroup bool `gorm:"not null;default:false"`
TwinDisabledByPeer bool `gorm:"not null;default:false"`
CreatedAt time.Time
}
type ConversationParticipant struct {
ID uint `gorm:"primaryKey"`
ConversationID uint `gorm:"not null;index"`
UserID uint `gorm:"not null;index"`
}
type Message struct {
ID uint `gorm:"primaryKey"`
ConversationID uint `gorm:"not null;index"`
SenderID uint `gorm:"not null"`
SenderMode SenderMode `gorm:"not null;default:human"`
Text string `gorm:"not null"`
// Retracted is the one-tap undo for an L2 auto-send (PRD.md §3.1,
// AGENTS.md "every automatic action needs post-hoc notification +
// one-tap undo") -- set via POST /messages/:id/retract, twin-authored
// messages only.
Retracted bool `gorm:"not null;default:false"`
CreatedAt time.Time
}
type TwinSettings struct {
ID uint `gorm:"primaryKey"`
UserID uint `gorm:"uniqueIndex;not null"`
AutonomyLevel AutonomyLevel `gorm:"not null;default:L0"`
CreatedAt time.Time
UpdatedAt time.Time
}
// WhitelistRule is the L2 auto-send whitelist -- a (contact, topic) pair the
// owner has approved for unattended replies. ContactID nil = any counterpart
// (PRD.md §3.1).
type WhitelistRule struct {
ID uint `gorm:"primaryKey"`
UserID uint `gorm:"not null;index"`
ContactID *uint
TopicKeyword string `gorm:"not null"`
CreatedAt time.Time
}
// EscalationLog is one row per escalation_filter trigger -- the post-hoc
// notification + undo trail required by AGENTS.md's absolute safety
// invariants.
type EscalationLog struct {
ID uint `gorm:"primaryKey"`
UserID uint `gorm:"not null;index"`
ConversationID uint `gorm:"not null;index"`
Reason string `gorm:"not null"`
MessageSnippet string `gorm:"not null"`
Resolved bool `gorm:"not null;default:false"`
CreatedAt time.Time
}
// DeviceToken stores an FCM registration token for push (roadmap B).
// Sending pushes is wired later; v1 persists tokens per user/device.
type DeviceToken struct {
ID uint `gorm:"primaryKey"`
UserID uint `gorm:"not null;index"`
Token string `gorm:"uniqueIndex;not null"`
Platform string `gorm:"not null;default:android"`
CreatedAt time.Time
UpdatedAt time.Time
}
var allModels = []interface{}{
&User{},
&Session{},
&InviteCode{},
&Contact{},
&Conversation{},
&ConversationParticipant{},
&Message{},
&TwinSettings{},
&WhitelistRule{},
&EscalationLog{},
&DeviceToken{},
}