Repository: dragonflyoss/Dragonfly2
Pull request #1142 (2022-03-09 06:38:39)

Remove residues left by E2E tests
Signed-off-by: sunwp <244372610@qq.com>

<!--- Provide a general summary of your changes in the Title above -->

## Description
Remove residues left by E2E tests
<!--- Describe your changes in detail -->

## Related Issue

<!--- This project only accepts pull requests related to open issues -->
<!--- If suggesting a new feature or change, please discuss it in an issue first -->
<!--- If fixing a bug, there should be an issue describing it with steps to reproduce -->
<!--- Please link to the issue here: -->

## Motivation and Context

<!--- Why is this change required? What problem does it solve? -->

## Screenshots (if appropriate)

## Types of changes

<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->

- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
- [ ] Documentation Update (if none of the other choices apply)

## Checklist

<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->

- [ ] Code compiles correctly.
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have read the **CONTRIBUTING** document.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.


codecov-commenter: # [Codecov](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/1142?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss) Report
> Merging [#1142](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/1142?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss) (650ecba) into [main](https://codecov.io/gh/dragonflyoss/Dragonfly2/commit/fc63c3714de313f0a7799f3d54c326417452aabc?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss) (fc63c37) will **increase** coverage by `0.05%`.
> The diff coverage is `n/a`.

[![Impacted file tree graph](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/1142/graphs/tree.svg?width=650&height=150&src=pr&token=PUICJYSAT7&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss)](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/1142?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss)

```diff
@@            Coverage Diff             @@
##             main    #1142      +/-   ##
==========================================
+ Coverage   45.91%   45.97%   +0.05%     
==========================================
  Files         130      130              
  Lines       10035    10039       +4     
==========================================
+ Hits         4608     4615       +7     
+ Misses       4905     4902       -3     
  Partials      522      522              
```

| Flag | Coverage Δ | |
|---|---|---|
| Object-compatibility-e2etests | `∅ <ø> (∅)` | |
| e2etests | `?` | |
| unittests | `45.97% <ø> (+0.05%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/1142?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss) | Coverage Δ | |
|---|---|---|
| [cdn/supervisor/progress/progress.go](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/1142/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss#diff-Y2RuL3N1cGVydmlzb3IvcHJvZ3Jlc3MvcHJvZ3Jlc3MuZ28=) | `90.62% <0.00%> (-6.25%)` | :arrow_down: |
| [scheduler/service/service.go](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/1142/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss#diff-c2NoZWR1bGVyL3NlcnZpY2Uvc2VydmljZS5nbw==) | `87.68% <0.00%> (+0.22%)` | :arrow_up: |
| [scheduler/resource/peer.go](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/1142/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss#diff-c2NoZWR1bGVyL3Jlc291cmNlL3BlZXIuZ28=) | `77.70% <0.00%> (+0.58%)` | :arrow_up: |
| [client/daemon/peer/piece\_broker.go](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/1142/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss#diff-Y2xpZW50L2RhZW1vbi9wZWVyL3BpZWNlX2Jyb2tlci5nbw==) | `88.88% <0.00%> (+3.70%)` | :arrow_up: |
| [scheduler/rpcserver/rpcserver.go](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/1142/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss#diff-c2NoZWR1bGVyL3JwY3NlcnZlci9ycGNzZXJ2ZXIuZ28=) | `25.00% <0.00%> (+6.81%)` | :arrow_up: |


Patch:
diff --git a/Makefile b/Makefile
index a5c5199c..c1d3716b 100644
--- a/Makefile
+++ b/Makefile
@@ -236,6 +236,8 @@ e2e-test-coverage: install-e2e-test build-e2e-sha256sum
 # Clean E2E tests
 clean-e2e-test: 
 	@kind delete cluster
+	@echo "cleaning log file"
+	@rm -rf test/e2e/*.log
 .PHONY: clean-e2e-test
 
 # Kind load dragonlfy
diff --git a/api/manager/docs.go b/api/manager/docs.go
index e72ce9a0..e1bbc3fb 100644
--- a/api/manager/docs.go
+++ b/api/manager/docs.go
@@ -2,16 +2,9 @@
 // This file was generated by swaggo/swag
 package manager
 
-import (
-	"bytes"
-	"encoding/json"
-	"strings"
-	"text/template"
+import "github.com/swaggo/swag"
 
-	"github.com/swaggo/swag"
-)
-
-var doc = `{
+const docTemplate = `{
     "schemes": {{ marshal .Schemes }},
     "swagger": "2.0",
     "info": {
@@ -3299,6 +3292,54 @@ var doc = `{
                         "description": ""
                     }
                 }
+            },
+            "patch": {
+                "description": "Update by json config",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "User"
+                ],
+                "summary": "Update User",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "description": "User",
+                        "name": "User",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/types.UpdateUserRequest"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/model.User"
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
             }
         },
         "/users/{id}/reset_password": {
@@ -3887,7 +3928,11 @@ var doc = `{
             ],
             "properties": {
                 "action": {
-                    "type": "string"
+                    "type": "string",
+                    "enum": [
+                        "read",
+                        "*"
+                    ]
                 },
                 "object": {
                     "type": "string"
@@ -3902,7 +3947,11 @@ var doc = `{
             ],
             "properties": {
                 "action": {
-                    "type": "string"
+                    "type": "string",
+                    "enum": [
+                        "read",
+                        "*"
+                    ]
                 },
                 "object": {
                     "type": "string"
@@ -3913,7 +3962,9 @@ var doc = `{
             "type": "object",
             "properties": {
                 "load_limit": {
-                    "type": "integer"
+                    "type": "integer",
+                    "maximum": 10000,
+                    "minimum": 1
                 },
                 "net_topology": {
                     "type": "string"
@@ -3937,7 +3988,11 @@ var doc = `{
                     "type": "string"
                 },
                 "state": {
-                    "type": "string"
+                    "type": "string",
+                    "enum": [
+                        "enable",
+                        "disable"
+                    ]
                 },
                 "url": {
                     "type": "string"
@@ -4079,7 +4134,11 @@ var doc = `{
                     "type": "string"
                 },
                 "name": {
-                    "type": "string"
+                    "type": "string",
+                    "enum": [
+                        "github",
+                        "google"
+                    ]
                 },
                 "redirect_url": {
                     "type": "string"
@@ -4224,7 +4283,11 @@ var doc = `{
                     }
                 },
                 "type": {
-                    "type": "string"
+                    "type": "string",
+                    "enum": [
+                        "image",
+                        "file"
+                    ]
                 },
                 "url": {
                     "type": "string"
@@ -4247,7 +4310,11 @@ var doc = `{
             ],
             "properties": {
                 "action": {
-                    "type": "string"
+                    "type": "string",
+                    "enum": [
+                        "read",
+                        "*"
+                    ]
                 },
                 "object": {
                     "type": "string"
@@ -4279,10 +4346,14 @@ var doc = `{
             ],
             "properties": {
                 "new_password": {
-                    "type": "string"
+                    "type": "string",
+                    "maxLength": 20,
+                    "minLength": 8
                 },
                 "old_password": {
-                    "type": "string"
+                    "type": "string",
+                    "maxLength": 20,
+                    "minLength": 8
                 }
             }
         },
@@ -4290,12 +4361,26 @@ var doc = `{
             "type": "object",
             "properties": {
                 "load_limit": {
-                    "type": "integer"
+                    "type": "integer",
+                    "maximum": 10000,
+                    "minimum": 1
+                },
+                "parallel_count": {
+                    "type": "integer",
+                    "maximum": 50,
+                    "minimum": 1
                 }
             }
         },
         "types.SchedulerClusterConfig": {
-            "type": "object"
+            "type": "object",
+            "properties": {
+                "filter_parent_limit": {
+                    "type": "integer",
+                    "maximum": 100,
+                    "minimum": 1
+                }
+            }
         },
         "types.SchedulerClusterScopes": {
             "type": "object",
@@ -4332,10 +4417,14 @@ var doc = `{
                     "type": "string"
                 },
                 "name": {
-                    "type": "string"
+                    "type": "string",
+                    "maxLength": 10,
+                    "minLength": 3
                 },
                 "password": {
-                    "type": "string"
+                    "type": "string",
+                    "maxLength": 20,
+                    "minLength": 8
                 },
                 "phone": {
                     "type": "string"
@@ -4358,7 +4447,11 @@ var doc = `{
                     "type": "string"
                 },
                 "state": {
-                    "type": "string"
+                    "type": "string",
+                    "enum": [
+                        "enable",
+                        "disable"
+                    ]
                 },
                 "url": {
                     "type": "string"
@@ -4449,7 +4542,11 @@ var doc = `{
                     "type": "string"
                 },
                 "name": {
-                    "type": "string"
+                    "type": "string",
+                    "enum": [
+                        "github",
+                        "google"
+                    ]
                 },
                 "redirect_url": {
                     "type": "string"
@@ -4539,60 +4636,42 @@ var doc = `{
                     "type": "string"
                 }
             }
+        },
+        "types.UpdateUserRequest": {
+            "type": "object",
+            "properties": {
+                "avatar": {
+                    "type": "string"
+                },
+                "bio": {
+                    "type": "string"
+                },
+                "email": {
+                    "type": "string"
+                },
+                "location": {
+                    "type": "string"
+                },
+                "phone": {
+                    "type": "string"
+                }
+            }
         }
     }
 }`
 
-type swaggerInfo struct {
-	Version     string
-	Host        string
-	BasePath    string
-	Schemes     []string
-	Title       string
-	Description string
-}
-
 // SwaggerInfo holds exported Swagger Info so clients can modify it
-var SwaggerInfo = swaggerInfo{
-	Version:     "1.0.0",
-	Host:        "localhost:8080",
-	BasePath:    "/api/v1",
-	Schemes:     []string{},
-	Title:       "Dragonfly Manager",
-	Description: "Dragonfly Manager Server",
-}
-
-type s struct{}
-
-func (s *s) ReadDoc() string {
-	sInfo := SwaggerInfo
-	sInfo.Description = strings.Replace(sInfo.Description, "\n", "\\n", -1)
-
-	t, err := template.New("swagger_info").Funcs(template.FuncMap{
-		"marshal": func(v interface{}) string {
-			a, _ := json.Marshal(v)
-			return string(a)
-		},
-		"escape": func(v interface{}) string {
-			// escape tabs
-			str := strings.Replace(v.(string), "\t", "\\t", -1)
-			// replace " with \", and if that results in \\", replace that with \\\"
-			str = strings.Replace(str, "\"", "\\\"", -1)
-			return strings.Replace(str, "\\\\\"", "\\\\\\\"", -1)
-		},
-	}).Parse(doc)
-	if err != nil {
-		return doc
-	}
-
-	var tpl bytes.Buffer
-	if err := t.Execute(&tpl, sInfo); err != nil {
-		return doc
-	}
-
-	return tpl.String()
+var SwaggerInfo = &swag.Spec{
+	Version:          "1.0.0",
+	Host:             "localhost:8080",
+	BasePath:         "/api/v1",
+	Schemes:          []string{},
+	Title:            "Dragonfly Manager",
+	Description:      "Dragonfly Manager Server",
+	InfoInstanceName: "swagger",
+	SwaggerTemplate:  docTemplate,
 }
 
 func init() {
-	swag.Register("swagger", &s{})
+	swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
 }
diff --git a/api/manager/swagger.json b/api/manager/swagger.json
index 955e4808..fff95002 100644
--- a/api/manager/swagger.json
+++ b/api/manager/swagger.json
@@ -3285,6 +3285,54 @@
                         "description": ""
                     }
                 }
+            },
+            "patch": {
+                "description": "Update by json config",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "User"
+                ],
+                "summary": "Update User",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "description": "User",
+                        "name": "User",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/types.UpdateUserRequest"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/model.User"
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
             }
         },
         "/users/{id}/reset_password": {
@@ -3873,7 +3921,11 @@
             ],
             "properties": {
                 "action": {
-                    "type": "string"
+                    "type": "string",
+                    "enum": [
+                        "read",
+                        "*"
+                    ]
                 },
                 "object": {
                     "type": "string"
@@ -3888,7 +3940,11 @@
             ],
             "properties": {
                 "action": {
-                    "type": "string"
+                    "type": "string",
+                    "enum": [
+                        "read",
+                        "*"
+                    ]
                 },
                 "object": {
                     "type": "string"
@@ -3899,7 +3955,9 @@
             "type": "object",
             "properties": {
                 "load_limit": {
-                    "type": "integer"
+                    "type": "integer",
+                    "maximum": 10000,
+                    "minimum": 1
                 },
                 "net_topology": {
                     "type": "string"
@@ -3923,7 +3981,11 @@
                     "type": "string"
                 },
                 "state": {
-                    "type": "string"
+                    "type": "string",
+                    "enum": [
+                        "enable",
+                        "disable"
+                    ]
                 },
                 "url": {
                     "type": "string"
@@ -4065,7 +4127,11 @@
                     "type": "string"
                 },
                 "name": {
-                    "type": "string"
+                    "type": "string",
+                    "enum": [
+                        "github",
+                        "google"
+                    ]
                 },
                 "redirect_url": {
                     "type": "string"
@@ -4210,7 +4276,11 @@
                     }
                 },
                 "type": {
-                    "type": "string"
+                    "type": "string",
+                    "enum": [
+                        "image",
+                        "file"
+                    ]
                 },
                 "url": {
                     "type": "string"
@@ -4233,7 +4303,11 @@
             ],
             "properties": {
                 "action": {
-                    "type": "string"
+                    "type": "string",
+                    "enum": [
+                        "read",
+                        "*"
+                    ]
                 },
                 "object": {
                     "type": "string"
@@ -4265,10 +4339,14 @@
             ],
             "properties": {
                 "new_password": {
-                    "type": "string"
+                    "type": "string",
+                    "maxLength": 20,
+                    "minLength": 8
                 },
                 "old_password": {
-                    "type": "string"
+                    "type": "string",
+                    "maxLength": 20,
+                    "minLength": 8
                 }
             }
         },
@@ -4276,12 +4354,26 @@
             "type": "object",
             "properties": {
                 "load_limit": {
-                    "type": "integer"
+                    "type": "integer",
+                    "maximum": 10000,
+                    "minimum": 1
+                },
+                "parallel_count": {
+                    "type": "integer",
+                    "maximum": 50,
+                    "minimum": 1
                 }
             }
         },
         "types.SchedulerClusterConfig": {
-            "type": "object"
+            "type": "object",
+            "properties": {
+                "filter_parent_limit": {
+                    "type": "integer",
+                    "maximum": 100,
+                    "minimum": 1
+                }
+            }
         },
         "types.SchedulerClusterScopes": {
             "type": "object",
@@ -4318,10 +4410,14 @@
                     "type": "string"
                 },
                 "name": {
-                    "type": "string"
+                    "type": "string",
+                    "maxLength": 10,
+                    "minLength": 3
                 },
                 "password": {
-                    "type": "string"
+                    "type": "string",
+                    "maxLength": 20,
+                    "minLength": 8
                 },
                 "phone": {
                     "type": "string"
@@ -4344,7 +4440,11 @@
                     "type": "string"
                 },
                 "state": {
-                    "type": "string"
+                    "type": "string",
+                    "enum": [
+                        "enable",
+                        "disable"
+                    ]
                 },
                 "url": {
                     "type": "string"
@@ -4435,7 +4535,11 @@
                     "type": "string"
                 },
                 "name": {
-                    "type": "string"
+                    "type": "string",
+                    "enum": [
+                        "github",
+                        "google"
+                    ]
                 },
                 "redirect_url": {
                     "type": "string"
@@ -4525,6 +4629,26 @@
                     "type": "string"
                 }
             }
+        },
+        "types.UpdateUserRequest": {
+            "type": "object",
+            "properties": {
+                "avatar": {
+                    "type": "string"
+                },
+                "bio": {
+                    "type": "string"
+                },
+                "email": {
+                    "type": "string"
+                },
+                "location": {
+                    "type": "string"
+                },
+                "phone": {
+                    "type": "string"
+                }
+            }
         }
     }
 }
