257 lines
8.1 KiB
Go
257 lines
8.1 KiB
Go
package lib
|
|
|
|
import "database/sql"
|
|
|
|
// Entry is the single core data type. One table to rule them all.
|
|
type Entry struct {
|
|
EntryID string `json:"entry_id"`
|
|
ProjectID string `json:"project_id"`
|
|
ParentID string `json:"parent_id"`
|
|
Type string `json:"type"`
|
|
Depth int `json:"depth"`
|
|
SearchKey []byte `json:"-"` // blind index (HMAC-SHA256)
|
|
SearchKey2 []byte `json:"-"` // blind index (HMAC-SHA256)
|
|
Summary []byte `json:"-"` // packed: zstd + AES-256-GCM
|
|
Data []byte `json:"-"` // packed: zstd + AES-256-GCM
|
|
Stage string `json:"stage"`
|
|
|
|
// Workflow routing (plain, indexed)
|
|
AssigneeID string `json:"assignee_id"`
|
|
ReturnToID string `json:"return_to_id"`
|
|
OriginID string `json:"origin_id"`
|
|
|
|
// Optimistic locking
|
|
Version int `json:"version"`
|
|
|
|
// Soft delete
|
|
DeletedAt *int64 `json:"deleted_at,omitempty"`
|
|
DeletedBy *string `json:"deleted_by,omitempty"`
|
|
|
|
// Key rotation
|
|
KeyVersion int `json:"key_version"`
|
|
|
|
// Timestamps
|
|
CreatedAt int64 `json:"created_at"`
|
|
UpdatedAt int64 `json:"updated_at"`
|
|
CreatedBy string `json:"created_by"`
|
|
|
|
// Unpacked fields (not stored directly, populated by lib)
|
|
SummaryText string `json:"summary,omitempty"`
|
|
DataText string `json:"data_text,omitempty"`
|
|
}
|
|
|
|
// Entry types
|
|
const (
|
|
TypeProject = "project"
|
|
TypeWorkstream = "workstream"
|
|
TypeRequestList = "request_list"
|
|
TypeRequest = "request"
|
|
TypeAnswer = "answer"
|
|
TypeComment = "comment"
|
|
TypeOrganization = "organization"
|
|
TypeDealOrg = "deal_org"
|
|
)
|
|
|
|
// Stages
|
|
const (
|
|
StagePreDataroom = "pre_dataroom"
|
|
StageDataroom = "dataroom"
|
|
StageClosed = "closed"
|
|
)
|
|
|
|
// OrgData is the JSON structure packed into an organization entry's Data field.
|
|
type OrgData struct {
|
|
Name string `json:"name"`
|
|
Domains []string `json:"domains"` // required, e.g. ["kaseya.com","datto.com"]
|
|
Role string `json:"role"` // seller | buyer | ib | advisor
|
|
Website string `json:"website,omitempty"`
|
|
Description string `json:"description,omitempty"`
|
|
ContactName string `json:"contact_name,omitempty"`
|
|
ContactEmail string `json:"contact_email,omitempty"`
|
|
}
|
|
|
|
// DealOrgData is the JSON structure packed into a deal_org entry's Data field.
|
|
// A deal_org entry links an organization into a specific deal (project).
|
|
type DealOrgData struct {
|
|
OrgID string `json:"org_id"` // entry_id of the organization
|
|
Role string `json:"role"` // seller | buyer | ib | advisor
|
|
DomainLock bool `json:"domain_lock"` // if true, enforce domain check on invites
|
|
}
|
|
|
|
// User represents an account.
|
|
type User struct {
|
|
UserID string `json:"user_id"`
|
|
Email string `json:"email"`
|
|
Name string `json:"name"`
|
|
Password string `json:"-"` // bcrypt
|
|
OrgID string `json:"org_id"`
|
|
OrgName string `json:"org_name"`
|
|
MFASecret string `json:"-"`
|
|
Active bool `json:"active"`
|
|
CreatedAt int64 `json:"created_at"`
|
|
UpdatedAt int64 `json:"updated_at"`
|
|
}
|
|
|
|
// Access is an RBAC grant.
|
|
type Access struct {
|
|
ID string `json:"id"`
|
|
ProjectID string `json:"project_id"`
|
|
WorkstreamID string `json:"workstream_id,omitempty"`
|
|
UserID string `json:"user_id"`
|
|
Role string `json:"role"`
|
|
Ops string `json:"ops"`
|
|
CanGrant bool `json:"can_grant"`
|
|
GrantedBy string `json:"granted_by"`
|
|
GrantedAt int64 `json:"granted_at"`
|
|
RevokedAt *int64 `json:"revoked_at,omitempty"`
|
|
RevokedBy string `json:"revoked_by,omitempty"`
|
|
}
|
|
|
|
// Roles
|
|
const (
|
|
RoleSuperAdmin = "super_admin"
|
|
RoleIBAdmin = "ib_admin"
|
|
RoleIBMember = "ib_member"
|
|
RoleSellerAdmin = "seller_admin"
|
|
RoleSellerMember = "seller_member"
|
|
RoleBuyerAdmin = "buyer_admin"
|
|
RoleBuyerMember = "buyer_member"
|
|
RoleObserver = "observer"
|
|
)
|
|
|
|
// RoleHierarchy defines privilege levels. Higher = more privileged.
|
|
var RoleHierarchy = map[string]int{
|
|
RoleSuperAdmin: 200,
|
|
RoleIBAdmin: 100,
|
|
RoleIBMember: 80,
|
|
RoleSellerAdmin: 70,
|
|
RoleSellerMember: 50,
|
|
RoleBuyerAdmin: 40,
|
|
RoleBuyerMember: 30,
|
|
RoleObserver: 10,
|
|
}
|
|
|
|
// AnswerLink connects answers to requests (many-to-many).
|
|
type AnswerLink struct {
|
|
AnswerID string `json:"answer_id"`
|
|
RequestID string `json:"request_id"`
|
|
LinkedBy string `json:"linked_by"`
|
|
LinkedAt int64 `json:"linked_at"`
|
|
Confirmed bool `json:"confirmed"`
|
|
AIScore *float64 `json:"ai_score,omitempty"`
|
|
Status string `json:"status"`
|
|
ReviewedBy string `json:"reviewed_by,omitempty"`
|
|
ReviewedAt *int64 `json:"reviewed_at,omitempty"`
|
|
RejectReason string `json:"reject_reason,omitempty"`
|
|
}
|
|
|
|
// EntryEvent is a workflow thread item.
|
|
type EntryEvent struct {
|
|
ID string `json:"id"`
|
|
EntryID string `json:"entry_id"`
|
|
ActorID string `json:"actor_id"`
|
|
Channel string `json:"channel"`
|
|
Action []byte `json:"-"` // packed
|
|
Data []byte `json:"-"` // packed
|
|
Ts int64 `json:"ts"`
|
|
|
|
ActionText string `json:"action,omitempty"`
|
|
DataText string `json:"data_text,omitempty"`
|
|
}
|
|
|
|
// AuditEntry is a security audit log row.
|
|
type AuditEntry struct {
|
|
ID string `json:"id"`
|
|
ProjectID string `json:"project_id"`
|
|
ActorID string `json:"actor_id"`
|
|
Action []byte `json:"-"`
|
|
TargetID string `json:"target_id,omitempty"`
|
|
Details []byte `json:"-"`
|
|
IP string `json:"ip,omitempty"`
|
|
Ts int64 `json:"ts"`
|
|
|
|
ActionText string `json:"action,omitempty"`
|
|
DetailsText string `json:"details,omitempty"`
|
|
}
|
|
|
|
// Session tracks active user sessions.
|
|
type Session struct {
|
|
ID string `json:"id"`
|
|
UserID string `json:"user_id"`
|
|
Fingerprint string `json:"fingerprint"`
|
|
CreatedAt int64 `json:"created_at"`
|
|
ExpiresAt int64 `json:"expires_at"`
|
|
Revoked bool `json:"revoked"`
|
|
}
|
|
|
|
// Theme is a CSS custom properties bundle.
|
|
type Theme struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name"`
|
|
ProjectID string `json:"project_id,omitempty"`
|
|
Properties string `json:"properties"` // packed CSS vars as JSON
|
|
}
|
|
|
|
// EntryFilter is used by EntryRead to query entries.
|
|
type EntryFilter struct {
|
|
ProjectID string
|
|
ParentID *string
|
|
Type string
|
|
Stage string
|
|
AssigneeID string
|
|
SearchKey []byte // blind index to match
|
|
Limit int
|
|
Offset int
|
|
}
|
|
|
|
// DB wraps the database connection.
|
|
type DB struct {
|
|
Conn *sql.DB
|
|
}
|
|
|
|
// Challenge represents an email OTP challenge for passwordless login.
|
|
type Challenge struct {
|
|
ChallengeID string `json:"challenge_id"`
|
|
Email string `json:"email"`
|
|
Code string `json:"code"`
|
|
CreatedAt int64 `json:"created_at"`
|
|
ExpiresAt int64 `json:"expires_at"`
|
|
Used bool `json:"used"`
|
|
}
|
|
|
|
// Config holds application configuration.
|
|
type Config struct {
|
|
MasterKey []byte
|
|
DBPath string
|
|
StorePath string
|
|
Port string
|
|
Env string // "development" | "production"
|
|
JWTSecret []byte
|
|
Mailer *Mailer
|
|
BackdoorCode string // OTP backdoor for dev/testing
|
|
}
|
|
|
|
// RequestData is the JSON structure packed into a request entry's Data field.
|
|
// Represents a diligence request item imported from CSV/XLSX.
|
|
type RequestData struct {
|
|
Title string `json:"title"` // human-readable description
|
|
ItemNumber string `json:"item_number"` // e.g. "1.3", "A-12"
|
|
Section string `json:"section"` // e.g. "Financial", "Legal"
|
|
Description string `json:"description"` // full detail / context
|
|
Priority string `json:"priority"` // high | medium | low
|
|
Status string `json:"status"` // open | in_progress | answered | not_applicable
|
|
AssigneeID string `json:"assignee_id,omitempty"`
|
|
AssigneeName string `json:"assignee_name,omitempty"`
|
|
DueDate string `json:"due_date,omitempty"` // YYYY-MM-DD
|
|
BuyerComment string `json:"buyer_comment,omitempty"`
|
|
SellerComment string `json:"seller_comment,omitempty"`
|
|
Tags []string `json:"tags,omitempty"`
|
|
LinkedEntryIDs []string `json:"linked_entry_ids,omitempty"` // linked answer/file entries
|
|
}
|
|
|
|
// WorkstreamData is the JSON structure packed into a workstream entry's Data field.
|
|
type WorkstreamData struct {
|
|
Name string `json:"name"`
|
|
Description string `json:"description,omitempty"`
|
|
}
|