Files
Memoh/internal/db/sqlc/models.go
T
BBQ cc5f00355f feat: add email service with multi-adapter support (#146)
* feat: add email service with multi-adapter support

Implement a full-stack email service with global provider management,
per-bot bindings with granular read/write permissions, outbox audit
storage, and MCP tool integration for direct mailbox access.

Backend:
- Email providers: CRUD with dynamic config schema (generic SMTP/IMAP, Mailgun)
- Generic adapter: go-mail (SMTP) + go-imap/v2 (IMAP IDLE real-time push via
  UnilateralDataHandler + UID-based tracking + periodic check fallback)
- Mailgun adapter: mailgun-go/v5 with dual inbound mode (webhook + poll)
- Bot email bindings: per-bot provider binding with independent r/w permissions
- Outbox: outbound email audit log with status tracking
- Trigger: inbound emails push notification to bot_inbox (from/subject only,
  LLM reads full content on demand via MCP tools)
- MailboxReader interface: on-demand IMAP queries for listing/reading emails
- MCP tools: email_accounts, email_send, email_list (paginated mailbox),
  email_read (by UID) — all with multi-binding and provider_id selection
- Webhook: /email/mailgun/webhook/:config_id (JWT-skipped, signature-verified)
- DB migration: 0019_add_email (email_providers, bot_email_bindings, email_outbox)

Frontend:
- Email Providers page: /email-providers with MasterDetailSidebarLayout
- Dynamic config form rendered from ordered provider meta schema with i18n keys
- Bot detail: Email tab with bindings management + outbox audit table
- Sidebar navigation entry
- Full i18n support (en + zh)
- Auto-generated SDK from Swagger

Closes #17

* feat(email): trigger bot conversation immediately on inbound email

Instead of only storing an inbox item and waiting for the next chat,
the email trigger now proactively invokes the conversation resolver
so the bot processes new emails right away — aligned with the
schedule/heartbeat trigger pattern.

* fix: lint

---------

Co-authored-by: Acbox <acbox0328@gmail.com>
2026-02-28 21:03:59 +08:00

380 lines
16 KiB
Go

// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.30.0
package sqlc
import (
"github.com/jackc/pgx/v5/pgtype"
)
type Bot struct {
ID pgtype.UUID `json:"id"`
OwnerUserID pgtype.UUID `json:"owner_user_id"`
Type string `json:"type"`
DisplayName pgtype.Text `json:"display_name"`
AvatarUrl pgtype.Text `json:"avatar_url"`
IsActive bool `json:"is_active"`
Status string `json:"status"`
MaxContextLoadTime int32 `json:"max_context_load_time"`
MaxContextTokens int32 `json:"max_context_tokens"`
Language string `json:"language"`
AllowGuest bool `json:"allow_guest"`
ReasoningEnabled bool `json:"reasoning_enabled"`
ReasoningEffort string `json:"reasoning_effort"`
MaxInboxItems int32 `json:"max_inbox_items"`
ChatModelID pgtype.UUID `json:"chat_model_id"`
MemoryModelID pgtype.UUID `json:"memory_model_id"`
EmbeddingModelID pgtype.UUID `json:"embedding_model_id"`
SearchProviderID pgtype.UUID `json:"search_provider_id"`
HeartbeatEnabled bool `json:"heartbeat_enabled"`
HeartbeatInterval int32 `json:"heartbeat_interval"`
HeartbeatPrompt string `json:"heartbeat_prompt"`
HeartbeatModelID pgtype.UUID `json:"heartbeat_model_id"`
Metadata []byte `json:"metadata"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}
type BotChannelConfig struct {
ID pgtype.UUID `json:"id"`
BotID pgtype.UUID `json:"bot_id"`
ChannelType string `json:"channel_type"`
Credentials []byte `json:"credentials"`
ExternalIdentity pgtype.Text `json:"external_identity"`
SelfIdentity []byte `json:"self_identity"`
Routing []byte `json:"routing"`
Capabilities []byte `json:"capabilities"`
Disabled bool `json:"disabled"`
VerifiedAt pgtype.Timestamptz `json:"verified_at"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}
type BotChannelRoute struct {
ID pgtype.UUID `json:"id"`
BotID pgtype.UUID `json:"bot_id"`
ChannelType string `json:"channel_type"`
ChannelConfigID pgtype.UUID `json:"channel_config_id"`
ExternalConversationID string `json:"external_conversation_id"`
ExternalThreadID pgtype.Text `json:"external_thread_id"`
ConversationType pgtype.Text `json:"conversation_type"`
DefaultReplyTarget pgtype.Text `json:"default_reply_target"`
Metadata []byte `json:"metadata"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}
type BotEmailBinding struct {
ID pgtype.UUID `json:"id"`
BotID pgtype.UUID `json:"bot_id"`
EmailProviderID pgtype.UUID `json:"email_provider_id"`
EmailAddress string `json:"email_address"`
CanRead bool `json:"can_read"`
CanWrite bool `json:"can_write"`
CanDelete bool `json:"can_delete"`
Config []byte `json:"config"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}
type BotHeartbeatLog struct {
ID pgtype.UUID `json:"id"`
BotID pgtype.UUID `json:"bot_id"`
Status string `json:"status"`
ResultText string `json:"result_text"`
ErrorMessage string `json:"error_message"`
Usage []byte `json:"usage"`
StartedAt pgtype.Timestamptz `json:"started_at"`
CompletedAt pgtype.Timestamptz `json:"completed_at"`
}
type BotHistoryMessage struct {
ID pgtype.UUID `json:"id"`
BotID pgtype.UUID `json:"bot_id"`
RouteID pgtype.UUID `json:"route_id"`
SenderChannelIdentityID pgtype.UUID `json:"sender_channel_identity_id"`
SenderAccountUserID pgtype.UUID `json:"sender_account_user_id"`
ChannelType pgtype.Text `json:"channel_type"`
SourceMessageID pgtype.Text `json:"source_message_id"`
SourceReplyToMessageID pgtype.Text `json:"source_reply_to_message_id"`
Role string `json:"role"`
Content []byte `json:"content"`
Metadata []byte `json:"metadata"`
Usage []byte `json:"usage"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}
type BotHistoryMessageAsset struct {
ID pgtype.UUID `json:"id"`
MessageID pgtype.UUID `json:"message_id"`
Role string `json:"role"`
Ordinal int32 `json:"ordinal"`
ContentHash string `json:"content_hash"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}
type BotInbox struct {
ID pgtype.UUID `json:"id"`
BotID pgtype.UUID `json:"bot_id"`
Source string `json:"source"`
Header []byte `json:"header"`
Content string `json:"content"`
Action string `json:"action"`
IsRead bool `json:"is_read"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
ReadAt pgtype.Timestamptz `json:"read_at"`
}
type BotMember struct {
BotID pgtype.UUID `json:"bot_id"`
UserID pgtype.UUID `json:"user_id"`
Role string `json:"role"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}
type BotPreauthKey struct {
ID pgtype.UUID `json:"id"`
BotID pgtype.UUID `json:"bot_id"`
Token string `json:"token"`
IssuedByUserID pgtype.UUID `json:"issued_by_user_id"`
ExpiresAt pgtype.Timestamptz `json:"expires_at"`
UsedAt pgtype.Timestamptz `json:"used_at"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}
type BotStorageBinding struct {
ID pgtype.UUID `json:"id"`
BotID pgtype.UUID `json:"bot_id"`
StorageProviderID pgtype.UUID `json:"storage_provider_id"`
BasePath string `json:"base_path"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}
type ChannelIdentity struct {
ID pgtype.UUID `json:"id"`
UserID pgtype.UUID `json:"user_id"`
ChannelType string `json:"channel_type"`
ChannelSubjectID string `json:"channel_subject_id"`
DisplayName pgtype.Text `json:"display_name"`
AvatarUrl pgtype.Text `json:"avatar_url"`
Metadata []byte `json:"metadata"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}
type ChannelIdentityBindCode struct {
ID pgtype.UUID `json:"id"`
Token string `json:"token"`
IssuedByUserID pgtype.UUID `json:"issued_by_user_id"`
ChannelType pgtype.Text `json:"channel_type"`
ExpiresAt pgtype.Timestamptz `json:"expires_at"`
UsedAt pgtype.Timestamptz `json:"used_at"`
UsedByChannelIdentityID pgtype.UUID `json:"used_by_channel_identity_id"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}
type Container struct {
ID pgtype.UUID `json:"id"`
BotID pgtype.UUID `json:"bot_id"`
ContainerID string `json:"container_id"`
ContainerName string `json:"container_name"`
Image string `json:"image"`
Status string `json:"status"`
Namespace string `json:"namespace"`
AutoStart bool `json:"auto_start"`
HostPath pgtype.Text `json:"host_path"`
ContainerPath string `json:"container_path"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
LastStartedAt pgtype.Timestamptz `json:"last_started_at"`
LastStoppedAt pgtype.Timestamptz `json:"last_stopped_at"`
}
type ContainerVersion struct {
ID pgtype.UUID `json:"id"`
ContainerID string `json:"container_id"`
SnapshotID pgtype.UUID `json:"snapshot_id"`
Version int32 `json:"version"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}
type EmailOutbox struct {
ID pgtype.UUID `json:"id"`
ProviderID pgtype.UUID `json:"provider_id"`
BotID pgtype.UUID `json:"bot_id"`
MessageID string `json:"message_id"`
FromAddress string `json:"from_address"`
ToAddresses []byte `json:"to_addresses"`
Subject string `json:"subject"`
BodyText string `json:"body_text"`
BodyHtml string `json:"body_html"`
Attachments []byte `json:"attachments"`
Status string `json:"status"`
Error string `json:"error"`
SentAt pgtype.Timestamptz `json:"sent_at"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}
type EmailProvider struct {
ID pgtype.UUID `json:"id"`
Name string `json:"name"`
Provider string `json:"provider"`
Config []byte `json:"config"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}
type LifecycleEvent struct {
ID string `json:"id"`
ContainerID string `json:"container_id"`
EventType string `json:"event_type"`
Payload []byte `json:"payload"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}
type LlmProvider struct {
ID pgtype.UUID `json:"id"`
Name string `json:"name"`
BaseUrl string `json:"base_url"`
ApiKey string `json:"api_key"`
Metadata []byte `json:"metadata"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}
type McpConnection struct {
ID pgtype.UUID `json:"id"`
BotID pgtype.UUID `json:"bot_id"`
Name string `json:"name"`
Type string `json:"type"`
Config []byte `json:"config"`
IsActive bool `json:"is_active"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}
type MediaAsset struct {
ID pgtype.UUID `json:"id"`
BotID pgtype.UUID `json:"bot_id"`
StorageProviderID pgtype.UUID `json:"storage_provider_id"`
ContentHash string `json:"content_hash"`
MediaType string `json:"media_type"`
Mime string `json:"mime"`
SizeBytes int64 `json:"size_bytes"`
StorageKey string `json:"storage_key"`
OriginalName pgtype.Text `json:"original_name"`
Width pgtype.Int4 `json:"width"`
Height pgtype.Int4 `json:"height"`
DurationMs pgtype.Int8 `json:"duration_ms"`
Metadata []byte `json:"metadata"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}
type Model struct {
ID pgtype.UUID `json:"id"`
ModelID string `json:"model_id"`
Name pgtype.Text `json:"name"`
LlmProviderID pgtype.UUID `json:"llm_provider_id"`
ClientType pgtype.Text `json:"client_type"`
Dimensions pgtype.Int4 `json:"dimensions"`
InputModalities []string `json:"input_modalities"`
SupportsReasoning bool `json:"supports_reasoning"`
Type string `json:"type"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}
type ModelVariant struct {
ID pgtype.UUID `json:"id"`
ModelUuid pgtype.UUID `json:"model_uuid"`
VariantID string `json:"variant_id"`
Weight int32 `json:"weight"`
Metadata []byte `json:"metadata"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}
type Schedule struct {
ID pgtype.UUID `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Pattern string `json:"pattern"`
MaxCalls pgtype.Int4 `json:"max_calls"`
CurrentCalls int32 `json:"current_calls"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
Enabled bool `json:"enabled"`
Command string `json:"command"`
BotID pgtype.UUID `json:"bot_id"`
}
type SearchProvider struct {
ID pgtype.UUID `json:"id"`
Name string `json:"name"`
Provider string `json:"provider"`
Config []byte `json:"config"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}
type Snapshot struct {
ID pgtype.UUID `json:"id"`
ContainerID string `json:"container_id"`
RuntimeSnapshotName string `json:"runtime_snapshot_name"`
ParentRuntimeSnapshotName pgtype.Text `json:"parent_runtime_snapshot_name"`
Snapshotter string `json:"snapshotter"`
Source string `json:"source"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}
type StorageProvider struct {
ID pgtype.UUID `json:"id"`
Name string `json:"name"`
Provider string `json:"provider"`
Config []byte `json:"config"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}
type Subagent struct {
ID pgtype.UUID `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
Deleted bool `json:"deleted"`
DeletedAt pgtype.Timestamptz `json:"deleted_at"`
BotID pgtype.UUID `json:"bot_id"`
Messages []byte `json:"messages"`
Metadata []byte `json:"metadata"`
Skills []byte `json:"skills"`
Usage []byte `json:"usage"`
}
type User struct {
ID pgtype.UUID `json:"id"`
Username pgtype.Text `json:"username"`
Email pgtype.Text `json:"email"`
PasswordHash pgtype.Text `json:"password_hash"`
Role string `json:"role"`
DisplayName pgtype.Text `json:"display_name"`
AvatarUrl pgtype.Text `json:"avatar_url"`
DataRoot pgtype.Text `json:"data_root"`
LastLoginAt pgtype.Timestamptz `json:"last_login_at"`
IsActive bool `json:"is_active"`
Metadata []byte `json:"metadata"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}
type UserChannelBinding struct {
ID pgtype.UUID `json:"id"`
UserID pgtype.UUID `json:"user_id"`
ChannelType string `json:"channel_type"`
Config []byte `json:"config"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}