diff --git a/backend/ent/client.go b/backend/ent/client.go index 7cdb7394fd..0e8efec58e 100644 --- a/backend/ent/client.go +++ b/backend/ent/client.go @@ -4595,6 +4595,22 @@ func (c *ProxyClient) QueryAccounts(_m *Proxy) *AccountQuery { return query } +// QueryPrimaryProxies queries the primary_proxies edge of a Proxy. +func (c *ProxyClient) QueryPrimaryProxies(_m *Proxy) *ProxyQuery { + query := (&ProxyClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(proxy.Table, proxy.FieldID, id), + sqlgraph.To(proxy.Table, proxy.FieldID), + sqlgraph.Edge(sqlgraph.O2M, true, proxy.PrimaryProxiesTable, proxy.PrimaryProxiesColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + // QueryBackupProxy queries the backup_proxy edge of a Proxy. func (c *ProxyClient) QueryBackupProxy(_m *Proxy) *ProxyQuery { query := (&ProxyClient{config: c.config}).Query() @@ -4603,7 +4619,7 @@ func (c *ProxyClient) QueryBackupProxy(_m *Proxy) *ProxyQuery { step := sqlgraph.NewStep( sqlgraph.From(proxy.Table, proxy.FieldID, id), sqlgraph.To(proxy.Table, proxy.FieldID), - sqlgraph.Edge(sqlgraph.O2O, false, proxy.BackupProxyTable, proxy.BackupProxyColumn), + sqlgraph.Edge(sqlgraph.M2O, false, proxy.BackupProxyTable, proxy.BackupProxyColumn), ) fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) return fromV, nil diff --git a/backend/ent/migrate/schema.go b/backend/ent/migrate/schema.go index b6d6b8bf2f..0264fdd170 100644 --- a/backend/ent/migrate/schema.go +++ b/backend/ent/migrate/schema.go @@ -1413,7 +1413,7 @@ var ( {Name: "expires_at", Type: field.TypeTime, Nullable: true}, {Name: "fallback_mode", Type: field.TypeString, Size: 20, Default: "none"}, {Name: "expiry_warn_days", Type: field.TypeInt, Default: 7}, - {Name: "backup_proxy_id", Type: field.TypeInt64, Unique: true, Nullable: true}, + {Name: "backup_proxy_id", Type: field.TypeInt64, Nullable: true}, } // ProxiesTable holds the schema information for the "proxies" table. ProxiesTable = &schema.Table{ diff --git a/backend/ent/mutation.go b/backend/ent/mutation.go index cfa09d102f..3367fab567 100644 --- a/backend/ent/mutation.go +++ b/backend/ent/mutation.go @@ -37366,33 +37366,36 @@ func (m *PromoCodeUsageMutation) ResetEdge(name string) error { // ProxyMutation represents an operation that mutates the Proxy nodes in the graph. type ProxyMutation struct { config - op Op - typ string - id *int64 - created_at *time.Time - updated_at *time.Time - deleted_at *time.Time - name *string - protocol *string - host *string - port *int - addport *int - username *string - password *string - status *string - expires_at *time.Time - fallback_mode *string - expiry_warn_days *int - addexpiry_warn_days *int - clearedFields map[string]struct{} - accounts map[int64]struct{} - removedaccounts map[int64]struct{} - clearedaccounts bool - backup_proxy *int64 - clearedbackup_proxy bool - done bool - oldValue func(context.Context) (*Proxy, error) - predicates []predicate.Proxy + op Op + typ string + id *int64 + created_at *time.Time + updated_at *time.Time + deleted_at *time.Time + name *string + protocol *string + host *string + port *int + addport *int + username *string + password *string + status *string + expires_at *time.Time + fallback_mode *string + expiry_warn_days *int + addexpiry_warn_days *int + clearedFields map[string]struct{} + accounts map[int64]struct{} + removedaccounts map[int64]struct{} + clearedaccounts bool + primary_proxies map[int64]struct{} + removedprimary_proxies map[int64]struct{} + clearedprimary_proxies bool + backup_proxy *int64 + clearedbackup_proxy bool + done bool + oldValue func(context.Context) (*Proxy, error) + predicates []predicate.Proxy } var _ ent.Mutation = (*ProxyMutation)(nil) @@ -38156,6 +38159,60 @@ func (m *ProxyMutation) ResetAccounts() { m.removedaccounts = nil } +// AddPrimaryProxyIDs adds the "primary_proxies" edge to the Proxy entity by ids. +func (m *ProxyMutation) AddPrimaryProxyIDs(ids ...int64) { + if m.primary_proxies == nil { + m.primary_proxies = make(map[int64]struct{}) + } + for i := range ids { + m.primary_proxies[ids[i]] = struct{}{} + } +} + +// ClearPrimaryProxies clears the "primary_proxies" edge to the Proxy entity. +func (m *ProxyMutation) ClearPrimaryProxies() { + m.clearedprimary_proxies = true +} + +// PrimaryProxiesCleared reports if the "primary_proxies" edge to the Proxy entity was cleared. +func (m *ProxyMutation) PrimaryProxiesCleared() bool { + return m.clearedprimary_proxies +} + +// RemovePrimaryProxyIDs removes the "primary_proxies" edge to the Proxy entity by IDs. +func (m *ProxyMutation) RemovePrimaryProxyIDs(ids ...int64) { + if m.removedprimary_proxies == nil { + m.removedprimary_proxies = make(map[int64]struct{}) + } + for i := range ids { + delete(m.primary_proxies, ids[i]) + m.removedprimary_proxies[ids[i]] = struct{}{} + } +} + +// RemovedPrimaryProxies returns the removed IDs of the "primary_proxies" edge to the Proxy entity. +func (m *ProxyMutation) RemovedPrimaryProxiesIDs() (ids []int64) { + for id := range m.removedprimary_proxies { + ids = append(ids, id) + } + return +} + +// PrimaryProxiesIDs returns the "primary_proxies" edge IDs in the mutation. +func (m *ProxyMutation) PrimaryProxiesIDs() (ids []int64) { + for id := range m.primary_proxies { + ids = append(ids, id) + } + return +} + +// ResetPrimaryProxies resets all changes to the "primary_proxies" edge. +func (m *ProxyMutation) ResetPrimaryProxies() { + m.primary_proxies = nil + m.clearedprimary_proxies = false + m.removedprimary_proxies = nil +} + // ClearBackupProxy clears the "backup_proxy" edge to the Proxy entity. func (m *ProxyMutation) ClearBackupProxy() { m.clearedbackup_proxy = true @@ -38597,10 +38654,13 @@ func (m *ProxyMutation) ResetField(name string) error { // AddedEdges returns all edge names that were set/added in this mutation. func (m *ProxyMutation) AddedEdges() []string { - edges := make([]string, 0, 2) + edges := make([]string, 0, 3) if m.accounts != nil { edges = append(edges, proxy.EdgeAccounts) } + if m.primary_proxies != nil { + edges = append(edges, proxy.EdgePrimaryProxies) + } if m.backup_proxy != nil { edges = append(edges, proxy.EdgeBackupProxy) } @@ -38617,6 +38677,12 @@ func (m *ProxyMutation) AddedIDs(name string) []ent.Value { ids = append(ids, id) } return ids + case proxy.EdgePrimaryProxies: + ids := make([]ent.Value, 0, len(m.primary_proxies)) + for id := range m.primary_proxies { + ids = append(ids, id) + } + return ids case proxy.EdgeBackupProxy: if id := m.backup_proxy; id != nil { return []ent.Value{*id} @@ -38627,10 +38693,13 @@ func (m *ProxyMutation) AddedIDs(name string) []ent.Value { // RemovedEdges returns all edge names that were removed in this mutation. func (m *ProxyMutation) RemovedEdges() []string { - edges := make([]string, 0, 2) + edges := make([]string, 0, 3) if m.removedaccounts != nil { edges = append(edges, proxy.EdgeAccounts) } + if m.removedprimary_proxies != nil { + edges = append(edges, proxy.EdgePrimaryProxies) + } return edges } @@ -38644,16 +38713,25 @@ func (m *ProxyMutation) RemovedIDs(name string) []ent.Value { ids = append(ids, id) } return ids + case proxy.EdgePrimaryProxies: + ids := make([]ent.Value, 0, len(m.removedprimary_proxies)) + for id := range m.removedprimary_proxies { + ids = append(ids, id) + } + return ids } return nil } // ClearedEdges returns all edge names that were cleared in this mutation. func (m *ProxyMutation) ClearedEdges() []string { - edges := make([]string, 0, 2) + edges := make([]string, 0, 3) if m.clearedaccounts { edges = append(edges, proxy.EdgeAccounts) } + if m.clearedprimary_proxies { + edges = append(edges, proxy.EdgePrimaryProxies) + } if m.clearedbackup_proxy { edges = append(edges, proxy.EdgeBackupProxy) } @@ -38666,6 +38744,8 @@ func (m *ProxyMutation) EdgeCleared(name string) bool { switch name { case proxy.EdgeAccounts: return m.clearedaccounts + case proxy.EdgePrimaryProxies: + return m.clearedprimary_proxies case proxy.EdgeBackupProxy: return m.clearedbackup_proxy } @@ -38690,6 +38770,9 @@ func (m *ProxyMutation) ResetEdge(name string) error { case proxy.EdgeAccounts: m.ResetAccounts() return nil + case proxy.EdgePrimaryProxies: + m.ResetPrimaryProxies() + return nil case proxy.EdgeBackupProxy: m.ResetBackupProxy() return nil diff --git a/backend/ent/proxy.go b/backend/ent/proxy.go index 3dc4491bd1..cc0f3f656b 100644 --- a/backend/ent/proxy.go +++ b/backend/ent/proxy.go @@ -55,11 +55,13 @@ type Proxy struct { type ProxyEdges struct { // Accounts holds the value of the accounts edge. Accounts []*Account `json:"accounts,omitempty"` + // PrimaryProxies holds the value of the primary_proxies edge. + PrimaryProxies []*Proxy `json:"primary_proxies,omitempty"` // BackupProxy holds the value of the backup_proxy edge. BackupProxy *Proxy `json:"backup_proxy,omitempty"` // loadedTypes holds the information for reporting if a // type was loaded (or requested) in eager-loading or not. - loadedTypes [2]bool + loadedTypes [3]bool } // AccountsOrErr returns the Accounts value or an error if the edge @@ -71,12 +73,21 @@ func (e ProxyEdges) AccountsOrErr() ([]*Account, error) { return nil, &NotLoadedError{edge: "accounts"} } +// PrimaryProxiesOrErr returns the PrimaryProxies value or an error if the edge +// was not loaded in eager-loading. +func (e ProxyEdges) PrimaryProxiesOrErr() ([]*Proxy, error) { + if e.loadedTypes[1] { + return e.PrimaryProxies, nil + } + return nil, &NotLoadedError{edge: "primary_proxies"} +} + // BackupProxyOrErr returns the BackupProxy value or an error if the edge // was not loaded in eager-loading, or loaded but was not found. func (e ProxyEdges) BackupProxyOrErr() (*Proxy, error) { if e.BackupProxy != nil { return e.BackupProxy, nil - } else if e.loadedTypes[1] { + } else if e.loadedTypes[2] { return nil, &NotFoundError{label: proxy.Label} } return nil, &NotLoadedError{edge: "backup_proxy"} @@ -221,6 +232,11 @@ func (_m *Proxy) QueryAccounts() *AccountQuery { return NewProxyClient(_m.config).QueryAccounts(_m) } +// QueryPrimaryProxies queries the "primary_proxies" edge of the Proxy entity. +func (_m *Proxy) QueryPrimaryProxies() *ProxyQuery { + return NewProxyClient(_m.config).QueryPrimaryProxies(_m) +} + // QueryBackupProxy queries the "backup_proxy" edge of the Proxy entity. func (_m *Proxy) QueryBackupProxy() *ProxyQuery { return NewProxyClient(_m.config).QueryBackupProxy(_m) diff --git a/backend/ent/proxy/proxy.go b/backend/ent/proxy/proxy.go index afd45e3af5..e7647436c9 100644 --- a/backend/ent/proxy/proxy.go +++ b/backend/ent/proxy/proxy.go @@ -45,6 +45,8 @@ const ( FieldExpiryWarnDays = "expiry_warn_days" // EdgeAccounts holds the string denoting the accounts edge name in mutations. EdgeAccounts = "accounts" + // EdgePrimaryProxies holds the string denoting the primary_proxies edge name in mutations. + EdgePrimaryProxies = "primary_proxies" // EdgeBackupProxy holds the string denoting the backup_proxy edge name in mutations. EdgeBackupProxy = "backup_proxy" // Table holds the table name of the proxy in the database. @@ -56,6 +58,10 @@ const ( AccountsInverseTable = "accounts" // AccountsColumn is the table column denoting the accounts relation/edge. AccountsColumn = "proxy_id" + // PrimaryProxiesTable is the table that holds the primary_proxies relation/edge. + PrimaryProxiesTable = "proxies" + // PrimaryProxiesColumn is the table column denoting the primary_proxies relation/edge. + PrimaryProxiesColumn = "backup_proxy_id" // BackupProxyTable is the table that holds the backup_proxy relation/edge. BackupProxyTable = "proxies" // BackupProxyColumn is the table column denoting the backup_proxy relation/edge. @@ -219,6 +225,20 @@ func ByAccounts(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { } } +// ByPrimaryProxiesCount orders the results by primary_proxies count. +func ByPrimaryProxiesCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newPrimaryProxiesStep(), opts...) + } +} + +// ByPrimaryProxies orders the results by primary_proxies terms. +func ByPrimaryProxies(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newPrimaryProxiesStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} + // ByBackupProxyField orders the results by backup_proxy field. func ByBackupProxyField(field string, opts ...sql.OrderTermOption) OrderOption { return func(s *sql.Selector) { @@ -232,10 +252,17 @@ func newAccountsStep() *sqlgraph.Step { sqlgraph.Edge(sqlgraph.O2M, true, AccountsTable, AccountsColumn), ) } +func newPrimaryProxiesStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, true, PrimaryProxiesTable, PrimaryProxiesColumn), + ) +} func newBackupProxyStep() *sqlgraph.Step { return sqlgraph.NewStep( sqlgraph.From(Table, FieldID), sqlgraph.To(Table, FieldID), - sqlgraph.Edge(sqlgraph.O2O, false, BackupProxyTable, BackupProxyColumn), + sqlgraph.Edge(sqlgraph.M2O, false, BackupProxyTable, BackupProxyColumn), ) } diff --git a/backend/ent/proxy/where.go b/backend/ent/proxy/where.go index 5150196a41..51830f8acc 100644 --- a/backend/ent/proxy/where.go +++ b/backend/ent/proxy/where.go @@ -913,12 +913,35 @@ func HasAccountsWith(preds ...predicate.Account) predicate.Proxy { }) } +// HasPrimaryProxies applies the HasEdge predicate on the "primary_proxies" edge. +func HasPrimaryProxies() predicate.Proxy { + return predicate.Proxy(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, true, PrimaryProxiesTable, PrimaryProxiesColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasPrimaryProxiesWith applies the HasEdge predicate on the "primary_proxies" edge with a given conditions (other predicates). +func HasPrimaryProxiesWith(preds ...predicate.Proxy) predicate.Proxy { + return predicate.Proxy(func(s *sql.Selector) { + step := newPrimaryProxiesStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + // HasBackupProxy applies the HasEdge predicate on the "backup_proxy" edge. func HasBackupProxy() predicate.Proxy { return predicate.Proxy(func(s *sql.Selector) { step := sqlgraph.NewStep( sqlgraph.From(Table, FieldID), - sqlgraph.Edge(sqlgraph.O2O, false, BackupProxyTable, BackupProxyColumn), + sqlgraph.Edge(sqlgraph.M2O, false, BackupProxyTable, BackupProxyColumn), ) sqlgraph.HasNeighbors(s, step) }) diff --git a/backend/ent/proxy_create.go b/backend/ent/proxy_create.go index 925ce9cb31..0db9f20e77 100644 --- a/backend/ent/proxy_create.go +++ b/backend/ent/proxy_create.go @@ -202,6 +202,21 @@ func (_c *ProxyCreate) AddAccounts(v ...*Account) *ProxyCreate { return _c.AddAccountIDs(ids...) } +// AddPrimaryProxyIDs adds the "primary_proxies" edge to the Proxy entity by IDs. +func (_c *ProxyCreate) AddPrimaryProxyIDs(ids ...int64) *ProxyCreate { + _c.mutation.AddPrimaryProxyIDs(ids...) + return _c +} + +// AddPrimaryProxies adds the "primary_proxies" edges to the Proxy entity. +func (_c *ProxyCreate) AddPrimaryProxies(v ...*Proxy) *ProxyCreate { + ids := make([]int64, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _c.AddPrimaryProxyIDs(ids...) +} + // SetBackupProxy sets the "backup_proxy" edge to the Proxy entity. func (_c *ProxyCreate) SetBackupProxy(v *Proxy) *ProxyCreate { return _c.SetBackupProxyID(v.ID) @@ -432,13 +447,29 @@ func (_c *ProxyCreate) createSpec() (*Proxy, *sqlgraph.CreateSpec) { } _spec.Edges = append(_spec.Edges, edge) } + if nodes := _c.mutation.PrimaryProxiesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: proxy.PrimaryProxiesTable, + Columns: []string{proxy.PrimaryProxiesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(proxy.FieldID, field.TypeInt64), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } if nodes := _c.mutation.BackupProxyIDs(); len(nodes) > 0 { edge := &sqlgraph.EdgeSpec{ - Rel: sqlgraph.O2O, + Rel: sqlgraph.M2O, Inverse: false, Table: proxy.BackupProxyTable, Columns: []string{proxy.BackupProxyColumn}, - Bidi: true, + Bidi: false, Target: &sqlgraph.EdgeTarget{ IDSpec: sqlgraph.NewFieldSpec(proxy.FieldID, field.TypeInt64), }, diff --git a/backend/ent/proxy_query.go b/backend/ent/proxy_query.go index 93caf34665..0418f7e04d 100644 --- a/backend/ent/proxy_query.go +++ b/backend/ent/proxy_query.go @@ -21,13 +21,14 @@ import ( // ProxyQuery is the builder for querying Proxy entities. type ProxyQuery struct { config - ctx *QueryContext - order []proxy.OrderOption - inters []Interceptor - predicates []predicate.Proxy - withAccounts *AccountQuery - withBackupProxy *ProxyQuery - modifiers []func(*sql.Selector) + ctx *QueryContext + order []proxy.OrderOption + inters []Interceptor + predicates []predicate.Proxy + withAccounts *AccountQuery + withPrimaryProxies *ProxyQuery + withBackupProxy *ProxyQuery + modifiers []func(*sql.Selector) // intermediate query (i.e. traversal path). sql *sql.Selector path func(context.Context) (*sql.Selector, error) @@ -86,6 +87,28 @@ func (_q *ProxyQuery) QueryAccounts() *AccountQuery { return query } +// QueryPrimaryProxies chains the current query on the "primary_proxies" edge. +func (_q *ProxyQuery) QueryPrimaryProxies() *ProxyQuery { + query := (&ProxyClient{config: _q.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + selector := _q.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(proxy.Table, proxy.FieldID, selector), + sqlgraph.To(proxy.Table, proxy.FieldID), + sqlgraph.Edge(sqlgraph.O2M, true, proxy.PrimaryProxiesTable, proxy.PrimaryProxiesColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + // QueryBackupProxy chains the current query on the "backup_proxy" edge. func (_q *ProxyQuery) QueryBackupProxy() *ProxyQuery { query := (&ProxyClient{config: _q.config}).Query() @@ -100,7 +123,7 @@ func (_q *ProxyQuery) QueryBackupProxy() *ProxyQuery { step := sqlgraph.NewStep( sqlgraph.From(proxy.Table, proxy.FieldID, selector), sqlgraph.To(proxy.Table, proxy.FieldID), - sqlgraph.Edge(sqlgraph.O2O, false, proxy.BackupProxyTable, proxy.BackupProxyColumn), + sqlgraph.Edge(sqlgraph.M2O, false, proxy.BackupProxyTable, proxy.BackupProxyColumn), ) fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) return fromU, nil @@ -295,13 +318,14 @@ func (_q *ProxyQuery) Clone() *ProxyQuery { return nil } return &ProxyQuery{ - config: _q.config, - ctx: _q.ctx.Clone(), - order: append([]proxy.OrderOption{}, _q.order...), - inters: append([]Interceptor{}, _q.inters...), - predicates: append([]predicate.Proxy{}, _q.predicates...), - withAccounts: _q.withAccounts.Clone(), - withBackupProxy: _q.withBackupProxy.Clone(), + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]proxy.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.Proxy{}, _q.predicates...), + withAccounts: _q.withAccounts.Clone(), + withPrimaryProxies: _q.withPrimaryProxies.Clone(), + withBackupProxy: _q.withBackupProxy.Clone(), // clone intermediate query. sql: _q.sql.Clone(), path: _q.path, @@ -319,6 +343,17 @@ func (_q *ProxyQuery) WithAccounts(opts ...func(*AccountQuery)) *ProxyQuery { return _q } +// WithPrimaryProxies tells the query-builder to eager-load the nodes that are connected to +// the "primary_proxies" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *ProxyQuery) WithPrimaryProxies(opts ...func(*ProxyQuery)) *ProxyQuery { + query := (&ProxyClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withPrimaryProxies = query + return _q +} + // WithBackupProxy tells the query-builder to eager-load the nodes that are connected to // the "backup_proxy" edge. The optional arguments are used to configure the query builder of the edge. func (_q *ProxyQuery) WithBackupProxy(opts ...func(*ProxyQuery)) *ProxyQuery { @@ -408,8 +443,9 @@ func (_q *ProxyQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Proxy, var ( nodes = []*Proxy{} _spec = _q.querySpec() - loadedTypes = [2]bool{ + loadedTypes = [3]bool{ _q.withAccounts != nil, + _q.withPrimaryProxies != nil, _q.withBackupProxy != nil, } ) @@ -441,6 +477,13 @@ func (_q *ProxyQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Proxy, return nil, err } } + if query := _q.withPrimaryProxies; query != nil { + if err := _q.loadPrimaryProxies(ctx, query, nodes, + func(n *Proxy) { n.Edges.PrimaryProxies = []*Proxy{} }, + func(n *Proxy, e *Proxy) { n.Edges.PrimaryProxies = append(n.Edges.PrimaryProxies, e) }); err != nil { + return nil, err + } + } if query := _q.withBackupProxy; query != nil { if err := _q.loadBackupProxy(ctx, query, nodes, nil, func(n *Proxy, e *Proxy) { n.Edges.BackupProxy = e }); err != nil { @@ -483,6 +526,39 @@ func (_q *ProxyQuery) loadAccounts(ctx context.Context, query *AccountQuery, nod } return nil } +func (_q *ProxyQuery) loadPrimaryProxies(ctx context.Context, query *ProxyQuery, nodes []*Proxy, init func(*Proxy), assign func(*Proxy, *Proxy)) error { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[int64]*Proxy) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + if init != nil { + init(nodes[i]) + } + } + if len(query.ctx.Fields) > 0 { + query.ctx.AppendFieldOnce(proxy.FieldBackupProxyID) + } + query.Where(predicate.Proxy(func(s *sql.Selector) { + s.Where(sql.InValues(s.C(proxy.PrimaryProxiesColumn), fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + fk := n.BackupProxyID + if fk == nil { + return fmt.Errorf(`foreign-key "backup_proxy_id" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return fmt.Errorf(`unexpected referenced foreign-key "backup_proxy_id" returned %v for node %v`, *fk, n.ID) + } + assign(node, n) + } + return nil +} func (_q *ProxyQuery) loadBackupProxy(ctx context.Context, query *ProxyQuery, nodes []*Proxy, init func(*Proxy), assign func(*Proxy, *Proxy)) error { ids := make([]int64, 0, len(nodes)) nodeids := make(map[int64][]*Proxy) diff --git a/backend/ent/proxy_update.go b/backend/ent/proxy_update.go index 36b49ced6d..f4617c89c2 100644 --- a/backend/ent/proxy_update.go +++ b/backend/ent/proxy_update.go @@ -262,6 +262,21 @@ func (_u *ProxyUpdate) AddAccounts(v ...*Account) *ProxyUpdate { return _u.AddAccountIDs(ids...) } +// AddPrimaryProxyIDs adds the "primary_proxies" edge to the Proxy entity by IDs. +func (_u *ProxyUpdate) AddPrimaryProxyIDs(ids ...int64) *ProxyUpdate { + _u.mutation.AddPrimaryProxyIDs(ids...) + return _u +} + +// AddPrimaryProxies adds the "primary_proxies" edges to the Proxy entity. +func (_u *ProxyUpdate) AddPrimaryProxies(v ...*Proxy) *ProxyUpdate { + ids := make([]int64, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddPrimaryProxyIDs(ids...) +} + // SetBackupProxy sets the "backup_proxy" edge to the Proxy entity. func (_u *ProxyUpdate) SetBackupProxy(v *Proxy) *ProxyUpdate { return _u.SetBackupProxyID(v.ID) @@ -293,6 +308,27 @@ func (_u *ProxyUpdate) RemoveAccounts(v ...*Account) *ProxyUpdate { return _u.RemoveAccountIDs(ids...) } +// ClearPrimaryProxies clears all "primary_proxies" edges to the Proxy entity. +func (_u *ProxyUpdate) ClearPrimaryProxies() *ProxyUpdate { + _u.mutation.ClearPrimaryProxies() + return _u +} + +// RemovePrimaryProxyIDs removes the "primary_proxies" edge to Proxy entities by IDs. +func (_u *ProxyUpdate) RemovePrimaryProxyIDs(ids ...int64) *ProxyUpdate { + _u.mutation.RemovePrimaryProxyIDs(ids...) + return _u +} + +// RemovePrimaryProxies removes "primary_proxies" edges to Proxy entities. +func (_u *ProxyUpdate) RemovePrimaryProxies(v ...*Proxy) *ProxyUpdate { + ids := make([]int64, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemovePrimaryProxyIDs(ids...) +} + // ClearBackupProxy clears the "backup_proxy" edge to the Proxy entity. func (_u *ProxyUpdate) ClearBackupProxy() *ProxyUpdate { _u.mutation.ClearBackupProxy() @@ -492,13 +528,58 @@ func (_u *ProxyUpdate) sqlSave(ctx context.Context) (_node int, err error) { } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if _u.mutation.PrimaryProxiesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: proxy.PrimaryProxiesTable, + Columns: []string{proxy.PrimaryProxiesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(proxy.FieldID, field.TypeInt64), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedPrimaryProxiesIDs(); len(nodes) > 0 && !_u.mutation.PrimaryProxiesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: proxy.PrimaryProxiesTable, + Columns: []string{proxy.PrimaryProxiesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(proxy.FieldID, field.TypeInt64), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.PrimaryProxiesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: proxy.PrimaryProxiesTable, + Columns: []string{proxy.PrimaryProxiesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(proxy.FieldID, field.TypeInt64), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } if _u.mutation.BackupProxyCleared() { edge := &sqlgraph.EdgeSpec{ - Rel: sqlgraph.O2O, + Rel: sqlgraph.M2O, Inverse: false, Table: proxy.BackupProxyTable, Columns: []string{proxy.BackupProxyColumn}, - Bidi: true, + Bidi: false, Target: &sqlgraph.EdgeTarget{ IDSpec: sqlgraph.NewFieldSpec(proxy.FieldID, field.TypeInt64), }, @@ -507,11 +588,11 @@ func (_u *ProxyUpdate) sqlSave(ctx context.Context) (_node int, err error) { } if nodes := _u.mutation.BackupProxyIDs(); len(nodes) > 0 { edge := &sqlgraph.EdgeSpec{ - Rel: sqlgraph.O2O, + Rel: sqlgraph.M2O, Inverse: false, Table: proxy.BackupProxyTable, Columns: []string{proxy.BackupProxyColumn}, - Bidi: true, + Bidi: false, Target: &sqlgraph.EdgeTarget{ IDSpec: sqlgraph.NewFieldSpec(proxy.FieldID, field.TypeInt64), }, @@ -774,6 +855,21 @@ func (_u *ProxyUpdateOne) AddAccounts(v ...*Account) *ProxyUpdateOne { return _u.AddAccountIDs(ids...) } +// AddPrimaryProxyIDs adds the "primary_proxies" edge to the Proxy entity by IDs. +func (_u *ProxyUpdateOne) AddPrimaryProxyIDs(ids ...int64) *ProxyUpdateOne { + _u.mutation.AddPrimaryProxyIDs(ids...) + return _u +} + +// AddPrimaryProxies adds the "primary_proxies" edges to the Proxy entity. +func (_u *ProxyUpdateOne) AddPrimaryProxies(v ...*Proxy) *ProxyUpdateOne { + ids := make([]int64, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddPrimaryProxyIDs(ids...) +} + // SetBackupProxy sets the "backup_proxy" edge to the Proxy entity. func (_u *ProxyUpdateOne) SetBackupProxy(v *Proxy) *ProxyUpdateOne { return _u.SetBackupProxyID(v.ID) @@ -805,6 +901,27 @@ func (_u *ProxyUpdateOne) RemoveAccounts(v ...*Account) *ProxyUpdateOne { return _u.RemoveAccountIDs(ids...) } +// ClearPrimaryProxies clears all "primary_proxies" edges to the Proxy entity. +func (_u *ProxyUpdateOne) ClearPrimaryProxies() *ProxyUpdateOne { + _u.mutation.ClearPrimaryProxies() + return _u +} + +// RemovePrimaryProxyIDs removes the "primary_proxies" edge to Proxy entities by IDs. +func (_u *ProxyUpdateOne) RemovePrimaryProxyIDs(ids ...int64) *ProxyUpdateOne { + _u.mutation.RemovePrimaryProxyIDs(ids...) + return _u +} + +// RemovePrimaryProxies removes "primary_proxies" edges to Proxy entities. +func (_u *ProxyUpdateOne) RemovePrimaryProxies(v ...*Proxy) *ProxyUpdateOne { + ids := make([]int64, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemovePrimaryProxyIDs(ids...) +} + // ClearBackupProxy clears the "backup_proxy" edge to the Proxy entity. func (_u *ProxyUpdateOne) ClearBackupProxy() *ProxyUpdateOne { _u.mutation.ClearBackupProxy() @@ -1034,13 +1151,58 @@ func (_u *ProxyUpdateOne) sqlSave(ctx context.Context) (_node *Proxy, err error) } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if _u.mutation.PrimaryProxiesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: proxy.PrimaryProxiesTable, + Columns: []string{proxy.PrimaryProxiesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(proxy.FieldID, field.TypeInt64), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedPrimaryProxiesIDs(); len(nodes) > 0 && !_u.mutation.PrimaryProxiesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: proxy.PrimaryProxiesTable, + Columns: []string{proxy.PrimaryProxiesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(proxy.FieldID, field.TypeInt64), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.PrimaryProxiesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: proxy.PrimaryProxiesTable, + Columns: []string{proxy.PrimaryProxiesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(proxy.FieldID, field.TypeInt64), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } if _u.mutation.BackupProxyCleared() { edge := &sqlgraph.EdgeSpec{ - Rel: sqlgraph.O2O, + Rel: sqlgraph.M2O, Inverse: false, Table: proxy.BackupProxyTable, Columns: []string{proxy.BackupProxyColumn}, - Bidi: true, + Bidi: false, Target: &sqlgraph.EdgeTarget{ IDSpec: sqlgraph.NewFieldSpec(proxy.FieldID, field.TypeInt64), }, @@ -1049,11 +1211,11 @@ func (_u *ProxyUpdateOne) sqlSave(ctx context.Context) (_node *Proxy, err error) } if nodes := _u.mutation.BackupProxyIDs(); len(nodes) > 0 { edge := &sqlgraph.EdgeSpec{ - Rel: sqlgraph.O2O, + Rel: sqlgraph.M2O, Inverse: false, Table: proxy.BackupProxyTable, Columns: []string{proxy.BackupProxyColumn}, - Bidi: true, + Bidi: false, Target: &sqlgraph.EdgeTarget{ IDSpec: sqlgraph.NewFieldSpec(proxy.FieldID, field.TypeInt64), }, diff --git a/backend/ent/schema/proxy.go b/backend/ent/schema/proxy.go index f544811c5c..46ab1ffcb2 100644 --- a/backend/ent/schema/proxy.go +++ b/backend/ent/schema/proxy.go @@ -73,6 +73,10 @@ func (Proxy) Edges() []ent.Edge { // accounts: 使用此代理的账户(反向边) edge.From("accounts", Account.Type). Ref("proxy"), + // Directed many-to-one: a backup can serve multiple primary proxies. + // The inverse edge prevents Ent from treating this self-reference as symmetric. + edge.From("primary_proxies", Proxy.Type). + Ref("backup_proxy"), edge.To("backup_proxy", Proxy.Type). Field("backup_proxy_id"). Unique(), diff --git a/backend/go.sum b/backend/go.sum index cbb257f12f..ccb7ebb4e6 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -327,6 +327,8 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mdelapenya/tlscert v0.2.0 h1:7H81W6Z/4weDvZBNOfQte5GpIMo0lGYEeWbkGp5LJHI= @@ -365,6 +367,8 @@ github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJm github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= @@ -399,6 +403,8 @@ github.com/refraction-networking/utls v1.8.2 h1:j4Q1gJj0xngdeH+Ox/qND11aEfhpgoEv github.com/refraction-networking/utls v1.8.2/go.mod h1:jkSOEkLqn+S/jtpEHPOsVv/4V4EVnelwbMQl4vCWXAM= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= @@ -434,6 +440,8 @@ github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= diff --git a/backend/internal/repository/proxy_backup_reference_integration_test.go b/backend/internal/repository/proxy_backup_reference_integration_test.go new file mode 100644 index 0000000000..a8ebe039a4 --- /dev/null +++ b/backend/internal/repository/proxy_backup_reference_integration_test.go @@ -0,0 +1,65 @@ +//go:build integration + +package repository + +import "github.com/Wei-Shaw/sub2api/internal/service" + +func (s *ProxyExpirySuite) TestBackupReferencesAreDirectedAndShareable() { + backupID := s.mkProxy("shared-backup", service.FallbackModeNone, nil, nil) + firstID := s.mkProxy("first-primary", service.FallbackModeProxy, nil, &backupID) + backup, err := s.repo.GetByID(s.ctx, backupID) + s.Require().NoError(err) + s.Require().Nil(backup.BackupProxyID, "assigning a backup must not overwrite its own configuration") + + secondID := s.mkProxy("second-primary", service.FallbackModeProxy, nil, &backupID) + for _, id := range []int64{firstID, secondID} { + primary, err := s.repo.GetByID(s.ctx, id) + s.Require().NoError(err) + s.Require().Equal(&backupID, primary.BackupProxyID) + } + backup, err = s.repo.GetByID(s.ctx, backupID) + s.Require().NoError(err) + s.Require().Nil(backup.BackupProxyID) +} + +func (s *ProxyExpirySuite) TestBackupUpdateAndClearLeaveOtherReferencesIntact() { + backupID := s.mkProxy("old-backup", service.FallbackModeNone, nil, nil) + replacementID := s.mkProxy("new-backup", service.FallbackModeNone, nil, nil) + firstID := s.mkProxy("primary-to-change", service.FallbackModeProxy, nil, &backupID) + secondID := s.mkProxy("primary-to-keep", service.FallbackModeProxy, nil, &backupID) + first, err := s.repo.GetByID(s.ctx, firstID) + s.Require().NoError(err) + first.BackupProxyID = &replacementID + s.Require().NoError(s.repo.Update(s.ctx, first)) + first, err = s.repo.GetByID(s.ctx, firstID) + s.Require().NoError(err) + s.Require().Equal(&replacementID, first.BackupProxyID) + first.BackupProxyID = nil + first.FallbackMode = service.FallbackModeNone + s.Require().NoError(s.repo.Update(s.ctx, first)) + first, err = s.repo.GetByID(s.ctx, firstID) + s.Require().NoError(err) + s.Require().Nil(first.BackupProxyID) + second, err := s.repo.GetByID(s.ctx, secondID) + s.Require().NoError(err) + s.Require().Equal(&backupID, second.BackupProxyID) + for _, id := range []int64{backupID, replacementID} { + backup, err := s.repo.GetByID(s.ctx, id) + s.Require().NoError(err) + s.Require().Nil(backup.BackupProxyID) + } +} + +func (s *ProxyExpirySuite) TestBackupReferencesSupportDirectedChains() { + lastID := s.mkProxy("chain-last", service.FallbackModeNone, nil, nil) + middleID := s.mkProxy("chain-middle", service.FallbackModeProxy, nil, &lastID) + firstID := s.mkProxy("chain-first", service.FallbackModeProxy, nil, &middleID) + for _, pair := range [][2]int64{{firstID, middleID}, {middleID, lastID}} { + primary, err := s.repo.GetByID(s.ctx, pair[0]) + s.Require().NoError(err) + s.Require().Equal(&pair[1], primary.BackupProxyID) + } + last, err := s.repo.GetByID(s.ctx, lastID) + s.Require().NoError(err) + s.Require().Nil(last.BackupProxyID) +}