2025-12-29 10:03:27 +08:00
|
|
|
// Code generated by ent, DO NOT EDIT.
|
|
|
|
|
|
|
|
|
|
package migrate
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"entgo.io/ent/dialect/entsql"
|
|
|
|
|
"entgo.io/ent/dialect/sql/schema"
|
|
|
|
|
"entgo.io/ent/schema/field"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var (
|
2026-01-04 19:27:53 +08:00
|
|
|
// APIKeysColumns holds the columns for the "api_keys" table.
|
|
|
|
|
APIKeysColumns = []*schema.Column{
|
|
|
|
|
{Name: "id", Type: field.TypeInt64, Increment: true},
|
|
|
|
|
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "deleted_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "key", Type: field.TypeString, Unique: true, Size: 128},
|
|
|
|
|
{Name: "name", Type: field.TypeString, Size: 100},
|
|
|
|
|
{Name: "status", Type: field.TypeString, Size: 20, Default: "active"},
|
2026-02-22 22:07:17 +08:00
|
|
|
{Name: "last_used_at", Type: field.TypeTime, Nullable: true},
|
2026-01-09 21:59:32 +08:00
|
|
|
{Name: "ip_whitelist", Type: field.TypeJSON, Nullable: true},
|
|
|
|
|
{Name: "ip_blacklist", Type: field.TypeJSON, Nullable: true},
|
2026-02-03 19:01:49 +08:00
|
|
|
{Name: "quota", Type: field.TypeFloat64, Default: 0, SchemaType: map[string]string{"postgres": "decimal(20,8)"}},
|
|
|
|
|
{Name: "quota_used", Type: field.TypeFloat64, Default: 0, SchemaType: map[string]string{"postgres": "decimal(20,8)"}},
|
|
|
|
|
{Name: "expires_at", Type: field.TypeTime, Nullable: true},
|
2026-03-03 15:01:10 +08:00
|
|
|
{Name: "rate_limit_5h", Type: field.TypeFloat64, Default: 0, SchemaType: map[string]string{"postgres": "decimal(20,8)"}},
|
|
|
|
|
{Name: "rate_limit_1d", Type: field.TypeFloat64, Default: 0, SchemaType: map[string]string{"postgres": "decimal(20,8)"}},
|
|
|
|
|
{Name: "rate_limit_7d", Type: field.TypeFloat64, Default: 0, SchemaType: map[string]string{"postgres": "decimal(20,8)"}},
|
|
|
|
|
{Name: "usage_5h", Type: field.TypeFloat64, Default: 0, SchemaType: map[string]string{"postgres": "decimal(20,8)"}},
|
|
|
|
|
{Name: "usage_1d", Type: field.TypeFloat64, Default: 0, SchemaType: map[string]string{"postgres": "decimal(20,8)"}},
|
|
|
|
|
{Name: "usage_7d", Type: field.TypeFloat64, Default: 0, SchemaType: map[string]string{"postgres": "decimal(20,8)"}},
|
|
|
|
|
{Name: "window_5h_start", Type: field.TypeTime, Nullable: true},
|
|
|
|
|
{Name: "window_1d_start", Type: field.TypeTime, Nullable: true},
|
|
|
|
|
{Name: "window_7d_start", Type: field.TypeTime, Nullable: true},
|
2026-01-04 19:27:53 +08:00
|
|
|
{Name: "group_id", Type: field.TypeInt64, Nullable: true},
|
|
|
|
|
{Name: "user_id", Type: field.TypeInt64},
|
|
|
|
|
}
|
|
|
|
|
// APIKeysTable holds the schema information for the "api_keys" table.
|
|
|
|
|
APIKeysTable = &schema.Table{
|
|
|
|
|
Name: "api_keys",
|
|
|
|
|
Columns: APIKeysColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{APIKeysColumns[0]},
|
|
|
|
|
ForeignKeys: []*schema.ForeignKey{
|
|
|
|
|
{
|
|
|
|
|
Symbol: "api_keys_groups_api_keys",
|
2026-03-03 15:01:10 +08:00
|
|
|
Columns: []*schema.Column{APIKeysColumns[22]},
|
2026-01-04 19:27:53 +08:00
|
|
|
RefColumns: []*schema.Column{GroupsColumns[0]},
|
|
|
|
|
OnDelete: schema.SetNull,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Symbol: "api_keys_users_api_keys",
|
2026-03-03 15:01:10 +08:00
|
|
|
Columns: []*schema.Column{APIKeysColumns[23]},
|
2026-01-04 19:27:53 +08:00
|
|
|
RefColumns: []*schema.Column{UsersColumns[0]},
|
|
|
|
|
OnDelete: schema.NoAction,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
Indexes: []*schema.Index{
|
|
|
|
|
{
|
|
|
|
|
Name: "apikey_user_id",
|
|
|
|
|
Unique: false,
|
2026-03-03 15:01:10 +08:00
|
|
|
Columns: []*schema.Column{APIKeysColumns[23]},
|
2026-01-04 19:27:53 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "apikey_group_id",
|
|
|
|
|
Unique: false,
|
2026-03-03 15:01:10 +08:00
|
|
|
Columns: []*schema.Column{APIKeysColumns[22]},
|
2026-01-04 19:27:53 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "apikey_status",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{APIKeysColumns[6]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "apikey_deleted_at",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{APIKeysColumns[3]},
|
|
|
|
|
},
|
2026-02-22 22:07:17 +08:00
|
|
|
{
|
|
|
|
|
Name: "apikey_last_used_at",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{APIKeysColumns[7]},
|
|
|
|
|
},
|
2026-02-03 19:01:49 +08:00
|
|
|
{
|
|
|
|
|
Name: "apikey_quota_quota_used",
|
|
|
|
|
Unique: false,
|
2026-02-22 22:07:17 +08:00
|
|
|
Columns: []*schema.Column{APIKeysColumns[10], APIKeysColumns[11]},
|
2026-02-03 19:01:49 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "apikey_expires_at",
|
|
|
|
|
Unique: false,
|
2026-02-22 22:07:17 +08:00
|
|
|
Columns: []*schema.Column{APIKeysColumns[12]},
|
2026-02-03 19:01:49 +08:00
|
|
|
},
|
2026-01-04 19:27:53 +08:00
|
|
|
},
|
|
|
|
|
}
|
2025-12-29 10:03:27 +08:00
|
|
|
// AccountsColumns holds the columns for the "accounts" table.
|
|
|
|
|
AccountsColumns = []*schema.Column{
|
|
|
|
|
{Name: "id", Type: field.TypeInt64, Increment: true},
|
|
|
|
|
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "deleted_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "name", Type: field.TypeString, Size: 100},
|
2026-01-05 14:07:33 +08:00
|
|
|
{Name: "notes", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "text"}},
|
2025-12-29 10:03:27 +08:00
|
|
|
{Name: "platform", Type: field.TypeString, Size: 50},
|
|
|
|
|
{Name: "type", Type: field.TypeString, Size: 20},
|
|
|
|
|
{Name: "credentials", Type: field.TypeJSON, SchemaType: map[string]string{"postgres": "jsonb"}},
|
|
|
|
|
{Name: "extra", Type: field.TypeJSON, SchemaType: map[string]string{"postgres": "jsonb"}},
|
|
|
|
|
{Name: "concurrency", Type: field.TypeInt, Default: 3},
|
2026-03-06 05:07:10 +08:00
|
|
|
{Name: "load_factor", Type: field.TypeInt, Nullable: true},
|
2025-12-29 10:03:27 +08:00
|
|
|
{Name: "priority", Type: field.TypeInt, Default: 50},
|
2026-01-14 16:12:08 +08:00
|
|
|
{Name: "rate_multiplier", Type: field.TypeFloat64, Default: 1, SchemaType: map[string]string{"postgres": "decimal(10,4)"}},
|
2025-12-29 10:03:27 +08:00
|
|
|
{Name: "status", Type: field.TypeString, Size: 20, Default: "active"},
|
|
|
|
|
{Name: "error_message", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "last_used_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
2026-01-07 16:59:35 +08:00
|
|
|
{Name: "expires_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "auto_pause_on_expired", Type: field.TypeBool, Default: true},
|
2025-12-29 10:03:27 +08:00
|
|
|
{Name: "schedulable", Type: field.TypeBool, Default: true},
|
|
|
|
|
{Name: "rate_limited_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "rate_limit_reset_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "overload_until", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
2026-02-28 15:01:20 +08:00
|
|
|
{Name: "temp_unschedulable_until", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "temp_unschedulable_reason", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "text"}},
|
2025-12-29 10:03:27 +08:00
|
|
|
{Name: "session_window_start", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "session_window_end", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "session_window_status", Type: field.TypeString, Nullable: true, Size: 20},
|
2025-12-31 14:11:57 +08:00
|
|
|
{Name: "proxy_id", Type: field.TypeInt64, Nullable: true},
|
2025-12-29 10:03:27 +08:00
|
|
|
}
|
|
|
|
|
// AccountsTable holds the schema information for the "accounts" table.
|
|
|
|
|
AccountsTable = &schema.Table{
|
|
|
|
|
Name: "accounts",
|
|
|
|
|
Columns: AccountsColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{AccountsColumns[0]},
|
2025-12-31 14:11:57 +08:00
|
|
|
ForeignKeys: []*schema.ForeignKey{
|
|
|
|
|
{
|
|
|
|
|
Symbol: "accounts_proxies_proxy",
|
2026-03-06 05:07:10 +08:00
|
|
|
Columns: []*schema.Column{AccountsColumns[28]},
|
2025-12-31 14:11:57 +08:00
|
|
|
RefColumns: []*schema.Column{ProxiesColumns[0]},
|
|
|
|
|
OnDelete: schema.SetNull,
|
|
|
|
|
},
|
|
|
|
|
},
|
2025-12-29 10:03:27 +08:00
|
|
|
Indexes: []*schema.Index{
|
|
|
|
|
{
|
|
|
|
|
Name: "account_platform",
|
|
|
|
|
Unique: false,
|
2026-01-05 14:07:33 +08:00
|
|
|
Columns: []*schema.Column{AccountsColumns[6]},
|
2025-12-29 10:03:27 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "account_type",
|
|
|
|
|
Unique: false,
|
2026-01-05 14:07:33 +08:00
|
|
|
Columns: []*schema.Column{AccountsColumns[7]},
|
2025-12-29 10:03:27 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "account_status",
|
|
|
|
|
Unique: false,
|
2026-03-06 05:07:10 +08:00
|
|
|
Columns: []*schema.Column{AccountsColumns[14]},
|
2025-12-29 10:03:27 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "account_proxy_id",
|
|
|
|
|
Unique: false,
|
2026-03-06 05:07:10 +08:00
|
|
|
Columns: []*schema.Column{AccountsColumns[28]},
|
2025-12-29 10:03:27 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "account_priority",
|
|
|
|
|
Unique: false,
|
2026-03-06 05:07:10 +08:00
|
|
|
Columns: []*schema.Column{AccountsColumns[12]},
|
2025-12-29 10:03:27 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "account_last_used_at",
|
|
|
|
|
Unique: false,
|
2026-03-06 05:07:10 +08:00
|
|
|
Columns: []*schema.Column{AccountsColumns[16]},
|
2025-12-29 10:03:27 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "account_schedulable",
|
|
|
|
|
Unique: false,
|
2026-03-06 05:07:10 +08:00
|
|
|
Columns: []*schema.Column{AccountsColumns[19]},
|
2025-12-29 10:03:27 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "account_rate_limited_at",
|
|
|
|
|
Unique: false,
|
2026-03-06 05:07:10 +08:00
|
|
|
Columns: []*schema.Column{AccountsColumns[20]},
|
2025-12-29 10:03:27 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "account_rate_limit_reset_at",
|
|
|
|
|
Unique: false,
|
2026-03-06 05:07:10 +08:00
|
|
|
Columns: []*schema.Column{AccountsColumns[21]},
|
2025-12-29 10:03:27 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "account_overload_until",
|
|
|
|
|
Unique: false,
|
2026-03-06 05:07:10 +08:00
|
|
|
Columns: []*schema.Column{AccountsColumns[22]},
|
2025-12-29 10:03:27 +08:00
|
|
|
},
|
2026-02-28 15:01:20 +08:00
|
|
|
{
|
|
|
|
|
Name: "account_platform_priority",
|
|
|
|
|
Unique: false,
|
2026-03-06 05:07:10 +08:00
|
|
|
Columns: []*schema.Column{AccountsColumns[6], AccountsColumns[12]},
|
2026-02-28 15:01:20 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "account_priority_status",
|
|
|
|
|
Unique: false,
|
2026-03-06 05:07:10 +08:00
|
|
|
Columns: []*schema.Column{AccountsColumns[12], AccountsColumns[14]},
|
2026-02-28 15:01:20 +08:00
|
|
|
},
|
2025-12-29 10:03:27 +08:00
|
|
|
{
|
|
|
|
|
Name: "account_deleted_at",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{AccountsColumns[3]},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
// AccountGroupsColumns holds the columns for the "account_groups" table.
|
|
|
|
|
AccountGroupsColumns = []*schema.Column{
|
|
|
|
|
{Name: "priority", Type: field.TypeInt, Default: 50},
|
2025-12-31 14:11:57 +08:00
|
|
|
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
2025-12-29 10:03:27 +08:00
|
|
|
{Name: "account_id", Type: field.TypeInt64},
|
|
|
|
|
{Name: "group_id", Type: field.TypeInt64},
|
|
|
|
|
}
|
|
|
|
|
// AccountGroupsTable holds the schema information for the "account_groups" table.
|
|
|
|
|
AccountGroupsTable = &schema.Table{
|
|
|
|
|
Name: "account_groups",
|
|
|
|
|
Columns: AccountGroupsColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{AccountGroupsColumns[2], AccountGroupsColumns[3]},
|
|
|
|
|
ForeignKeys: []*schema.ForeignKey{
|
|
|
|
|
{
|
|
|
|
|
Symbol: "account_groups_accounts_account",
|
|
|
|
|
Columns: []*schema.Column{AccountGroupsColumns[2]},
|
|
|
|
|
RefColumns: []*schema.Column{AccountsColumns[0]},
|
|
|
|
|
OnDelete: schema.NoAction,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Symbol: "account_groups_groups_group",
|
|
|
|
|
Columns: []*schema.Column{AccountGroupsColumns[3]},
|
|
|
|
|
RefColumns: []*schema.Column{GroupsColumns[0]},
|
|
|
|
|
OnDelete: schema.NoAction,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
Indexes: []*schema.Index{
|
|
|
|
|
{
|
|
|
|
|
Name: "accountgroup_group_id",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{AccountGroupsColumns[3]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "accountgroup_priority",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{AccountGroupsColumns[0]},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
2026-01-30 16:45:04 +08:00
|
|
|
// AnnouncementsColumns holds the columns for the "announcements" table.
|
|
|
|
|
AnnouncementsColumns = []*schema.Column{
|
|
|
|
|
{Name: "id", Type: field.TypeInt64, Increment: true},
|
|
|
|
|
{Name: "title", Type: field.TypeString, Size: 200},
|
|
|
|
|
{Name: "content", Type: field.TypeString, SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "status", Type: field.TypeString, Size: 20, Default: "draft"},
|
2026-03-07 15:06:13 +08:00
|
|
|
{Name: "notify_mode", Type: field.TypeString, Size: 20, Default: "silent"},
|
2026-01-30 16:45:04 +08:00
|
|
|
{Name: "targeting", Type: field.TypeJSON, Nullable: true, SchemaType: map[string]string{"postgres": "jsonb"}},
|
|
|
|
|
{Name: "starts_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "ends_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "created_by", Type: field.TypeInt64, Nullable: true},
|
|
|
|
|
{Name: "updated_by", Type: field.TypeInt64, Nullable: true},
|
|
|
|
|
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
}
|
|
|
|
|
// AnnouncementsTable holds the schema information for the "announcements" table.
|
|
|
|
|
AnnouncementsTable = &schema.Table{
|
|
|
|
|
Name: "announcements",
|
|
|
|
|
Columns: AnnouncementsColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{AnnouncementsColumns[0]},
|
|
|
|
|
Indexes: []*schema.Index{
|
|
|
|
|
{
|
|
|
|
|
Name: "announcement_status",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{AnnouncementsColumns[3]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "announcement_created_at",
|
|
|
|
|
Unique: false,
|
2026-03-07 15:06:13 +08:00
|
|
|
Columns: []*schema.Column{AnnouncementsColumns[10]},
|
2026-01-30 16:45:04 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "announcement_starts_at",
|
|
|
|
|
Unique: false,
|
2026-03-07 15:06:13 +08:00
|
|
|
Columns: []*schema.Column{AnnouncementsColumns[6]},
|
2026-01-30 16:45:04 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "announcement_ends_at",
|
|
|
|
|
Unique: false,
|
2026-03-07 15:06:13 +08:00
|
|
|
Columns: []*schema.Column{AnnouncementsColumns[7]},
|
2026-01-30 16:45:04 +08:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
// AnnouncementReadsColumns holds the columns for the "announcement_reads" table.
|
|
|
|
|
AnnouncementReadsColumns = []*schema.Column{
|
|
|
|
|
{Name: "id", Type: field.TypeInt64, Increment: true},
|
|
|
|
|
{Name: "read_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "announcement_id", Type: field.TypeInt64},
|
|
|
|
|
{Name: "user_id", Type: field.TypeInt64},
|
|
|
|
|
}
|
|
|
|
|
// AnnouncementReadsTable holds the schema information for the "announcement_reads" table.
|
|
|
|
|
AnnouncementReadsTable = &schema.Table{
|
|
|
|
|
Name: "announcement_reads",
|
|
|
|
|
Columns: AnnouncementReadsColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{AnnouncementReadsColumns[0]},
|
|
|
|
|
ForeignKeys: []*schema.ForeignKey{
|
|
|
|
|
{
|
|
|
|
|
Symbol: "announcement_reads_announcements_reads",
|
|
|
|
|
Columns: []*schema.Column{AnnouncementReadsColumns[3]},
|
|
|
|
|
RefColumns: []*schema.Column{AnnouncementsColumns[0]},
|
|
|
|
|
OnDelete: schema.NoAction,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Symbol: "announcement_reads_users_announcement_reads",
|
|
|
|
|
Columns: []*schema.Column{AnnouncementReadsColumns[4]},
|
|
|
|
|
RefColumns: []*schema.Column{UsersColumns[0]},
|
|
|
|
|
OnDelete: schema.NoAction,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
Indexes: []*schema.Index{
|
|
|
|
|
{
|
|
|
|
|
Name: "announcementread_announcement_id",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{AnnouncementReadsColumns[3]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "announcementread_user_id",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{AnnouncementReadsColumns[4]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "announcementread_read_at",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{AnnouncementReadsColumns[1]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "announcementread_announcement_id_user_id",
|
|
|
|
|
Unique: true,
|
|
|
|
|
Columns: []*schema.Column{AnnouncementReadsColumns[3], AnnouncementReadsColumns[4]},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
2026-04-20 17:39:57 +08:00
|
|
|
// AuthIdentitiesColumns holds the columns for the "auth_identities" table.
|
|
|
|
|
AuthIdentitiesColumns = []*schema.Column{
|
|
|
|
|
{Name: "id", Type: field.TypeInt64, Increment: true},
|
|
|
|
|
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "provider_type", Type: field.TypeString, Size: 20},
|
|
|
|
|
{Name: "provider_key", Type: field.TypeString, SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "provider_subject", Type: field.TypeString, SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "verified_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "issuer", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "metadata", Type: field.TypeJSON, SchemaType: map[string]string{"postgres": "jsonb"}},
|
|
|
|
|
{Name: "user_id", Type: field.TypeInt64},
|
|
|
|
|
}
|
|
|
|
|
// AuthIdentitiesTable holds the schema information for the "auth_identities" table.
|
|
|
|
|
AuthIdentitiesTable = &schema.Table{
|
|
|
|
|
Name: "auth_identities",
|
|
|
|
|
Columns: AuthIdentitiesColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{AuthIdentitiesColumns[0]},
|
|
|
|
|
ForeignKeys: []*schema.ForeignKey{
|
|
|
|
|
{
|
|
|
|
|
Symbol: "auth_identities_users_auth_identities",
|
|
|
|
|
Columns: []*schema.Column{AuthIdentitiesColumns[9]},
|
|
|
|
|
RefColumns: []*schema.Column{UsersColumns[0]},
|
2026-04-22 10:26:22 +08:00
|
|
|
OnDelete: schema.Cascade,
|
2026-04-20 17:39:57 +08:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
Indexes: []*schema.Index{
|
|
|
|
|
{
|
|
|
|
|
Name: "authidentity_provider_type_provider_key_provider_subject",
|
|
|
|
|
Unique: true,
|
|
|
|
|
Columns: []*schema.Column{AuthIdentitiesColumns[3], AuthIdentitiesColumns[4], AuthIdentitiesColumns[5]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "authidentity_user_id",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{AuthIdentitiesColumns[9]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "authidentity_user_id_provider_type",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{AuthIdentitiesColumns[9], AuthIdentitiesColumns[3]},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
// AuthIdentityChannelsColumns holds the columns for the "auth_identity_channels" table.
|
|
|
|
|
AuthIdentityChannelsColumns = []*schema.Column{
|
|
|
|
|
{Name: "id", Type: field.TypeInt64, Increment: true},
|
|
|
|
|
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "provider_type", Type: field.TypeString, Size: 20},
|
|
|
|
|
{Name: "provider_key", Type: field.TypeString, SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "channel", Type: field.TypeString, Size: 20},
|
|
|
|
|
{Name: "channel_app_id", Type: field.TypeString, SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "channel_subject", Type: field.TypeString, SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "metadata", Type: field.TypeJSON, SchemaType: map[string]string{"postgres": "jsonb"}},
|
|
|
|
|
{Name: "identity_id", Type: field.TypeInt64},
|
|
|
|
|
}
|
|
|
|
|
// AuthIdentityChannelsTable holds the schema information for the "auth_identity_channels" table.
|
|
|
|
|
AuthIdentityChannelsTable = &schema.Table{
|
|
|
|
|
Name: "auth_identity_channels",
|
|
|
|
|
Columns: AuthIdentityChannelsColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{AuthIdentityChannelsColumns[0]},
|
|
|
|
|
ForeignKeys: []*schema.ForeignKey{
|
|
|
|
|
{
|
|
|
|
|
Symbol: "auth_identity_channels_auth_identities_channels",
|
|
|
|
|
Columns: []*schema.Column{AuthIdentityChannelsColumns[9]},
|
|
|
|
|
RefColumns: []*schema.Column{AuthIdentitiesColumns[0]},
|
2026-04-22 10:26:22 +08:00
|
|
|
OnDelete: schema.Cascade,
|
2026-04-20 17:39:57 +08:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
Indexes: []*schema.Index{
|
|
|
|
|
{
|
|
|
|
|
Name: "authidentitychannel_provider_type_provider_key_channel_channel_app_id_channel_subject",
|
|
|
|
|
Unique: true,
|
|
|
|
|
Columns: []*schema.Column{AuthIdentityChannelsColumns[3], AuthIdentityChannelsColumns[4], AuthIdentityChannelsColumns[5], AuthIdentityChannelsColumns[6], AuthIdentityChannelsColumns[7]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "authidentitychannel_identity_id",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{AuthIdentityChannelsColumns[9]},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
2026-04-20 20:21:02 +08:00
|
|
|
// ChannelMonitorsColumns holds the columns for the "channel_monitors" table.
|
|
|
|
|
ChannelMonitorsColumns = []*schema.Column{
|
|
|
|
|
{Name: "id", Type: field.TypeInt64, Increment: true},
|
|
|
|
|
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "name", Type: field.TypeString, Size: 100},
|
|
|
|
|
{Name: "provider", Type: field.TypeEnum, Enums: []string{"openai", "anthropic", "gemini"}},
|
|
|
|
|
{Name: "endpoint", Type: field.TypeString, Size: 500},
|
|
|
|
|
{Name: "api_key_encrypted", Type: field.TypeString},
|
|
|
|
|
{Name: "primary_model", Type: field.TypeString, Size: 200},
|
|
|
|
|
{Name: "extra_models", Type: field.TypeJSON},
|
|
|
|
|
{Name: "group_name", Type: field.TypeString, Nullable: true, Size: 100, Default: ""},
|
|
|
|
|
{Name: "enabled", Type: field.TypeBool, Default: true},
|
|
|
|
|
{Name: "interval_seconds", Type: field.TypeInt},
|
|
|
|
|
{Name: "last_checked_at", Type: field.TypeTime, Nullable: true},
|
|
|
|
|
{Name: "created_by", Type: field.TypeInt64},
|
feat(channel-monitor): request templates with snapshot apply + headers/body override
Problem:
Upstream channels can reject monitor probes based on client fingerprint
(e.g. "only Claude Code clients allowed"). The monitor had no way to
customize the outgoing request to bypass such restrictions.
Solution:
Introduce reusable request templates that carry extra_headers plus an
optional body override; monitors reference a template and receive a
snapshot copy on apply. Template edits do NOT auto-propagate — users
must click "apply to associated monitors" to refresh snapshots, so a
bad template edit cannot instantly break all production monitors.
Data model (migration 112):
- channel_monitor_request_templates: id, name, provider, description,
extra_headers jsonb, body_override_mode ('off'|'merge'|'replace'),
body_override jsonb. Unique (provider, name).
- channel_monitors: +template_id (FK, ON DELETE SET NULL), +extra_headers,
+body_override_mode, +body_override (the three runtime snapshot fields).
Checker (channel_monitor_checker.go):
- callProvider + runCheckForModel accept a CheckOptions carrying the
snapshot fields. mergeHeaders applies user headers on top of adapter
defaults (forbidden list: Host / Content-Length / Transfer-Encoding /
Connection / Content-Encoding).
- buildRequestBody:
off -> adapter default body
merge -> shallow-merge over default; per-provider deny list
(model/messages/contents) protects the challenge contract
replace -> user body verbatim
- Replace mode skips challenge validation; instead HTTP 2xx + non-empty
extracted response text = operational, empty = failed.
- 4 new unit tests cover all three modes + replace/empty-response case.
Admin API:
- /admin/channel-monitor-templates CRUD + /:id/apply (overwrite snapshot
on all template_id=id monitors, returns affected count).
- channel_monitor request/response DTOs gain the 4 new fields.
Frontend:
- channelMonitorTemplate.ts API client.
- MonitorAdvancedRequestConfig.vue shared component for headers textarea
+ body mode radio + body JSON editor; used by both template and monitor
forms.
- MonitorTemplateManagerDialog.vue: provider tabs, list/create/edit/
delete/apply, live "associated monitors" count per row.
- MonitorFiltersBar: new 模板管理 button next to 新增监控.
- MonitorFormDialog: collapsible 高级 section with template dropdown
(filtered by form.provider, clears on provider change) + embedded
AdvancedRequestConfig. Picking a template copies its fields into the
form (snapshot semantics mirrored on the client).
- i18n zh/en entries for all new copy.
chore: bump version to 0.1.114.32
2026-04-21 14:14:49 +08:00
|
|
|
{Name: "extra_headers", Type: field.TypeJSON},
|
|
|
|
|
{Name: "body_override_mode", Type: field.TypeString, Size: 10, Default: "off"},
|
|
|
|
|
{Name: "body_override", Type: field.TypeJSON, Nullable: true},
|
|
|
|
|
{Name: "template_id", Type: field.TypeInt64, Nullable: true},
|
2026-04-20 20:21:02 +08:00
|
|
|
}
|
|
|
|
|
// ChannelMonitorsTable holds the schema information for the "channel_monitors" table.
|
|
|
|
|
ChannelMonitorsTable = &schema.Table{
|
|
|
|
|
Name: "channel_monitors",
|
|
|
|
|
Columns: ChannelMonitorsColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{ChannelMonitorsColumns[0]},
|
feat(channel-monitor): request templates with snapshot apply + headers/body override
Problem:
Upstream channels can reject monitor probes based on client fingerprint
(e.g. "only Claude Code clients allowed"). The monitor had no way to
customize the outgoing request to bypass such restrictions.
Solution:
Introduce reusable request templates that carry extra_headers plus an
optional body override; monitors reference a template and receive a
snapshot copy on apply. Template edits do NOT auto-propagate — users
must click "apply to associated monitors" to refresh snapshots, so a
bad template edit cannot instantly break all production monitors.
Data model (migration 112):
- channel_monitor_request_templates: id, name, provider, description,
extra_headers jsonb, body_override_mode ('off'|'merge'|'replace'),
body_override jsonb. Unique (provider, name).
- channel_monitors: +template_id (FK, ON DELETE SET NULL), +extra_headers,
+body_override_mode, +body_override (the three runtime snapshot fields).
Checker (channel_monitor_checker.go):
- callProvider + runCheckForModel accept a CheckOptions carrying the
snapshot fields. mergeHeaders applies user headers on top of adapter
defaults (forbidden list: Host / Content-Length / Transfer-Encoding /
Connection / Content-Encoding).
- buildRequestBody:
off -> adapter default body
merge -> shallow-merge over default; per-provider deny list
(model/messages/contents) protects the challenge contract
replace -> user body verbatim
- Replace mode skips challenge validation; instead HTTP 2xx + non-empty
extracted response text = operational, empty = failed.
- 4 new unit tests cover all three modes + replace/empty-response case.
Admin API:
- /admin/channel-monitor-templates CRUD + /:id/apply (overwrite snapshot
on all template_id=id monitors, returns affected count).
- channel_monitor request/response DTOs gain the 4 new fields.
Frontend:
- channelMonitorTemplate.ts API client.
- MonitorAdvancedRequestConfig.vue shared component for headers textarea
+ body mode radio + body JSON editor; used by both template and monitor
forms.
- MonitorTemplateManagerDialog.vue: provider tabs, list/create/edit/
delete/apply, live "associated monitors" count per row.
- MonitorFiltersBar: new 模板管理 button next to 新增监控.
- MonitorFormDialog: collapsible 高级 section with template dropdown
(filtered by form.provider, clears on provider change) + embedded
AdvancedRequestConfig. Picking a template copies its fields into the
form (snapshot semantics mirrored on the client).
- i18n zh/en entries for all new copy.
chore: bump version to 0.1.114.32
2026-04-21 14:14:49 +08:00
|
|
|
ForeignKeys: []*schema.ForeignKey{
|
|
|
|
|
{
|
|
|
|
|
Symbol: "channel_monitors_channel_monitor_request_templates_request_template",
|
|
|
|
|
Columns: []*schema.Column{ChannelMonitorsColumns[17]},
|
|
|
|
|
RefColumns: []*schema.Column{ChannelMonitorRequestTemplatesColumns[0]},
|
|
|
|
|
OnDelete: schema.SetNull,
|
|
|
|
|
},
|
|
|
|
|
},
|
2026-04-20 20:21:02 +08:00
|
|
|
Indexes: []*schema.Index{
|
|
|
|
|
{
|
|
|
|
|
Name: "channelmonitor_enabled_last_checked_at",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{ChannelMonitorsColumns[10], ChannelMonitorsColumns[12]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "channelmonitor_provider",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{ChannelMonitorsColumns[4]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "channelmonitor_group_name",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{ChannelMonitorsColumns[9]},
|
|
|
|
|
},
|
feat(channel-monitor): request templates with snapshot apply + headers/body override
Problem:
Upstream channels can reject monitor probes based on client fingerprint
(e.g. "only Claude Code clients allowed"). The monitor had no way to
customize the outgoing request to bypass such restrictions.
Solution:
Introduce reusable request templates that carry extra_headers plus an
optional body override; monitors reference a template and receive a
snapshot copy on apply. Template edits do NOT auto-propagate — users
must click "apply to associated monitors" to refresh snapshots, so a
bad template edit cannot instantly break all production monitors.
Data model (migration 112):
- channel_monitor_request_templates: id, name, provider, description,
extra_headers jsonb, body_override_mode ('off'|'merge'|'replace'),
body_override jsonb. Unique (provider, name).
- channel_monitors: +template_id (FK, ON DELETE SET NULL), +extra_headers,
+body_override_mode, +body_override (the three runtime snapshot fields).
Checker (channel_monitor_checker.go):
- callProvider + runCheckForModel accept a CheckOptions carrying the
snapshot fields. mergeHeaders applies user headers on top of adapter
defaults (forbidden list: Host / Content-Length / Transfer-Encoding /
Connection / Content-Encoding).
- buildRequestBody:
off -> adapter default body
merge -> shallow-merge over default; per-provider deny list
(model/messages/contents) protects the challenge contract
replace -> user body verbatim
- Replace mode skips challenge validation; instead HTTP 2xx + non-empty
extracted response text = operational, empty = failed.
- 4 new unit tests cover all three modes + replace/empty-response case.
Admin API:
- /admin/channel-monitor-templates CRUD + /:id/apply (overwrite snapshot
on all template_id=id monitors, returns affected count).
- channel_monitor request/response DTOs gain the 4 new fields.
Frontend:
- channelMonitorTemplate.ts API client.
- MonitorAdvancedRequestConfig.vue shared component for headers textarea
+ body mode radio + body JSON editor; used by both template and monitor
forms.
- MonitorTemplateManagerDialog.vue: provider tabs, list/create/edit/
delete/apply, live "associated monitors" count per row.
- MonitorFiltersBar: new 模板管理 button next to 新增监控.
- MonitorFormDialog: collapsible 高级 section with template dropdown
(filtered by form.provider, clears on provider change) + embedded
AdvancedRequestConfig. Picking a template copies its fields into the
form (snapshot semantics mirrored on the client).
- i18n zh/en entries for all new copy.
chore: bump version to 0.1.114.32
2026-04-21 14:14:49 +08:00
|
|
|
{
|
|
|
|
|
Name: "channelmonitor_template_id",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{ChannelMonitorsColumns[17]},
|
|
|
|
|
},
|
2026-04-20 20:21:02 +08:00
|
|
|
},
|
|
|
|
|
}
|
2026-04-21 10:10:56 +08:00
|
|
|
// ChannelMonitorDailyRollupsColumns holds the columns for the "channel_monitor_daily_rollups" table.
|
|
|
|
|
ChannelMonitorDailyRollupsColumns = []*schema.Column{
|
|
|
|
|
{Name: "id", Type: field.TypeInt64, Increment: true},
|
|
|
|
|
{Name: "model", Type: field.TypeString, Size: 200},
|
|
|
|
|
{Name: "bucket_date", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "date"}},
|
|
|
|
|
{Name: "total_checks", Type: field.TypeInt, Default: 0},
|
|
|
|
|
{Name: "ok_count", Type: field.TypeInt, Default: 0},
|
|
|
|
|
{Name: "operational_count", Type: field.TypeInt, Default: 0},
|
|
|
|
|
{Name: "degraded_count", Type: field.TypeInt, Default: 0},
|
|
|
|
|
{Name: "failed_count", Type: field.TypeInt, Default: 0},
|
|
|
|
|
{Name: "error_count", Type: field.TypeInt, Default: 0},
|
|
|
|
|
{Name: "sum_latency_ms", Type: field.TypeInt64, Default: 0},
|
|
|
|
|
{Name: "count_latency", Type: field.TypeInt, Default: 0},
|
|
|
|
|
{Name: "sum_ping_latency_ms", Type: field.TypeInt64, Default: 0},
|
|
|
|
|
{Name: "count_ping_latency", Type: field.TypeInt, Default: 0},
|
|
|
|
|
{Name: "computed_at", Type: field.TypeTime},
|
|
|
|
|
{Name: "monitor_id", Type: field.TypeInt64},
|
|
|
|
|
}
|
|
|
|
|
// ChannelMonitorDailyRollupsTable holds the schema information for the "channel_monitor_daily_rollups" table.
|
|
|
|
|
ChannelMonitorDailyRollupsTable = &schema.Table{
|
|
|
|
|
Name: "channel_monitor_daily_rollups",
|
|
|
|
|
Columns: ChannelMonitorDailyRollupsColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{ChannelMonitorDailyRollupsColumns[0]},
|
|
|
|
|
ForeignKeys: []*schema.ForeignKey{
|
|
|
|
|
{
|
|
|
|
|
Symbol: "channel_monitor_daily_rollups_channel_monitors_daily_rollups",
|
2026-04-21 10:45:30 +08:00
|
|
|
Columns: []*schema.Column{ChannelMonitorDailyRollupsColumns[14]},
|
2026-04-21 10:10:56 +08:00
|
|
|
RefColumns: []*schema.Column{ChannelMonitorsColumns[0]},
|
|
|
|
|
OnDelete: schema.Cascade,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
Indexes: []*schema.Index{
|
|
|
|
|
{
|
|
|
|
|
Name: "channelmonitordailyrollup_monitor_id_model_bucket_date",
|
|
|
|
|
Unique: true,
|
2026-04-21 10:45:30 +08:00
|
|
|
Columns: []*schema.Column{ChannelMonitorDailyRollupsColumns[14], ChannelMonitorDailyRollupsColumns[1], ChannelMonitorDailyRollupsColumns[2]},
|
2026-04-21 10:10:56 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "channelmonitordailyrollup_bucket_date",
|
|
|
|
|
Unique: false,
|
2026-04-21 10:45:30 +08:00
|
|
|
Columns: []*schema.Column{ChannelMonitorDailyRollupsColumns[2]},
|
2026-04-21 10:10:56 +08:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
2026-04-20 20:21:02 +08:00
|
|
|
// ChannelMonitorHistoriesColumns holds the columns for the "channel_monitor_histories" table.
|
|
|
|
|
ChannelMonitorHistoriesColumns = []*schema.Column{
|
|
|
|
|
{Name: "id", Type: field.TypeInt64, Increment: true},
|
|
|
|
|
{Name: "model", Type: field.TypeString, Size: 200},
|
|
|
|
|
{Name: "status", Type: field.TypeEnum, Enums: []string{"operational", "degraded", "failed", "error"}},
|
|
|
|
|
{Name: "latency_ms", Type: field.TypeInt, Nullable: true},
|
|
|
|
|
{Name: "ping_latency_ms", Type: field.TypeInt, Nullable: true},
|
|
|
|
|
{Name: "message", Type: field.TypeString, Nullable: true, Size: 500, Default: ""},
|
|
|
|
|
{Name: "checked_at", Type: field.TypeTime},
|
|
|
|
|
{Name: "monitor_id", Type: field.TypeInt64},
|
|
|
|
|
}
|
|
|
|
|
// ChannelMonitorHistoriesTable holds the schema information for the "channel_monitor_histories" table.
|
|
|
|
|
ChannelMonitorHistoriesTable = &schema.Table{
|
|
|
|
|
Name: "channel_monitor_histories",
|
|
|
|
|
Columns: ChannelMonitorHistoriesColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{ChannelMonitorHistoriesColumns[0]},
|
|
|
|
|
ForeignKeys: []*schema.ForeignKey{
|
|
|
|
|
{
|
|
|
|
|
Symbol: "channel_monitor_histories_channel_monitors_history",
|
2026-04-21 10:45:30 +08:00
|
|
|
Columns: []*schema.Column{ChannelMonitorHistoriesColumns[7]},
|
2026-04-20 20:21:02 +08:00
|
|
|
RefColumns: []*schema.Column{ChannelMonitorsColumns[0]},
|
|
|
|
|
OnDelete: schema.Cascade,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
Indexes: []*schema.Index{
|
|
|
|
|
{
|
|
|
|
|
Name: "channelmonitorhistory_monitor_id_model_checked_at",
|
|
|
|
|
Unique: false,
|
2026-04-21 10:45:30 +08:00
|
|
|
Columns: []*schema.Column{ChannelMonitorHistoriesColumns[7], ChannelMonitorHistoriesColumns[1], ChannelMonitorHistoriesColumns[6]},
|
2026-04-20 20:21:02 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "channelmonitorhistory_checked_at",
|
|
|
|
|
Unique: false,
|
2026-04-21 10:45:30 +08:00
|
|
|
Columns: []*schema.Column{ChannelMonitorHistoriesColumns[6]},
|
2026-04-20 20:21:02 +08:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
feat(channel-monitor): request templates with snapshot apply + headers/body override
Problem:
Upstream channels can reject monitor probes based on client fingerprint
(e.g. "only Claude Code clients allowed"). The monitor had no way to
customize the outgoing request to bypass such restrictions.
Solution:
Introduce reusable request templates that carry extra_headers plus an
optional body override; monitors reference a template and receive a
snapshot copy on apply. Template edits do NOT auto-propagate — users
must click "apply to associated monitors" to refresh snapshots, so a
bad template edit cannot instantly break all production monitors.
Data model (migration 112):
- channel_monitor_request_templates: id, name, provider, description,
extra_headers jsonb, body_override_mode ('off'|'merge'|'replace'),
body_override jsonb. Unique (provider, name).
- channel_monitors: +template_id (FK, ON DELETE SET NULL), +extra_headers,
+body_override_mode, +body_override (the three runtime snapshot fields).
Checker (channel_monitor_checker.go):
- callProvider + runCheckForModel accept a CheckOptions carrying the
snapshot fields. mergeHeaders applies user headers on top of adapter
defaults (forbidden list: Host / Content-Length / Transfer-Encoding /
Connection / Content-Encoding).
- buildRequestBody:
off -> adapter default body
merge -> shallow-merge over default; per-provider deny list
(model/messages/contents) protects the challenge contract
replace -> user body verbatim
- Replace mode skips challenge validation; instead HTTP 2xx + non-empty
extracted response text = operational, empty = failed.
- 4 new unit tests cover all three modes + replace/empty-response case.
Admin API:
- /admin/channel-monitor-templates CRUD + /:id/apply (overwrite snapshot
on all template_id=id monitors, returns affected count).
- channel_monitor request/response DTOs gain the 4 new fields.
Frontend:
- channelMonitorTemplate.ts API client.
- MonitorAdvancedRequestConfig.vue shared component for headers textarea
+ body mode radio + body JSON editor; used by both template and monitor
forms.
- MonitorTemplateManagerDialog.vue: provider tabs, list/create/edit/
delete/apply, live "associated monitors" count per row.
- MonitorFiltersBar: new 模板管理 button next to 新增监控.
- MonitorFormDialog: collapsible 高级 section with template dropdown
(filtered by form.provider, clears on provider change) + embedded
AdvancedRequestConfig. Picking a template copies its fields into the
form (snapshot semantics mirrored on the client).
- i18n zh/en entries for all new copy.
chore: bump version to 0.1.114.32
2026-04-21 14:14:49 +08:00
|
|
|
// ChannelMonitorRequestTemplatesColumns holds the columns for the "channel_monitor_request_templates" table.
|
|
|
|
|
ChannelMonitorRequestTemplatesColumns = []*schema.Column{
|
|
|
|
|
{Name: "id", Type: field.TypeInt64, Increment: true},
|
|
|
|
|
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "name", Type: field.TypeString, Size: 100},
|
|
|
|
|
{Name: "provider", Type: field.TypeEnum, Enums: []string{"openai", "anthropic", "gemini"}},
|
|
|
|
|
{Name: "description", Type: field.TypeString, Nullable: true, Size: 500, Default: ""},
|
|
|
|
|
{Name: "extra_headers", Type: field.TypeJSON},
|
|
|
|
|
{Name: "body_override_mode", Type: field.TypeString, Size: 10, Default: "off"},
|
|
|
|
|
{Name: "body_override", Type: field.TypeJSON, Nullable: true},
|
|
|
|
|
}
|
|
|
|
|
// ChannelMonitorRequestTemplatesTable holds the schema information for the "channel_monitor_request_templates" table.
|
|
|
|
|
ChannelMonitorRequestTemplatesTable = &schema.Table{
|
|
|
|
|
Name: "channel_monitor_request_templates",
|
|
|
|
|
Columns: ChannelMonitorRequestTemplatesColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{ChannelMonitorRequestTemplatesColumns[0]},
|
|
|
|
|
Indexes: []*schema.Index{
|
|
|
|
|
{
|
|
|
|
|
Name: "channelmonitorrequesttemplate_provider_name",
|
|
|
|
|
Unique: true,
|
|
|
|
|
Columns: []*schema.Column{ChannelMonitorRequestTemplatesColumns[4], ChannelMonitorRequestTemplatesColumns[3]},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
2026-02-05 21:52:54 +08:00
|
|
|
// ErrorPassthroughRulesColumns holds the columns for the "error_passthrough_rules" table.
|
|
|
|
|
ErrorPassthroughRulesColumns = []*schema.Column{
|
|
|
|
|
{Name: "id", Type: field.TypeInt64, Increment: true},
|
|
|
|
|
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "name", Type: field.TypeString, Size: 100},
|
|
|
|
|
{Name: "enabled", Type: field.TypeBool, Default: true},
|
|
|
|
|
{Name: "priority", Type: field.TypeInt, Default: 0},
|
|
|
|
|
{Name: "error_codes", Type: field.TypeJSON, Nullable: true, SchemaType: map[string]string{"postgres": "jsonb"}},
|
|
|
|
|
{Name: "keywords", Type: field.TypeJSON, Nullable: true, SchemaType: map[string]string{"postgres": "jsonb"}},
|
|
|
|
|
{Name: "match_mode", Type: field.TypeString, Size: 10, Default: "any"},
|
|
|
|
|
{Name: "platforms", Type: field.TypeJSON, Nullable: true, SchemaType: map[string]string{"postgres": "jsonb"}},
|
|
|
|
|
{Name: "passthrough_code", Type: field.TypeBool, Default: true},
|
|
|
|
|
{Name: "response_code", Type: field.TypeInt, Nullable: true},
|
|
|
|
|
{Name: "passthrough_body", Type: field.TypeBool, Default: true},
|
|
|
|
|
{Name: "custom_message", Type: field.TypeString, Nullable: true, Size: 2147483647},
|
2026-02-10 11:42:39 +08:00
|
|
|
{Name: "skip_monitoring", Type: field.TypeBool, Default: false},
|
2026-02-05 21:52:54 +08:00
|
|
|
{Name: "description", Type: field.TypeString, Nullable: true, Size: 2147483647},
|
|
|
|
|
}
|
|
|
|
|
// ErrorPassthroughRulesTable holds the schema information for the "error_passthrough_rules" table.
|
|
|
|
|
ErrorPassthroughRulesTable = &schema.Table{
|
|
|
|
|
Name: "error_passthrough_rules",
|
|
|
|
|
Columns: ErrorPassthroughRulesColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{ErrorPassthroughRulesColumns[0]},
|
|
|
|
|
Indexes: []*schema.Index{
|
|
|
|
|
{
|
|
|
|
|
Name: "errorpassthroughrule_enabled",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{ErrorPassthroughRulesColumns[4]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "errorpassthroughrule_priority",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{ErrorPassthroughRulesColumns[5]},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
2025-12-29 10:03:27 +08:00
|
|
|
// GroupsColumns holds the columns for the "groups" table.
|
|
|
|
|
GroupsColumns = []*schema.Column{
|
|
|
|
|
{Name: "id", Type: field.TypeInt64, Increment: true},
|
|
|
|
|
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "deleted_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
2025-12-31 16:37:18 +08:00
|
|
|
{Name: "name", Type: field.TypeString, Size: 100},
|
2025-12-29 10:03:27 +08:00
|
|
|
{Name: "description", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "rate_multiplier", Type: field.TypeFloat64, Default: 1, SchemaType: map[string]string{"postgres": "decimal(10,4)"}},
|
|
|
|
|
{Name: "is_exclusive", Type: field.TypeBool, Default: false},
|
|
|
|
|
{Name: "status", Type: field.TypeString, Size: 20, Default: "active"},
|
|
|
|
|
{Name: "platform", Type: field.TypeString, Size: 50, Default: "anthropic"},
|
|
|
|
|
{Name: "subscription_type", Type: field.TypeString, Size: 20, Default: "standard"},
|
|
|
|
|
{Name: "daily_limit_usd", Type: field.TypeFloat64, Nullable: true, SchemaType: map[string]string{"postgres": "decimal(20,8)"}},
|
|
|
|
|
{Name: "weekly_limit_usd", Type: field.TypeFloat64, Nullable: true, SchemaType: map[string]string{"postgres": "decimal(20,8)"}},
|
|
|
|
|
{Name: "monthly_limit_usd", Type: field.TypeFloat64, Nullable: true, SchemaType: map[string]string{"postgres": "decimal(20,8)"}},
|
2025-12-31 14:11:57 +08:00
|
|
|
{Name: "default_validity_days", Type: field.TypeInt, Default: 30},
|
2026-01-05 17:07:29 +08:00
|
|
|
{Name: "image_price_1k", Type: field.TypeFloat64, Nullable: true, SchemaType: map[string]string{"postgres": "decimal(20,8)"}},
|
|
|
|
|
{Name: "image_price_2k", Type: field.TypeFloat64, Nullable: true, SchemaType: map[string]string{"postgres": "decimal(20,8)"}},
|
|
|
|
|
{Name: "image_price_4k", Type: field.TypeFloat64, Nullable: true, SchemaType: map[string]string{"postgres": "decimal(20,8)"}},
|
2026-01-08 23:07:00 +08:00
|
|
|
{Name: "claude_code_only", Type: field.TypeBool, Default: false},
|
|
|
|
|
{Name: "fallback_group_id", Type: field.TypeInt64, Nullable: true},
|
2026-01-23 22:24:46 +08:00
|
|
|
{Name: "fallback_group_id_on_invalid_request", Type: field.TypeInt64, Nullable: true},
|
2026-01-16 17:26:05 +08:00
|
|
|
{Name: "model_routing", Type: field.TypeJSON, Nullable: true, SchemaType: map[string]string{"postgres": "jsonb"}},
|
|
|
|
|
{Name: "model_routing_enabled", Type: field.TypeBool, Default: false},
|
2026-01-27 13:09:56 +08:00
|
|
|
{Name: "mcp_xml_inject", Type: field.TypeBool, Default: true},
|
2026-02-02 22:20:08 +08:00
|
|
|
{Name: "supported_model_scopes", Type: field.TypeJSON, SchemaType: map[string]string{"postgres": "jsonb"}},
|
2026-02-08 16:53:45 +08:00
|
|
|
{Name: "sort_order", Type: field.TypeInt, Default: 0},
|
2026-03-07 17:02:19 +08:00
|
|
|
{Name: "allow_messages_dispatch", Type: field.TypeBool, Default: false},
|
2026-03-27 18:02:48 +08:00
|
|
|
{Name: "require_oauth_only", Type: field.TypeBool, Default: false},
|
|
|
|
|
{Name: "require_privacy_set", Type: field.TypeBool, Default: false},
|
2026-03-07 17:02:19 +08:00
|
|
|
{Name: "default_mapped_model", Type: field.TypeString, Size: 100, Default: ""},
|
2026-04-09 12:29:28 +08:00
|
|
|
{Name: "messages_dispatch_model_config", Type: field.TypeJSON, SchemaType: map[string]string{"postgres": "jsonb"}},
|
2026-04-23 03:33:52 +08:00
|
|
|
{Name: "rpm_limit", Type: field.TypeInt, Default: 0},
|
2025-12-29 10:03:27 +08:00
|
|
|
}
|
|
|
|
|
// GroupsTable holds the schema information for the "groups" table.
|
|
|
|
|
GroupsTable = &schema.Table{
|
|
|
|
|
Name: "groups",
|
|
|
|
|
Columns: GroupsColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{GroupsColumns[0]},
|
|
|
|
|
Indexes: []*schema.Index{
|
|
|
|
|
{
|
|
|
|
|
Name: "group_status",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{GroupsColumns[8]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "group_platform",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{GroupsColumns[9]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "group_subscription_type",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{GroupsColumns[10]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "group_is_exclusive",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{GroupsColumns[7]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "group_deleted_at",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{GroupsColumns[3]},
|
|
|
|
|
},
|
2026-02-08 16:53:45 +08:00
|
|
|
{
|
|
|
|
|
Name: "group_sort_order",
|
|
|
|
|
Unique: false,
|
2026-04-05 17:19:07 +08:00
|
|
|
Columns: []*schema.Column{GroupsColumns[25]},
|
2026-02-28 15:01:20 +08:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
// IdempotencyRecordsColumns holds the columns for the "idempotency_records" table.
|
|
|
|
|
IdempotencyRecordsColumns = []*schema.Column{
|
|
|
|
|
{Name: "id", Type: field.TypeInt64, Increment: true},
|
|
|
|
|
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "scope", Type: field.TypeString, Size: 128},
|
|
|
|
|
{Name: "idempotency_key_hash", Type: field.TypeString, Size: 64},
|
|
|
|
|
{Name: "request_fingerprint", Type: field.TypeString, Size: 64},
|
|
|
|
|
{Name: "status", Type: field.TypeString, Size: 32},
|
|
|
|
|
{Name: "response_status", Type: field.TypeInt, Nullable: true},
|
|
|
|
|
{Name: "response_body", Type: field.TypeString, Nullable: true},
|
|
|
|
|
{Name: "error_reason", Type: field.TypeString, Nullable: true, Size: 128},
|
|
|
|
|
{Name: "locked_until", Type: field.TypeTime, Nullable: true},
|
|
|
|
|
{Name: "expires_at", Type: field.TypeTime},
|
|
|
|
|
}
|
|
|
|
|
// IdempotencyRecordsTable holds the schema information for the "idempotency_records" table.
|
|
|
|
|
IdempotencyRecordsTable = &schema.Table{
|
|
|
|
|
Name: "idempotency_records",
|
|
|
|
|
Columns: IdempotencyRecordsColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{IdempotencyRecordsColumns[0]},
|
|
|
|
|
Indexes: []*schema.Index{
|
|
|
|
|
{
|
|
|
|
|
Name: "idempotencyrecord_scope_idempotency_key_hash",
|
|
|
|
|
Unique: true,
|
|
|
|
|
Columns: []*schema.Column{IdempotencyRecordsColumns[3], IdempotencyRecordsColumns[4]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "idempotencyrecord_expires_at",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{IdempotencyRecordsColumns[11]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "idempotencyrecord_status_locked_until",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{IdempotencyRecordsColumns[6], IdempotencyRecordsColumns[10]},
|
2026-02-08 16:53:45 +08:00
|
|
|
},
|
2025-12-29 10:03:27 +08:00
|
|
|
},
|
|
|
|
|
}
|
2026-04-20 17:39:57 +08:00
|
|
|
// IdentityAdoptionDecisionsColumns holds the columns for the "identity_adoption_decisions" table.
|
|
|
|
|
IdentityAdoptionDecisionsColumns = []*schema.Column{
|
|
|
|
|
{Name: "id", Type: field.TypeInt64, Increment: true},
|
|
|
|
|
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "adopt_display_name", Type: field.TypeBool, Default: false},
|
|
|
|
|
{Name: "adopt_avatar", Type: field.TypeBool, Default: false},
|
|
|
|
|
{Name: "decided_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "identity_id", Type: field.TypeInt64, Nullable: true},
|
|
|
|
|
{Name: "pending_auth_session_id", Type: field.TypeInt64, Unique: true},
|
|
|
|
|
}
|
|
|
|
|
// IdentityAdoptionDecisionsTable holds the schema information for the "identity_adoption_decisions" table.
|
|
|
|
|
IdentityAdoptionDecisionsTable = &schema.Table{
|
|
|
|
|
Name: "identity_adoption_decisions",
|
|
|
|
|
Columns: IdentityAdoptionDecisionsColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{IdentityAdoptionDecisionsColumns[0]},
|
|
|
|
|
ForeignKeys: []*schema.ForeignKey{
|
|
|
|
|
{
|
|
|
|
|
Symbol: "identity_adoption_decisions_auth_identities_adoption_decisions",
|
|
|
|
|
Columns: []*schema.Column{IdentityAdoptionDecisionsColumns[6]},
|
|
|
|
|
RefColumns: []*schema.Column{AuthIdentitiesColumns[0]},
|
|
|
|
|
OnDelete: schema.SetNull,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Symbol: "identity_adoption_decisions_pending_auth_sessions_adoption_decision",
|
|
|
|
|
Columns: []*schema.Column{IdentityAdoptionDecisionsColumns[7]},
|
|
|
|
|
RefColumns: []*schema.Column{PendingAuthSessionsColumns[0]},
|
2026-04-22 10:26:22 +08:00
|
|
|
OnDelete: schema.Cascade,
|
2026-04-20 17:39:57 +08:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
Indexes: []*schema.Index{
|
|
|
|
|
{
|
|
|
|
|
Name: "identityadoptiondecision_pending_auth_session_id",
|
|
|
|
|
Unique: true,
|
|
|
|
|
Columns: []*schema.Column{IdentityAdoptionDecisionsColumns[7]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "identityadoptiondecision_identity_id",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{IdentityAdoptionDecisionsColumns[6]},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
2026-04-10 21:08:51 +08:00
|
|
|
// PaymentAuditLogsColumns holds the columns for the "payment_audit_logs" table.
|
|
|
|
|
PaymentAuditLogsColumns = []*schema.Column{
|
|
|
|
|
{Name: "id", Type: field.TypeInt64, Increment: true},
|
|
|
|
|
{Name: "order_id", Type: field.TypeString, Size: 64},
|
|
|
|
|
{Name: "action", Type: field.TypeString, Size: 50},
|
|
|
|
|
{Name: "detail", Type: field.TypeString, Default: "", SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "operator", Type: field.TypeString, Size: 100, Default: "system"},
|
|
|
|
|
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
}
|
|
|
|
|
// PaymentAuditLogsTable holds the schema information for the "payment_audit_logs" table.
|
|
|
|
|
PaymentAuditLogsTable = &schema.Table{
|
|
|
|
|
Name: "payment_audit_logs",
|
|
|
|
|
Columns: PaymentAuditLogsColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{PaymentAuditLogsColumns[0]},
|
|
|
|
|
Indexes: []*schema.Index{
|
|
|
|
|
{
|
|
|
|
|
Name: "paymentauditlog_order_id",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{PaymentAuditLogsColumns[1]},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
// PaymentOrdersColumns holds the columns for the "payment_orders" table.
|
|
|
|
|
PaymentOrdersColumns = []*schema.Column{
|
|
|
|
|
{Name: "id", Type: field.TypeInt64, Increment: true},
|
|
|
|
|
{Name: "user_email", Type: field.TypeString, Size: 255},
|
|
|
|
|
{Name: "user_name", Type: field.TypeString, Size: 100},
|
|
|
|
|
{Name: "user_notes", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "amount", Type: field.TypeFloat64, SchemaType: map[string]string{"postgres": "decimal(20,2)"}},
|
|
|
|
|
{Name: "pay_amount", Type: field.TypeFloat64, SchemaType: map[string]string{"postgres": "decimal(20,2)"}},
|
|
|
|
|
{Name: "fee_rate", Type: field.TypeFloat64, Default: 0, SchemaType: map[string]string{"postgres": "decimal(10,4)"}},
|
|
|
|
|
{Name: "recharge_code", Type: field.TypeString, Size: 64},
|
|
|
|
|
{Name: "out_trade_no", Type: field.TypeString, Size: 64, Default: ""},
|
|
|
|
|
{Name: "payment_type", Type: field.TypeString, Size: 30},
|
|
|
|
|
{Name: "payment_trade_no", Type: field.TypeString, Size: 128},
|
|
|
|
|
{Name: "pay_url", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "qr_code", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "qr_code_img", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "order_type", Type: field.TypeString, Size: 20, Default: "balance"},
|
|
|
|
|
{Name: "plan_id", Type: field.TypeInt64, Nullable: true},
|
|
|
|
|
{Name: "subscription_group_id", Type: field.TypeInt64, Nullable: true},
|
|
|
|
|
{Name: "subscription_days", Type: field.TypeInt, Nullable: true},
|
|
|
|
|
{Name: "provider_instance_id", Type: field.TypeString, Nullable: true, Size: 64},
|
2026-04-20 20:47:14 +08:00
|
|
|
{Name: "provider_key", Type: field.TypeString, Nullable: true, Size: 30},
|
2026-04-21 12:41:27 +08:00
|
|
|
{Name: "provider_snapshot", Type: field.TypeJSON, Nullable: true, SchemaType: map[string]string{"postgres": "jsonb"}},
|
2026-04-10 21:08:51 +08:00
|
|
|
{Name: "status", Type: field.TypeString, Size: 30, Default: "PENDING"},
|
|
|
|
|
{Name: "refund_amount", Type: field.TypeFloat64, Default: 0, SchemaType: map[string]string{"postgres": "decimal(20,2)"}},
|
|
|
|
|
{Name: "refund_reason", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "refund_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "force_refund", Type: field.TypeBool, Default: false},
|
|
|
|
|
{Name: "refund_requested_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "refund_request_reason", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "refund_requested_by", Type: field.TypeString, Nullable: true, Size: 20},
|
|
|
|
|
{Name: "expires_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "paid_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "completed_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "failed_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "failed_reason", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "client_ip", Type: field.TypeString, Size: 50},
|
|
|
|
|
{Name: "src_host", Type: field.TypeString, Size: 255},
|
|
|
|
|
{Name: "src_url", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "user_id", Type: field.TypeInt64},
|
|
|
|
|
}
|
|
|
|
|
// PaymentOrdersTable holds the schema information for the "payment_orders" table.
|
|
|
|
|
PaymentOrdersTable = &schema.Table{
|
|
|
|
|
Name: "payment_orders",
|
|
|
|
|
Columns: PaymentOrdersColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{PaymentOrdersColumns[0]},
|
|
|
|
|
ForeignKeys: []*schema.ForeignKey{
|
|
|
|
|
{
|
|
|
|
|
Symbol: "payment_orders_users_payment_orders",
|
2026-04-21 12:41:27 +08:00
|
|
|
Columns: []*schema.Column{PaymentOrdersColumns[39]},
|
2026-04-10 21:08:51 +08:00
|
|
|
RefColumns: []*schema.Column{UsersColumns[0]},
|
|
|
|
|
OnDelete: schema.NoAction,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
Indexes: []*schema.Index{
|
|
|
|
|
{
|
|
|
|
|
Name: "paymentorder_out_trade_no",
|
2026-04-22 10:26:22 +08:00
|
|
|
Unique: true,
|
2026-04-10 21:08:51 +08:00
|
|
|
Columns: []*schema.Column{PaymentOrdersColumns[8]},
|
2026-04-22 10:26:22 +08:00
|
|
|
Annotation: &entsql.IndexAnnotation{
|
|
|
|
|
Where: "out_trade_no <> ''",
|
|
|
|
|
},
|
2026-04-10 21:08:51 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "paymentorder_user_id",
|
|
|
|
|
Unique: false,
|
2026-04-21 12:41:27 +08:00
|
|
|
Columns: []*schema.Column{PaymentOrdersColumns[39]},
|
2026-04-10 21:08:51 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "paymentorder_status",
|
|
|
|
|
Unique: false,
|
2026-04-21 12:41:27 +08:00
|
|
|
Columns: []*schema.Column{PaymentOrdersColumns[21]},
|
2026-04-10 21:08:51 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "paymentorder_expires_at",
|
|
|
|
|
Unique: false,
|
2026-04-21 12:41:27 +08:00
|
|
|
Columns: []*schema.Column{PaymentOrdersColumns[29]},
|
2026-04-10 21:08:51 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "paymentorder_created_at",
|
|
|
|
|
Unique: false,
|
2026-04-21 12:41:27 +08:00
|
|
|
Columns: []*schema.Column{PaymentOrdersColumns[37]},
|
2026-04-10 21:08:51 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "paymentorder_paid_at",
|
|
|
|
|
Unique: false,
|
2026-04-21 12:41:27 +08:00
|
|
|
Columns: []*schema.Column{PaymentOrdersColumns[30]},
|
2026-04-10 21:08:51 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "paymentorder_payment_type_paid_at",
|
|
|
|
|
Unique: false,
|
2026-04-21 12:41:27 +08:00
|
|
|
Columns: []*schema.Column{PaymentOrdersColumns[9], PaymentOrdersColumns[30]},
|
2026-04-10 21:08:51 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "paymentorder_order_type",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{PaymentOrdersColumns[14]},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
// PaymentProviderInstancesColumns holds the columns for the "payment_provider_instances" table.
|
|
|
|
|
PaymentProviderInstancesColumns = []*schema.Column{
|
|
|
|
|
{Name: "id", Type: field.TypeInt64, Increment: true},
|
|
|
|
|
{Name: "provider_key", Type: field.TypeString, Size: 30},
|
|
|
|
|
{Name: "name", Type: field.TypeString, Size: 100, Default: ""},
|
|
|
|
|
{Name: "config", Type: field.TypeString, SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "supported_types", Type: field.TypeString, Size: 200, Default: ""},
|
|
|
|
|
{Name: "enabled", Type: field.TypeBool, Default: true},
|
|
|
|
|
{Name: "payment_mode", Type: field.TypeString, Size: 20, Default: ""},
|
|
|
|
|
{Name: "sort_order", Type: field.TypeInt, Default: 0},
|
|
|
|
|
{Name: "limits", Type: field.TypeString, Default: "", SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "refund_enabled", Type: field.TypeBool, Default: false},
|
2026-04-14 16:26:46 +08:00
|
|
|
{Name: "allow_user_refund", Type: field.TypeBool, Default: false},
|
2026-04-10 21:08:51 +08:00
|
|
|
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
}
|
|
|
|
|
// PaymentProviderInstancesTable holds the schema information for the "payment_provider_instances" table.
|
|
|
|
|
PaymentProviderInstancesTable = &schema.Table{
|
|
|
|
|
Name: "payment_provider_instances",
|
|
|
|
|
Columns: PaymentProviderInstancesColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{PaymentProviderInstancesColumns[0]},
|
|
|
|
|
Indexes: []*schema.Index{
|
|
|
|
|
{
|
|
|
|
|
Name: "paymentproviderinstance_provider_key",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{PaymentProviderInstancesColumns[1]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "paymentproviderinstance_enabled",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{PaymentProviderInstancesColumns[5]},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
2026-04-20 17:39:57 +08:00
|
|
|
// PendingAuthSessionsColumns holds the columns for the "pending_auth_sessions" table.
|
|
|
|
|
PendingAuthSessionsColumns = []*schema.Column{
|
|
|
|
|
{Name: "id", Type: field.TypeInt64, Increment: true},
|
|
|
|
|
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "session_token", Type: field.TypeString, Size: 255},
|
|
|
|
|
{Name: "intent", Type: field.TypeString, Size: 40},
|
|
|
|
|
{Name: "provider_type", Type: field.TypeString, Size: 20},
|
|
|
|
|
{Name: "provider_key", Type: field.TypeString, SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "provider_subject", Type: field.TypeString, SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "redirect_to", Type: field.TypeString, Default: "", SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "resolved_email", Type: field.TypeString, Default: "", SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "registration_password_hash", Type: field.TypeString, Default: "", SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "upstream_identity_claims", Type: field.TypeJSON, SchemaType: map[string]string{"postgres": "jsonb"}},
|
|
|
|
|
{Name: "local_flow_state", Type: field.TypeJSON, SchemaType: map[string]string{"postgres": "jsonb"}},
|
|
|
|
|
{Name: "browser_session_key", Type: field.TypeString, Default: "", SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "completion_code_hash", Type: field.TypeString, Default: "", SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "completion_code_expires_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "email_verified_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "password_verified_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "totp_verified_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "expires_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "consumed_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "target_user_id", Type: field.TypeInt64, Nullable: true},
|
|
|
|
|
}
|
|
|
|
|
// PendingAuthSessionsTable holds the schema information for the "pending_auth_sessions" table.
|
|
|
|
|
PendingAuthSessionsTable = &schema.Table{
|
|
|
|
|
Name: "pending_auth_sessions",
|
|
|
|
|
Columns: PendingAuthSessionsColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{PendingAuthSessionsColumns[0]},
|
|
|
|
|
ForeignKeys: []*schema.ForeignKey{
|
|
|
|
|
{
|
|
|
|
|
Symbol: "pending_auth_sessions_users_pending_auth_sessions",
|
|
|
|
|
Columns: []*schema.Column{PendingAuthSessionsColumns[21]},
|
|
|
|
|
RefColumns: []*schema.Column{UsersColumns[0]},
|
|
|
|
|
OnDelete: schema.SetNull,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
Indexes: []*schema.Index{
|
|
|
|
|
{
|
|
|
|
|
Name: "pendingauthsession_session_token",
|
|
|
|
|
Unique: true,
|
|
|
|
|
Columns: []*schema.Column{PendingAuthSessionsColumns[3]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "pendingauthsession_target_user_id",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{PendingAuthSessionsColumns[21]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "pendingauthsession_expires_at",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{PendingAuthSessionsColumns[19]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "pendingauthsession_provider_type_provider_key_provider_subject",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{PendingAuthSessionsColumns[5], PendingAuthSessionsColumns[6], PendingAuthSessionsColumns[7]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "pendingauthsession_completion_code_hash",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{PendingAuthSessionsColumns[14]},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
2026-01-10 13:14:35 +08:00
|
|
|
// PromoCodesColumns holds the columns for the "promo_codes" table.
|
|
|
|
|
PromoCodesColumns = []*schema.Column{
|
|
|
|
|
{Name: "id", Type: field.TypeInt64, Increment: true},
|
|
|
|
|
{Name: "code", Type: field.TypeString, Unique: true, Size: 32},
|
|
|
|
|
{Name: "bonus_amount", Type: field.TypeFloat64, Default: 0, SchemaType: map[string]string{"postgres": "decimal(20,8)"}},
|
|
|
|
|
{Name: "max_uses", Type: field.TypeInt, Default: 0},
|
|
|
|
|
{Name: "used_count", Type: field.TypeInt, Default: 0},
|
|
|
|
|
{Name: "status", Type: field.TypeString, Size: 20, Default: "active"},
|
|
|
|
|
{Name: "expires_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "notes", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
}
|
|
|
|
|
// PromoCodesTable holds the schema information for the "promo_codes" table.
|
|
|
|
|
PromoCodesTable = &schema.Table{
|
|
|
|
|
Name: "promo_codes",
|
|
|
|
|
Columns: PromoCodesColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{PromoCodesColumns[0]},
|
|
|
|
|
Indexes: []*schema.Index{
|
|
|
|
|
{
|
|
|
|
|
Name: "promocode_status",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{PromoCodesColumns[5]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "promocode_expires_at",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{PromoCodesColumns[6]},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
// PromoCodeUsagesColumns holds the columns for the "promo_code_usages" table.
|
|
|
|
|
PromoCodeUsagesColumns = []*schema.Column{
|
|
|
|
|
{Name: "id", Type: field.TypeInt64, Increment: true},
|
|
|
|
|
{Name: "bonus_amount", Type: field.TypeFloat64, SchemaType: map[string]string{"postgres": "decimal(20,8)"}},
|
|
|
|
|
{Name: "used_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "promo_code_id", Type: field.TypeInt64},
|
|
|
|
|
{Name: "user_id", Type: field.TypeInt64},
|
|
|
|
|
}
|
|
|
|
|
// PromoCodeUsagesTable holds the schema information for the "promo_code_usages" table.
|
|
|
|
|
PromoCodeUsagesTable = &schema.Table{
|
|
|
|
|
Name: "promo_code_usages",
|
|
|
|
|
Columns: PromoCodeUsagesColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{PromoCodeUsagesColumns[0]},
|
|
|
|
|
ForeignKeys: []*schema.ForeignKey{
|
|
|
|
|
{
|
|
|
|
|
Symbol: "promo_code_usages_promo_codes_usage_records",
|
|
|
|
|
Columns: []*schema.Column{PromoCodeUsagesColumns[3]},
|
|
|
|
|
RefColumns: []*schema.Column{PromoCodesColumns[0]},
|
|
|
|
|
OnDelete: schema.NoAction,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Symbol: "promo_code_usages_users_promo_code_usages",
|
|
|
|
|
Columns: []*schema.Column{PromoCodeUsagesColumns[4]},
|
|
|
|
|
RefColumns: []*schema.Column{UsersColumns[0]},
|
|
|
|
|
OnDelete: schema.NoAction,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
Indexes: []*schema.Index{
|
|
|
|
|
{
|
|
|
|
|
Name: "promocodeusage_promo_code_id",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{PromoCodeUsagesColumns[3]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "promocodeusage_user_id",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{PromoCodeUsagesColumns[4]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "promocodeusage_promo_code_id_user_id",
|
|
|
|
|
Unique: true,
|
|
|
|
|
Columns: []*schema.Column{PromoCodeUsagesColumns[3], PromoCodeUsagesColumns[4]},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
2025-12-29 10:03:27 +08:00
|
|
|
// ProxiesColumns holds the columns for the "proxies" table.
|
|
|
|
|
ProxiesColumns = []*schema.Column{
|
|
|
|
|
{Name: "id", Type: field.TypeInt64, Increment: true},
|
|
|
|
|
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "deleted_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "name", Type: field.TypeString, Size: 100},
|
|
|
|
|
{Name: "protocol", Type: field.TypeString, Size: 20},
|
|
|
|
|
{Name: "host", Type: field.TypeString, Size: 255},
|
|
|
|
|
{Name: "port", Type: field.TypeInt},
|
|
|
|
|
{Name: "username", Type: field.TypeString, Nullable: true, Size: 100},
|
|
|
|
|
{Name: "password", Type: field.TypeString, Nullable: true, Size: 100},
|
|
|
|
|
{Name: "status", Type: field.TypeString, Size: 20, Default: "active"},
|
|
|
|
|
}
|
|
|
|
|
// ProxiesTable holds the schema information for the "proxies" table.
|
|
|
|
|
ProxiesTable = &schema.Table{
|
|
|
|
|
Name: "proxies",
|
|
|
|
|
Columns: ProxiesColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{ProxiesColumns[0]},
|
|
|
|
|
Indexes: []*schema.Index{
|
|
|
|
|
{
|
|
|
|
|
Name: "proxy_status",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{ProxiesColumns[10]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "proxy_deleted_at",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{ProxiesColumns[3]},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
// RedeemCodesColumns holds the columns for the "redeem_codes" table.
|
|
|
|
|
RedeemCodesColumns = []*schema.Column{
|
|
|
|
|
{Name: "id", Type: field.TypeInt64, Increment: true},
|
|
|
|
|
{Name: "code", Type: field.TypeString, Unique: true, Size: 32},
|
|
|
|
|
{Name: "type", Type: field.TypeString, Size: 20, Default: "balance"},
|
|
|
|
|
{Name: "value", Type: field.TypeFloat64, Default: 0, SchemaType: map[string]string{"postgres": "decimal(20,8)"}},
|
|
|
|
|
{Name: "status", Type: field.TypeString, Size: 20, Default: "unused"},
|
|
|
|
|
{Name: "used_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "notes", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "validity_days", Type: field.TypeInt, Default: 30},
|
|
|
|
|
{Name: "group_id", Type: field.TypeInt64, Nullable: true},
|
|
|
|
|
{Name: "used_by", Type: field.TypeInt64, Nullable: true},
|
|
|
|
|
}
|
|
|
|
|
// RedeemCodesTable holds the schema information for the "redeem_codes" table.
|
|
|
|
|
RedeemCodesTable = &schema.Table{
|
|
|
|
|
Name: "redeem_codes",
|
|
|
|
|
Columns: RedeemCodesColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{RedeemCodesColumns[0]},
|
|
|
|
|
ForeignKeys: []*schema.ForeignKey{
|
|
|
|
|
{
|
|
|
|
|
Symbol: "redeem_codes_groups_redeem_codes",
|
|
|
|
|
Columns: []*schema.Column{RedeemCodesColumns[9]},
|
|
|
|
|
RefColumns: []*schema.Column{GroupsColumns[0]},
|
|
|
|
|
OnDelete: schema.SetNull,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Symbol: "redeem_codes_users_redeem_codes",
|
|
|
|
|
Columns: []*schema.Column{RedeemCodesColumns[10]},
|
|
|
|
|
RefColumns: []*schema.Column{UsersColumns[0]},
|
|
|
|
|
OnDelete: schema.SetNull,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
Indexes: []*schema.Index{
|
|
|
|
|
{
|
|
|
|
|
Name: "redeemcode_status",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{RedeemCodesColumns[4]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "redeemcode_used_by",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{RedeemCodesColumns[10]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "redeemcode_group_id",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{RedeemCodesColumns[9]},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
2026-02-12 11:41:20 +08:00
|
|
|
// SecuritySecretsColumns holds the columns for the "security_secrets" table.
|
|
|
|
|
SecuritySecretsColumns = []*schema.Column{
|
|
|
|
|
{Name: "id", Type: field.TypeInt64, Increment: true},
|
|
|
|
|
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "key", Type: field.TypeString, Unique: true, Size: 100},
|
|
|
|
|
{Name: "value", Type: field.TypeString, SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
}
|
|
|
|
|
// SecuritySecretsTable holds the schema information for the "security_secrets" table.
|
|
|
|
|
SecuritySecretsTable = &schema.Table{
|
|
|
|
|
Name: "security_secrets",
|
|
|
|
|
Columns: SecuritySecretsColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{SecuritySecretsColumns[0]},
|
|
|
|
|
}
|
2025-12-29 10:03:27 +08:00
|
|
|
// SettingsColumns holds the columns for the "settings" table.
|
|
|
|
|
SettingsColumns = []*schema.Column{
|
|
|
|
|
{Name: "id", Type: field.TypeInt64, Increment: true},
|
|
|
|
|
{Name: "key", Type: field.TypeString, Unique: true, Size: 100},
|
|
|
|
|
{Name: "value", Type: field.TypeString, SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
}
|
|
|
|
|
// SettingsTable holds the schema information for the "settings" table.
|
|
|
|
|
SettingsTable = &schema.Table{
|
|
|
|
|
Name: "settings",
|
|
|
|
|
Columns: SettingsColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{SettingsColumns[0]},
|
2025-12-31 14:11:57 +08:00
|
|
|
}
|
2026-04-10 21:08:51 +08:00
|
|
|
// SubscriptionPlansColumns holds the columns for the "subscription_plans" table.
|
|
|
|
|
SubscriptionPlansColumns = []*schema.Column{
|
|
|
|
|
{Name: "id", Type: field.TypeInt64, Increment: true},
|
|
|
|
|
{Name: "group_id", Type: field.TypeInt64},
|
|
|
|
|
{Name: "name", Type: field.TypeString, Size: 100},
|
|
|
|
|
{Name: "description", Type: field.TypeString, Default: "", SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "price", Type: field.TypeFloat64, SchemaType: map[string]string{"postgres": "decimal(20,2)"}},
|
|
|
|
|
{Name: "original_price", Type: field.TypeFloat64, Nullable: true, SchemaType: map[string]string{"postgres": "decimal(20,2)"}},
|
|
|
|
|
{Name: "validity_days", Type: field.TypeInt, Default: 30},
|
|
|
|
|
{Name: "validity_unit", Type: field.TypeString, Size: 10, Default: "day"},
|
|
|
|
|
{Name: "features", Type: field.TypeString, Default: "", SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "product_name", Type: field.TypeString, Size: 100, Default: ""},
|
|
|
|
|
{Name: "for_sale", Type: field.TypeBool, Default: true},
|
|
|
|
|
{Name: "sort_order", Type: field.TypeInt, Default: 0},
|
|
|
|
|
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
}
|
|
|
|
|
// SubscriptionPlansTable holds the schema information for the "subscription_plans" table.
|
|
|
|
|
SubscriptionPlansTable = &schema.Table{
|
|
|
|
|
Name: "subscription_plans",
|
|
|
|
|
Columns: SubscriptionPlansColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{SubscriptionPlansColumns[0]},
|
|
|
|
|
Indexes: []*schema.Index{
|
|
|
|
|
{
|
|
|
|
|
Name: "subscriptionplan_group_id",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{SubscriptionPlansColumns[1]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "subscriptionplan_for_sale",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{SubscriptionPlansColumns[10]},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
2026-03-27 14:23:28 +08:00
|
|
|
// TLSFingerprintProfilesColumns holds the columns for the "tls_fingerprint_profiles" table.
|
|
|
|
|
TLSFingerprintProfilesColumns = []*schema.Column{
|
|
|
|
|
{Name: "id", Type: field.TypeInt64, Increment: true},
|
|
|
|
|
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "name", Type: field.TypeString, Unique: true, Size: 100},
|
|
|
|
|
{Name: "description", Type: field.TypeString, Nullable: true, Size: 2147483647},
|
|
|
|
|
{Name: "enable_grease", Type: field.TypeBool, Default: false},
|
|
|
|
|
{Name: "cipher_suites", Type: field.TypeJSON, Nullable: true, SchemaType: map[string]string{"postgres": "jsonb"}},
|
|
|
|
|
{Name: "curves", Type: field.TypeJSON, Nullable: true, SchemaType: map[string]string{"postgres": "jsonb"}},
|
|
|
|
|
{Name: "point_formats", Type: field.TypeJSON, Nullable: true, SchemaType: map[string]string{"postgres": "jsonb"}},
|
|
|
|
|
{Name: "signature_algorithms", Type: field.TypeJSON, Nullable: true, SchemaType: map[string]string{"postgres": "jsonb"}},
|
|
|
|
|
{Name: "alpn_protocols", Type: field.TypeJSON, Nullable: true, SchemaType: map[string]string{"postgres": "jsonb"}},
|
|
|
|
|
{Name: "supported_versions", Type: field.TypeJSON, Nullable: true, SchemaType: map[string]string{"postgres": "jsonb"}},
|
|
|
|
|
{Name: "key_share_groups", Type: field.TypeJSON, Nullable: true, SchemaType: map[string]string{"postgres": "jsonb"}},
|
|
|
|
|
{Name: "psk_modes", Type: field.TypeJSON, Nullable: true, SchemaType: map[string]string{"postgres": "jsonb"}},
|
|
|
|
|
{Name: "extensions", Type: field.TypeJSON, Nullable: true, SchemaType: map[string]string{"postgres": "jsonb"}},
|
|
|
|
|
}
|
|
|
|
|
// TLSFingerprintProfilesTable holds the schema information for the "tls_fingerprint_profiles" table.
|
|
|
|
|
TLSFingerprintProfilesTable = &schema.Table{
|
|
|
|
|
Name: "tls_fingerprint_profiles",
|
|
|
|
|
Columns: TLSFingerprintProfilesColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{TLSFingerprintProfilesColumns[0]},
|
|
|
|
|
}
|
2026-01-18 14:18:28 +08:00
|
|
|
// UsageCleanupTasksColumns holds the columns for the "usage_cleanup_tasks" table.
|
|
|
|
|
UsageCleanupTasksColumns = []*schema.Column{
|
|
|
|
|
{Name: "id", Type: field.TypeInt64, Increment: true},
|
|
|
|
|
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "status", Type: field.TypeString, Size: 20},
|
|
|
|
|
{Name: "filters", Type: field.TypeJSON},
|
|
|
|
|
{Name: "created_by", Type: field.TypeInt64},
|
|
|
|
|
{Name: "deleted_rows", Type: field.TypeInt64, Default: 0},
|
|
|
|
|
{Name: "error_message", Type: field.TypeString, Nullable: true},
|
|
|
|
|
{Name: "canceled_by", Type: field.TypeInt64, Nullable: true},
|
|
|
|
|
{Name: "canceled_at", Type: field.TypeTime, Nullable: true},
|
|
|
|
|
{Name: "started_at", Type: field.TypeTime, Nullable: true},
|
|
|
|
|
{Name: "finished_at", Type: field.TypeTime, Nullable: true},
|
|
|
|
|
}
|
|
|
|
|
// UsageCleanupTasksTable holds the schema information for the "usage_cleanup_tasks" table.
|
|
|
|
|
UsageCleanupTasksTable = &schema.Table{
|
|
|
|
|
Name: "usage_cleanup_tasks",
|
|
|
|
|
Columns: UsageCleanupTasksColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{UsageCleanupTasksColumns[0]},
|
|
|
|
|
Indexes: []*schema.Index{
|
|
|
|
|
{
|
|
|
|
|
Name: "usagecleanuptask_status_created_at",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{UsageCleanupTasksColumns[3], UsageCleanupTasksColumns[1]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "usagecleanuptask_created_at",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{UsageCleanupTasksColumns[1]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "usagecleanuptask_canceled_at",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{UsageCleanupTasksColumns[9]},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
2025-12-31 14:11:57 +08:00
|
|
|
// UsageLogsColumns holds the columns for the "usage_logs" table.
|
|
|
|
|
UsageLogsColumns = []*schema.Column{
|
|
|
|
|
{Name: "id", Type: field.TypeInt64, Increment: true},
|
|
|
|
|
{Name: "request_id", Type: field.TypeString, Size: 64},
|
|
|
|
|
{Name: "model", Type: field.TypeString, Size: 100},
|
2026-03-21 01:21:21 +08:00
|
|
|
{Name: "requested_model", Type: field.TypeString, Nullable: true, Size: 100},
|
2026-03-17 19:25:17 +08:00
|
|
|
{Name: "upstream_model", Type: field.TypeString, Nullable: true, Size: 100},
|
2026-03-30 22:13:16 +08:00
|
|
|
{Name: "channel_id", Type: field.TypeInt64, Nullable: true},
|
|
|
|
|
{Name: "model_mapping_chain", Type: field.TypeString, Nullable: true, Size: 500},
|
|
|
|
|
{Name: "billing_tier", Type: field.TypeString, Nullable: true, Size: 50},
|
|
|
|
|
{Name: "billing_mode", Type: field.TypeString, Nullable: true, Size: 20},
|
2025-12-31 14:11:57 +08:00
|
|
|
{Name: "input_tokens", Type: field.TypeInt, Default: 0},
|
|
|
|
|
{Name: "output_tokens", Type: field.TypeInt, Default: 0},
|
|
|
|
|
{Name: "cache_creation_tokens", Type: field.TypeInt, Default: 0},
|
|
|
|
|
{Name: "cache_read_tokens", Type: field.TypeInt, Default: 0},
|
|
|
|
|
{Name: "cache_creation_5m_tokens", Type: field.TypeInt, Default: 0},
|
|
|
|
|
{Name: "cache_creation_1h_tokens", Type: field.TypeInt, Default: 0},
|
|
|
|
|
{Name: "input_cost", Type: field.TypeFloat64, Default: 0, SchemaType: map[string]string{"postgres": "decimal(20,10)"}},
|
|
|
|
|
{Name: "output_cost", Type: field.TypeFloat64, Default: 0, SchemaType: map[string]string{"postgres": "decimal(20,10)"}},
|
|
|
|
|
{Name: "cache_creation_cost", Type: field.TypeFloat64, Default: 0, SchemaType: map[string]string{"postgres": "decimal(20,10)"}},
|
|
|
|
|
{Name: "cache_read_cost", Type: field.TypeFloat64, Default: 0, SchemaType: map[string]string{"postgres": "decimal(20,10)"}},
|
|
|
|
|
{Name: "total_cost", Type: field.TypeFloat64, Default: 0, SchemaType: map[string]string{"postgres": "decimal(20,10)"}},
|
|
|
|
|
{Name: "actual_cost", Type: field.TypeFloat64, Default: 0, SchemaType: map[string]string{"postgres": "decimal(20,10)"}},
|
|
|
|
|
{Name: "rate_multiplier", Type: field.TypeFloat64, Default: 1, SchemaType: map[string]string{"postgres": "decimal(10,4)"}},
|
2026-01-14 16:12:08 +08:00
|
|
|
{Name: "account_rate_multiplier", Type: field.TypeFloat64, Nullable: true, SchemaType: map[string]string{"postgres": "decimal(10,4)"}},
|
2025-12-31 14:11:57 +08:00
|
|
|
{Name: "billing_type", Type: field.TypeInt8, Default: 0},
|
|
|
|
|
{Name: "stream", Type: field.TypeBool, Default: false},
|
|
|
|
|
{Name: "duration_ms", Type: field.TypeInt, Nullable: true},
|
|
|
|
|
{Name: "first_token_ms", Type: field.TypeInt, Nullable: true},
|
2026-01-06 16:23:56 +08:00
|
|
|
{Name: "user_agent", Type: field.TypeString, Nullable: true, Size: 512},
|
2026-01-09 21:59:32 +08:00
|
|
|
{Name: "ip_address", Type: field.TypeString, Nullable: true, Size: 45},
|
2026-01-05 17:07:29 +08:00
|
|
|
{Name: "image_count", Type: field.TypeInt, Default: 0},
|
|
|
|
|
{Name: "image_size", Type: field.TypeString, Nullable: true, Size: 10},
|
2026-02-17 11:22:08 +03:00
|
|
|
{Name: "cache_ttl_overridden", Type: field.TypeBool, Default: false},
|
2025-12-31 14:11:57 +08:00
|
|
|
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
2026-01-03 06:37:08 -08:00
|
|
|
{Name: "api_key_id", Type: field.TypeInt64},
|
2026-01-04 19:27:53 +08:00
|
|
|
{Name: "account_id", Type: field.TypeInt64},
|
2025-12-31 14:11:57 +08:00
|
|
|
{Name: "group_id", Type: field.TypeInt64, Nullable: true},
|
|
|
|
|
{Name: "user_id", Type: field.TypeInt64},
|
|
|
|
|
{Name: "subscription_id", Type: field.TypeInt64, Nullable: true},
|
|
|
|
|
}
|
|
|
|
|
// UsageLogsTable holds the schema information for the "usage_logs" table.
|
|
|
|
|
UsageLogsTable = &schema.Table{
|
|
|
|
|
Name: "usage_logs",
|
|
|
|
|
Columns: UsageLogsColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{UsageLogsColumns[0]},
|
|
|
|
|
ForeignKeys: []*schema.ForeignKey{
|
|
|
|
|
{
|
2026-01-04 19:27:53 +08:00
|
|
|
Symbol: "usage_logs_api_keys_usage_logs",
|
2026-04-05 17:19:07 +08:00
|
|
|
Columns: []*schema.Column{UsageLogsColumns[33]},
|
2026-01-04 19:27:53 +08:00
|
|
|
RefColumns: []*schema.Column{APIKeysColumns[0]},
|
2025-12-31 14:11:57 +08:00
|
|
|
OnDelete: schema.NoAction,
|
|
|
|
|
},
|
|
|
|
|
{
|
2026-01-04 19:27:53 +08:00
|
|
|
Symbol: "usage_logs_accounts_usage_logs",
|
2026-04-05 17:19:07 +08:00
|
|
|
Columns: []*schema.Column{UsageLogsColumns[34]},
|
2026-01-04 19:27:53 +08:00
|
|
|
RefColumns: []*schema.Column{AccountsColumns[0]},
|
2025-12-31 14:11:57 +08:00
|
|
|
OnDelete: schema.NoAction,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Symbol: "usage_logs_groups_usage_logs",
|
2026-04-05 17:19:07 +08:00
|
|
|
Columns: []*schema.Column{UsageLogsColumns[35]},
|
2025-12-31 14:11:57 +08:00
|
|
|
RefColumns: []*schema.Column{GroupsColumns[0]},
|
|
|
|
|
OnDelete: schema.SetNull,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Symbol: "usage_logs_users_usage_logs",
|
2026-04-05 17:19:07 +08:00
|
|
|
Columns: []*schema.Column{UsageLogsColumns[36]},
|
2025-12-31 14:11:57 +08:00
|
|
|
RefColumns: []*schema.Column{UsersColumns[0]},
|
|
|
|
|
OnDelete: schema.NoAction,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Symbol: "usage_logs_user_subscriptions_usage_logs",
|
2026-04-05 17:19:07 +08:00
|
|
|
Columns: []*schema.Column{UsageLogsColumns[37]},
|
2025-12-31 14:11:57 +08:00
|
|
|
RefColumns: []*schema.Column{UserSubscriptionsColumns[0]},
|
|
|
|
|
OnDelete: schema.SetNull,
|
|
|
|
|
},
|
|
|
|
|
},
|
2025-12-29 10:03:27 +08:00
|
|
|
Indexes: []*schema.Index{
|
|
|
|
|
{
|
2025-12-31 14:11:57 +08:00
|
|
|
Name: "usagelog_user_id",
|
|
|
|
|
Unique: false,
|
2026-04-05 17:19:07 +08:00
|
|
|
Columns: []*schema.Column{UsageLogsColumns[36]},
|
2025-12-31 14:11:57 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "usagelog_api_key_id",
|
|
|
|
|
Unique: false,
|
2026-04-05 17:19:07 +08:00
|
|
|
Columns: []*schema.Column{UsageLogsColumns[33]},
|
2025-12-31 14:11:57 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "usagelog_account_id",
|
|
|
|
|
Unique: false,
|
2026-04-05 17:19:07 +08:00
|
|
|
Columns: []*schema.Column{UsageLogsColumns[34]},
|
2025-12-31 14:11:57 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "usagelog_group_id",
|
|
|
|
|
Unique: false,
|
2026-04-05 17:19:07 +08:00
|
|
|
Columns: []*schema.Column{UsageLogsColumns[35]},
|
2025-12-31 14:11:57 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "usagelog_subscription_id",
|
|
|
|
|
Unique: false,
|
2026-04-05 17:19:07 +08:00
|
|
|
Columns: []*schema.Column{UsageLogsColumns[37]},
|
2025-12-31 14:11:57 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "usagelog_created_at",
|
|
|
|
|
Unique: false,
|
2026-04-05 17:19:07 +08:00
|
|
|
Columns: []*schema.Column{UsageLogsColumns[32]},
|
2025-12-31 14:11:57 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "usagelog_model",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{UsageLogsColumns[2]},
|
|
|
|
|
},
|
2026-03-21 01:21:21 +08:00
|
|
|
{
|
|
|
|
|
Name: "usagelog_requested_model",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{UsageLogsColumns[3]},
|
|
|
|
|
},
|
2025-12-31 14:11:57 +08:00
|
|
|
{
|
|
|
|
|
Name: "usagelog_request_id",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{UsageLogsColumns[1]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "usagelog_user_id_created_at",
|
|
|
|
|
Unique: false,
|
2026-04-05 17:19:07 +08:00
|
|
|
Columns: []*schema.Column{UsageLogsColumns[36], UsageLogsColumns[32]},
|
2025-12-31 14:11:57 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "usagelog_api_key_id_created_at",
|
|
|
|
|
Unique: false,
|
2026-04-05 17:19:07 +08:00
|
|
|
Columns: []*schema.Column{UsageLogsColumns[33], UsageLogsColumns[32]},
|
2025-12-29 10:03:27 +08:00
|
|
|
},
|
2026-02-28 15:01:20 +08:00
|
|
|
{
|
|
|
|
|
Name: "usagelog_group_id_created_at",
|
|
|
|
|
Unique: false,
|
2026-04-05 17:19:07 +08:00
|
|
|
Columns: []*schema.Column{UsageLogsColumns[35], UsageLogsColumns[32]},
|
2026-02-28 15:01:20 +08:00
|
|
|
},
|
2025-12-29 10:03:27 +08:00
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
// UsersColumns holds the columns for the "users" table.
|
|
|
|
|
UsersColumns = []*schema.Column{
|
|
|
|
|
{Name: "id", Type: field.TypeInt64, Increment: true},
|
|
|
|
|
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "deleted_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
2025-12-31 16:37:18 +08:00
|
|
|
{Name: "email", Type: field.TypeString, Size: 255},
|
2025-12-29 10:03:27 +08:00
|
|
|
{Name: "password_hash", Type: field.TypeString, Size: 255},
|
|
|
|
|
{Name: "role", Type: field.TypeString, Size: 20, Default: "user"},
|
|
|
|
|
{Name: "balance", Type: field.TypeFloat64, Default: 0, SchemaType: map[string]string{"postgres": "decimal(20,8)"}},
|
|
|
|
|
{Name: "concurrency", Type: field.TypeInt, Default: 5},
|
|
|
|
|
{Name: "status", Type: field.TypeString, Size: 20, Default: "active"},
|
|
|
|
|
{Name: "username", Type: field.TypeString, Size: 100, Default: ""},
|
|
|
|
|
{Name: "notes", Type: field.TypeString, Default: "", SchemaType: map[string]string{"postgres": "text"}},
|
2026-01-26 08:45:43 +08:00
|
|
|
{Name: "totp_secret_encrypted", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "totp_enabled", Type: field.TypeBool, Default: false},
|
|
|
|
|
{Name: "totp_enabled_at", Type: field.TypeTime, Nullable: true},
|
2026-04-20 20:21:02 +08:00
|
|
|
{Name: "signup_source", Type: field.TypeString, Default: "email"},
|
2026-04-20 17:39:57 +08:00
|
|
|
{Name: "last_login_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "last_active_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
2026-04-12 02:48:57 +08:00
|
|
|
{Name: "balance_notify_enabled", Type: field.TypeBool, Default: true},
|
2026-04-12 13:53:02 +08:00
|
|
|
{Name: "balance_notify_threshold_type", Type: field.TypeString, Default: "fixed"},
|
2026-04-12 02:48:57 +08:00
|
|
|
{Name: "balance_notify_threshold", Type: field.TypeFloat64, Nullable: true, SchemaType: map[string]string{"postgres": "decimal(20,8)"}},
|
|
|
|
|
{Name: "balance_notify_extra_emails", Type: field.TypeString, Default: "[]", SchemaType: map[string]string{"postgres": "text"}},
|
2026-04-12 13:53:02 +08:00
|
|
|
{Name: "total_recharged", Type: field.TypeFloat64, Default: 0, SchemaType: map[string]string{"postgres": "decimal(20,8)"}},
|
2026-04-23 03:33:52 +08:00
|
|
|
{Name: "rpm_limit", Type: field.TypeInt, Default: 0},
|
2025-12-29 10:03:27 +08:00
|
|
|
}
|
|
|
|
|
// UsersTable holds the schema information for the "users" table.
|
|
|
|
|
UsersTable = &schema.Table{
|
|
|
|
|
Name: "users",
|
|
|
|
|
Columns: UsersColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{UsersColumns[0]},
|
|
|
|
|
Indexes: []*schema.Index{
|
|
|
|
|
{
|
|
|
|
|
Name: "user_status",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{UsersColumns[9]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "user_deleted_at",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{UsersColumns[3]},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
// UserAllowedGroupsColumns holds the columns for the "user_allowed_groups" table.
|
|
|
|
|
UserAllowedGroupsColumns = []*schema.Column{
|
2025-12-31 14:11:57 +08:00
|
|
|
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
2025-12-29 10:03:27 +08:00
|
|
|
{Name: "user_id", Type: field.TypeInt64},
|
|
|
|
|
{Name: "group_id", Type: field.TypeInt64},
|
|
|
|
|
}
|
|
|
|
|
// UserAllowedGroupsTable holds the schema information for the "user_allowed_groups" table.
|
|
|
|
|
UserAllowedGroupsTable = &schema.Table{
|
|
|
|
|
Name: "user_allowed_groups",
|
|
|
|
|
Columns: UserAllowedGroupsColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{UserAllowedGroupsColumns[1], UserAllowedGroupsColumns[2]},
|
|
|
|
|
ForeignKeys: []*schema.ForeignKey{
|
|
|
|
|
{
|
|
|
|
|
Symbol: "user_allowed_groups_users_user",
|
|
|
|
|
Columns: []*schema.Column{UserAllowedGroupsColumns[1]},
|
|
|
|
|
RefColumns: []*schema.Column{UsersColumns[0]},
|
|
|
|
|
OnDelete: schema.NoAction,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Symbol: "user_allowed_groups_groups_group",
|
|
|
|
|
Columns: []*schema.Column{UserAllowedGroupsColumns[2]},
|
|
|
|
|
RefColumns: []*schema.Column{GroupsColumns[0]},
|
|
|
|
|
OnDelete: schema.NoAction,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
Indexes: []*schema.Index{
|
|
|
|
|
{
|
|
|
|
|
Name: "userallowedgroup_group_id",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{UserAllowedGroupsColumns[2]},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
2026-01-01 18:58:34 +08:00
|
|
|
// UserAttributeDefinitionsColumns holds the columns for the "user_attribute_definitions" table.
|
|
|
|
|
UserAttributeDefinitionsColumns = []*schema.Column{
|
|
|
|
|
{Name: "id", Type: field.TypeInt64, Increment: true},
|
|
|
|
|
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "deleted_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "key", Type: field.TypeString, Size: 100},
|
|
|
|
|
{Name: "name", Type: field.TypeString, Size: 255},
|
|
|
|
|
{Name: "description", Type: field.TypeString, Default: "", SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "type", Type: field.TypeString, Size: 20},
|
|
|
|
|
{Name: "options", Type: field.TypeJSON, SchemaType: map[string]string{"postgres": "jsonb"}},
|
|
|
|
|
{Name: "required", Type: field.TypeBool, Default: false},
|
|
|
|
|
{Name: "validation", Type: field.TypeJSON, SchemaType: map[string]string{"postgres": "jsonb"}},
|
|
|
|
|
{Name: "placeholder", Type: field.TypeString, Size: 255, Default: ""},
|
|
|
|
|
{Name: "display_order", Type: field.TypeInt, Default: 0},
|
|
|
|
|
{Name: "enabled", Type: field.TypeBool, Default: true},
|
|
|
|
|
}
|
|
|
|
|
// UserAttributeDefinitionsTable holds the schema information for the "user_attribute_definitions" table.
|
|
|
|
|
UserAttributeDefinitionsTable = &schema.Table{
|
|
|
|
|
Name: "user_attribute_definitions",
|
|
|
|
|
Columns: UserAttributeDefinitionsColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{UserAttributeDefinitionsColumns[0]},
|
|
|
|
|
Indexes: []*schema.Index{
|
|
|
|
|
{
|
|
|
|
|
Name: "userattributedefinition_key",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{UserAttributeDefinitionsColumns[4]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "userattributedefinition_enabled",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{UserAttributeDefinitionsColumns[13]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "userattributedefinition_display_order",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{UserAttributeDefinitionsColumns[12]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "userattributedefinition_deleted_at",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{UserAttributeDefinitionsColumns[3]},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
// UserAttributeValuesColumns holds the columns for the "user_attribute_values" table.
|
|
|
|
|
UserAttributeValuesColumns = []*schema.Column{
|
|
|
|
|
{Name: "id", Type: field.TypeInt64, Increment: true},
|
|
|
|
|
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "value", Type: field.TypeString, Size: 2147483647, Default: ""},
|
|
|
|
|
{Name: "user_id", Type: field.TypeInt64},
|
|
|
|
|
{Name: "attribute_id", Type: field.TypeInt64},
|
|
|
|
|
}
|
|
|
|
|
// UserAttributeValuesTable holds the schema information for the "user_attribute_values" table.
|
|
|
|
|
UserAttributeValuesTable = &schema.Table{
|
|
|
|
|
Name: "user_attribute_values",
|
|
|
|
|
Columns: UserAttributeValuesColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{UserAttributeValuesColumns[0]},
|
|
|
|
|
ForeignKeys: []*schema.ForeignKey{
|
|
|
|
|
{
|
|
|
|
|
Symbol: "user_attribute_values_users_attribute_values",
|
|
|
|
|
Columns: []*schema.Column{UserAttributeValuesColumns[4]},
|
|
|
|
|
RefColumns: []*schema.Column{UsersColumns[0]},
|
|
|
|
|
OnDelete: schema.NoAction,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Symbol: "user_attribute_values_user_attribute_definitions_values",
|
|
|
|
|
Columns: []*schema.Column{UserAttributeValuesColumns[5]},
|
|
|
|
|
RefColumns: []*schema.Column{UserAttributeDefinitionsColumns[0]},
|
|
|
|
|
OnDelete: schema.NoAction,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
Indexes: []*schema.Index{
|
|
|
|
|
{
|
|
|
|
|
Name: "userattributevalue_user_id_attribute_id",
|
|
|
|
|
Unique: true,
|
|
|
|
|
Columns: []*schema.Column{UserAttributeValuesColumns[4], UserAttributeValuesColumns[5]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "userattributevalue_attribute_id",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{UserAttributeValuesColumns[5]},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
2025-12-29 10:03:27 +08:00
|
|
|
// UserSubscriptionsColumns holds the columns for the "user_subscriptions" table.
|
|
|
|
|
UserSubscriptionsColumns = []*schema.Column{
|
|
|
|
|
{Name: "id", Type: field.TypeInt64, Increment: true},
|
|
|
|
|
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
2025-12-31 14:11:57 +08:00
|
|
|
{Name: "deleted_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
2025-12-29 10:03:27 +08:00
|
|
|
{Name: "starts_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "expires_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "status", Type: field.TypeString, Size: 20, Default: "active"},
|
|
|
|
|
{Name: "daily_window_start", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "weekly_window_start", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "monthly_window_start", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "daily_usage_usd", Type: field.TypeFloat64, Default: 0, SchemaType: map[string]string{"postgres": "decimal(20,10)"}},
|
|
|
|
|
{Name: "weekly_usage_usd", Type: field.TypeFloat64, Default: 0, SchemaType: map[string]string{"postgres": "decimal(20,10)"}},
|
|
|
|
|
{Name: "monthly_usage_usd", Type: field.TypeFloat64, Default: 0, SchemaType: map[string]string{"postgres": "decimal(20,10)"}},
|
|
|
|
|
{Name: "assigned_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
|
|
|
|
{Name: "notes", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "text"}},
|
|
|
|
|
{Name: "group_id", Type: field.TypeInt64},
|
|
|
|
|
{Name: "user_id", Type: field.TypeInt64},
|
|
|
|
|
{Name: "assigned_by", Type: field.TypeInt64, Nullable: true},
|
|
|
|
|
}
|
|
|
|
|
// UserSubscriptionsTable holds the schema information for the "user_subscriptions" table.
|
|
|
|
|
UserSubscriptionsTable = &schema.Table{
|
|
|
|
|
Name: "user_subscriptions",
|
|
|
|
|
Columns: UserSubscriptionsColumns,
|
|
|
|
|
PrimaryKey: []*schema.Column{UserSubscriptionsColumns[0]},
|
|
|
|
|
ForeignKeys: []*schema.ForeignKey{
|
|
|
|
|
{
|
|
|
|
|
Symbol: "user_subscriptions_groups_subscriptions",
|
2025-12-31 14:11:57 +08:00
|
|
|
Columns: []*schema.Column{UserSubscriptionsColumns[15]},
|
2025-12-29 10:03:27 +08:00
|
|
|
RefColumns: []*schema.Column{GroupsColumns[0]},
|
|
|
|
|
OnDelete: schema.NoAction,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Symbol: "user_subscriptions_users_subscriptions",
|
2025-12-31 14:11:57 +08:00
|
|
|
Columns: []*schema.Column{UserSubscriptionsColumns[16]},
|
2025-12-29 10:03:27 +08:00
|
|
|
RefColumns: []*schema.Column{UsersColumns[0]},
|
|
|
|
|
OnDelete: schema.NoAction,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Symbol: "user_subscriptions_users_assigned_subscriptions",
|
2025-12-31 14:11:57 +08:00
|
|
|
Columns: []*schema.Column{UserSubscriptionsColumns[17]},
|
2025-12-29 10:03:27 +08:00
|
|
|
RefColumns: []*schema.Column{UsersColumns[0]},
|
|
|
|
|
OnDelete: schema.SetNull,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
Indexes: []*schema.Index{
|
|
|
|
|
{
|
|
|
|
|
Name: "usersubscription_user_id",
|
|
|
|
|
Unique: false,
|
2025-12-31 14:11:57 +08:00
|
|
|
Columns: []*schema.Column{UserSubscriptionsColumns[16]},
|
2025-12-29 10:03:27 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "usersubscription_group_id",
|
|
|
|
|
Unique: false,
|
2025-12-31 14:11:57 +08:00
|
|
|
Columns: []*schema.Column{UserSubscriptionsColumns[15]},
|
2025-12-29 10:03:27 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "usersubscription_status",
|
|
|
|
|
Unique: false,
|
2025-12-31 14:11:57 +08:00
|
|
|
Columns: []*schema.Column{UserSubscriptionsColumns[6]},
|
2025-12-29 10:03:27 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "usersubscription_expires_at",
|
|
|
|
|
Unique: false,
|
2025-12-31 14:11:57 +08:00
|
|
|
Columns: []*schema.Column{UserSubscriptionsColumns[5]},
|
2025-12-29 10:03:27 +08:00
|
|
|
},
|
2026-02-28 15:01:20 +08:00
|
|
|
{
|
|
|
|
|
Name: "usersubscription_user_id_status_expires_at",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{UserSubscriptionsColumns[16], UserSubscriptionsColumns[6], UserSubscriptionsColumns[5]},
|
|
|
|
|
},
|
2025-12-29 10:03:27 +08:00
|
|
|
{
|
|
|
|
|
Name: "usersubscription_assigned_by",
|
|
|
|
|
Unique: false,
|
2025-12-31 14:11:57 +08:00
|
|
|
Columns: []*schema.Column{UserSubscriptionsColumns[17]},
|
2025-12-29 10:03:27 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "usersubscription_user_id_group_id",
|
2025-12-31 16:37:18 +08:00
|
|
|
Unique: false,
|
2025-12-31 14:11:57 +08:00
|
|
|
Columns: []*schema.Column{UserSubscriptionsColumns[16], UserSubscriptionsColumns[15]},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "usersubscription_deleted_at",
|
|
|
|
|
Unique: false,
|
|
|
|
|
Columns: []*schema.Column{UserSubscriptionsColumns[3]},
|
2025-12-29 10:03:27 +08:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
// Tables holds all the tables in the schema.
|
|
|
|
|
Tables = []*schema.Table{
|
2026-01-04 19:27:53 +08:00
|
|
|
APIKeysTable,
|
2025-12-29 10:03:27 +08:00
|
|
|
AccountsTable,
|
|
|
|
|
AccountGroupsTable,
|
2026-01-30 16:45:04 +08:00
|
|
|
AnnouncementsTable,
|
|
|
|
|
AnnouncementReadsTable,
|
2026-04-20 17:39:57 +08:00
|
|
|
AuthIdentitiesTable,
|
|
|
|
|
AuthIdentityChannelsTable,
|
2026-04-20 20:21:02 +08:00
|
|
|
ChannelMonitorsTable,
|
2026-04-21 10:10:56 +08:00
|
|
|
ChannelMonitorDailyRollupsTable,
|
2026-04-20 20:21:02 +08:00
|
|
|
ChannelMonitorHistoriesTable,
|
feat(channel-monitor): request templates with snapshot apply + headers/body override
Problem:
Upstream channels can reject monitor probes based on client fingerprint
(e.g. "only Claude Code clients allowed"). The monitor had no way to
customize the outgoing request to bypass such restrictions.
Solution:
Introduce reusable request templates that carry extra_headers plus an
optional body override; monitors reference a template and receive a
snapshot copy on apply. Template edits do NOT auto-propagate — users
must click "apply to associated monitors" to refresh snapshots, so a
bad template edit cannot instantly break all production monitors.
Data model (migration 112):
- channel_monitor_request_templates: id, name, provider, description,
extra_headers jsonb, body_override_mode ('off'|'merge'|'replace'),
body_override jsonb. Unique (provider, name).
- channel_monitors: +template_id (FK, ON DELETE SET NULL), +extra_headers,
+body_override_mode, +body_override (the three runtime snapshot fields).
Checker (channel_monitor_checker.go):
- callProvider + runCheckForModel accept a CheckOptions carrying the
snapshot fields. mergeHeaders applies user headers on top of adapter
defaults (forbidden list: Host / Content-Length / Transfer-Encoding /
Connection / Content-Encoding).
- buildRequestBody:
off -> adapter default body
merge -> shallow-merge over default; per-provider deny list
(model/messages/contents) protects the challenge contract
replace -> user body verbatim
- Replace mode skips challenge validation; instead HTTP 2xx + non-empty
extracted response text = operational, empty = failed.
- 4 new unit tests cover all three modes + replace/empty-response case.
Admin API:
- /admin/channel-monitor-templates CRUD + /:id/apply (overwrite snapshot
on all template_id=id monitors, returns affected count).
- channel_monitor request/response DTOs gain the 4 new fields.
Frontend:
- channelMonitorTemplate.ts API client.
- MonitorAdvancedRequestConfig.vue shared component for headers textarea
+ body mode radio + body JSON editor; used by both template and monitor
forms.
- MonitorTemplateManagerDialog.vue: provider tabs, list/create/edit/
delete/apply, live "associated monitors" count per row.
- MonitorFiltersBar: new 模板管理 button next to 新增监控.
- MonitorFormDialog: collapsible 高级 section with template dropdown
(filtered by form.provider, clears on provider change) + embedded
AdvancedRequestConfig. Picking a template copies its fields into the
form (snapshot semantics mirrored on the client).
- i18n zh/en entries for all new copy.
chore: bump version to 0.1.114.32
2026-04-21 14:14:49 +08:00
|
|
|
ChannelMonitorRequestTemplatesTable,
|
2026-02-05 21:52:54 +08:00
|
|
|
ErrorPassthroughRulesTable,
|
2025-12-29 10:03:27 +08:00
|
|
|
GroupsTable,
|
2026-02-28 15:01:20 +08:00
|
|
|
IdempotencyRecordsTable,
|
2026-04-20 17:39:57 +08:00
|
|
|
IdentityAdoptionDecisionsTable,
|
2026-04-10 21:08:51 +08:00
|
|
|
PaymentAuditLogsTable,
|
|
|
|
|
PaymentOrdersTable,
|
|
|
|
|
PaymentProviderInstancesTable,
|
2026-04-20 17:39:57 +08:00
|
|
|
PendingAuthSessionsTable,
|
2026-01-10 13:14:35 +08:00
|
|
|
PromoCodesTable,
|
|
|
|
|
PromoCodeUsagesTable,
|
2025-12-29 10:03:27 +08:00
|
|
|
ProxiesTable,
|
|
|
|
|
RedeemCodesTable,
|
2026-02-12 11:41:20 +08:00
|
|
|
SecuritySecretsTable,
|
2025-12-29 10:03:27 +08:00
|
|
|
SettingsTable,
|
2026-04-10 21:08:51 +08:00
|
|
|
SubscriptionPlansTable,
|
2026-03-27 14:23:28 +08:00
|
|
|
TLSFingerprintProfilesTable,
|
2026-01-18 14:18:28 +08:00
|
|
|
UsageCleanupTasksTable,
|
2025-12-31 14:11:57 +08:00
|
|
|
UsageLogsTable,
|
2025-12-29 10:03:27 +08:00
|
|
|
UsersTable,
|
|
|
|
|
UserAllowedGroupsTable,
|
2026-01-01 18:58:34 +08:00
|
|
|
UserAttributeDefinitionsTable,
|
|
|
|
|
UserAttributeValuesTable,
|
2025-12-29 10:03:27 +08:00
|
|
|
UserSubscriptionsTable,
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func init() {
|
2026-01-04 19:27:53 +08:00
|
|
|
APIKeysTable.ForeignKeys[0].RefTable = GroupsTable
|
|
|
|
|
APIKeysTable.ForeignKeys[1].RefTable = UsersTable
|
|
|
|
|
APIKeysTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "api_keys",
|
|
|
|
|
}
|
2025-12-31 14:11:57 +08:00
|
|
|
AccountsTable.ForeignKeys[0].RefTable = ProxiesTable
|
2025-12-29 10:03:27 +08:00
|
|
|
AccountsTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "accounts",
|
|
|
|
|
}
|
|
|
|
|
AccountGroupsTable.ForeignKeys[0].RefTable = AccountsTable
|
|
|
|
|
AccountGroupsTable.ForeignKeys[1].RefTable = GroupsTable
|
|
|
|
|
AccountGroupsTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "account_groups",
|
2026-01-30 16:45:04 +08:00
|
|
|
}
|
|
|
|
|
AnnouncementsTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "announcements",
|
|
|
|
|
}
|
|
|
|
|
AnnouncementReadsTable.ForeignKeys[0].RefTable = AnnouncementsTable
|
|
|
|
|
AnnouncementReadsTable.ForeignKeys[1].RefTable = UsersTable
|
|
|
|
|
AnnouncementReadsTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "announcement_reads",
|
2026-02-05 21:52:54 +08:00
|
|
|
}
|
2026-04-20 17:39:57 +08:00
|
|
|
AuthIdentitiesTable.ForeignKeys[0].RefTable = UsersTable
|
|
|
|
|
AuthIdentitiesTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "auth_identities",
|
|
|
|
|
}
|
|
|
|
|
AuthIdentityChannelsTable.ForeignKeys[0].RefTable = AuthIdentitiesTable
|
|
|
|
|
AuthIdentityChannelsTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "auth_identity_channels",
|
|
|
|
|
}
|
feat(channel-monitor): request templates with snapshot apply + headers/body override
Problem:
Upstream channels can reject monitor probes based on client fingerprint
(e.g. "only Claude Code clients allowed"). The monitor had no way to
customize the outgoing request to bypass such restrictions.
Solution:
Introduce reusable request templates that carry extra_headers plus an
optional body override; monitors reference a template and receive a
snapshot copy on apply. Template edits do NOT auto-propagate — users
must click "apply to associated monitors" to refresh snapshots, so a
bad template edit cannot instantly break all production monitors.
Data model (migration 112):
- channel_monitor_request_templates: id, name, provider, description,
extra_headers jsonb, body_override_mode ('off'|'merge'|'replace'),
body_override jsonb. Unique (provider, name).
- channel_monitors: +template_id (FK, ON DELETE SET NULL), +extra_headers,
+body_override_mode, +body_override (the three runtime snapshot fields).
Checker (channel_monitor_checker.go):
- callProvider + runCheckForModel accept a CheckOptions carrying the
snapshot fields. mergeHeaders applies user headers on top of adapter
defaults (forbidden list: Host / Content-Length / Transfer-Encoding /
Connection / Content-Encoding).
- buildRequestBody:
off -> adapter default body
merge -> shallow-merge over default; per-provider deny list
(model/messages/contents) protects the challenge contract
replace -> user body verbatim
- Replace mode skips challenge validation; instead HTTP 2xx + non-empty
extracted response text = operational, empty = failed.
- 4 new unit tests cover all three modes + replace/empty-response case.
Admin API:
- /admin/channel-monitor-templates CRUD + /:id/apply (overwrite snapshot
on all template_id=id monitors, returns affected count).
- channel_monitor request/response DTOs gain the 4 new fields.
Frontend:
- channelMonitorTemplate.ts API client.
- MonitorAdvancedRequestConfig.vue shared component for headers textarea
+ body mode radio + body JSON editor; used by both template and monitor
forms.
- MonitorTemplateManagerDialog.vue: provider tabs, list/create/edit/
delete/apply, live "associated monitors" count per row.
- MonitorFiltersBar: new 模板管理 button next to 新增监控.
- MonitorFormDialog: collapsible 高级 section with template dropdown
(filtered by form.provider, clears on provider change) + embedded
AdvancedRequestConfig. Picking a template copies its fields into the
form (snapshot semantics mirrored on the client).
- i18n zh/en entries for all new copy.
chore: bump version to 0.1.114.32
2026-04-21 14:14:49 +08:00
|
|
|
ChannelMonitorsTable.ForeignKeys[0].RefTable = ChannelMonitorRequestTemplatesTable
|
2026-04-20 20:21:02 +08:00
|
|
|
ChannelMonitorsTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "channel_monitors",
|
|
|
|
|
}
|
2026-04-21 10:10:56 +08:00
|
|
|
ChannelMonitorDailyRollupsTable.ForeignKeys[0].RefTable = ChannelMonitorsTable
|
|
|
|
|
ChannelMonitorDailyRollupsTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "channel_monitor_daily_rollups",
|
|
|
|
|
}
|
2026-04-20 20:21:02 +08:00
|
|
|
ChannelMonitorHistoriesTable.ForeignKeys[0].RefTable = ChannelMonitorsTable
|
|
|
|
|
ChannelMonitorHistoriesTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "channel_monitor_histories",
|
|
|
|
|
}
|
feat(channel-monitor): request templates with snapshot apply + headers/body override
Problem:
Upstream channels can reject monitor probes based on client fingerprint
(e.g. "only Claude Code clients allowed"). The monitor had no way to
customize the outgoing request to bypass such restrictions.
Solution:
Introduce reusable request templates that carry extra_headers plus an
optional body override; monitors reference a template and receive a
snapshot copy on apply. Template edits do NOT auto-propagate — users
must click "apply to associated monitors" to refresh snapshots, so a
bad template edit cannot instantly break all production monitors.
Data model (migration 112):
- channel_monitor_request_templates: id, name, provider, description,
extra_headers jsonb, body_override_mode ('off'|'merge'|'replace'),
body_override jsonb. Unique (provider, name).
- channel_monitors: +template_id (FK, ON DELETE SET NULL), +extra_headers,
+body_override_mode, +body_override (the three runtime snapshot fields).
Checker (channel_monitor_checker.go):
- callProvider + runCheckForModel accept a CheckOptions carrying the
snapshot fields. mergeHeaders applies user headers on top of adapter
defaults (forbidden list: Host / Content-Length / Transfer-Encoding /
Connection / Content-Encoding).
- buildRequestBody:
off -> adapter default body
merge -> shallow-merge over default; per-provider deny list
(model/messages/contents) protects the challenge contract
replace -> user body verbatim
- Replace mode skips challenge validation; instead HTTP 2xx + non-empty
extracted response text = operational, empty = failed.
- 4 new unit tests cover all three modes + replace/empty-response case.
Admin API:
- /admin/channel-monitor-templates CRUD + /:id/apply (overwrite snapshot
on all template_id=id monitors, returns affected count).
- channel_monitor request/response DTOs gain the 4 new fields.
Frontend:
- channelMonitorTemplate.ts API client.
- MonitorAdvancedRequestConfig.vue shared component for headers textarea
+ body mode radio + body JSON editor; used by both template and monitor
forms.
- MonitorTemplateManagerDialog.vue: provider tabs, list/create/edit/
delete/apply, live "associated monitors" count per row.
- MonitorFiltersBar: new 模板管理 button next to 新增监控.
- MonitorFormDialog: collapsible 高级 section with template dropdown
(filtered by form.provider, clears on provider change) + embedded
AdvancedRequestConfig. Picking a template copies its fields into the
form (snapshot semantics mirrored on the client).
- i18n zh/en entries for all new copy.
chore: bump version to 0.1.114.32
2026-04-21 14:14:49 +08:00
|
|
|
ChannelMonitorRequestTemplatesTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "channel_monitor_request_templates",
|
|
|
|
|
}
|
2026-02-05 21:52:54 +08:00
|
|
|
ErrorPassthroughRulesTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "error_passthrough_rules",
|
2025-12-29 10:03:27 +08:00
|
|
|
}
|
|
|
|
|
GroupsTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "groups",
|
|
|
|
|
}
|
2026-02-28 15:01:20 +08:00
|
|
|
IdempotencyRecordsTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "idempotency_records",
|
|
|
|
|
}
|
2026-04-20 17:39:57 +08:00
|
|
|
IdentityAdoptionDecisionsTable.ForeignKeys[0].RefTable = AuthIdentitiesTable
|
|
|
|
|
IdentityAdoptionDecisionsTable.ForeignKeys[1].RefTable = PendingAuthSessionsTable
|
|
|
|
|
IdentityAdoptionDecisionsTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "identity_adoption_decisions",
|
|
|
|
|
}
|
2026-04-10 21:08:51 +08:00
|
|
|
PaymentAuditLogsTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "payment_audit_logs",
|
|
|
|
|
}
|
|
|
|
|
PaymentOrdersTable.ForeignKeys[0].RefTable = UsersTable
|
|
|
|
|
PaymentOrdersTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "payment_orders",
|
|
|
|
|
}
|
|
|
|
|
PaymentProviderInstancesTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "payment_provider_instances",
|
|
|
|
|
}
|
2026-04-20 17:39:57 +08:00
|
|
|
PendingAuthSessionsTable.ForeignKeys[0].RefTable = UsersTable
|
|
|
|
|
PendingAuthSessionsTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "pending_auth_sessions",
|
|
|
|
|
}
|
2026-01-10 13:14:35 +08:00
|
|
|
PromoCodesTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "promo_codes",
|
|
|
|
|
}
|
|
|
|
|
PromoCodeUsagesTable.ForeignKeys[0].RefTable = PromoCodesTable
|
|
|
|
|
PromoCodeUsagesTable.ForeignKeys[1].RefTable = UsersTable
|
|
|
|
|
PromoCodeUsagesTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "promo_code_usages",
|
|
|
|
|
}
|
2025-12-29 10:03:27 +08:00
|
|
|
ProxiesTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "proxies",
|
|
|
|
|
}
|
|
|
|
|
RedeemCodesTable.ForeignKeys[0].RefTable = GroupsTable
|
|
|
|
|
RedeemCodesTable.ForeignKeys[1].RefTable = UsersTable
|
|
|
|
|
RedeemCodesTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "redeem_codes",
|
|
|
|
|
}
|
2026-02-12 11:41:20 +08:00
|
|
|
SecuritySecretsTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "security_secrets",
|
|
|
|
|
}
|
2025-12-29 10:03:27 +08:00
|
|
|
SettingsTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "settings",
|
|
|
|
|
}
|
2026-04-10 21:08:51 +08:00
|
|
|
SubscriptionPlansTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "subscription_plans",
|
|
|
|
|
}
|
2026-03-27 14:23:28 +08:00
|
|
|
TLSFingerprintProfilesTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "tls_fingerprint_profiles",
|
|
|
|
|
}
|
2026-01-18 14:18:28 +08:00
|
|
|
UsageCleanupTasksTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "usage_cleanup_tasks",
|
|
|
|
|
}
|
2026-01-04 19:27:53 +08:00
|
|
|
UsageLogsTable.ForeignKeys[0].RefTable = APIKeysTable
|
|
|
|
|
UsageLogsTable.ForeignKeys[1].RefTable = AccountsTable
|
2025-12-31 14:11:57 +08:00
|
|
|
UsageLogsTable.ForeignKeys[2].RefTable = GroupsTable
|
|
|
|
|
UsageLogsTable.ForeignKeys[3].RefTable = UsersTable
|
|
|
|
|
UsageLogsTable.ForeignKeys[4].RefTable = UserSubscriptionsTable
|
|
|
|
|
UsageLogsTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "usage_logs",
|
|
|
|
|
}
|
2025-12-29 10:03:27 +08:00
|
|
|
UsersTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "users",
|
|
|
|
|
}
|
|
|
|
|
UserAllowedGroupsTable.ForeignKeys[0].RefTable = UsersTable
|
|
|
|
|
UserAllowedGroupsTable.ForeignKeys[1].RefTable = GroupsTable
|
|
|
|
|
UserAllowedGroupsTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "user_allowed_groups",
|
|
|
|
|
}
|
2026-01-01 18:58:34 +08:00
|
|
|
UserAttributeDefinitionsTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "user_attribute_definitions",
|
|
|
|
|
}
|
|
|
|
|
UserAttributeValuesTable.ForeignKeys[0].RefTable = UsersTable
|
|
|
|
|
UserAttributeValuesTable.ForeignKeys[1].RefTable = UserAttributeDefinitionsTable
|
|
|
|
|
UserAttributeValuesTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "user_attribute_values",
|
|
|
|
|
}
|
2025-12-29 10:03:27 +08:00
|
|
|
UserSubscriptionsTable.ForeignKeys[0].RefTable = GroupsTable
|
|
|
|
|
UserSubscriptionsTable.ForeignKeys[1].RefTable = UsersTable
|
|
|
|
|
UserSubscriptionsTable.ForeignKeys[2].RefTable = UsersTable
|
|
|
|
|
UserSubscriptionsTable.Annotation = &entsql.Annotation{
|
|
|
|
|
Table: "user_subscriptions",
|
|
|
|
|
}
|
|
|
|
|
}
|