\ No newline at end of file
diff --git a/api/manager/swagger.yaml b/api/manager/swagger.yaml
index 3d20e07f..1b3b1c6a 100644
--- a/api/manager/swagger.yaml
+++ b/api/manager/swagger.yaml
@@ -279,6 +279,9 @@ definitions:
   rbac.Permission:
     properties:
       action:
+        enum:
+        - read
+        - '*'
         type: string
       object:
         type: string
@@ -289,6 +292,9 @@ definitions:
   types.AddPermissionForRoleRequest:
     properties:
       action:
+        enum:
+        - read
+        - '*'
         type: string
       object:
         type: string
@@ -299,6 +305,8 @@ definitions:
   types.CDNClusterConfig:
     properties:
       load_limit:
+        maximum: 10000
+        minimum: 1
         type: integer
       net_topology:
         type: string
@@ -312,6 +320,9 @@ definitions:
       name:
         type: string
       state:
+        enum:
+        - enable
+        - disable
         type: string
       url:
         type: string
@@ -408,6 +419,9 @@ definitions:
       client_secret:
         type: string
       name:
+        enum:
+        - github
+        - google
         type: string
       redirect_url:
         type: string
@@ -507,6 +521,9 @@ definitions:
           type: string
         type: object
       type:
+        enum:
+        - image
+        - file
         type: string
       url:
         type: string
@@ -522,6 +539,9 @@ definitions:
   types.DeletePermissionForRoleRequest:
     properties:
       action:
+        enum:
+        - read
+        - '*'
         type: string
       object:
         type: string
@@ -543,8 +563,12 @@ definitions:
   types.ResetPasswordRequest:
     properties:
       new_password:
+        maxLength: 20
+        minLength: 8
         type: string
       old_password:
+        maxLength: 20
+        minLength: 8
         type: string
     required:
     - new_password
@@ -553,9 +577,20 @@ definitions:
   types.SchedulerClusterClientConfig:
     properties:
       load_limit:
+        maximum: 10000
+        minimum: 1
+        type: integer
+      parallel_count:
+        maximum: 50
+        minimum: 1
         type: integer
     type: object
   types.SchedulerClusterConfig:
+    properties:
+      filter_parent_limit:
+        maximum: 100
+        minimum: 1
+        type: integer
     type: object
   types.SchedulerClusterScopes:
     properties:
@@ -577,8 +612,12 @@ definitions:
       location:
         type: string
       name:
+        maxLength: 10
+        minLength: 3
         type: string
       password:
+        maxLength: 20
+        minLength: 8
         type: string
       phone:
         type: string
@@ -596,6 +635,9 @@ definitions:
       name:
         type: string
       state:
+        enum:
+        - enable
+        - disable
         type: string
       url:
         type: string
@@ -657,6 +699,9 @@ definitions:
       client_secret:
         type: string
       name:
+        enum:
+        - github
+        - google
         type: string
       redirect_url:
         type: string
@@ -716,6 +761,19 @@ definitions:
       proxy_domain:
         type: string
     type: object
+  types.UpdateUserRequest:
+    properties:
+      avatar:
+        type: string
+      bio:
+        type: string
+      email:
+        type: string
+      location:
+        type: string
+      phone:
+        type: string
+    type: object
 host: localhost:8080
 info:
   contact:
@@ -2912,6 +2970,38 @@ paths:
       summary: Get User
       tags:
       - User
+    patch:
+      consumes:
+      - application/json
+      description: Update by json config
+      parameters:
+      - description: id
+        in: path
+        name: id
+        required: true
+        type: string
+      - description: User
+        in: body
+        name: User
+        required: true
+        schema:
+          $ref: '#/definitions/types.UpdateUserRequest'
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            $ref: '#/definitions/model.User'
+        "400":
+          description: ""
+        "404":
+          description: ""
+        "500":
+          description: ""
+      summary: Update User
+      tags:
+      - User
   /users/{id}/reset_password:
     post:
       consumes:
