Prechádzať zdrojové kódy

feat(cdn): 新增 CDN相关功能

- 添加了多个 CDN 相关的结构体和接口
- 实现了用户注册、创建规则分组、分配套餐、续费套餐、创建网站、编辑协议、创建和编辑源站等功能
- 更新了配置文件中的 CDN 相关配置
- 在仓库中添加了 Redis 客户端支持
fusu 1 mesiac pred
rodič
commit
50723ec7a0

+ 92 - 0
api/v1/cdn.go

@@ -0,0 +1,92 @@
+package v1
+
+type User struct {
+	ID       int64  `json:"id" form:"id"`
+	Username string `json:"username" form:"username"`
+	Password string `json:"password" form:"password"`
+	Fullname string `json:"fullname" form:"fullname"`
+	Mobile 	string `json:"mobile" form:"mobile"`
+	Tel     string `json:"tel" form:"tel"`
+	Email    string `json:"email" form:"email"`
+	Remark   string `json:"remark" form:"remark"`
+	Source   string `json:"source" form:"source"` // 用户来源
+	NodeClusterId int64 `json:"nodeClusterId" form:"nodeClusterId"` // 节点集群ID
+}
+
+
+type Group struct {
+	Name string `json:"name" form:"name"`
+}
+
+type Plan struct {
+	UserId int64 `json:"userId" form:"userId"`
+	PlanId int64 `json:"planId" form:"planId"`
+	DayTo  string `json:"dayTo" form:"dayTo"` // 结束日期,格式:YYYY-MM-DD,适用于按带宽和流量计费的套餐
+	Period string `json:"period" form:"period"` // 周期类型:yearly、seasonally、monthly,适用于按时间周期计费的套餐
+	CountPeriod int64 `json:"countPeriod" form:"countPeriod"` // 周期数量,适用于带宽和流量计费
+	Name   string `json:"name" form:"name"` // 备注名称
+	IsFree bool `json:"isFree" form:"isFree"` // 可选项,是否免费
+	PeriodDayTo string `json:"periodDayTo" form:"periodDayTo"` // 可选项, 按周期计费的套餐结束日期
+}
+
+type RenewalPlan struct {
+	UserPlanId int64 `json:"userPlanId" form:"userPlanId"`
+	DayTo  string `json:"dayTo" form:"dayTo"` // 结束日期,格式:YYYY-MM-DD,适用于按带宽和流量计费的套餐
+	Period string `json:"period" form:"period"` // 周期类型:yearly、seasonally、monthly,适用于按时间周期计费的套餐
+	CountPeriod int64 `json:"countPeriod" form:"countPeriod"` // 周期数量,适用于带宽和流量计费
+	IsFree bool `json:"isFree" form:"isFree"` // 可选项,是否免费
+	PeriodDayTo string `json:"periodDayTo" form:"periodDayTo"` // 可选项, 按周期计费的套餐结束日期
+}
+
+type Website struct {
+	UserId int64 `json:"userId" form:"userId"` //可选项,用户ID,如果不想指定用户,此值可以为0
+	AdminId int64 `json:"adminId" form:"adminId"` //可选项,管理员ID
+	Type string `json:"type" form:"type"` // 类型:httpProxy(HTTP反向代理,一般CDN服务都选这个)、httpWeb(静态文件服务,只会从服务器上读取文件内容,不会转发到源站)、tcpProxy(TCP反向代理)、udpProxy(UDP反向代理)
+	Name string `json:"name" form:"name"` // 网站名称
+	Description string `json:"description" form:"description"` // 可选项,网站描述
+	ServerNamesJSON byte `json:"serverNamesJSON" form:"serverNamesJSON"` // 域名列表 json:server_names
+	HttpJSON byte `json:"httpJSON" form:"httpJSON"` // HTTP协议设置,当type为httpProxy或者httpWeb时填写 json:http_protocol
+	HttpsJSON byte `json:"httpsJSON" form:"httpsJSON"` // HTTPS协议设置,当type为httpProxy或者httpWeb时填写 json:https_protocol
+	TcpJSON byte `json:"tcpJSON" form:"tcpJSON"` // TCP协议设置,当type为tcpProxy时填写 json:tcp_protocol
+	TlsJSON byte `json:"tlsJSON" form:"tlsJSON"` // TLS协议设置,当type为tcpProxy时填写 json:tls_protocol
+	UdpJSON byte `json:"udpJSON" form:"udpJSON"` // UDP协议设置,当type为udpProxy时填写 json:udp_protocol
+	WebId int64 `json:"webId" form:"webId"` // 可选项,Web配置ID,当type为httpProxy或者httpWeb时填写,可以通过 /HTTPWebService/createHTTPWeb 接口创建;如果你需要配置缓存等信息时需要在 HTTPWebService 接口操作=
+	ReverseProxyJSON byte `json:"reverseProxyJSON" form:"reverseProxyJSON"` // 反向代理(包含源站)配置引用,此项可以在创建网站后再设置 json:reverse_proxy_ref
+	ServerGroupIds []int64 `json:"serverGroupIds" form:"serverGroupIds"` // 可选项,所属网站分组ID列表
+	UserPlanId int64 `json:"userPlanId" form:"userPlanId"` // 可选项,套餐ID
+	NodeClusterId int64 `json:"nodeClusterId" form:"nodeClusterId"` // 所部署的集群ID
+	IncludeNodesJSON string `json:"includeNodesJSON" form:"includeNodesJSON"` // 备用参数,不用填写
+	ExcludeNodesJSON string `json:"excludeNodesJSON" form:"excludeNodesJSON"` // 备用参数,不用填写
+}
+
+type TypeJSON struct {
+	IsOn bool `json:"isOn" form:"isOn"` // 是否开启
+	Listen []Listen `json:"listen" form:"listen"` // 监听端口
+}
+
+type Listen struct {
+	Protocol string `json:"protocol" form:"protocol"` // 协议类型:tcp、udp
+	Host string `json:"host" form:"host"` // 监听地址
+	Port int64 `json:"port" form:"port"` // 端口
+}
+
+type ProxyJson struct {
+	ServerId int64 `json:"serverId" form:"serverId"` // 网站ID
+	JSON  byte  `json:"JSON" form:"JSON"`   // TCP协议设置
+}
+
+type Origin struct {
+	OriginId int64 `json:"originId" form:"originId"`
+	Name string `json:"name" form:"name"` // 源站名称
+	Addr string `json:"addr" form:"addr"` // 源站地址
+	OssJSON byte `json:"ossJSON" form:"ossJSON"` // Oss配置 json:oss_config
+	Description string `json:"description" form:"description"` // 备注
+	Weight int64 `json:"weight" form:"weight"` // 权重
+	IsOn bool `json:"isOn" form:"isOn"` // 是否开启
+	Domains []string `json:"domains" form:"domains"` // 域名
+	CertRefJSON byte `json:"certRefJSON" form:"certRefJSON"` // 证书引用,可选项
+	Host string `json:"host" form:"host"` // 回源主机域名,可选项
+	FollowPort bool `json:"followPort" form:"followPort"` // 是否跟随端口,可选项
+	Http2Enabled bool `json:"http2Enabled" form:"http2Enabled"` //可选项,是否支持HTTP/2,只在HTTPS源站时生效
+	TlsSecurityVerifyMode string `json:"tlsSecurityVerifyMode" form:"tlsSecurityVerifyMode"` // // 安全校验模式:auto系统默认,force强校验,skip不校验
+}

