307 lines
8.7 KiB
Go
307 lines
8.7 KiB
Go
package sqlite
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
type LogicalGroupRoute struct {
|
|
ID int64
|
|
RouteID string
|
|
LogicalGroupID string
|
|
Name string
|
|
Status string
|
|
Priority int
|
|
Weight int
|
|
ShadowGroupID string
|
|
ShadowHostID string
|
|
UpstreamBaseURLHint string
|
|
CooldownUntil string
|
|
CreatedAt string
|
|
UpdatedAt string
|
|
}
|
|
|
|
type LogicalGroupRoutesRepo struct {
|
|
db execQuerier
|
|
}
|
|
|
|
func newLogicalGroupRoutesRepo(db execQuerier) *LogicalGroupRoutesRepo {
|
|
return &LogicalGroupRoutesRepo{db: db}
|
|
}
|
|
|
|
func (r *LogicalGroupRoutesRepo) Create(ctx context.Context, route LogicalGroupRoute) (int64, error) {
|
|
route, err := normalizeLogicalGroupRoute(route)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
result, err := r.db.ExecContext(
|
|
ctx,
|
|
`INSERT INTO logical_group_routes (
|
|
route_id,
|
|
logical_group_id,
|
|
name,
|
|
status,
|
|
priority,
|
|
weight,
|
|
shadow_group_id,
|
|
shadow_host_id,
|
|
upstream_base_url_hint,
|
|
cooldown_until
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
route.RouteID,
|
|
route.LogicalGroupID,
|
|
route.Name,
|
|
route.Status,
|
|
route.Priority,
|
|
route.Weight,
|
|
route.ShadowGroupID,
|
|
route.ShadowHostID,
|
|
route.UpstreamBaseURLHint,
|
|
route.CooldownUntil,
|
|
)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("insert logical group route %q: %w", route.RouteID, err)
|
|
}
|
|
|
|
id, err := result.LastInsertId()
|
|
if err != nil {
|
|
return 0, fmt.Errorf("read inserted logical group route id for %q: %w", route.RouteID, err)
|
|
}
|
|
return id, nil
|
|
}
|
|
|
|
func (r *LogicalGroupRoutesRepo) GetByRouteID(ctx context.Context, routeID string) (LogicalGroupRoute, error) {
|
|
routeID = strings.TrimSpace(routeID)
|
|
if routeID == "" {
|
|
return LogicalGroupRoute{}, fmt.Errorf("route_id is required")
|
|
}
|
|
|
|
var route LogicalGroupRoute
|
|
if err := r.db.QueryRowContext(
|
|
ctx,
|
|
`SELECT id, route_id, logical_group_id, name, status, priority, weight, shadow_group_id, shadow_host_id, upstream_base_url_hint, cooldown_until, created_at, updated_at
|
|
FROM logical_group_routes
|
|
WHERE route_id = ?`,
|
|
routeID,
|
|
).Scan(
|
|
&route.ID,
|
|
&route.RouteID,
|
|
&route.LogicalGroupID,
|
|
&route.Name,
|
|
&route.Status,
|
|
&route.Priority,
|
|
&route.Weight,
|
|
&route.ShadowGroupID,
|
|
&route.ShadowHostID,
|
|
&route.UpstreamBaseURLHint,
|
|
&route.CooldownUntil,
|
|
&route.CreatedAt,
|
|
&route.UpdatedAt,
|
|
); err != nil {
|
|
return LogicalGroupRoute{}, err
|
|
}
|
|
return route, nil
|
|
}
|
|
|
|
func (r *LogicalGroupRoutesRepo) ListByLogicalGroupID(ctx context.Context, logicalGroupID string) ([]LogicalGroupRoute, error) {
|
|
logicalGroupID = strings.TrimSpace(logicalGroupID)
|
|
if logicalGroupID == "" {
|
|
return nil, fmt.Errorf("logical_group_id is required")
|
|
}
|
|
|
|
rows, err := r.db.QueryContext(
|
|
ctx,
|
|
`SELECT id, route_id, logical_group_id, name, status, priority, weight, shadow_group_id, shadow_host_id, upstream_base_url_hint, cooldown_until, created_at, updated_at
|
|
FROM logical_group_routes
|
|
WHERE logical_group_id = ?
|
|
ORDER BY priority ASC, id ASC`,
|
|
logicalGroupID,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("list logical group routes for %q: %w", logicalGroupID, err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
routes := make([]LogicalGroupRoute, 0)
|
|
for rows.Next() {
|
|
var route LogicalGroupRoute
|
|
if err := rows.Scan(
|
|
&route.ID,
|
|
&route.RouteID,
|
|
&route.LogicalGroupID,
|
|
&route.Name,
|
|
&route.Status,
|
|
&route.Priority,
|
|
&route.Weight,
|
|
&route.ShadowGroupID,
|
|
&route.ShadowHostID,
|
|
&route.UpstreamBaseURLHint,
|
|
&route.CooldownUntil,
|
|
&route.CreatedAt,
|
|
&route.UpdatedAt,
|
|
); err != nil {
|
|
return nil, fmt.Errorf("scan logical group route: %w", err)
|
|
}
|
|
routes = append(routes, route)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, fmt.Errorf("iterate logical group routes for %q: %w", logicalGroupID, err)
|
|
}
|
|
return routes, nil
|
|
}
|
|
|
|
func (r *LogicalGroupRoutesRepo) UpdateByRouteID(ctx context.Context, route LogicalGroupRoute) error {
|
|
route, err := normalizeLogicalGroupRoute(route)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
result, err := r.db.ExecContext(
|
|
ctx,
|
|
`UPDATE logical_group_routes
|
|
SET logical_group_id = ?, name = ?, status = ?, priority = ?, weight = ?, shadow_group_id = ?, shadow_host_id = ?, upstream_base_url_hint = ?, cooldown_until = ?, updated_at = CURRENT_TIMESTAMP
|
|
WHERE route_id = ?`,
|
|
route.LogicalGroupID,
|
|
route.Name,
|
|
route.Status,
|
|
route.Priority,
|
|
route.Weight,
|
|
route.ShadowGroupID,
|
|
route.ShadowHostID,
|
|
route.UpstreamBaseURLHint,
|
|
route.CooldownUntil,
|
|
route.RouteID,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("update logical group route %q: %w", route.RouteID, err)
|
|
}
|
|
affected, err := result.RowsAffected()
|
|
if err != nil {
|
|
return fmt.Errorf("read updated logical group route rows for %q: %w", route.RouteID, err)
|
|
}
|
|
if affected == 0 {
|
|
return fmt.Errorf("logical group route %q not found", route.RouteID)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *LogicalGroupRoutesRepo) DeleteByRouteID(ctx context.Context, routeID string) error {
|
|
routeID = strings.TrimSpace(routeID)
|
|
if routeID == "" {
|
|
return fmt.Errorf("route_id is required")
|
|
}
|
|
|
|
result, err := r.db.ExecContext(ctx, `DELETE FROM logical_group_routes WHERE route_id = ?`, routeID)
|
|
if err != nil {
|
|
return fmt.Errorf("delete logical group route %q: %w", routeID, err)
|
|
}
|
|
affected, err := result.RowsAffected()
|
|
if err != nil {
|
|
return fmt.Errorf("read deleted logical group route rows for %q: %w", routeID, err)
|
|
}
|
|
if affected == 0 {
|
|
return fmt.Errorf("logical group route %q not found", routeID)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *LogicalGroupRoutesRepo) GetByShadowBinding(ctx context.Context, shadowHostID, shadowGroupID string) (LogicalGroupRoute, error) {
|
|
routes, err := r.ListByShadowBinding(ctx, shadowHostID, shadowGroupID)
|
|
if err != nil {
|
|
return LogicalGroupRoute{}, err
|
|
}
|
|
switch len(routes) {
|
|
case 0:
|
|
return LogicalGroupRoute{}, sql.ErrNoRows
|
|
case 1:
|
|
return routes[0], nil
|
|
default:
|
|
return LogicalGroupRoute{}, fmt.Errorf("multiple logical group routes match shadow binding %q/%q", strings.TrimSpace(shadowHostID), strings.TrimSpace(shadowGroupID))
|
|
}
|
|
}
|
|
|
|
func (r *LogicalGroupRoutesRepo) ListByShadowBinding(ctx context.Context, shadowHostID, shadowGroupID string) ([]LogicalGroupRoute, error) {
|
|
shadowHostID = strings.TrimSpace(shadowHostID)
|
|
shadowGroupID = strings.TrimSpace(shadowGroupID)
|
|
if shadowHostID == "" {
|
|
return nil, fmt.Errorf("shadow_host_id is required")
|
|
}
|
|
if shadowGroupID == "" {
|
|
return nil, fmt.Errorf("shadow_group_id is required")
|
|
}
|
|
|
|
rows, err := r.db.QueryContext(
|
|
ctx,
|
|
`SELECT id, route_id, logical_group_id, name, status, priority, weight, shadow_group_id, shadow_host_id, upstream_base_url_hint, cooldown_until, created_at, updated_at
|
|
FROM logical_group_routes
|
|
WHERE shadow_host_id = ? AND shadow_group_id = ?
|
|
ORDER BY priority ASC, id ASC`,
|
|
shadowHostID,
|
|
shadowGroupID,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("list logical group routes by shadow binding %q/%q: %w", shadowHostID, shadowGroupID, err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
routes := make([]LogicalGroupRoute, 0, 4)
|
|
for rows.Next() {
|
|
var route LogicalGroupRoute
|
|
if err := rows.Scan(
|
|
&route.ID,
|
|
&route.RouteID,
|
|
&route.LogicalGroupID,
|
|
&route.Name,
|
|
&route.Status,
|
|
&route.Priority,
|
|
&route.Weight,
|
|
&route.ShadowGroupID,
|
|
&route.ShadowHostID,
|
|
&route.UpstreamBaseURLHint,
|
|
&route.CooldownUntil,
|
|
&route.CreatedAt,
|
|
&route.UpdatedAt,
|
|
); err != nil {
|
|
return nil, fmt.Errorf("scan logical group route by shadow binding: %w", err)
|
|
}
|
|
routes = append(routes, route)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, fmt.Errorf("iterate logical group route by shadow binding %q/%q: %w", shadowHostID, shadowGroupID, err)
|
|
}
|
|
return routes, nil
|
|
}
|
|
|
|
func normalizeLogicalGroupRoute(route LogicalGroupRoute) (LogicalGroupRoute, error) {
|
|
route.RouteID = strings.TrimSpace(route.RouteID)
|
|
route.LogicalGroupID = strings.TrimSpace(route.LogicalGroupID)
|
|
route.Name = strings.TrimSpace(route.Name)
|
|
route.Status = strings.TrimSpace(route.Status)
|
|
route.ShadowGroupID = strings.TrimSpace(route.ShadowGroupID)
|
|
route.ShadowHostID = strings.TrimSpace(route.ShadowHostID)
|
|
route.UpstreamBaseURLHint = strings.TrimSpace(route.UpstreamBaseURLHint)
|
|
route.CooldownUntil = strings.TrimSpace(route.CooldownUntil)
|
|
|
|
switch {
|
|
case route.RouteID == "":
|
|
return LogicalGroupRoute{}, fmt.Errorf("route_id is required")
|
|
case route.LogicalGroupID == "":
|
|
return LogicalGroupRoute{}, fmt.Errorf("logical_group_id is required")
|
|
case route.Name == "":
|
|
return LogicalGroupRoute{}, fmt.Errorf("name is required")
|
|
case route.Status == "":
|
|
return LogicalGroupRoute{}, fmt.Errorf("status is required")
|
|
case route.ShadowGroupID == "":
|
|
return LogicalGroupRoute{}, fmt.Errorf("shadow_group_id is required")
|
|
case route.ShadowHostID == "":
|
|
return LogicalGroupRoute{}, fmt.Errorf("shadow_host_id is required")
|
|
}
|
|
if route.Weight <= 0 {
|
|
route.Weight = 100
|
|
}
|
|
return route, nil
|
|
}
|