diff --git a/cdn/supervisor/cdn/storage/disk/config.go b/cdn/supervisor/cdn/storage/disk/config.go
index d59d0ee7..8a8873a6 100644
--- a/cdn/supervisor/cdn/storage/disk/config.go
+++ b/cdn/supervisor/cdn/storage/disk/config.go
@@ -65,7 +65,7 @@ func getDiskDriverConfig(diskDriver storedriver.Driver) (*storage.DriverConfig,
 		BaseDir: diskDriver.GetBaseDir(),
 		DriverGCConfig: &storage.DriverGCConfig{
 			YoungGCThreshold:  yongGCThreshold,
-			FullGCThreshold:   25 * unit.GB,
+			FullGCThreshold:   5 * unit.GB,
 			IntervalThreshold: 2 * time.Hour,
 			CleanRatio:        1,
 		},
diff --git a/cdn/supervisor/cdn/storage/hybrid/config.go b/cdn/supervisor/cdn/storage/hybrid/config.go
index e9958619..71fa9685 100644
--- a/cdn/supervisor/cdn/storage/hybrid/config.go
+++ b/cdn/supervisor/cdn/storage/hybrid/config.go
@@ -70,7 +70,7 @@ func getDiskDriverConfig(diskDriver storedriver.Driver) (*storage.DriverConfig,
 		BaseDir: diskDriver.GetBaseDir(),
 		DriverGCConfig: &storage.DriverGCConfig{
 			YoungGCThreshold:  yongGCThreshold,
-			FullGCThreshold:   25 * unit.GB,
+			FullGCThreshold:   5 * unit.GB,
 			IntervalThreshold: 2 * time.Hour,
 			CleanRatio:        1,
 		},
diff --git a/client/config/peerhost.go b/client/config/peerhost.go
index be187a41..29e79879 100644
--- a/client/config/peerhost.go
+++ b/client/config/peerhost.go
@@ -38,7 +38,6 @@ import (
 	"d7y.io/dragonfly/v2/internal/dfnet"
 	"d7y.io/dragonfly/v2/pkg/unit"
 	"d7y.io/dragonfly/v2/pkg/util/net/iputils"
-	"d7y.io/dragonfly/v2/pkg/util/stringutils"
 )
 
 type DaemonConfig = DaemonOption
@@ -57,14 +56,13 @@ type DaemonOption struct {
 	DataDir     string `mapstructure:"dataDir" yaml:"dataDir"`
 	KeepStorage bool   `mapstructure:"keepStorage" yaml:"keepStorage"`
 
-	Scheduler    SchedulerOption `mapstructure:"scheduler" yaml:"scheduler"`
-	Host         HostOption      `mapstructure:"host" yaml:"host"`
-	Download     DownloadOption  `mapstructure:"download" yaml:"download"`
-	Proxy        *ProxyOption    `mapstructure:"proxy" yaml:"proxy"`
-	Upload       UploadOption    `mapstructure:"upload" yaml:"upload"`
-	Storage      StorageOption   `mapstructure:"storage" yaml:"storage"`
-	ConfigServer string          `mapstructure:"configServer" yaml:"configServer"`
-	Health       *HealthOption   `mapstructure:"health" yaml:"health"`
+	Scheduler SchedulerOption `mapstructure:"scheduler" yaml:"scheduler"`
+	Host      HostOption      `mapstructure:"host" yaml:"host"`
+	Download  DownloadOption  `mapstructure:"download" yaml:"download"`
+	Proxy     *ProxyOption    `mapstructure:"proxy" yaml:"proxy"`
+	Upload    UploadOption    `mapstructure:"upload" yaml:"upload"`
+	Storage   StorageOption   `mapstructure:"storage" yaml:"storage"`
+	Health    *HealthOption   `mapstructure:"health" yaml:"health"`
 	// TODO WIP, did not use
 	Reload ReloadOption `mapstructure:"reloadOption" yaml:"reloadOption"`
 }
@@ -115,20 +113,19 @@ func (p *DaemonOption) Convert() error {
 }
 
 func (p *DaemonOption) Validate() error {
-	if len(p.Scheduler.NetAddrs) == 0 && stringutils.IsBlank(p.ConfigServer) {
-		return errors.New("empty schedulers and config server is not specified")
-	}
-
 	if p.Scheduler.Manager.Enable {
-		if len(p.Scheduler.NetAddrs) == 0 {
+		if len(p.Scheduler.Manager.NetAddrs) == 0 {
 			return errors.New("manager addr is not specified")
 		}
 
 		if p.Scheduler.Manager.RefreshInterval == 0 {
 			return errors.New("manager refreshInterval is not specified")
 		}
+		return nil
+	}
+	if len(p.Scheduler.NetAddrs) == 0 {
+		return errors.New("empty schedulers and config server is not specified")
 	}
-
 	return nil
 }
 
diff --git a/docs/en/api-reference/api-reference.md b/docs/en/api-reference/api-reference.md
index 625f59ea..d8541fe3 100644
--- a/docs/en/api-reference/api-reference.md
+++ b/docs/en/api-reference/api-reference.md
@@ -3284,6 +3284,50 @@ Get User by id
 * User
 
 
+<a name="api-v1-users-id-patch"></a>
+### Update User
+```
+PATCH /api/v1/users/{id}
+```
+
+
+#### Description
+Update by json config
+
+
+#### Parameters
+
+|Type|Name|Description|Schema|
+|---|---|---|---|
+|**Path**|**id**  <br>*required*|id|string|
+|**Body**|**User**  <br>*required*|User|[types.UpdateUserRequest](#types-updateuserrequest)|
+
+
+#### Responses
+
+|HTTP Code|Description|Schema|
+|---|---|---|
+|**200**|OK|[model.User](#model-user)|
+|**400**||No Content|
+|**404**||No Content|
+|**500**||No Content|
+
+
+#### Consumes
+
+* `application/json`
+
+
+#### Produces
+
+* `application/json`
+
+
+#### Tags
+
+* User
+
+
 <a name="api-v1-users-id-reset_password-post"></a>
 ### Reset Password For User
 ```
@@ -3651,7 +3695,7 @@ delete role by uri config
 
 |Name|Schema|
 |---|---|
-|**action**  <br>*required*|string|
+|**action**  <br>*required*|enum (read, *)|
 |**object**  <br>*required*|string|
 
 
@@ -3660,17 +3704,17 @@ delete role by uri config
 
 |Name|Schema|
 |---|---|
-|**action**  <br>*required*|string|
+|**action**  <br>*required*|enum (read, *)|
 |**object**  <br>*required*|string|
 
 
 <a name="types-cdnclusterconfig"></a>
 ### types.CDNClusterConfig
 
-|Name|Schema|
-|---|---|
-|**load_limit**  <br>*optional*|integer|
-|**net_topology**  <br>*optional*|string|
+|Name|Description|Schema|
+|---|---|---|
+|**load_limit**  <br>*optional*|**Minimum value** : `1`  <br>**Maximum value** : `10000`|integer|
+|**net_topology**  <br>*optional*||string|
 
 
 <a name="types-createapplicationrequest"></a>
@@ -3681,7 +3725,7 @@ delete role by uri config
 |**bio**  <br>*optional*|string|
 |**download_rate_limit**  <br>*optional*|integer|
 |**name**  <br>*required*|string|
-|**state**  <br>*optional*|string|
+|**state**  <br>*optional*|enum (enable, disable)|
 |**url**  <br>*optional*|string|
 |**user_id**  <br>*required*|integer|
 
@@ -3744,7 +3788,7 @@ delete role by uri config
 |**bio**  <br>*optional*|string|
 |**client_id**  <br>*required*|string|
 |**client_secret**  <br>*required*|string|
-|**name**  <br>*required*|string|
+|**name**  <br>*required*|enum (github, google)|
 |**redirect_url**  <br>*optional*|string|
 
 
@@ -3813,7 +3857,7 @@ delete role by uri config
 |---|---|
 |**filter**  <br>*optional*|string|
 |**headers**  <br>*optional*|< string, string > map|
-|**type**  <br>*required*|string|
+|**type**  <br>*required*|enum (image, file)|
 |**url**  <br>*required*|string|
 
 
@@ -3830,7 +3874,7 @@ delete role by uri config
 
 |Name|Schema|
 |---|---|
-|**action**  <br>*required*|string|
+|**action**  <br>*required*|enum (read, *)|
 |**object**  <br>*required*|string|
 
 
@@ -3848,23 +3892,27 @@ delete role by uri config
 <a name="types-resetpasswordrequest"></a>
 ### types.ResetPasswordRequest
 
-|Name|Schema|
-|---|---|
-|**new_password**  <br>*required*|string|
-|**old_password**  <br>*required*|string|
+|Name|Description|Schema|
+|---|---|---|
+|**new_password**  <br>*required*|**Length** : `8 - 20`|string|
+|**old_password**  <br>*required*|**Length** : `8 - 20`|string|
 
 
 <a name="types-schedulerclusterclientconfig"></a>
 ### types.SchedulerClusterClientConfig
 
-|Name|Schema|
-|---|---|
-|**load_limit**  <br>*optional*|integer|
+|Name|Description|Schema|
+|---|---|---|
+|**load_limit**  <br>*optional*|**Minimum value** : `1`  <br>**Maximum value** : `10000`|integer|
+|**parallel_count**  <br>*optional*|**Minimum value** : `1`  <br>**Maximum value** : `50`|integer|
 
 
 <a name="types-schedulerclusterconfig"></a>
 ### types.SchedulerClusterConfig
-*Type* : object
+
+|Name|Description|Schema|
+|---|---|---|
+|**filter_parent_limit**  <br>*optional*|**Minimum value** : `1`  <br>**Maximum value** : `100`|integer|
 
 
 <a name="types-schedulerclusterscopes"></a>
@@ -3880,15 +3928,15 @@ delete role by uri config
 <a name="types-signuprequest"></a>
 ### types.SignUpRequest
 
-|Name|Schema|
-|---|---|
-|**avatar**  <br>*optional*|string|
-|**bio**  <br>*optional*|string|
-|**email**  <br>*required*|string|
-|**location**  <br>*optional*|string|
-|**name**  <br>*required*|string|
-|**password**  <br>*required*|string|
-|**phone**  <br>*optional*|string|
+|Name|Description|Schema|
+|---|---|---|
+|**avatar**  <br>*optional*||string|
+|**bio**  <br>*optional*||string|
+|**email**  <br>*required*||string|
+|**location**  <br>*optional*||string|
+|**name**  <br>*required*|**Length** : `3 - 10`|string|
+|**password**  <br>*required*|**Length** : `8 - 20`|string|
+|**phone**  <br>*optional*||string|
 
 
 <a name="types-updateapplicationrequest"></a>
@@ -3899,7 +3947,7 @@ delete role by uri config
 |**bio**  <br>*optional*|string|
 |**download_rate_limit**  <br>*optional*|integer|
 |**name**  <br>*optional*|string|
-|**state**  <br>*optional*|string|
+|**state**  <br>*optional*|enum (enable, disable)|
 |**url**  <br>*optional*|string|
 |**user_id**  <br>*required*|integer|
 
@@ -3956,7 +4004,7 @@ delete role by uri config
 |**bio**  <br>*optional*|string|
 |**client_id**  <br>*optional*|string|
 |**client_secret**  <br>*optional*|string|
-|**name**  <br>*optional*|string|
+|**name**  <br>*optional*|enum (github, google)|
 |**redirect_url**  <br>*optional*|string|
 
 
@@ -4009,6 +4057,18 @@ delete role by uri config
 |**proxy_domain**  <br>*optional*|string|
 
 
+<a name="types-updateuserrequest"></a>
+### types.UpdateUserRequest
+
+|Name|Schema|
+|---|---|
+|**avatar**  <br>*optional*|string|
+|**bio**  <br>*optional*|string|
+|**email**  <br>*optional*|string|
+|**location**  <br>*optional*|string|
+|**phone**  <br>*optional*|string|
+
+
 
 
 
diff --git a/docs/zh-CN/api-reference/api-reference.md b/docs/zh-CN/api-reference/api-reference.md
index 035e03cf..b2a3aa78 100644
--- a/docs/zh-CN/api-reference/api-reference.md
+++ b/docs/zh-CN/api-reference/api-reference.md
@@ -3284,6 +3284,50 @@ Get User by id
 * User
 
 
+<a name="api-v1-users-id-patch"></a>
+### Update User
+```
+PATCH /api/v1/users/{id}
+```
+
+
+#### 说明
+Update by json config
+
+
+#### 参数
+
+|类型|名称|说明|类型|
+|---|---|---|---|
+|**Path**|**id**  <br>*必填*|id|string|
+|**Body**|**User**  <br>*必填*|User|[types.UpdateUserRequest](#types-updateuserrequest)|
+
+
+#### 响应
+
+|HTTP代码|说明|类型|
+|---|---|---|
+|**200**|OK|[model.User](#model-user)|
+|**400**||无内容|
+|**404**||无内容|
+|**500**||无内容|
+
+
+#### 消耗
+
+* `application/json`
+
+
+#### 生成
+
+* `application/json`
+
+
+#### 标签
+
+* User
+
+
 <a name="api-v1-users-id-reset_password-post"></a>
 ### Reset Password For User
 ```
@@ -3651,7 +3695,7 @@ delete role by uri config
 
 |名称|类型|
 |---|---|
-|**action**  <br>*必填*|string|
+|**action**  <br>*必填*|enum (read, *)|
 |**object**  <br>*必填*|string|
 
 
@@ -3660,17 +3704,17 @@ delete role by uri config
 
 |名称|类型|
 |---|---|
-|**action**  <br>*必填*|string|
+|**action**  <br>*必填*|enum (read, *)|
 |**object**  <br>*必填*|string|
 
 
 <a name="types-cdnclusterconfig"></a>
 ### types.CDNClusterConfig
 
-|名称|类型|
-|---|---|
-|**load_limit**  <br>*可选*|integer|
-|**net_topology**  <br>*可选*|string|
+|名称|说明|类型|
+|---|---|---|
+|**load_limit**  <br>*可选*|**最小值** : `1`  <br>**最大值** : `10000`|integer|
+|**net_topology**  <br>*可选*||string|
 
 
 <a name="types-createapplicationrequest"></a>
@@ -3681,7 +3725,7 @@ delete role by uri config
 |**bio**  <br>*可选*|string|
 |**download_rate_limit**  <br>*可选*|integer|
 |**name**  <br>*必填*|string|
-|**state**  <br>*可选*|string|
+|**state**  <br>*可选*|enum (enable, disable)|
 |**url**  <br>*可选*|string|
 |**user_id**  <br>*必填*|integer|
 
@@ -3744,7 +3788,7 @@ delete role by uri config
 |**bio**  <br>*可选*|string|
 |**client_id**  <br>*必填*|string|
 |**client_secret**  <br>*必填*|string|
-|**name**  <br>*必填*|string|
+|**name**  <br>*必填*|enum (github, google)|
 |**redirect_url**  <br>*可选*|string|
 
 
@@ -3813,7 +3857,7 @@ delete role by uri config
 |---|---|
 |**filter**  <br>*可选*|string|
 |**headers**  <br>*可选*|< string, string > map|
-|**type**  <br>*必填*|string|
+|**type**  <br>*必填*|enum (image, file)|
 |**url**  <br>*必填*|string|
 
 
@@ -3830,7 +3874,7 @@ delete role by uri config
 
 |名称|类型|
 |---|---|
-|**action**  <br>*必填*|string|
+|**action**  <br>*必填*|enum (read, *)|
 |**object**  <br>*必填*|string|
 
 
@@ -3848,23 +3892,27 @@ delete role by uri config
 <a name="types-resetpasswordrequest"></a>
 ### types.ResetPasswordRequest
 
-|名称|类型|
-|---|---|
-|**new_password**  <br>*必填*|string|
-|**old_password**  <br>*必填*|string|
+|名称|说明|类型|
+|---|---|---|
+|**new_password**  <br>*必填*|**长度** : `8 - 20`|string|
+|**old_password**  <br>*必填*|**长度** : `8 - 20`|string|
 
 
 <a name="types-schedulerclusterclientconfig"></a>
 ### types.SchedulerClusterClientConfig
 
-|名称|类型|
-|---|---|
-|**load_limit**  <br>*可选*|integer|
+|名称|说明|类型|
+|---|---|---|
+|**load_limit**  <br>*可选*|**最小值** : `1`  <br>**最大值** : `10000`|integer|
+|**parallel_count**  <br>*可选*|**最小值** : `1`  <br>**最大值** : `50`|integer|
 
 
 <a name="types-schedulerclusterconfig"></a>
 ### types.SchedulerClusterConfig
-*类型* : object
+
+|名称|说明|类型|
+|---|---|---|
+|**filter_parent_limit**  <br>*可选*|**最小值** : `1`  <br>**最大值** : `100`|integer|
 
 
 <a name="types-schedulerclusterscopes"></a>
@@ -3880,15 +3928,15 @@ delete role by uri config
 <a name="types-signuprequest"></a>
 ### types.SignUpRequest
 
-|名称|类型|
-|---|---|
-|**avatar**  <br>*可选*|string|
-|**bio**  <br>*可选*|string|
-|**email**  <br>*必填*|string|
-|**location**  <br>*可选*|string|
-|**name**  <br>*必填*|string|
-|**password**  <br>*必填*|string|
-|**phone**  <br>*可选*|string|
+|名称|说明|类型|
+|---|---|---|
+|**avatar**  <br>*可选*||string|
+|**bio**  <br>*可选*||string|
+|**email**  <br>*必填*||string|
+|**location**  <br>*可选*||string|
+|**name**  <br>*必填*|**长度** : `3 - 10`|string|
+|**password**  <br>*必填*|**长度** : `8 - 20`|string|
+|**phone**  <br>*可选*||string|
 
 
 <a name="types-updateapplicationrequest"></a>
@@ -3899,7 +3947,7 @@ delete role by uri config
 |**bio**  <br>*可选*|string|
 |**download_rate_limit**  <br>*可选*|integer|
 |**name**  <br>*可选*|string|
-|**state**  <br>*可选*|string|
+|**state**  <br>*可选*|enum (enable, disable)|
 |**url**  <br>*可选*|string|
 |**user_id**  <br>*必填*|integer|
 
@@ -3956,7 +4004,7 @@ delete role by uri config
 |**bio**  <br>*可选*|string|
 |**client_id**  <br>*可选*|string|
 |**client_secret**  <br>*可选*|string|
-|**name**  <br>*可选*|string|
+|**name**  <br>*可选*|enum (github, google)|
 |**redirect_url**  <br>*可选*|string|
 
 
@@ -4009,6 +4057,18 @@ delete role by uri config
 |**proxy_domain**  <br>*可选*|string|
 
 
+<a name="types-updateuserrequest"></a>
+### types.UpdateUserRequest
+
+|名称|类型|
+|---|---|
+|**avatar**  <br>*可选*|string|
+|**bio**  <br>*可选*|string|
+|**email**  <br>*可选*|string|
+|**location**  <br>*可选*|string|
+|**phone**  <br>*可选*|string|
+
+
 
 
 
diff --git a/go.mod b/go.mod
index 31514a8f..acd91373 100644
--- a/go.mod
+++ b/go.mod
@@ -6,7 +6,7 @@ require (
 	github.com/RichardKnop/machinery v1.10.6
 	github.com/Showmax/go-fqdn v1.0.0
 	github.com/VividCortex/mysqlerr v1.0.0
-	github.com/agiledragon/gomonkey/v2 v2.3.0
+	github.com/agiledragon/gomonkey/v2 v2.3.1
 	github.com/aliyun/aliyun-oss-go-sdk v2.1.6+incompatible
 	github.com/appleboy/gin-jwt/v2 v2.6.5-0.20210827121450-79689222c755
 	github.com/bits-and-blooms/bitset v1.2.1
@@ -57,7 +57,7 @@ require (
 	github.com/stretchr/testify v1.7.0
 	github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2
 	github.com/swaggo/gin-swagger v1.3.1
-	github.com/swaggo/swag v1.7.1
+	github.com/swaggo/swag v1.8.0
 	go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.29.0
 	go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.29.0
 	go.opentelemetry.io/otel v1.4.1
@@ -97,20 +97,21 @@ require (
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b // indirect
 	github.com/cespare/xxhash/v2 v2.1.2 // indirect
-	github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
+	github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc // indirect
 	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
 	github.com/fsnotify/fsnotify v1.4.9 // indirect
+	github.com/ghodss/yaml v1.0.0 // indirect
 	github.com/gin-contrib/sse v0.1.0 // indirect
 	github.com/go-echarts/go-echarts/v2 v2.2.3 // indirect
 	github.com/go-logr/logr v1.2.2 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
 	github.com/go-ole/go-ole v1.2.6 // indirect
 	github.com/go-openapi/jsonpointer v0.19.5 // indirect
-	github.com/go-openapi/jsonreference v0.19.5 // indirect
-	github.com/go-openapi/spec v0.20.3 // indirect
-	github.com/go-openapi/swag v0.19.14 // indirect
+	github.com/go-openapi/jsonreference v0.19.6 // indirect
+	github.com/go-openapi/spec v0.20.4 // indirect
+	github.com/go-openapi/swag v0.21.1 // indirect
 	github.com/go-playground/locales v0.14.0 // indirect
 	github.com/go-playground/universal-translator v0.18.0 // indirect
 	github.com/go-redsync/redsync/v4 v4.0.4 // indirect
@@ -150,7 +151,7 @@ require (
 	github.com/klauspost/compress v1.13.1 // indirect
 	github.com/leodido/go-urn v1.2.1 // indirect
 	github.com/magiconair/properties v1.8.5 // indirect
-	github.com/mailru/easyjson v0.7.6 // indirect
+	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/mattn/go-isatty v0.0.14 // indirect
 	github.com/mattn/go-runewidth v0.0.13 // indirect
 	github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
@@ -176,6 +177,7 @@ require (
 	github.com/streadway/amqp v1.0.0 // indirect
 	github.com/subosito/gotenv v1.2.0 // indirect
 	github.com/ugorji/go/codec v1.2.7 // indirect
+	github.com/urfave/cli/v2 v2.3.0 // indirect
 	github.com/vmihailenco/bufpool v0.1.11 // indirect
 	github.com/vmihailenco/go-tinylfu v0.2.0 // indirect
 	github.com/vmihailenco/msgpack/v5 v5.1.0 // indirect
@@ -192,11 +194,11 @@ require (
 	go.uber.org/multierr v1.6.0 // indirect
 	golang.org/x/exp v0.0.0-20201221025956-e89b829e73ea // indirect
 	golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
-	golang.org/x/mod v0.4.2 // indirect
+	golang.org/x/mod v0.5.1 // indirect
 	golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
 	golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
 	golang.org/x/text v0.3.7 // indirect
-	golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff // indirect
+	golang.org/x/tools v0.1.9 // indirect
 	golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
 	google.golang.org/appengine v1.6.7 // indirect
 	google.golang.org/genproto v0.0.0-20220308174144-ae0e22291548 // indirect
diff --git a/go.sum b/go.sum
index eb08333e..0de56521 100644
--- a/go.sum
+++ b/go.sum
@@ -79,6 +79,8 @@ github.com/VividCortex/mysqlerr v1.0.0 h1:5pZ2TZA+YnzPgzBfiUWGqWmKDVNBdrkf9g+DNe
 github.com/VividCortex/mysqlerr v1.0.0/go.mod h1:xERx8E4tBhLvpjzdUyQiSfUxeMcATEQrflDAfXsqcAE=
 github.com/agiledragon/gomonkey/v2 v2.3.0 h1:olsJjnSDpvqWl4FHByeMUs/r54/az+gQitU3VeEbC98=
 github.com/agiledragon/gomonkey/v2 v2.3.0/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY=
+github.com/agiledragon/gomonkey/v2 v2.3.1 h1:k+UnUY0EMNYUFUAQVETGY9uUTxjMdnUkP0ARyJS1zzs=
+github.com/agiledragon/gomonkey/v2 v2.3.1/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY=
 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@@ -155,6 +157,8 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
 github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
+github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
@@ -202,6 +206,7 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
 github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg=
+github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/gin-contrib/cors v1.3.1 h1:doAsuITavI4IOcd0Y19U4B+O0dNWihRyX//nn4sEmgA=
 github.com/gin-contrib/cors v1.3.1/go.mod h1:jjEJ4268OPZUcU7k9Pm653S7lXUGcqMADzFA61xsmDk=
@@ -252,13 +257,20 @@ github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3Hfo
 github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
 github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM=
 github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
+github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs=
+github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=
 github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
 github.com/go-openapi/spec v0.20.3 h1:uH9RQ6vdyPSs2pSy9fL8QPspDF2AMIMPtmK5coSSjtQ=
 github.com/go-openapi/spec v0.20.3/go.mod h1:gG4F8wdEDN+YPBMVnzE85Rbhf+Th2DTvA9nFPQ5AYEg=
+github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M=
+github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I=
 github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
 github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
 github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng=
 github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
+github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
+github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU=
+github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
 github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
 github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
 github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
@@ -611,6 +623,8 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN
 github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
 github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
+github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
 github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
 github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
 github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
@@ -833,6 +847,8 @@ github.com/swaggo/gin-swagger v1.3.1/go.mod h1:Z6NtRBK2PRig0EUmy1Xu75CnCEs6vGYu9
 github.com/swaggo/swag v1.5.1/go.mod h1:1Bl9F/ZBpVWh22nY0zmYyASPO1lI/zIwRDrpZU+tv8Y=
 github.com/swaggo/swag v1.7.1 h1:gY9ZakXlNWg/i/v5bQBic7VMZ4teq4m89lpiao74p/s=
 github.com/swaggo/swag v1.7.1/go.mod h1:gAiHxNTb9cIpNmA/VEGUP+CyZMCP/EW7mdtc8Bny+p8=
+github.com/swaggo/swag v1.8.0 h1:80NNhvpJcuItNpBDqgJwDuKlMmaZ/OATOzhG3bhcM3w=
+github.com/swaggo/swag v1.8.0/go.mod h1:gZ+TJ2w/Ve1RwQsA2IRoSOTidHz6DX+PIG8GWvbnoLU=
 github.com/tidwall/gjson v1.6.0 h1:9VEQWz6LLMUsUl6PueE49ir4Ka6CzLymOAZDxpFsTDc=
 github.com/tidwall/gjson v1.6.0/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls=
 github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc=
@@ -851,7 +867,9 @@ github.com/ugorji/go/codec v1.1.13/go.mod h1:oNVt3Dq+FO91WNQ/9JnHKQP2QJxTzoN7wCB
 github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
 github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
 github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
+github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
 github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
+github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
 github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
 github.com/vmihailenco/bufpool v0.1.11 h1:gOq2WmBrq0i2yW5QJ16ykccQ4wH9UyEsgLm6czKAd94=
 github.com/vmihailenco/bufpool v0.1.11/go.mod h1:AFf/MOy3l2CFTKbxwt0mp2MwnqjNEs5H/UxrkA5jxTQ=
@@ -1023,6 +1041,7 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1071,6 +1090,7 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
 golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
 golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@@ -1174,6 +1194,7 @@ golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -1282,6 +1303,8 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff h1:VX/uD7MK0AHXGiScH3fsieUQUcpmRERPDYtqZdJnA+Q=
 golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff/go.mod h1:YD9qOF0M9xpSpdWTBbzEl5e/RnCefISl8E5Noe10jFM=
+golang.org/x/tools v0.1.9 h1:j9KsMiaP1c3B0OTQGth0/k+miLGTgLsAFUCrF2vLcF8=
+golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
 golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/manager/handlers/handlers.go b/manager/handlers/handlers.go
index b9d4ec73..11620de2 100644
--- a/manager/handlers/handlers.go
+++ b/manager/handlers/handlers.go
@@ -27,10 +27,10 @@ import (
 )
 
 type Handlers struct {
-	service service.REST
+	service service.Service
 }
 
-func New(service service.REST) *Handlers {
+func New(service service.Service) *Handlers {
 	return &Handlers{
 		service: service,
 	}
diff --git a/manager/handlers/user.go b/manager/handlers/user.go
index ca381215..ea73ed36 100644
--- a/manager/handlers/user.go
+++ b/manager/handlers/user.go
@@ -27,6 +27,40 @@ import (
 	"d7y.io/dragonfly/v2/manager/types"
 )
 
+// @Summary Update User
+// @Description Update by json config
+// @Tags User
+// @Accept json
+// @Produce json
+// @Param id path string true "id"
+// @Param User body types.UpdateUserRequest true "User"
+// @Success 200 {object} model.User
+// @Failure 400
+// @Failure 404
+// @Failure 500
+// @Router /users/{id} [patch]
+func (h *Handlers) UpdateUser(ctx *gin.Context) {
+	var params types.UserParams
+	if err := ctx.ShouldBindUri(&params); err != nil {
+		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
+		return
+	}
+
+	var json types.UpdateUserRequest
+	if err := ctx.ShouldBindJSON(&json); err != nil {
+		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
+		return
+	}
+
+	user, err := h.service.UpdateUser(ctx.Request.Context(), params.ID, json)
+	if err != nil {
+		ctx.Error(err) // nolint: errcheck
+		return
+	}
+
+	ctx.JSON(http.StatusOK, user)
+}
+
 // @Summary Get User
 // @Description Get User by id
 // @Tags User
diff --git a/manager/manager.go b/manager/manager.go
index f0815c78..0f80ad3a 100644
--- a/manager/manager.go
+++ b/manager/manager.go
@@ -88,7 +88,7 @@ func New(cfg *config.Config, d dfpath.Dfpath) (*Server, error) {
 	}
 
 	// Initialize REST server
-	restService := service.NewREST(db, cache, job, enforcer)
+	restService := service.New(db, cache, job, enforcer)
 	router, err := router.Init(cfg, d.LogDir(), restService, enforcer)
 	if err != nil {
 		return nil, err
diff --git a/manager/middlewares/jwt.go b/manager/middlewares/jwt.go
index f8ccd6cb..5d90d6c2 100644
--- a/manager/middlewares/jwt.go
+++ b/manager/middlewares/jwt.go
@@ -29,7 +29,7 @@ import (
 	"d7y.io/dragonfly/v2/manager/types"
 )
 
-func Jwt(service service.REST) (*jwt.GinJWTMiddleware, error) {
+func Jwt(service service.Service) (*jwt.GinJWTMiddleware, error) {
 	identityKey := "id"
 
 	authMiddleware, err := jwt.New(&jwt.GinJWTMiddleware{
diff --git a/manager/router/router.go b/manager/router/router.go
index e33ed602..30de995b 100644
--- a/manager/router/router.go
+++ b/manager/router/router.go
@@ -43,7 +43,7 @@ const (
 
 var GinLogFileName = "gin.log"
 
-func Init(cfg *config.Config, logDir string, service service.REST, enforcer *casbin.Enforcer) (*gin.Engine, error) {
+func Init(cfg *config.Config, logDir string, service service.Service, enforcer *casbin.Enforcer) (*gin.Engine, error) {
 	// Set mode
 	if !cfg.Verbose {
 		gin.SetMode(gin.ReleaseMode)
@@ -93,6 +93,7 @@ func Init(cfg *config.Config, logDir string, service service.REST, enforcer *cas
 
 	// User
 	u := apiv1.Group("/users")
+	u.PATCH(":id", jwt.MiddlewareFunc(), rbac, h.UpdateUser)
 	u.GET("/:id", jwt.MiddlewareFunc(), rbac, h.GetUser)
 	u.GET("", jwt.MiddlewareFunc(), rbac, h.GetUsers)
 	u.POST("/signin", jwt.LoginHandler)
diff --git a/manager/service/application.go b/manager/service/application.go
index 3abfdc5c..e03decaf 100644
--- a/manager/service/application.go
+++ b/manager/service/application.go
@@ -23,7 +23,7 @@ import (
 	"d7y.io/dragonfly/v2/manager/types"
 )
 
-func (s *rest) CreateApplication(ctx context.Context, json types.CreateApplicationRequest) (*model.Application, error) {
+func (s *service) CreateApplication(ctx context.Context, json types.CreateApplicationRequest) (*model.Application, error) {
 	application := model.Application{
 		Name:              json.Name,
 		DownloadRateLimit: json.DownloadRateLimit,
@@ -40,7 +40,7 @@ func (s *rest) CreateApplication(ctx context.Context, json types.CreateApplicati
 	return &application, nil
 }
 
-func (s *rest) DestroyApplication(ctx context.Context, id uint) error {
+func (s *service) DestroyApplication(ctx context.Context, id uint) error {
 	application := model.Application{}
 	if err := s.db.WithContext(ctx).First(&application, id).Error; err != nil {
 		return err
@@ -53,7 +53,7 @@ func (s *rest) DestroyApplication(ctx context.Context, id uint) error {
 	return nil
 }
 
-func (s *rest) UpdateApplication(ctx context.Context, id uint, json types.UpdateApplicationRequest) (*model.Application, error) {
+func (s *service) UpdateApplication(ctx context.Context, id uint, json types.UpdateApplicationRequest) (*model.Application, error) {
 	application := model.Application{}
 	if err := s.db.WithContext(ctx).Preload("CDNClusters").Preload("SchedulerClusters").Preload("User").First(&application, id).Updates(model.Application{
 		Name:              json.Name,
@@ -69,7 +69,7 @@ func (s *rest) UpdateApplication(ctx context.Context, id uint, json types.Update
 	return &application, nil
 }
 
-func (s *rest) GetApplication(ctx context.Context, id uint) (*model.Application, error) {
+func (s *service) GetApplication(ctx context.Context, id uint) (*model.Application, error) {
 	application := model.Application{}
 	if err := s.db.WithContext(ctx).Preload("CDNClusters").Preload("SchedulerClusters").Preload("User").First(&application, id).Error; err != nil {
 		return nil, err
@@ -78,7 +78,7 @@ func (s *rest) GetApplication(ctx context.Context, id uint) (*model.Application,
 	return &application, nil
 }
 
-func (s *rest) GetApplications(ctx context.Context, q types.GetApplicationsQuery) (*[]model.Application, int64, error) {
+func (s *service) GetApplications(ctx context.Context, q types.GetApplicationsQuery) (*[]model.Application, int64, error) {
 	var count int64
 	applications := []model.Application{}
 	if err := s.db.WithContext(ctx).Scopes(model.Paginate(q.Page, q.PerPage)).Preload("CDNClusters").Preload("SchedulerClusters").Preload("User").Find(&applications).Count(&count).Error; err != nil {
@@ -88,7 +88,7 @@ func (s *rest) GetApplications(ctx context.Context, q types.GetApplicationsQuery
 	return &applications, count, nil
 }
 
-func (s *rest) AddSchedulerClusterToApplication(ctx context.Context, id, schedulerClusterID uint) error {
+func (s *service) AddSchedulerClusterToApplication(ctx context.Context, id, schedulerClusterID uint) error {
 	application := model.Application{}
 	if err := s.db.WithContext(ctx).First(&application, id).Error; err != nil {
 		return err
@@ -106,7 +106,7 @@ func (s *rest) AddSchedulerClusterToApplication(ctx context.Context, id, schedul
 	return nil
 }
 
-func (s *rest) DeleteSchedulerClusterToApplication(ctx context.Context, id, schedulerClusterID uint) error {
+func (s *service) DeleteSchedulerClusterToApplication(ctx context.Context, id, schedulerClusterID uint) error {
 	application := model.Application{}
 	if err := s.db.WithContext(ctx).First(&application, id).Error; err != nil {
 		return err
@@ -124,7 +124,7 @@ func (s *rest) DeleteSchedulerClusterToApplication(ctx context.Context, id, sche
 	return nil
 }
 
-func (s *rest) AddCDNClusterToApplication(ctx context.Context, id, cdnClusterID uint) error {
+func (s *service) AddCDNClusterToApplication(ctx context.Context, id, cdnClusterID uint) error {
 	application := model.Application{}
 	if err := s.db.WithContext(ctx).First(&application, id).Error; err != nil {
 		return err
@@ -142,7 +142,7 @@ func (s *rest) AddCDNClusterToApplication(ctx context.Context, id, cdnClusterID
 	return nil
 }
 
-func (s *rest) DeleteCDNClusterToApplication(ctx context.Context, id, cdnClusterID uint) error {
+func (s *service) DeleteCDNClusterToApplication(ctx context.Context, id, cdnClusterID uint) error {
 	application := model.Application{}
 	if err := s.db.WithContext(ctx).First(&application, id).Error; err != nil {
 		return err
diff --git a/manager/service/cdn.go b/manager/service/cdn.go
index 831c3a6c..346be9b7 100644
--- a/manager/service/cdn.go
+++ b/manager/service/cdn.go
@@ -23,7 +23,7 @@ import (
 	"d7y.io/dragonfly/v2/manager/types"
 )
 
-func (s *rest) CreateCDN(ctx context.Context, json types.CreateCDNRequest) (*model.CDN, error) {
+func (s *service) CreateCDN(ctx context.Context, json types.CreateCDNRequest) (*model.CDN, error) {
 	cdn := model.CDN{
 		HostName:     json.HostName,
 		IDC:          json.IDC,
@@ -41,7 +41,7 @@ func (s *rest) CreateCDN(ctx context.Context, json types.CreateCDNRequest) (*mod
 	return &cdn, nil
 }
 
-func (s *rest) DestroyCDN(ctx context.Context, id uint) error {
+func (s *service) DestroyCDN(ctx context.Context, id uint) error {
 	cdn := model.CDN{}
 	if err := s.db.WithContext(ctx).First(&cdn, id).Error; err != nil {
 		return err
@@ -54,7 +54,7 @@ func (s *rest) DestroyCDN(ctx context.Context, id uint) error {
 	return nil
 }
 
-func (s *rest) UpdateCDN(ctx context.Context, id uint, json types.UpdateCDNRequest) (*model.CDN, error) {
+func (s *service) UpdateCDN(ctx context.Context, id uint, json types.UpdateCDNRequest) (*model.CDN, error) {
 	cdn := model.CDN{}
 	if err := s.db.WithContext(ctx).First(&cdn, id).Updates(model.CDN{
 		IDC:          json.IDC,
@@ -70,7 +70,7 @@ func (s *rest) UpdateCDN(ctx context.Context, id uint, json types.UpdateCDNReque
 	return &cdn, nil
 }
 
-func (s *rest) GetCDN(ctx context.Context, id uint) (*model.CDN, error) {
+func (s *service) GetCDN(ctx context.Context, id uint) (*model.CDN, error) {
 	cdn := model.CDN{}
 	if err := s.db.WithContext(ctx).First(&cdn, id).Error; err != nil {
 		return nil, err
@@ -79,7 +79,7 @@ func (s *rest) GetCDN(ctx context.Context, id uint) (*model.CDN, error) {
 	return &cdn, nil
 }
 
-func (s *rest) GetCDNs(ctx context.Context, q types.GetCDNsQuery) (*[]model.CDN, int64, error) {
+func (s *service) GetCDNs(ctx context.Context, q types.GetCDNsQuery) (*[]model.CDN, int64, error) {
 	var count int64
 	var cdns []model.CDN
 	if err := s.db.WithContext(ctx).Scopes(model.Paginate(q.Page, q.PerPage)).Where(&model.CDN{
diff --git a/manager/service/cdn_cluster.go b/manager/service/cdn_cluster.go
index 414eecc0..cf941ce7 100644
--- a/manager/service/cdn_cluster.go
+++ b/manager/service/cdn_cluster.go
@@ -24,7 +24,7 @@ import (
 	"d7y.io/dragonfly/v2/pkg/util/structutils"
 )
 
-func (s *rest) CreateCDNCluster(ctx context.Context, json types.CreateCDNClusterRequest) (*model.CDNCluster, error) {
+func (s *service) CreateCDNCluster(ctx context.Context, json types.CreateCDNClusterRequest) (*model.CDNCluster, error) {
 	config, err := structutils.StructToMap(json.Config)
 	if err != nil {
 		return nil, err
@@ -44,7 +44,7 @@ func (s *rest) CreateCDNCluster(ctx context.Context, json types.CreateCDNCluster
 	return &cdnCluster, nil
 }
 
-func (s *rest) DestroyCDNCluster(ctx context.Context, id uint) error {
+func (s *service) DestroyCDNCluster(ctx context.Context, id uint) error {
 	cdnCluster := model.CDNCluster{}
 	if err := s.db.WithContext(ctx).First(&cdnCluster, id).Error; err != nil {
 		return err
@@ -57,7 +57,7 @@ func (s *rest) DestroyCDNCluster(ctx context.Context, id uint) error {
 	return nil
 }
 
-func (s *rest) UpdateCDNCluster(ctx context.Context, id uint, json types.UpdateCDNClusterRequest) (*model.CDNCluster, error) {
+func (s *service) UpdateCDNCluster(ctx context.Context, id uint, json types.UpdateCDNClusterRequest) (*model.CDNCluster, error) {
 	config, err := structutils.StructToMap(json.Config)
 	if err != nil {
 		return nil, err
@@ -76,7 +76,7 @@ func (s *rest) UpdateCDNCluster(ctx context.Context, id uint, json types.UpdateC
 	return &cdnCluster, nil
 }
 
-func (s *rest) GetCDNCluster(ctx context.Context, id uint) (*model.CDNCluster, error) {
+func (s *service) GetCDNCluster(ctx context.Context, id uint) (*model.CDNCluster, error) {
 	cdnCluster := model.CDNCluster{}
 	if err := s.db.WithContext(ctx).First(&cdnCluster, id).Error; err != nil {
 		return nil, err
@@ -85,7 +85,7 @@ func (s *rest) GetCDNCluster(ctx context.Context, id uint) (*model.CDNCluster, e
 	return &cdnCluster, nil
 }
 
-func (s *rest) GetCDNClusters(ctx context.Context, q types.GetCDNClustersQuery) (*[]model.CDNCluster, int64, error) {
+func (s *service) GetCDNClusters(ctx context.Context, q types.GetCDNClustersQuery) (*[]model.CDNCluster, int64, error) {
 	var count int64
 	var cdnClusters []model.CDNCluster
 	if err := s.db.WithContext(ctx).Scopes(model.Paginate(q.Page, q.PerPage)).Where(&model.CDNCluster{
@@ -97,7 +97,7 @@ func (s *rest) GetCDNClusters(ctx context.Context, q types.GetCDNClustersQuery)
 	return &cdnClusters, count, nil
 }
 
-func (s *rest) AddCDNToCDNCluster(ctx context.Context, id, cdnID uint) error {
+func (s *service) AddCDNToCDNCluster(ctx context.Context, id, cdnID uint) error {
 	cdnCluster := model.CDNCluster{}
 	if err := s.db.WithContext(ctx).First(&cdnCluster, id).Error; err != nil {
 		return err
@@ -115,7 +115,7 @@ func (s *rest) AddCDNToCDNCluster(ctx context.Context, id, cdnID uint) error {
 	return nil
 }
 
-func (s *rest) AddSchedulerClusterToCDNCluster(ctx context.Context, id, schedulerClusterID uint) error {
+func (s *service) AddSchedulerClusterToCDNCluster(ctx context.Context, id, schedulerClusterID uint) error {
 	cdnCluster := model.CDNCluster{}
 	if err := s.db.WithContext(ctx).First(&cdnCluster, id).Error; err != nil {
 		return err
diff --git a/manager/service/config.go b/manager/service/config.go
index 35d073fd..d872c293 100644
--- a/manager/service/config.go
+++ b/manager/service/config.go
@@ -23,7 +23,7 @@ import (
 	"d7y.io/dragonfly/v2/manager/types"
 )
 
-func (s *rest) CreateConfig(ctx context.Context, json types.CreateConfigRequest) (*model.Config, error) {
+func (s *service) CreateConfig(ctx context.Context, json types.CreateConfigRequest) (*model.Config, error) {
 	config := model.Config{
 		Name:   json.Name,
 		Value:  json.Value,
@@ -38,7 +38,7 @@ func (s *rest) CreateConfig(ctx context.Context, json types.CreateConfigRequest)
 	return &config, nil
 }
 
-func (s *rest) DestroyConfig(ctx context.Context, id uint) error {
+func (s *service) DestroyConfig(ctx context.Context, id uint) error {
 	config := model.Config{}
 	if err := s.db.WithContext(ctx).First(&config, id).Error; err != nil {
 		return err
@@ -51,7 +51,7 @@ func (s *rest) DestroyConfig(ctx context.Context, id uint) error {
 	return nil
 }
 
-func (s *rest) UpdateConfig(ctx context.Context, id uint, json types.UpdateConfigRequest) (*model.Config, error) {
+func (s *service) UpdateConfig(ctx context.Context, id uint, json types.UpdateConfigRequest) (*model.Config, error) {
 	config := model.Config{}
 	if err := s.db.WithContext(ctx).First(&config, id).Updates(model.Config{
 		Name:   json.Name,
@@ -65,7 +65,7 @@ func (s *rest) UpdateConfig(ctx context.Context, id uint, json types.UpdateConfi
 	return &config, nil
 }
 
-func (s *rest) GetConfig(ctx context.Context, id uint) (*model.Config, error) {
+func (s *service) GetConfig(ctx context.Context, id uint) (*model.Config, error) {
 	config := model.Config{}
 	if err := s.db.WithContext(ctx).First(&config, id).Error; err != nil {
 		return nil, err
@@ -74,7 +74,7 @@ func (s *rest) GetConfig(ctx context.Context, id uint) (*model.Config, error) {
 	return &config, nil
 }
 
-func (s *rest) GetConfigs(ctx context.Context, q types.GetConfigsQuery) (*[]model.Config, int64, error) {
+func (s *service) GetConfigs(ctx context.Context, q types.GetConfigsQuery) (*[]model.Config, int64, error) {
 	var count int64
 	var configs []model.Config
 	if err := s.db.WithContext(ctx).Scopes(model.Paginate(q.Page, q.PerPage)).Where(&model.Config{
diff --git a/manager/service/job.go b/manager/service/job.go
index 245ddb2a..959cdeaf 100644
--- a/manager/service/job.go
+++ b/manager/service/job.go
@@ -29,7 +29,7 @@ import (
 	"d7y.io/dragonfly/v2/pkg/util/structutils"
 )
 
-func (s *rest) CreatePreheatJob(ctx context.Context, json types.CreatePreheatJobRequest) (*model.Job, error) {
+func (s *service) CreatePreheatJob(ctx context.Context, json types.CreatePreheatJobRequest) (*model.Job, error) {
 	var schedulers []model.Scheduler
 	var schedulerClusters []model.SchedulerCluster
 
@@ -97,7 +97,7 @@ func (s *rest) CreatePreheatJob(ctx context.Context, json types.CreatePreheatJob
 	return &job, nil
 }
 
-func (s *rest) pollingJob(ctx context.Context, id uint, taskID string) {
+func (s *service) pollingJob(ctx context.Context, id uint, taskID string) {
 	var job model.Job
 
 	if _, _, err := retry.Run(ctx, func() (interface{}, bool, error) {
@@ -140,7 +140,7 @@ func (s *rest) pollingJob(ctx context.Context, id uint, taskID string) {
 	}
 }
 
-func (s *rest) DestroyJob(ctx context.Context, id uint) error {
+func (s *service) DestroyJob(ctx context.Context, id uint) error {
 	job := model.Job{}
 	if err := s.db.WithContext(ctx).First(&job, id).Error; err != nil {
 		return err
@@ -153,7 +153,7 @@ func (s *rest) DestroyJob(ctx context.Context, id uint) error {
 	return nil
 }
 
-func (s *rest) UpdateJob(ctx context.Context, id uint, json types.UpdateJobRequest) (*model.Job, error) {
+func (s *service) UpdateJob(ctx context.Context, id uint, json types.UpdateJobRequest) (*model.Job, error) {
 	job := model.Job{}
 	if err := s.db.WithContext(ctx).Preload("CDNClusters").Preload("SchedulerClusters").First(&job, id).Updates(model.Job{
 		BIO:    json.BIO,
@@ -165,7 +165,7 @@ func (s *rest) UpdateJob(ctx context.Context, id uint, json types.UpdateJobReque
 	return &job, nil
 }
 
-func (s *rest) GetJob(ctx context.Context, id uint) (*model.Job, error) {
+func (s *service) GetJob(ctx context.Context, id uint) (*model.Job, error) {
 	job := model.Job{}
 	if err := s.db.WithContext(ctx).Preload("CDNClusters").Preload("SchedulerClusters").First(&job, id).Error; err != nil {
 		return nil, err
@@ -174,7 +174,7 @@ func (s *rest) GetJob(ctx context.Context, id uint) (*model.Job, error) {
 	return &job, nil
 }
 
-func (s *rest) GetJobs(ctx context.Context, q types.GetJobsQuery) (*[]model.Job, int64, error) {
+func (s *service) GetJobs(ctx context.Context, q types.GetJobsQuery) (*[]model.Job, int64, error) {
 	var count int64
 	var jobs []model.Job
 	if err := s.db.WithContext(ctx).Scopes(model.Paginate(q.Page, q.PerPage)).Where(&model.Job{
@@ -188,7 +188,7 @@ func (s *rest) GetJobs(ctx context.Context, q types.GetJobsQuery) (*[]model.Job,
 	return &jobs, count, nil
 }
 
-func (s *rest) AddJobToSchedulerClusters(ctx context.Context, id, schedulerClusterIDs []uint) error {
+func (s *service) AddJobToSchedulerClusters(ctx context.Context, id, schedulerClusterIDs []uint) error {
 	job := model.Job{}
 	if err := s.db.WithContext(ctx).First(&job, id).Error; err != nil {
 		return err
@@ -210,7 +210,7 @@ func (s *rest) AddJobToSchedulerClusters(ctx context.Context, id, schedulerClust
 	return nil
 }
 
-func (s *rest) AddJobToCDNClusters(ctx context.Context, id, cdnClusterIDs []uint) error {
+func (s *service) AddJobToCDNClusters(ctx context.Context, id, cdnClusterIDs []uint) error {
 	job := model.Job{}
 	if err := s.db.WithContext(ctx).First(&job, id).Error; err != nil {
 		return err
diff --git a/manager/service/oauth.go b/manager/service/oauth.go
index aa944a1d..33e20d63 100644
--- a/manager/service/oauth.go
+++ b/manager/service/oauth.go
@@ -23,7 +23,7 @@ import (
 	"d7y.io/dragonfly/v2/manager/types"
 )
 
-func (s *rest) CreateOauth(ctx context.Context, json types.CreateOauthRequest) (*model.Oauth, error) {
+func (s *service) CreateOauth(ctx context.Context, json types.CreateOauthRequest) (*model.Oauth, error) {
 	oauth := model.Oauth{
 		Name:         json.Name,
 		BIO:          json.BIO,
@@ -39,7 +39,7 @@ func (s *rest) CreateOauth(ctx context.Context, json types.CreateOauthRequest) (
 	return &oauth, nil
 }
 
-func (s *rest) DestroyOauth(ctx context.Context, id uint) error {
+func (s *service) DestroyOauth(ctx context.Context, id uint) error {
 	oauth := model.Oauth{}
 	if err := s.db.WithContext(ctx).First(&oauth, id).Error; err != nil {
 		return err
@@ -52,7 +52,7 @@ func (s *rest) DestroyOauth(ctx context.Context, id uint) error {
 	return nil
 }
 
-func (s *rest) UpdateOauth(ctx context.Context, id uint, json types.UpdateOauthRequest) (*model.Oauth, error) {
+func (s *service) UpdateOauth(ctx context.Context, id uint, json types.UpdateOauthRequest) (*model.Oauth, error) {
 	oauth := model.Oauth{}
 	if err := s.db.WithContext(ctx).First(&oauth, id).Updates(model.Oauth{
 		Name:         json.Name,
@@ -67,7 +67,7 @@ func (s *rest) UpdateOauth(ctx context.Context, id uint, json types.UpdateOauthR
 	return &oauth, nil
 }
 
-func (s *rest) GetOauth(ctx context.Context, id uint) (*model.Oauth, error) {
+func (s *service) GetOauth(ctx context.Context, id uint) (*model.Oauth, error) {
 	oauth := model.Oauth{}
 	if err := s.db.WithContext(ctx).First(&oauth, id).Error; err != nil {
 		return nil, err
@@ -76,7 +76,7 @@ func (s *rest) GetOauth(ctx context.Context, id uint) (*model.Oauth, error) {
 	return &oauth, nil
 }
 
-func (s *rest) GetOauths(ctx context.Context, q types.GetOauthsQuery) (*[]model.Oauth, int64, error) {
+func (s *service) GetOauths(ctx context.Context, q types.GetOauthsQuery) (*[]model.Oauth, int64, error) {
 	var count int64
 	var oauths []model.Oauth
 	if err := s.db.WithContext(ctx).Scopes(model.Paginate(q.Page, q.PerPage)).Where(&model.Oauth{
diff --git a/manager/service/permission.go b/manager/service/permission.go
index 7219b92a..8b6015e0 100644
--- a/manager/service/permission.go
+++ b/manager/service/permission.go
@@ -24,6 +24,6 @@ import (
 	"d7y.io/dragonfly/v2/manager/permission/rbac"
 )
 
-func (s *rest) GetPermissions(ctx context.Context, g *gin.Engine) []rbac.Permission {
+func (s *service) GetPermissions(ctx context.Context, g *gin.Engine) []rbac.Permission {
 	return rbac.GetPermissions(g)
 }
diff --git a/manager/service/preheat.go b/manager/service/preheat.go
index b17d102b..5cde547b 100644
--- a/manager/service/preheat.go
+++ b/manager/service/preheat.go
@@ -44,7 +44,7 @@ const (
 	V1PreheatingStateFail = "FAIL"
 )
 
-func (s *rest) CreateV1Preheat(ctx context.Context, json types.CreateV1PreheatRequest) (*types.CreateV1PreheatResponse, error) {
+func (s *service) CreateV1Preheat(ctx context.Context, json types.CreateV1PreheatRequest) (*types.CreateV1PreheatResponse, error) {
 	job, err := s.CreatePreheatJob(ctx, types.CreatePreheatJobRequest{
 		Type: internaljob.PreheatJob,
 		Args: types.PreheatArgs{
@@ -63,7 +63,7 @@ func (s *rest) CreateV1Preheat(ctx context.Context, json types.CreateV1PreheatRe
 	}, nil
 }
 
-func (s *rest) GetV1Preheat(ctx context.Context, rawID string) (*types.GetV1PreheatResponse, error) {
+func (s *service) GetV1Preheat(ctx context.Context, rawID string) (*types.GetV1PreheatResponse, error) {
 	id, err := strconv.ParseUint(rawID, 10, 32)
 	if err != nil {
 		logger.Errorf("preheat convert error", err)
diff --git a/manager/service/role.go b/manager/service/role.go
index e2ba5e14..1df80198 100644
--- a/manager/service/role.go
+++ b/manager/service/role.go
@@ -22,7 +22,7 @@ import (
 	"d7y.io/dragonfly/v2/manager/types"
 )
 
-func (s *rest) CreateRole(ctx context.Context, json types.CreateRoleRequest) error {
+func (s *service) CreateRole(ctx context.Context, json types.CreateRoleRequest) error {
 	for _, permission := range json.Permissions {
 		_, err := s.enforcer.AddPermissionForUser(json.Role, permission.Object, permission.Action)
 		if err != nil {
@@ -33,22 +33,22 @@ func (s *rest) CreateRole(ctx context.Context, json types.CreateRoleRequest) err
 	return nil
 }
 
-func (s *rest) DestroyRole(ctx context.Context, role string) (bool, error) {
+func (s *service) DestroyRole(ctx context.Context, role string) (bool, error) {
 	return s.enforcer.DeleteRole(role)
 }
 
-func (s *rest) GetRole(ctx context.Context, role string) [][]string {
+func (s *service) GetRole(ctx context.Context, role string) [][]string {
 	return s.enforcer.GetPermissionsForUser(role)
 }
 
-func (s *rest) GetRoles(ctx context.Context) []string {
+func (s *service) GetRoles(ctx context.Context) []string {
 	return s.enforcer.GetAllSubjects()
 }
 
-func (s *rest) AddPermissionForRole(ctx context.Context, role string, json types.AddPermissionForRoleRequest) (bool, error) {
+func (s *service) AddPermissionForRole(ctx context.Context, role string, json types.AddPermissionForRoleRequest) (bool, error) {
 	return s.enforcer.AddPermissionForUser(role, json.Object, json.Action)
 }
 
-func (s *rest) DeletePermissionForRole(ctx context.Context, role string, json types.DeletePermissionForRoleRequest) (bool, error) {
+func (s *service) DeletePermissionForRole(ctx context.Context, role string, json types.DeletePermissionForRoleRequest) (bool, error) {
 	return s.enforcer.DeletePermissionForUser(role, json.Object, json.Action)
 }
diff --git a/manager/service/scheduler.go b/manager/service/scheduler.go
index e87f4062..46d4cf3d 100644
--- a/manager/service/scheduler.go
+++ b/manager/service/scheduler.go
@@ -23,7 +23,7 @@ import (
 	"d7y.io/dragonfly/v2/manager/types"
 )
 
-func (s *rest) CreateScheduler(ctx context.Context, json types.CreateSchedulerRequest) (*model.Scheduler, error) {
+func (s *service) CreateScheduler(ctx context.Context, json types.CreateSchedulerRequest) (*model.Scheduler, error) {
 	scheduler := model.Scheduler{
 		HostName:           json.HostName,
 		VIPs:               json.VIPs,
@@ -42,7 +42,7 @@ func (s *rest) CreateScheduler(ctx context.Context, json types.CreateSchedulerRe
 	return &scheduler, nil
 }
 
-func (s *rest) DestroyScheduler(ctx context.Context, id uint) error {
+func (s *service) DestroyScheduler(ctx context.Context, id uint) error {
 	scheduler := model.Scheduler{}
 	if err := s.db.WithContext(ctx).First(&scheduler, id).Error; err != nil {
 		return err
@@ -55,7 +55,7 @@ func (s *rest) DestroyScheduler(ctx context.Context, id uint) error {
 	return nil
 }
 
-func (s *rest) UpdateScheduler(ctx context.Context, id uint, json types.UpdateSchedulerRequest) (*model.Scheduler, error) {
+func (s *service) UpdateScheduler(ctx context.Context, id uint, json types.UpdateSchedulerRequest) (*model.Scheduler, error) {
 	scheduler := model.Scheduler{}
 	if err := s.db.WithContext(ctx).First(&scheduler, id).Updates(model.Scheduler{
 		VIPs:               json.VIPs,
@@ -72,7 +72,7 @@ func (s *rest) UpdateScheduler(ctx context.Context, id uint, json types.UpdateSc
 	return &scheduler, nil
 }
 
-func (s *rest) GetScheduler(ctx context.Context, id uint) (*model.Scheduler, error) {
+func (s *service) GetScheduler(ctx context.Context, id uint) (*model.Scheduler, error) {
 	scheduler := model.Scheduler{}
 	if err := s.db.WithContext(ctx).First(&scheduler, id).Error; err != nil {
 		return nil, err
@@ -81,7 +81,7 @@ func (s *rest) GetScheduler(ctx context.Context, id uint) (*model.Scheduler, err
 	return &scheduler, nil
 }
 
-func (s *rest) GetSchedulers(ctx context.Context, q types.GetSchedulersQuery) (*[]model.Scheduler, int64, error) {
+func (s *service) GetSchedulers(ctx context.Context, q types.GetSchedulersQuery) (*[]model.Scheduler, int64, error) {
 	var count int64
 	var schedulers []model.Scheduler
 	if err := s.db.WithContext(ctx).Scopes(model.Paginate(q.Page, q.PerPage)).Where(&model.Scheduler{
diff --git a/manager/service/scheduler_cluster.go b/manager/service/scheduler_cluster.go
index 247ff31e..026fc6f1 100644
--- a/manager/service/scheduler_cluster.go
+++ b/manager/service/scheduler_cluster.go
@@ -24,7 +24,7 @@ import (
 	"d7y.io/dragonfly/v2/pkg/util/structutils"
 )
 
-func (s *rest) CreateSchedulerCluster(ctx context.Context, json types.CreateSchedulerClusterRequest) (*model.SchedulerCluster, error) {
+func (s *service) CreateSchedulerCluster(ctx context.Context, json types.CreateSchedulerClusterRequest) (*model.SchedulerCluster, error) {
 	config, err := structutils.StructToMap(json.Config)
 	if err != nil {
 		return nil, err
@@ -62,7 +62,7 @@ func (s *rest) CreateSchedulerCluster(ctx context.Context, json types.CreateSche
 	return &schedulerCluster, nil
 }
 
-func (s *rest) DestroySchedulerCluster(ctx context.Context, id uint) error {
+func (s *service) DestroySchedulerCluster(ctx context.Context, id uint) error {
 	schedulerCluster := model.SchedulerCluster{}
 	if err := s.db.WithContext(ctx).First(&schedulerCluster, id).Error; err != nil {
 		return err
@@ -75,7 +75,7 @@ func (s *rest) DestroySchedulerCluster(ctx context.Context, id uint) error {
 	return nil
 }
 
-func (s *rest) UpdateSchedulerCluster(ctx context.Context, id uint, json types.UpdateSchedulerClusterRequest) (*model.SchedulerCluster, error) {
+func (s *service) UpdateSchedulerCluster(ctx context.Context, id uint, json types.UpdateSchedulerClusterRequest) (*model.SchedulerCluster, error) {
 	config, err := structutils.StructToMap(json.Config)
 	if err != nil {
 		return nil, err
@@ -112,7 +112,7 @@ func (s *rest) UpdateSchedulerCluster(ctx context.Context, id uint, json types.U
 	return &schedulerCluster, nil
 }
 
-func (s *rest) GetSchedulerCluster(ctx context.Context, id uint) (*model.SchedulerCluster, error) {
+func (s *service) GetSchedulerCluster(ctx context.Context, id uint) (*model.SchedulerCluster, error) {
 	schedulerCluster := model.SchedulerCluster{}
 	if err := s.db.WithContext(ctx).Preload("CDNClusters").First(&schedulerCluster, id).Error; err != nil {
 		return nil, err
@@ -121,7 +121,7 @@ func (s *rest) GetSchedulerCluster(ctx context.Context, id uint) (*model.Schedul
 	return &schedulerCluster, nil
 }
 
-func (s *rest) GetSchedulerClusters(ctx context.Context, q types.GetSchedulerClustersQuery) (*[]model.SchedulerCluster, int64, error) {
+func (s *service) GetSchedulerClusters(ctx context.Context, q types.GetSchedulerClustersQuery) (*[]model.SchedulerCluster, int64, error) {
 	var count int64
 	var schedulerClusters []model.SchedulerCluster
 	if err := s.db.WithContext(ctx).Scopes(model.Paginate(q.Page, q.PerPage)).Where(&model.SchedulerCluster{
@@ -133,7 +133,7 @@ func (s *rest) GetSchedulerClusters(ctx context.Context, q types.GetSchedulerClu
 	return &schedulerClusters, count, nil
 }
 
-func (s *rest) AddSchedulerToSchedulerCluster(ctx context.Context, id, schedulerID uint) error {
+func (s *service) AddSchedulerToSchedulerCluster(ctx context.Context, id, schedulerID uint) error {
 	schedulerCluster := model.SchedulerCluster{}
 	if err := s.db.WithContext(ctx).First(&schedulerCluster, id).Error; err != nil {
 		return err
diff --git a/manager/service/security_group.go b/manager/service/security_group.go
index f0788b1e..dfd36a76 100644
--- a/manager/service/security_group.go
+++ b/manager/service/security_group.go
@@ -23,7 +23,7 @@ import (
 	"d7y.io/dragonfly/v2/manager/types"
 )
 
-func (s *rest) CreateSecurityGroup(ctx context.Context, json types.CreateSecurityGroupRequest) (*model.SecurityGroup, error) {
+func (s *service) CreateSecurityGroup(ctx context.Context, json types.CreateSecurityGroupRequest) (*model.SecurityGroup, error) {
 	securityGroup := model.SecurityGroup{
 		Name: json.Name,
 		BIO:  json.BIO,
@@ -36,7 +36,7 @@ func (s *rest) CreateSecurityGroup(ctx context.Context, json types.CreateSecurit
 	return &securityGroup, nil
 }
 
-func (s *rest) DestroySecurityGroup(ctx context.Context, id uint) error {
+func (s *service) DestroySecurityGroup(ctx context.Context, id uint) error {
 	securityGroup := model.SecurityGroup{}
 	if err := s.db.WithContext(ctx).First(&securityGroup, id).Error; err != nil {
 		return err
@@ -49,7 +49,7 @@ func (s *rest) DestroySecurityGroup(ctx context.Context, id uint) error {
 	return nil
 }
 
-func (s *rest) UpdateSecurityGroup(ctx context.Context, id uint, json types.UpdateSecurityGroupRequest) (*model.SecurityGroup, error) {
+func (s *service) UpdateSecurityGroup(ctx context.Context, id uint, json types.UpdateSecurityGroupRequest) (*model.SecurityGroup, error) {
 	securityGroup := model.SecurityGroup{}
 	if err := s.db.WithContext(ctx).First(&securityGroup, id).Updates(model.SecurityGroup{
 		Name: json.Name,
@@ -61,7 +61,7 @@ func (s *rest) UpdateSecurityGroup(ctx context.Context, id uint, json types.Upda
 	return &securityGroup, nil
 }
 
-func (s *rest) GetSecurityGroup(ctx context.Context, id uint) (*model.SecurityGroup, error) {
+func (s *service) GetSecurityGroup(ctx context.Context, id uint) (*model.SecurityGroup, error) {
 	securityGroup := model.SecurityGroup{}
 	if err := s.db.WithContext(ctx).Preload("SecurityRules").First(&securityGroup, id).Error; err != nil {
 		return nil, err
@@ -70,7 +70,7 @@ func (s *rest) GetSecurityGroup(ctx context.Context, id uint) (*model.SecurityGr
 	return &securityGroup, nil
 }
 
-func (s *rest) GetSecurityGroups(ctx context.Context, q types.GetSecurityGroupsQuery) (*[]model.SecurityGroup, int64, error) {
+func (s *service) GetSecurityGroups(ctx context.Context, q types.GetSecurityGroupsQuery) (*[]model.SecurityGroup, int64, error) {
 	var count int64
 	var securityGroups []model.SecurityGroup
 	if err := s.db.WithContext(ctx).Scopes(model.Paginate(q.Page, q.PerPage)).Where(&model.SecurityGroup{
@@ -82,7 +82,7 @@ func (s *rest) GetSecurityGroups(ctx context.Context, q types.GetSecurityGroupsQ
 	return &securityGroups, count, nil
 }
 
-func (s *rest) AddSchedulerClusterToSecurityGroup(ctx context.Context, id, schedulerClusterID uint) error {
+func (s *service) AddSchedulerClusterToSecurityGroup(ctx context.Context, id, schedulerClusterID uint) error {
 	securityGroup := model.SecurityGroup{}
 	if err := s.db.WithContext(ctx).First(&securityGroup, id).Error; err != nil {
 		return err
@@ -100,7 +100,7 @@ func (s *rest) AddSchedulerClusterToSecurityGroup(ctx context.Context, id, sched
 	return nil
 }
 
-func (s *rest) AddCDNClusterToSecurityGroup(ctx context.Context, id, cdnClusterID uint) error {
+func (s *service) AddCDNClusterToSecurityGroup(ctx context.Context, id, cdnClusterID uint) error {
 	securityGroup := model.SecurityGroup{}
 	if err := s.db.WithContext(ctx).First(&securityGroup, id).Error; err != nil {
 		return err
@@ -118,7 +118,7 @@ func (s *rest) AddCDNClusterToSecurityGroup(ctx context.Context, id, cdnClusterI
 	return nil
 }
 
-func (s *rest) AddSecurityRuleToSecurityGroup(ctx context.Context, id, securityRuleID uint) error {
+func (s *service) AddSecurityRuleToSecurityGroup(ctx context.Context, id, securityRuleID uint) error {
 	securityGroup := model.SecurityGroup{}
 	if err := s.db.WithContext(ctx).First(&securityGroup, id).Error; err != nil {
 		return err
@@ -136,7 +136,7 @@ func (s *rest) AddSecurityRuleToSecurityGroup(ctx context.Context, id, securityR
 	return nil
 }
 
-func (s *rest) DestroySecurityRuleToSecurityGroup(ctx context.Context, id, securityRuleID uint) error {
+func (s *service) DestroySecurityRuleToSecurityGroup(ctx context.Context, id, securityRuleID uint) error {
 	securityGroup := model.SecurityGroup{}
 	if err := s.db.WithContext(ctx).First(&securityGroup, id).Error; err != nil {
 		return err
diff --git a/manager/service/security_rule.go b/manager/service/security_rule.go
index 0cf8a6a1..473c1878 100644
--- a/manager/service/security_rule.go
+++ b/manager/service/security_rule.go
@@ -23,7 +23,7 @@ import (
 	"d7y.io/dragonfly/v2/manager/types"
 )
 
-func (s *rest) CreateSecurityRule(ctx context.Context, json types.CreateSecurityRuleRequest) (*model.SecurityRule, error) {
+func (s *service) CreateSecurityRule(ctx context.Context, json types.CreateSecurityRuleRequest) (*model.SecurityRule, error) {
 	securityRule := model.SecurityRule{
 		Name:        json.Name,
 		BIO:         json.BIO,
@@ -38,7 +38,7 @@ func (s *rest) CreateSecurityRule(ctx context.Context, json types.CreateSecurity
 	return &securityRule, nil
 }
 
-func (s *rest) DestroySecurityRule(ctx context.Context, id uint) error {
+func (s *service) DestroySecurityRule(ctx context.Context, id uint) error {
 	securityRule := model.SecurityRule{}
 	if err := s.db.WithContext(ctx).First(&securityRule, id).Error; err != nil {
 		return err
@@ -51,7 +51,7 @@ func (s *rest) DestroySecurityRule(ctx context.Context, id uint) error {
 	return nil
 }
 
-func (s *rest) UpdateSecurityRule(ctx context.Context, id uint, json types.UpdateSecurityRuleRequest) (*model.SecurityRule, error) {
+func (s *service) UpdateSecurityRule(ctx context.Context, id uint, json types.UpdateSecurityRuleRequest) (*model.SecurityRule, error) {
 	securityRule := model.SecurityRule{}
 	if err := s.db.WithContext(ctx).First(&securityRule, id).Updates(model.SecurityRule{
 		Name:        json.Name,
@@ -65,7 +65,7 @@ func (s *rest) UpdateSecurityRule(ctx context.Context, id uint, json types.Updat
 	return &securityRule, nil
 }
 
-func (s *rest) GetSecurityRule(ctx context.Context, id uint) (*model.SecurityRule, error) {
+func (s *service) GetSecurityRule(ctx context.Context, id uint) (*model.SecurityRule, error) {
 	securityRule := model.SecurityRule{}
 	if err := s.db.WithContext(ctx).First(&securityRule, id).Error; err != nil {
 		return nil, err
@@ -74,7 +74,7 @@ func (s *rest) GetSecurityRule(ctx context.Context, id uint) (*model.SecurityRul
 	return &securityRule, nil
 }
 
-func (s *rest) GetSecurityRules(ctx context.Context, q types.GetSecurityRulesQuery) (*[]model.SecurityRule, int64, error) {
+func (s *service) GetSecurityRules(ctx context.Context, q types.GetSecurityRulesQuery) (*[]model.SecurityRule, int64, error) {
 	var count int64
 	var securityRules []model.SecurityRule
 	if err := s.db.WithContext(ctx).Scopes(model.Paginate(q.Page, q.PerPage)).Where(&model.SecurityRule{
diff --git a/manager/service/service.go b/manager/service/service.go
index 8a773e7b..be905398 100644
--- a/manager/service/service.go
+++ b/manager/service/service.go
@@ -32,7 +32,8 @@ import (
 	"d7y.io/dragonfly/v2/manager/types"
 )
 
-type REST interface {
+type Service interface {
+	UpdateUser(context.Context, uint, types.UpdateUserRequest) (*model.User, error)
 	GetUser(context.Context, uint) (*model.User, error)
 	GetUsers(context.Context, types.GetUsersQuery) (*[]model.User, int64, error)
 	SignIn(context.Context, types.SignInRequest) (*model.User, error)
@@ -128,7 +129,7 @@ type REST interface {
 	DeleteCDNClusterToApplication(context.Context, uint, uint) error
 }
 
-type rest struct {
+type service struct {
 	db       *gorm.DB
 	rdb      *redis.Client
 	cache    *cache.Cache
@@ -137,8 +138,8 @@ type rest struct {
 }
 
 // NewREST returns a new REST instence
-func NewREST(database *database.Database, cache *cache.Cache, job *job.Job, enforcer *casbin.Enforcer) REST {
-	return &rest{
+func New(database *database.Database, cache *cache.Cache, job *job.Job, enforcer *casbin.Enforcer) Service {
+	return &service{
 		db:       database.DB,
 		rdb:      database.RDB,
 		cache:    cache,
diff --git a/manager/service/user.go b/manager/service/user.go
index f981911a..5e400d6d 100644
--- a/manager/service/user.go
+++ b/manager/service/user.go
@@ -31,7 +31,22 @@ import (
 	"d7y.io/dragonfly/v2/manager/types"
 )
 
-func (s *rest) GetUser(ctx context.Context, id uint) (*model.User, error) {
+func (s *service) UpdateUser(ctx context.Context, id uint, json types.UpdateUserRequest) (*model.User, error) {
+	user := model.User{}
+	if err := s.db.WithContext(ctx).First(&user, id).Updates(model.User{
+		Email:    json.Email,
+		Phone:    json.Phone,
+		Avatar:   json.Avatar,
+		Location: json.Location,
+		BIO:      json.BIO,
+	}).Error; err != nil {
+		return nil, err
+	}
+
+	return &user, nil
+}
+
+func (s *service) GetUser(ctx context.Context, id uint) (*model.User, error) {
 	user := model.User{}
 	if err := s.db.WithContext(ctx).First(&user, id).Error; err != nil {
 		return nil, err
@@ -40,7 +55,7 @@ func (s *rest) GetUser(ctx context.Context, id uint) (*model.User, error) {
 	return &user, nil
 }
 
-func (s *rest) GetUsers(ctx context.Context, q types.GetUsersQuery) (*[]model.User, int64, error) {
+func (s *service) GetUsers(ctx context.Context, q types.GetUsersQuery) (*[]model.User, int64, error) {
 	var count int64
 	var users []model.User
 	if err := s.db.WithContext(ctx).Scopes(model.Paginate(q.Page, q.PerPage)).Where(&model.User{
@@ -55,7 +70,7 @@ func (s *rest) GetUsers(ctx context.Context, q types.GetUsersQuery) (*[]model.Us
 	return &users, count, nil
 }
 
-func (s *rest) SignIn(ctx context.Context, json types.SignInRequest) (*model.User, error) {
+func (s *service) SignIn(ctx context.Context, json types.SignInRequest) (*model.User, error) {
 	user := model.User{}
 	if err := s.db.WithContext(ctx).First(&user, model.User{
 		Name: json.Name,
@@ -70,7 +85,7 @@ func (s *rest) SignIn(ctx context.Context, json types.SignInRequest) (*model.Use
 	return &user, nil
 }
 
-func (s *rest) ResetPassword(ctx context.Context, id uint, json types.ResetPasswordRequest) error {
+func (s *service) ResetPassword(ctx context.Context, id uint, json types.ResetPasswordRequest) error {
 	user := model.User{}
 	if err := s.db.WithContext(ctx).First(&user, id).Error; err != nil {
 		return err
@@ -94,7 +109,7 @@ func (s *rest) ResetPassword(ctx context.Context, id uint, json types.ResetPassw
 	return nil
 }
 
-func (s *rest) SignUp(ctx context.Context, json types.SignUpRequest) (*model.User, error) {
+func (s *service) SignUp(ctx context.Context, json types.SignUpRequest) (*model.User, error) {
 	encryptedPasswordBytes, err := bcrypt.GenerateFromPassword([]byte(json.Password), bcrypt.MinCost)
 	if err != nil {
 		return nil, err
@@ -122,7 +137,7 @@ func (s *rest) SignUp(ctx context.Context, json types.SignUpRequest) (*model.Use
 	return &user, nil
 }
 
-func (s *rest) OauthSignin(ctx context.Context, name string) (string, error) {
+func (s *service) OauthSignin(ctx context.Context, name string) (string, error) {
 	oauth := model.Oauth{}
 	if err := s.db.WithContext(ctx).First(&oauth, model.Oauth{Name: name}).Error; err != nil {
 		return "", err
@@ -136,7 +151,7 @@ func (s *rest) OauthSignin(ctx context.Context, name string) (string, error) {
 	return o.AuthCodeURL()
 }
 
-func (s *rest) OauthSigninCallback(ctx context.Context, name, code string) (*model.User, error) {
+func (s *service) OauthSigninCallback(ctx context.Context, name, code string) (*model.User, error) {
 	oauth := model.Oauth{}
 	if err := s.db.WithContext(ctx).First(&oauth, model.Oauth{Name: name}).Error; err != nil {
 		return nil, err
@@ -178,14 +193,14 @@ func (s *rest) OauthSigninCallback(ctx context.Context, name, code string) (*mod
 	return &user, nil
 }
 
-func (s *rest) GetRolesForUser(ctx context.Context, id uint) ([]string, error) {
+func (s *service) GetRolesForUser(ctx context.Context, id uint) ([]string, error) {
 	return s.enforcer.GetRolesForUser(fmt.Sprint(id))
 }
 
-func (s *rest) AddRoleForUser(ctx context.Context, json types.AddRoleForUserParams) (bool, error) {
+func (s *service) AddRoleForUser(ctx context.Context, json types.AddRoleForUserParams) (bool, error) {
 	return s.enforcer.AddRoleForUser(fmt.Sprint(json.ID), json.Role)
 }
 
-func (s *rest) DeleteRoleForUser(ctx context.Context, json types.DeleteRoleForUserParams) (bool, error) {
+func (s *service) DeleteRoleForUser(ctx context.Context, json types.DeleteRoleForUserParams) (bool, error) {
 	return s.enforcer.DeleteRoleForUser(fmt.Sprint(json.ID), json.Role)
 }
diff --git a/manager/types/user.go b/manager/types/user.go
index b4d47c74..6885f362 100644
--- a/manager/types/user.go
+++ b/manager/types/user.go
@@ -16,6 +16,14 @@
 
 package types
 
+type UpdateUserRequest struct {
+	Email    string `json:"email" binding:"omitempty"`
+	Phone    string `json:"phone" binding:"omitempty"`
+	Avatar   string `json:"avatar" binding:"omitempty"`
+	Location string `json:"location" binding:"omitempty"`
+	BIO      string `json:"bio" binding:"omitempty"`
+}
+
 type UserParams struct {
 	ID uint `uri:"id" binding:"required"`
 }
diff --git a/scheduler/metrics/metrics.go b/scheduler/metrics/metrics.go
index 4a58228c..043d45d4 100644
--- a/scheduler/metrics/metrics.go
+++ b/scheduler/metrics/metrics.go
@@ -29,6 +29,20 @@ import (
 	"d7y.io/dragonfly/v2/scheduler/config"
 )
 
+var (
+	// TrafficP2PType is p2p type for traffic metrics
+	TrafficP2PType = "p2p"
+
+	// TrafficBackToSourceType is back-to-source type for traffic metrics
+	TrafficBackToSourceType = "back_to_source"
+
+	// PeerHostTrafficUploadType is upload traffic type for peer host traffic metrics
+	PeerHostTrafficUploadType = "upload"
+
+	// PeerHostTrafficDownloadType is download traffic type for peer host traffic metrics
+	PeerHostTrafficDownloadType = "download"
+)
+
 // Variables declared for metrics.
 var (
 	RegisterPeerTaskCount = promauto.NewCounterVec(prometheus.CounterOpts{
@@ -73,12 +87,12 @@ var (
 		Help:      "Counter of the number of failed of the leaving task.",
 	}, []string{"biz_tag"})
 
-	P2PTraffic = promauto.NewCounterVec(prometheus.CounterOpts{
+	Traffic = promauto.NewCounterVec(prometheus.CounterOpts{
 		Namespace: constants.MetricsNamespace,
 		Subsystem: constants.SchedulerMetricsName,
-		Name:      "p2p_traffic",
-		Help:      "Counter of the number of p2p traffic.",
-	}, []string{"biz_tag"})
+		Name:      "traffic",
+		Help:      "Counter of the number of traffic.",
+	}, []string{"biz_tag", "type"})
 
 	PeerHostTraffic = promauto.NewCounterVec(prometheus.CounterOpts{
 		Namespace: constants.MetricsNamespace,
diff --git a/scheduler/resource/cdn.go b/scheduler/resource/cdn.go
index 2b324921..4b7e1f50 100644
--- a/scheduler/resource/cdn.go
+++ b/scheduler/resource/cdn.go
@@ -37,6 +37,11 @@ import (
 	"d7y.io/dragonfly/v2/scheduler/config"
 )
 
+const (
+	// Default value of biz tag for cdn peer
+	cdnBizTag = "d7y/cdn"
+)
+
 type CDN interface {
 	// TriggerTask start to trigger cdn task
 	TriggerTask(context.Context, *Task) (*Peer, *rpcscheduler.PeerResult, error)
@@ -146,7 +151,7 @@ func (c *cdn) initPeer(task *Task, ps *cdnsystem.PieceSeed) (*Peer, error) {
 	}
 
 	// New cdn peer
-	peer = NewPeer(ps.PeerId, task, host)
+	peer = NewPeer(ps.PeerId, task, host, WithBizTag(cdnBizTag))
 	peer.Log.Info("new cdn peer successfully")
 
 	// Store cdn peer
diff --git a/scheduler/resource/peer.go b/scheduler/resource/peer.go
index 3807ad51..32fb70f6 100644
--- a/scheduler/resource/peer.go
+++ b/scheduler/resource/peer.go
@@ -37,6 +37,9 @@ import (
 const (
 	// Download tiny file timeout
 	downloadTinyFileContextTimeout = 2 * time.Minute
+
+	// Default value of biz tag
+	defaultBizTag = "unknow"
 )
 
 const (
@@ -156,6 +159,7 @@ type Peer struct {
 func NewPeer(id string, task *Task, host *Host, options ...PeerOption) *Peer {
 	p := &Peer{
 		ID:         id,
+		BizTag:     defaultBizTag,
 		Pieces:     &bitset.BitSet{},
 		pieceCosts: []int64{},
 		Stream:     &atomic.Value{},
diff --git a/scheduler/service/service.go b/scheduler/service/service.go
index 6e31f117..dc989667 100644
--- a/scheduler/service/service.go
+++ b/scheduler/service/service.go
@@ -21,6 +21,8 @@ import (
 	"fmt"
 	"io"
 
+	"go.opentelemetry.io/otel/trace"
+
 	"d7y.io/dragonfly/v2/internal/dferrors"
 	logger "d7y.io/dragonfly/v2/internal/dflog"
 	"d7y.io/dragonfly/v2/pkg/container/set"
@@ -276,13 +278,20 @@ func (s *Service) ReportPieceResult(stream rpcscheduler.Scheduler_ReportPieceRes
 
 			// Collect peer host traffic metrics
 			if s.config.Metrics != nil && s.config.Metrics.EnablePeerHost {
-				metrics.PeerHostTraffic.WithLabelValues(peer.BizTag, "download", peer.Host.ID, peer.Host.IP).Add(float64(piece.PieceInfo.RangeSize))
+				metrics.PeerHostTraffic.WithLabelValues(peer.BizTag, metrics.PeerHostTrafficDownloadType, peer.Host.ID, peer.Host.IP).Add(float64(piece.PieceInfo.RangeSize))
 				if parent, ok := s.resource.PeerManager().Load(piece.DstPid); ok {
-					metrics.PeerHostTraffic.WithLabelValues(peer.BizTag, "upload", parent.Host.ID, parent.Host.IP).Add(float64(piece.PieceInfo.RangeSize))
+					metrics.PeerHostTraffic.WithLabelValues(peer.BizTag, metrics.PeerHostTrafficUploadType, parent.Host.ID, parent.Host.IP).Add(float64(piece.PieceInfo.RangeSize))
 				} else {
 					peer.Log.Warnf("dst peer %s not found for piece %#v %#v", piece.DstPid, piece, piece.PieceInfo)
 				}
 			}
+
+			// Collect traffic metrics
+			if piece.DstPid != "" {
+				metrics.Traffic.WithLabelValues(peer.BizTag, metrics.TrafficP2PType).Add(float64(piece.PieceInfo.RangeSize))
+			} else {
+				metrics.Traffic.WithLabelValues(peer.BizTag, metrics.TrafficBackToSourceType).Add(float64(piece.PieceInfo.RangeSize))
+			}
 			continue
 		}
 
@@ -330,7 +339,6 @@ func (s *Service) ReportPeerResult(ctx context.Context, req *rpcscheduler.PeerRe
 	}
 	s.handlePeerSuccess(ctx, peer)
 
-	metrics.P2PTraffic.WithLabelValues(peer.BizTag).Add(float64(req.Traffic))
 	metrics.PeerTaskDownloadDuration.WithLabelValues(peer.BizTag).Observe(float64(req.Cost))
 	return nil
 }
@@ -441,7 +449,8 @@ func (s *Service) registerPeer(ctx context.Context, req *rpcscheduler.PeerTaskRe
 // triggerCDNTask starts trigger cdn task
 func (s *Service) triggerCDNTask(ctx context.Context, task *resource.Task) {
 	task.Log.Infof("trigger cdn download task and task status is %s", task.FSM.Current())
-	peer, endOfPiece, err := s.resource.CDN().TriggerTask(context.Background(), task)
+	peer, endOfPiece, err := s.resource.CDN().TriggerTask(
+		trace.ContextWithSpanContext(context.Background(), trace.SpanContextFromContext(ctx)), task)
 	if err != nil {
 		task.Log.Errorf("trigger cdn download task failed: %v", err)
 		s.handleTaskFail(ctx, task)


Test patch:
diff --git a/scheduler/service/service_test.go b/scheduler/service/service_test.go
index 373aa765..bd1820ce 100644
--- a/scheduler/service/service_test.go
+++ b/scheduler/service/service_test.go
@@ -2126,9 +2126,9 @@ func TestService_handlePieceFail(t *testing.T) {
 					mr.PeerManager().Return(peerManager).Times(1),
 					mp.Load(gomock.Eq(parent.ID)).Return(parent, true).Times(1),
 				)
-				ms.ScheduleParent(gomock.Any(), gomock.Eq(peer), gomock.Eq(blocklist)).Return().Times(1)
 				mr.CDN().Do(func() { wg.Done() }).Return(cdn).Times(1)
 				mc.TriggerTask(gomock.Any(), gomock.Any()).Do(func(ctx context.Context, task *resource.Task) { wg.Done() }).Return(peer, &rpcscheduler.PeerResult{}, nil).Times(1)
+				ms.ScheduleParent(gomock.Any(), gomock.Eq(peer), gomock.Eq(blocklist)).Return().AnyTimes()
 
 				svc.handlePieceFail(context.Background(), peer, piece)
 				assert := assert.New(t)