+ 1 - 2
cmd/server/wire/wire.go

@@ -21,7 +21,7 @@ import (
 
 var repositorySet = wire.NewSet(
 	repository.NewDB,
-	//repository.NewRedis,
+	repository.NewRedis,
 	repository.NewCasbinEnforcer,
 	repository.NewMongoClient,
 	repository.NewMongoDB,
@@ -112,7 +112,6 @@ var serverSet = wire.NewSet(
 	
 )
 
-// build App
 // build App
 func newApp(
 	httpServer *http.Server,

+ 3 - 4
cmd/server/wire/wire_gen.go

@@ -82,7 +82,7 @@ func NewWire(viperViper *viper.Viper, logger *log.Logger) (*app.App, func(), err
 	udpLimitRepository := repository.NewUdpLimitRepository(repositoryRepository)
 	udpLimitService := service.NewUdpLimitService(serviceService, udpLimitRepository, requiredService, crawlerService, parserService, hostService)
 	udpLimitHandler := handler.NewUdpLimitHandler(handlerHandler, udpLimitService)
-	requestService := service.NewRequestService(serviceService, viperViper)
+	requestService := service.NewRequestService(serviceService)
 	gatewayGroupService := service.NewGatewayGroupService(serviceService, gatewayGroupRepository, requiredService, parserService, requestService)
 	globalLimitService := service.NewGlobalLimitService(serviceService, globalLimitRepository, duedateService, crawlerService, viperViper, requiredService, parserService, hostService, tcpLimitService, udpLimitService, webLimitService, gatewayGroupService, hostRepository, gatewayGroupRepository)
 	globalLimitHandler := handler.NewGlobalLimitHandler(handlerHandler, globalLimitService)
@@ -90,7 +90,7 @@ func NewWire(viperViper *viper.Viper, logger *log.Logger) (*app.App, func(), err
 	adminService := service.NewAdminService(serviceService, adminRepository)
 	adminHandler := handler.NewAdminHandler(handlerHandler, adminService)
 	gatewayGroupHandler := handler.NewGatewayGroupHandler(handlerHandler, gatewayGroupService)
-	gateWayGroupIpService := service.NewGateWayGroupIpService(serviceService, gateWayGroupIpRepository)
+	gateWayGroupIpService := service.NewGateWayGroupIpService(serviceService, gateWayGroupIpRepository, requestService)
 	gateWayGroupIpHandler := handler.NewGateWayGroupIpHandler(handlerHandler, gateWayGroupIpService)
 	httpServer := server.NewHTTPServer(logger, viperViper, jwtJWT, syncedEnforcer, limiterLimiter, handlerFunc, userHandler, gameShieldHandler, gameShieldBackendHandler, webForwardingHandler, webLimitHandler, tcpforwardingHandler, udpForWardingHandler, tcpLimitHandler, udpLimitHandler, globalLimitHandler, adminHandler, gatewayGroupHandler, gateWayGroupIpHandler)
 	appApp := newApp(httpServer)
@@ -101,7 +101,7 @@ func NewWire(viperViper *viper.Viper, logger *log.Logger) (*app.App, func(), err
 
 // wire.go:
 
-var repositorySet = wire.NewSet(repository.NewDB, repository.NewCasbinEnforcer, repository.NewMongoClient, repository.NewMongoDB, repository.NewRabbitMQ, repository.NewRepository, repository.NewTransaction, repository.NewAdminRepository, repository.NewUserRepository, repository.NewGameShieldRepository, repository.NewGameShieldPublicIpRepository, repository.NewWebForwardingRepository, repository.NewTcpforwardingRepository, repository.NewUdpForWardingRepository, repository.NewGameShieldUserIpRepository, repository.NewWebLimitRepository, repository.NewTcpLimitRepository, repository.NewUdpLimitRepository, repository.NewGameShieldBackendRepository, repository.NewGameShieldSdkIpRepository, repository.NewHostRepository, repository.NewGlobalLimitRepository, repository.NewGatewayGroupRepository, repository.NewGateWayGroupIpRepository)
+var repositorySet = wire.NewSet(repository.NewDB, repository.NewRedis, repository.NewCasbinEnforcer, repository.NewMongoClient, repository.NewMongoDB, repository.NewRabbitMQ, repository.NewRepository, repository.NewTransaction, repository.NewAdminRepository, repository.NewUserRepository, repository.NewGameShieldRepository, repository.NewGameShieldPublicIpRepository, repository.NewWebForwardingRepository, repository.NewTcpforwardingRepository, repository.NewUdpForWardingRepository, repository.NewGameShieldUserIpRepository, repository.NewWebLimitRepository, repository.NewTcpLimitRepository, repository.NewUdpLimitRepository, repository.NewGameShieldBackendRepository, repository.NewGameShieldSdkIpRepository, repository.NewHostRepository, repository.NewGlobalLimitRepository, repository.NewGatewayGroupRepository, repository.NewGateWayGroupIpRepository)
 
 var serviceSet = wire.NewSet(service.NewService, service.NewAoDunService, service.NewUserService, service.NewAdminService, service.NewGameShieldService, service.NewGameShieldPublicIpService, service.NewDuedateService, service.NewFormatterService, service.NewParserService, service.NewRequiredService, service.NewCrawlerService, service.NewWebForwardingService, service.NewTcpforwardingService, service.NewUdpForWardingService, service.NewGameShieldUserIpService, service.NewWebLimitService, service.NewTcpLimitService, service.NewUdpLimitService, service.NewGameShieldBackendService, service.NewGameShieldSdkIpService, service.NewHostService, service.NewGlobalLimitService, service.NewGatewayGroupService, service.NewWafFormatterService, service.NewGateWayGroupIpService, service.NewRequestService)
 
@@ -112,7 +112,6 @@ var limiterSet = wire.NewSet(limiter.NewLimiter, middleware.NewRateLimitMiddlewa
 
 var serverSet = wire.NewSet(server.NewHTTPServer)
 
-// build App
 // build App
 func newApp(
 	httpServer *http.Server,

+ 4 - 4
config/local.yml

@@ -33,8 +33,8 @@ data:
     timeout: 10s  # 连接超时时间
     max_pool_size: 100  # 连接池大小
   redis:
-    addr: 127.0.0.1:6350
-    password: ""
+    addr: 110.42.96.15:26739
+    password: "rzcmhtLCKpsYfFcZ"
     db: 0
     read_timeout: 0.2s
     write_timeout: 0.2s
@@ -65,8 +65,8 @@ crawler:
   keyUrl: "http://115.238.186.121:13350/sdk/key?app_name="
 
 flexCdn:
-  AccessKeyID : "7P9yRc3eTKXxaKIE"
-  AccessKeySecret : "rOfLMlrsRrb1T46PlNMqkFG1RdGewNhr"
+  AccessKeyID : "oH3v5Kr0Q9WLKxzA"
+  AccessKeySecret : "MAB1z3KdEFj8NCZN4KQtE7srTdkNqmYm"
   Url: "http://110.42.96.120:8002/"
 
 # 令牌桶限流配置

+ 4 - 4
config/prod.yml

@@ -36,8 +36,8 @@ data:
     timeout: 10s  # 连接超时时间
     max_pool_size: 100  # 连接池大小
   redis:
-    addr: 127.0.0.1:6350
-    password: ""
+    addr: 110.42.96.15:26739
+    password: "rzcmhtLCKpsYfFcZ"
     db: 0
     read_timeout: 0.2s
     write_timeout: 0.2s
@@ -68,8 +68,8 @@ crawler:
 #  keyUrl: "http://115.238.186.121:13350/sdk/key?app_name="
 
 flexCdn:
-  AccessKeyID : "7P9yRc3eTKXxaKIE"
-  AccessKeySecret : "rOfLMlrsRrb1T46PlNMqkFG1RdGewNhr"
+  AccessKeyID : "oH3v5Kr0Q9WLKxzA"
+  AccessKeySecret : "MAB1z3KdEFj8NCZN4KQtE7srTdkNqmYm"
   Url: "http://110.42.96.120:8002/"
 # 令牌桶限流配置
 limiter:

+ 5 - 2
internal/repository/repository.go

@@ -25,16 +25,18 @@ const ctxTxKey = "TxKey"
 
 type Repository struct {
 	db          *gorm.DB
+	rdb         *redis.Client
 	mongoClient *qmgo.Client
 	mongoDB     *qmgo.Database
 	mq          *rabbitmq.RabbitMQ
 	logger      *log.Logger
-	e  			*casbin.SyncedEnforcer
+	e           *casbin.SyncedEnforcer
 }
 
 func NewRepository(
 	logger *log.Logger,
 	db *gorm.DB,
+	rdb *redis.Client,
 	mongoClient *qmgo.Client,
 	mongoDB *qmgo.Database,
 	mq *rabbitmq.RabbitMQ,
@@ -42,11 +44,12 @@ func NewRepository(
 ) *Repository {
 	return &Repository{
 		db:          db,
+		rdb:         rdb,
 		mongoClient: mongoClient,
 		mongoDB:     mongoDB,
 		mq:          mq,
 		logger:      logger,
-		e: 			 e,
+		e:           e,
 	}
 }
 

+ 299 - 0
internal/service/cdn.go

@@ -5,6 +5,7 @@ import (
 	"encoding/json"
 	"fmt"
 	v1 "github.com/go-nunu/nunu-layout-advanced/api/v1"
+	"github.com/go-nunu/nunu-layout-advanced/internal/repository"
 	"github.com/spf13/viper"
 )
 
@@ -15,6 +16,7 @@ func NewCdnService(
     service *Service,
 	conf *viper.Viper,
 	request RequestService,
+	cdnRepository repository.CdnRepository,
 ) CdnService {
 	return &cdnService{
 		Service:        service,
@@ -22,6 +24,7 @@ func NewCdnService(
 		AccessKeyID:     conf.GetString("flexCdn.AccessKeyID"),
 		AccessKeySecret: conf.GetString("flexCdn.AccessKeySecret"),
 		request:         request,
+		cdnRepository:   cdnRepository,
 	}
 }
 
@@ -31,6 +34,19 @@ type cdnService struct {
 	AccessKeyID     string
 	AccessKeySecret string
 	request         RequestService
+	cdnRepository   repository.CdnRepository
+}
+
+func (s *cdnService) SendData(ctx context.Context,  formData map[string]interface{}, apiUrl string,) ([]byte, error)  {
+	token, err := s.Toekn(ctx)
+	if err != nil {
+		return nil, err
+	}
+	resBody, err := s.request.Request(ctx, formData, apiUrl, "X-Cloud-Access-Token", token)
+	if err != nil {
+		return nil, err
+	}
+	return resBody, nil
 }
 
 func (s *cdnService) GetToken(ctx context.Context) (string, error) {
@@ -54,7 +70,290 @@ func (s *cdnService) GetToken(ctx context.Context) (string, error) {
 		return "", fmt.Errorf("API 错误: code %d, msg '%s'", res.Code, res.Message)
 	}
 
+	err = s.cdnRepository.PutToken(ctx, res.Data.Token)
+	if err != nil {
+		return "", err
+	}
 	return res.Data.Token, nil
 }
 
+func (s *cdnService) Toekn(ctx context.Context) (string, error)  {
+	token, err := s.cdnRepository.GetToken(ctx)
+	if err != nil {
+		return "", err
+	}
+	if token == "" {
+		token, err = s.GetToken(ctx)
+		if err != nil {
+			return "", err
+		}
+	}
+	return token, nil
+}
+
+//注册用户
+func (s *cdnService) AddUser(ctx context.Context, req v1.User) (int64, error) {
+	formData := map[string]interface{}{
+		"id":       req.ID,
+		"username": req.Username,
+		"password": "a7fKiKujgAzzsJ6",
+		"fullname": req.Fullname,
+		"mobile":   req.Mobile,
+		"tel":      req.Tel,
+		"email":    req.Email,
+		"remark":   req.Remark,
+		"source":   req.Source,
+		"nodeClusterId": 1,
+	}
+	apiUrl := s.Url + "UserService/createUser"
+	resBody, err := s.SendData(ctx, formData, apiUrl)
+	if err != nil {
+		return 0, err
+	}
+	type DataStr struct {
+		UserId int64 `json:"userId" form:"userId"`
+	}
+	var res v1.GeneralResponse[DataStr]
+	if err := json.Unmarshal(resBody, &res); err != nil {
+		return  0, fmt.Errorf("反序列化响应 JSON 失败 (内容: %s): %w", string(resBody), err)
+	}
+
+	if res.Code != 200 {
+		return 0, fmt.Errorf("API 错误: code %d, msg '%s'", res.Code, res.Message)
+	}
+	if res.Data.UserId == 0 {
+		return 0, fmt.Errorf("添加用户失败")
+	}
+	return res.Data.UserId, nil
+}
+
+//创建规则分组
+func (s *cdnService) CreateGroup(ctx context.Context, req v1.Group) (int64, error) {
+	formData := map[string]interface{}{
+		"name": req.Name,
+	}
+	apiUrl := s.Url + "ServerGroupService/createServerGroup"
+	resBody, err := s.SendData(ctx, formData, apiUrl)
+	if err != nil {
+		return 0, err
+	}
+	type DataStr struct {
+		ServerGroupId int64 `json:"serverGroupId" form:"serverGroupId"`
+	}
+	var res v1.GeneralResponse[DataStr]
+	if err := json.Unmarshal(resBody, &res); err != nil {
+		return  0, fmt.Errorf("反序列化响应 JSON 失败 (内容: %s): %w", string(resBody), err)
+	}
+	if res.Code != 200 {
+		return 0, fmt.Errorf("API 错误: code %d, msg '%s'", res.Code, res.Message)
+	}
+	if res.Data.ServerGroupId == 0 {
+		return 0, fmt.Errorf("创建规则分组失败")
+	}
+	return res.Data.ServerGroupId, nil
+}
+
+
+//分配套餐
+func (s *cdnService) BindPlan(ctx context.Context, req v1.Plan) (int64, error) {
+	formData := map[string]interface{}{
+		"userId": req.UserId,
+		"planId": req.PlanId,
+		"dayTo":  req.DayTo,
+		"period": req.Period,
+		"countPeriod": req.CountPeriod,
+		"name":   req.Name,
+		"isFree": req.IsFree,
+		"periodDayTo": req.PeriodDayTo,
+	}
+	apiUrl := s.Url + "UserPlanService/buyUserPlan"
+	resBody, err := s.SendData(ctx, formData, apiUrl)
+	if err != nil {
+		return 0, err
+	}
+	type DataStr struct {
+		UserPlanId int64 `json:"userPlanId" form:"userPlanId"`
+	}
+	var res v1.GeneralResponse[DataStr]
+	if err := json.Unmarshal(resBody, &res); err != nil {
+		return  0, fmt.Errorf("反序列化响应 JSON 失败 (内容: %s): %w", string(resBody), err)
+	}
+	if res.Code != 200 {
+		return 0, fmt.Errorf("API 错误: code %d, msg '%s'", res.Code, res.Message)
+	}
+	if res.Data.UserPlanId == 0 {
+		return 0, fmt.Errorf("分配套餐失败")
+	}
+	return res.Data.UserPlanId, nil
+}
+
+//续费套餐
+func (s *cdnService) RenewPlan(ctx context.Context, req v1.RenewalPlan) error {
+	formData := map[string]interface{}{
+		"userPlanId": req.UserPlanId,
+		"dayTo":  req.DayTo,
+		"period": req.Period,
+		"countPeriod": req.CountPeriod,
+		"isFree": req.IsFree,
+		"periodDayTo": req.PeriodDayTo,
+	}
+	apiUrl := s.Url + "UserPlanService/renewUserPlan"
+	resBody, err := s.SendData(ctx, formData, apiUrl)
+	if err != nil {
+		return  err
+	}
+	var res v1.GeneralResponse[any]
+	if err := json.Unmarshal(resBody, &res); err != nil {
+		return fmt.Errorf("反序列化响应 JSON 失败 (内容: %s): %w", string(resBody), err)
+	}
+	if res.Code != 200 {
+		return fmt.Errorf("API 错误: code %d, msg '%s'", res.Code, res.Message)
+	}
+	return nil
+}
 
+
+//创建网站
+func (s *cdnService) CreateWebsite(ctx context.Context, req v1.Website) (int64, error) {
+	formData := map[string]interface{}{
+		"userId": req.UserId,
+		"type": req.Type,
+		"name": req.Name,
+		"description": req.Description,
+		"serverNamesJSON": req.ServerNamesJSON,
+		"httpJSON": req.HttpJSON,
+		"httpsJSON": req.HttpsJSON,
+		"tcpJSON": req.TcpJSON,
+		"tlsJSON": req.TlsJSON,
+		"udpJSON": req.UdpJSON,
+		"webId": req.WebId,
+		"reverseProxyJSON": req.ReverseProxyJSON,
+		"serverGroupIds": req.ServerGroupIds,
+		"userPlanId": req.UserPlanId,
+		"nodeClusterId": req.NodeClusterId,
+	}
+	apiUrl := s.Url + "ServerService/createServer"
+	resBody, err := s.SendData(ctx, formData, apiUrl)
+	if err != nil {
+		return 0, err
+	}
+	type DataStr struct {
+		WebsiteId int64 `json:"websiteId" form:"websiteId"`
+	}
+	var res v1.GeneralResponse[DataStr]
+	if err := json.Unmarshal(resBody, &res); err != nil {
+		return  0, fmt.Errorf("反序列化响应 JSON 失败 (内容: %s): %w", string(resBody), err)
+	}
+	if res.Code != 200 {
+		return 0, fmt.Errorf("API 错误: code %d, msg '%s'", res.Code, res.Message)
+	}
+	if res.Data.WebsiteId == 0 {
+		return 0, fmt.Errorf("创建网站失败")
+	}
+	return res.Data.WebsiteId, nil
+}
+
+func (s *cdnService) EditTcpProtocol(ctx context.Context, req v1.ProxyJson,action string)  error  {
+	formData := map[string]interface{}{
+		"serverId": req.ServerId,
+	}
+	var apiUrl string
+	switch action {
+	case "tcp":
+		formData["tcpJSON"] = req.JSON
+		apiUrl = s.Url + "ServerService/updateServerTCP"
+	case "tls":
+		formData["tlsJSON"] = req.JSON
+		apiUrl = s.Url + "ServerService/updateServerTLS"
+	case "udp":
+		formData["udpJSON"] = req.JSON
+		apiUrl = s.Url + "ServerService/updateServerUDP"
+	case "http":
+		formData["httpJSON"] = req.JSON
+		apiUrl = s.Url + "ServerService/updateServerHTTP"
+	case "https":
+		formData["httpsJSON"] = req.JSON
+		apiUrl = s.Url + "ServerService/updateServerHTTPS"
+	default:
+		return fmt.Errorf("不支持的协议类型")
+	}
+	resBody, err := s.SendData(ctx, formData, apiUrl)
+	if err != nil {
+		return err
+	}
+	var res v1.GeneralResponse[any]
+	if err := json.Unmarshal(resBody, &res); err != nil {
+		return fmt.Errorf("反序列化响应 JSON 失败 (内容: %s): %w", string(resBody), err)
+	}
+	if res.Code != 200 {
+		return fmt.Errorf("API 错误: code %d, msg '%s'", res.Code, res.Message)
+	}
+	return nil
+}
+
+func (s *cdnService) CreateOrigin(ctx context.Context, req v1.Origin) (int64, error)  {
+	formData := map[string]interface{}{
+		"name": req.Name,
+		"addr": req.Addr,
+		"ossJSON": req.OssJSON,
+		"description": req.Description,
+		"weight": req.Weight,
+		"isOn": req.IsOn,
+		"domains": req.Domains,
+		"certRefJSON": req.CertRefJSON,
+		"host": req.Host,
+		"followPort": req.FollowPort,
+		"http2Enabled": req.Http2Enabled,
+		"tlsSecurityVerifyMode": req.TlsSecurityVerifyMode,
+	}
+	apiUrl := s.Url + "OriginService/createOrigin"
+	resBody, err := s.SendData(ctx, formData, apiUrl)
+	if err != nil {
+		return 0, err
+	}
+	type DataStr struct {
+		OriginId int64 `json:"originId" form:"originId"`
+	}
+	var res v1.GeneralResponse[DataStr]
+	if err := json.Unmarshal(resBody, &res); err != nil {
+		return  0, fmt.Errorf("反序列化响应 JSON 失败 (内容: %s): %w", string(resBody), err)
+	}
+	if res.Code != 200 {
+		return 0, fmt.Errorf("API 错误: code %d, msg '%s'", res.Code, res.Message)
+	}
+	if res.Data.OriginId == 0 {
+		return 0, fmt.Errorf("创建源站失败")
+	}
+	return res.Data.OriginId, nil
+}
+
+func (s *cdnService) EditOrigin(ctx context.Context, req v1.Origin) error {
+	formData := map[string]interface{}{
+		"originId": req.OriginId,
+		"name": req.Name,
+		"addr": req.Addr,
+		"ossJSON": req.OssJSON,
+		"description": req.Description,
+		"weight": req.Weight,
+		"isOn": req.IsOn,
+		"domains": req.Domains,
+		"certRefJSON": req.CertRefJSON,
+		"host": req.Host,
+		"followPort": req.FollowPort,
+		"http2Enabled": req.Http2Enabled,
+		"tlsSecurityVerifyMode": req.TlsSecurityVerifyMode,
+	}
+	apiUrl := s.Url + "OriginService/updateOrigin"
+	resBody, err := s.SendData(ctx, formData, apiUrl)
+	if err != nil {
+		return err
+	}
+	var res v1.GeneralResponse[any]
+	if err := json.Unmarshal(resBody, &res); err != nil {
+		return fmt.Errorf("反序列化响应 JSON 失败 (内容: %s): %w", string(resBody), err)
+	}
+	if res.Code != 200 {
+		return fmt.Errorf("API 错误: code %d, msg '%s'", res.Code, res.Message)
+	}
+	return nil
+}