mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-28 05:46:36 +08:00
943818f7d4
将 OpenFlare 与平台业务的数据访问从 model 与 apps 直连迁入 repository, model 仅保留实体与无 IO 规则;补充 code-check 架构守卫与开发规范。
155 lines
5.5 KiB
Go
155 lines
5.5 KiB
Go
// Copyright 2026 Arctel.net
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package repository
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/clause"
|
|
|
|
db "github.com/Rain-kl/Wavelet/internal/infra/persistence"
|
|
"github.com/Rain-kl/Wavelet/internal/model"
|
|
)
|
|
|
|
// Zone domain binding sentinel errors (shared by proxy_route and zone binding helpers).
|
|
var (
|
|
// ErrZoneDomainBoundToAnotherRoute is returned when a domain is already bound to a different route.
|
|
ErrZoneDomainBoundToAnotherRoute = errors.New("zone domain is already bound to another proxy route")
|
|
// ErrZoneDomainNotFound is returned when one or more requested domain IDs do not exist.
|
|
ErrZoneDomainNotFound = errors.New("one or more zone domains do not exist")
|
|
)
|
|
|
|
// WithProxyRouteTx runs fn inside a database transaction for proxy-route multi-step work.
|
|
func WithProxyRouteTx(ctx context.Context, fn func(tx *gorm.DB) error) error {
|
|
return db.DB(ctx).Transaction(fn)
|
|
}
|
|
|
|
// ListProxyRoutes 列出全部代理规则。
|
|
func ListProxyRoutes(ctx context.Context) ([]*model.ProxyRoute, error) {
|
|
var routes []*model.ProxyRoute
|
|
if err := db.DB(ctx).Order("id desc").Find(&routes).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
return routes, nil
|
|
}
|
|
|
|
// GetProxyRouteByID 按 ID 查询代理规则。
|
|
func GetProxyRouteByID(ctx context.Context, id uint) (*model.ProxyRoute, error) {
|
|
var route model.ProxyRoute
|
|
if err := db.DB(ctx).First(&route, id).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
return &route, nil
|
|
}
|
|
|
|
// CreateProxyRouteRecord 创建代理规则。
|
|
func CreateProxyRouteRecord(ctx context.Context, route *model.ProxyRoute) error {
|
|
return CreateProxyRouteRecordTx(db.DB(ctx), route)
|
|
}
|
|
|
|
// CreateProxyRouteRecordTx creates a proxy route within an existing transaction.
|
|
func CreateProxyRouteRecordTx(tx *gorm.DB, route *model.ProxyRoute) error {
|
|
return tx.Create(route).Error
|
|
}
|
|
|
|
// UpdateProxyRouteRecord 更新代理规则。
|
|
func UpdateProxyRouteRecord(ctx context.Context, route *model.ProxyRoute) error {
|
|
return UpdateProxyRouteRecordTx(db.DB(ctx), route)
|
|
}
|
|
|
|
// UpdateProxyRouteRecordTx updates a proxy route within an existing transaction.
|
|
func UpdateProxyRouteRecordTx(tx *gorm.DB, route *model.ProxyRoute) error {
|
|
return tx.Model(&model.ProxyRoute{}).Where("id = ?", route.ID).Updates(proxyRouteUpdateMap(route)).Error
|
|
}
|
|
|
|
func proxyRouteUpdateMap(route *model.ProxyRoute) map[string]any {
|
|
return map[string]any{
|
|
"site_name": route.SiteName,
|
|
"origin_id": route.OriginID,
|
|
"origin_url": route.OriginURL,
|
|
"origin_host": route.OriginHost,
|
|
"upstreams": route.Upstreams,
|
|
colEnabled: route.Enabled,
|
|
"enable_https": route.EnableHTTPS,
|
|
"redirect_http": route.RedirectHTTP,
|
|
"limit_conn_per_server": route.LimitConnPerServer,
|
|
"limit_conn_per_ip": route.LimitConnPerIP,
|
|
"limit_rate": route.LimitRate,
|
|
"limit_req_per_ip": route.LimitReqPerIP,
|
|
"cache_enabled": route.CacheEnabled,
|
|
"cache_policy": route.CachePolicy,
|
|
"cache_rules": route.CacheRules,
|
|
"custom_headers": route.CustomHeaders,
|
|
"basic_auth_enabled": route.BasicAuthEnabled,
|
|
"basic_auth_username": route.BasicAuthUsername,
|
|
"basic_auth_password": route.BasicAuthPassword,
|
|
"upstream_type": route.UpstreamType,
|
|
"tunnel_node_id": route.TunnelNodeID,
|
|
"tunnel_target_addr": route.TunnelTargetAddr,
|
|
"tunnel_target_protocol": route.TunnelTargetProtocol,
|
|
"pages_project_id": route.PagesProjectID,
|
|
}
|
|
}
|
|
|
|
// DeleteProxyRouteRecord 删除代理规则。
|
|
func DeleteProxyRouteRecord(ctx context.Context, id uint) error {
|
|
return DeleteProxyRouteRecordTx(db.DB(ctx), id)
|
|
}
|
|
|
|
// DeleteProxyRouteRecordTx deletes a proxy route within an existing transaction.
|
|
func DeleteProxyRouteRecordTx(tx *gorm.DB, id uint) error {
|
|
return tx.Delete(&model.ProxyRoute{}, id).Error
|
|
}
|
|
|
|
// ClearZoneDomainProxyRouteBindingsTx unbinds every zone domain from a proxy route.
|
|
func ClearZoneDomainProxyRouteBindingsTx(tx *gorm.DB, routeID uint) error {
|
|
return tx.Model(&model.ZoneDomain{}).Where("proxy_route_id = ?", routeID).Update("proxy_route_id", nil).Error
|
|
}
|
|
|
|
// ReplaceZoneDomainRouteBindingsTx replaces every ZoneDomain binding for a proxy route
|
|
// inside the caller's transaction (with row locks on requested domains).
|
|
func ReplaceZoneDomainRouteBindingsTx(tx *gorm.DB, routeID uint, domainIDs []uint) error {
|
|
var requested []model.ZoneDomain
|
|
if len(domainIDs) > 0 {
|
|
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
|
|
Where("id IN ?", domainIDs).
|
|
Find(&requested).Error; err != nil {
|
|
return err
|
|
}
|
|
if len(requested) != len(uniqueZoneDomainIDs(domainIDs)) {
|
|
return ErrZoneDomainNotFound
|
|
}
|
|
for _, domain := range requested {
|
|
if domain.ProxyRouteID != nil && *domain.ProxyRouteID != routeID {
|
|
return ErrZoneDomainBoundToAnotherRoute
|
|
}
|
|
}
|
|
}
|
|
|
|
current := tx.Model(&model.ZoneDomain{}).Where("proxy_route_id = ?", routeID)
|
|
if len(domainIDs) > 0 {
|
|
current = current.Where("id NOT IN ?", domainIDs)
|
|
}
|
|
if err := current.Update("proxy_route_id", nil).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(domainIDs) == 0 {
|
|
return nil
|
|
}
|
|
return tx.Model(&model.ZoneDomain{}).Where("id IN ?", domainIDs).Update("proxy_route_id", routeID).Error
|
|
}
|
|
|
|
// DeleteProxyRouteAndUnbind clears domain bindings then deletes the proxy route in one transaction.
|
|
func DeleteProxyRouteAndUnbind(ctx context.Context, id uint) error {
|
|
return WithProxyRouteTx(ctx, func(tx *gorm.DB) error {
|
|
if err := ClearZoneDomainProxyRouteBindingsTx(tx, id); err != nil {
|
|
return err
|
|
}
|
|
return DeleteProxyRouteRecordTx(tx, id)
|
|
})
|
|
}
|