You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by ju...@apache.org on 2020/09/27 02:40:40 UTC

[apisix-dashboard] branch refactor updated: feat: add Service and Upstream refactoring (#497)

This is an automated email from the ASF dual-hosted git repository.

juzhiyuan pushed a commit to branch refactor
in repository https://gitbox.apache.org/repos/asf/apisix-dashboard.git


The following commit(s) were added to refs/heads/refactor by this push:
     new 094c5ee  feat: add Service and Upstream refactoring (#497)
094c5ee is described below

commit 094c5ee6bddd95bb455ad79ad60cf9e17218026c
Author: nic-chen <33...@users.noreply.github.com>
AuthorDate: Sun Sep 27 10:40:34 2020 +0800

    feat: add Service and Upstream refactoring (#497)
    
    * feat: add Service and Upstream refactoring
    
    * fix add license
---
 api/internal/core/entity/entity.go        |  26 ++++
 api/internal/core/storage/etcd.go         |  16 +++
 api/internal/core/storage/storage.go      |  16 +++
 api/internal/core/store/store.go          |  16 +++
 api/internal/core/store/store_test.go     |  16 +++
 api/internal/core/store/validate.go       |  16 +++
 api/internal/core/store/validate_test.go  |  16 +++
 api/internal/handler/consumer/consumer.go |  16 +++
 api/internal/handler/handler.go           |  16 +++
 api/internal/handler/route/route.go       |  16 +++
 api/internal/handler/service/service.go   | 189 +++++++++++++++++++++++++++++
 api/internal/handler/ssl/ssl.go           |  16 +++
 api/internal/handler/upstream/upstream.go | 192 ++++++++++++++++++++++++++++++
 api/internal/utils/closer.go              |  16 +++
 api/internal/utils/consts/error.go        |  16 +++
 api/route/base.go                         |   8 +-
 16 files changed, 605 insertions(+), 2 deletions(-)

diff --git a/api/internal/core/entity/entity.go b/api/internal/core/entity/entity.go
index f588feb..baad4aa 100644
--- a/api/internal/core/entity/entity.go
+++ b/api/internal/core/entity/entity.go
@@ -1,3 +1,19 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 package entity
 
 type Route struct {
@@ -120,3 +136,13 @@ type SSL struct {
 	ExpTime int      `json:"exptime"`
 	Status  int      `json:"status"`
 }
+
+type Service struct {
+	ID         string      `json:"id"`
+	Name       string      `json:"name,omitempty"`
+	Desc       string      `json:"desc,omitempty"`
+	Upstream   Upstream    `json:"upstream,omitempty"`
+	UpstreamID string      `json:"upstream_id,omitempty"`
+	Plugins    interface{} `json:"plugins,omitempty"`
+	Script     string      `json:"script,omitempty"`
+}
diff --git a/api/internal/core/storage/etcd.go b/api/internal/core/storage/etcd.go
index 2f8f257..aff2d4a 100644
--- a/api/internal/core/storage/etcd.go
+++ b/api/internal/core/storage/etcd.go
@@ -1,3 +1,19 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 package storage
 
 import (
diff --git a/api/internal/core/storage/storage.go b/api/internal/core/storage/storage.go
index c807520..c4b5399 100644
--- a/api/internal/core/storage/storage.go
+++ b/api/internal/core/storage/storage.go
@@ -1,3 +1,19 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 package storage
 
 import "context"
diff --git a/api/internal/core/store/store.go b/api/internal/core/store/store.go
index 46793e6..6b63e6a 100644
--- a/api/internal/core/store/store.go
+++ b/api/internal/core/store/store.go
@@ -1,3 +1,19 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 package store
 
 import (
diff --git a/api/internal/core/store/store_test.go b/api/internal/core/store/store_test.go
index 536cc79..cc074cb 100644
--- a/api/internal/core/store/store_test.go
+++ b/api/internal/core/store/store_test.go
@@ -1,3 +1,19 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 package store
 
 import (
diff --git a/api/internal/core/store/validate.go b/api/internal/core/store/validate.go
index 1ee0960..b936cc0 100644
--- a/api/internal/core/store/validate.go
+++ b/api/internal/core/store/validate.go
@@ -1,3 +1,19 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 package store
 
 import (
diff --git a/api/internal/core/store/validate_test.go b/api/internal/core/store/validate_test.go
index bf5ab05..3821154 100644
--- a/api/internal/core/store/validate_test.go
+++ b/api/internal/core/store/validate_test.go
@@ -1,3 +1,19 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 package store
 
 import (
diff --git a/api/internal/handler/consumer/consumer.go b/api/internal/handler/consumer/consumer.go
index 36180fe..048b1cc 100644
--- a/api/internal/handler/consumer/consumer.go
+++ b/api/internal/handler/consumer/consumer.go
@@ -1,3 +1,19 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 package consumer
 
 import (
diff --git a/api/internal/handler/handler.go b/api/internal/handler/handler.go
index 0c62c7c..2736700 100644
--- a/api/internal/handler/handler.go
+++ b/api/internal/handler/handler.go
@@ -1,3 +1,19 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 package handler
 
 import "github.com/gin-gonic/gin"
diff --git a/api/internal/handler/route/route.go b/api/internal/handler/route/route.go
index d69263c..7107555 100644
--- a/api/internal/handler/route/route.go
+++ b/api/internal/handler/route/route.go
@@ -1,3 +1,19 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 package route
 
 import (
diff --git a/api/internal/handler/service/service.go b/api/internal/handler/service/service.go
new file mode 100644
index 0000000..2255bdd
--- /dev/null
+++ b/api/internal/handler/service/service.go
@@ -0,0 +1,189 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package service
+
+import (
+	"reflect"
+	"strings"
+
+	"github.com/api7/go-jsonpatch"
+	"github.com/gin-gonic/gin"
+	"github.com/shiningrush/droplet"
+	"github.com/shiningrush/droplet/data"
+	"github.com/shiningrush/droplet/wrapper"
+	wgin "github.com/shiningrush/droplet/wrapper/gin"
+
+	"github.com/apisix/manager-api/internal/core/entity"
+	"github.com/apisix/manager-api/internal/core/store"
+	"github.com/apisix/manager-api/internal/handler"
+	"github.com/apisix/manager-api/internal/utils"
+)
+
+type Handler struct {
+	serviceStore *store.GenericStore
+}
+
+func NewHandler() (handler.RouteRegister, error) {
+	s, err := store.NewGenericStore(store.GenericStoreOption{
+		BasePath: "/apisix/services",
+		ObjType:  reflect.TypeOf(entity.Service{}),
+		KeyFunc: func(obj interface{}) string {
+			r := obj.(*entity.Service)
+			return r.ID
+		},
+	})
+	if err != nil {
+		return nil, err
+	}
+	if err := s.Init(); err != nil {
+		return nil, err
+	}
+
+	utils.AppendToClosers(s.Close)
+	return &Handler{
+		serviceStore: s,
+	}, nil
+}
+
+func (h *Handler) ApplyRoute(r *gin.Engine) {
+	r.GET("/apisix/admin/services/:id", wgin.Wraps(h.Get,
+		wrapper.InputType(reflect.TypeOf(GetInput{}))))
+	r.GET("/apisix/admin/services", wgin.Wraps(h.List,
+		wrapper.InputType(reflect.TypeOf(ListInput{}))))
+	r.POST("/apisix/admin/services", wgin.Wraps(h.Create,
+		wrapper.InputType(reflect.TypeOf(entity.Service{}))))
+	r.PUT("/apisix/admin/services/:id", wgin.Wraps(h.Update,
+		wrapper.InputType(reflect.TypeOf(UpdateInput{}))))
+	r.DELETE("/apisix/admin/services", wgin.Wraps(h.BatchDelete,
+		wrapper.InputType(reflect.TypeOf(BatchDelete{}))))
+}
+
+type GetInput struct {
+	ID string `auto_read:"id,path" validate:"required"`
+}
+
+func (h *Handler) Get(c droplet.Context) (interface{}, error) {
+	input := c.Input().(*GetInput)
+
+	r, err := h.serviceStore.Get(input.ID)
+	if err != nil {
+		return nil, err
+	}
+	return r, nil
+}
+
+type ListInput struct {
+	ID string `auto_read:"id,query"`
+	data.Pager
+}
+
+func (h *Handler) List(c droplet.Context) (interface{}, error) {
+	input := c.Input().(*ListInput)
+
+	ret, err := h.serviceStore.List(store.ListInput{
+		Predicate: func(obj interface{}) bool {
+			return true
+		},
+		PageSize:   input.PageSize,
+		PageNumber: input.PageNumber,
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	return ret, nil
+}
+
+func (h *Handler) Create(c droplet.Context) (interface{}, error) {
+	input := c.Input().(*entity.Service)
+
+	if err := h.serviceStore.Create(c.Context(), input); err != nil {
+		return nil, err
+	}
+
+	return nil, nil
+}
+
+type UpdateInput struct {
+	ID string `auto_read:"id,path"`
+	entity.Service
+}
+
+func (h *Handler) Update(c droplet.Context) (interface{}, error) {
+	input := c.Input().(*UpdateInput)
+	input.Service.ID = input.ID
+
+	if err := h.serviceStore.Update(c.Context(), &input.Service); err != nil {
+		return nil, err
+	}
+
+	return nil, nil
+}
+
+type BatchDelete struct {
+	IDs string `auto_read:"ids,query"`
+}
+
+func (h *Handler) BatchDelete(c droplet.Context) (interface{}, error) {
+	input := c.Input().(*BatchDelete)
+
+	if err := h.serviceStore.BatchDelete(c.Context(), strings.Split(input.IDs, ",")); err != nil {
+		return nil, err
+	}
+
+	return nil, nil
+}
+
+func (h *Handler) Patch(c droplet.Context) (interface{}, error) {
+	input := c.Input().(*UpdateInput)
+	arr := strings.Split(input.ID, "/")
+	var subPath string
+	if len(arr) > 1 {
+		input.ID = arr[0]
+		subPath = arr[1]
+	}
+
+	stored, err := h.serviceStore.Get(input.ID)
+	if err != nil {
+		return nil, err
+	}
+
+	var patch jsonpatch.Patch
+	if subPath != "" {
+		patch = jsonpatch.Patch{
+			Operations: []jsonpatch.PatchOperation{
+				{Op: jsonpatch.Replace, Path: subPath, Value: c.Input()},
+			},
+		}
+	} else {
+		patch, err = jsonpatch.MakePatch(stored, input.Service)
+		if err != nil {
+			panic(err)
+		}
+	}
+
+	err = patch.Apply(&stored)
+	if err != nil {
+		panic(err)
+	}
+
+	if err := h.serviceStore.Update(c.Context(), &stored); err != nil {
+		return nil, err
+	}
+
+	return nil, nil
+}
diff --git a/api/internal/handler/ssl/ssl.go b/api/internal/handler/ssl/ssl.go
index a9a0882..46d4126 100644
--- a/api/internal/handler/ssl/ssl.go
+++ b/api/internal/handler/ssl/ssl.go
@@ -1,3 +1,19 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 package ssl
 
 import (
diff --git a/api/internal/handler/upstream/upstream.go b/api/internal/handler/upstream/upstream.go
new file mode 100644
index 0000000..1c0b41b
--- /dev/null
+++ b/api/internal/handler/upstream/upstream.go
@@ -0,0 +1,192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package upstream
+
+import (
+	"reflect"
+	"strings"
+
+	"github.com/api7/go-jsonpatch"
+	"github.com/gin-gonic/gin"
+	"github.com/shiningrush/droplet"
+	"github.com/shiningrush/droplet/data"
+	"github.com/shiningrush/droplet/wrapper"
+	wgin "github.com/shiningrush/droplet/wrapper/gin"
+
+	"github.com/apisix/manager-api/internal/core/entity"
+	"github.com/apisix/manager-api/internal/core/store"
+	"github.com/apisix/manager-api/internal/handler"
+	"github.com/apisix/manager-api/internal/utils"
+)
+
+type Handler struct {
+	upstreamStore *store.GenericStore
+}
+
+func NewHandler() (handler.RouteRegister, error) {
+	s, err := store.NewGenericStore(store.GenericStoreOption{
+		BasePath: "/apisix/upstreams",
+		ObjType:  reflect.TypeOf(entity.Upstream{}),
+		KeyFunc: func(obj interface{}) string {
+			r := obj.(*entity.Upstream)
+			return r.ID
+		},
+	})
+	if err != nil {
+		return nil, err
+	}
+	if err := s.Init(); err != nil {
+		return nil, err
+	}
+
+	utils.AppendToClosers(s.Close)
+	return &Handler{
+		upstreamStore: s,
+	}, nil
+}
+
+func (h *Handler) ApplyRoute(r *gin.Engine) {
+	r.GET("/apisix/admin/upstreams/:id", wgin.Wraps(h.Get,
+		wrapper.InputType(reflect.TypeOf(GetInput{}))))
+	r.GET("/apisix/admin/upstreams", wgin.Wraps(h.List,
+		wrapper.InputType(reflect.TypeOf(ListInput{}))))
+	r.POST("/apisix/admin/upstreams", wgin.Wraps(h.Create,
+		wrapper.InputType(reflect.TypeOf(entity.Upstream{}))))
+	r.PUT("/apisix/admin/upstreams/:id", wgin.Wraps(h.Update,
+		wrapper.InputType(reflect.TypeOf(UpdateInput{}))))
+	r.DELETE("/apisix/admin/upstreams", wgin.Wraps(h.BatchDelete,
+		wrapper.InputType(reflect.TypeOf(BatchDelete{}))))
+}
+
+type GetInput struct {
+	ID string `auto_read:"id,path" validate:"required"`
+}
+
+func (h *Handler) Get(c droplet.Context) (interface{}, error) {
+	input := c.Input().(*GetInput)
+
+	r, err := h.upstreamStore.Get(input.ID)
+	if err != nil {
+		return nil, err
+	}
+	return r, nil
+}
+
+type ListInput struct {
+	ID string `auto_read:"id,query"`
+	data.Pager
+}
+
+func (h *Handler) List(c droplet.Context) (interface{}, error) {
+	input := c.Input().(*ListInput)
+
+	ret, err := h.upstreamStore.List(store.ListInput{
+		Predicate: func(obj interface{}) bool {
+			if input.ID != "" {
+				return strings.Index(obj.(*entity.Upstream).ID, input.ID) > 0
+			}
+			return true
+		},
+		PageSize:   input.PageSize,
+		PageNumber: input.PageNumber,
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	return ret, nil
+}
+
+func (h *Handler) Create(c droplet.Context) (interface{}, error) {
+	input := c.Input().(*entity.Upstream)
+
+	if err := h.upstreamStore.Create(c.Context(), input); err != nil {
+		return nil, err
+	}
+
+	return nil, nil
+}
+
+type UpdateInput struct {
+	ID string `auto_read:"id,path"`
+	entity.Upstream
+}
+
+func (h *Handler) Update(c droplet.Context) (interface{}, error) {
+	input := c.Input().(*UpdateInput)
+	input.Upstream.ID = input.ID
+
+	if err := h.upstreamStore.Update(c.Context(), &input.Upstream); err != nil {
+		return nil, err
+	}
+
+	return nil, nil
+}
+
+type BatchDelete struct {
+	IDs string `auto_read:"ids,query"`
+}
+
+func (h *Handler) BatchDelete(c droplet.Context) (interface{}, error) {
+	input := c.Input().(*BatchDelete)
+
+	if err := h.upstreamStore.BatchDelete(c.Context(), strings.Split(input.IDs, ",")); err != nil {
+		return nil, err
+	}
+
+	return nil, nil
+}
+
+func (h *Handler) Patch(c droplet.Context) (interface{}, error) {
+	input := c.Input().(*UpdateInput)
+	arr := strings.Split(input.ID, "/")
+	var subPath string
+	if len(arr) > 1 {
+		input.ID = arr[0]
+		subPath = arr[1]
+	}
+
+	stored, err := h.upstreamStore.Get(input.ID)
+	if err != nil {
+		return nil, err
+	}
+
+	var patch jsonpatch.Patch
+	if subPath != "" {
+		patch = jsonpatch.Patch{
+			Operations: []jsonpatch.PatchOperation{
+				{Op: jsonpatch.Replace, Path: subPath, Value: c.Input()},
+			},
+		}
+	} else {
+		patch, err = jsonpatch.MakePatch(stored, input.Upstream)
+		if err != nil {
+			panic(err)
+		}
+	}
+
+	err = patch.Apply(&stored)
+	if err != nil {
+		panic(err)
+	}
+
+	if err := h.upstreamStore.Update(c.Context(), &stored); err != nil {
+		return nil, err
+	}
+
+	return nil, nil
+}
diff --git a/api/internal/utils/closer.go b/api/internal/utils/closer.go
index 46c4d11..b6e7d8d 100644
--- a/api/internal/utils/closer.go
+++ b/api/internal/utils/closer.go
@@ -1,3 +1,19 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 package utils
 
 import "log"
diff --git a/api/internal/utils/consts/error.go b/api/internal/utils/consts/error.go
index 4f8f1d4..09f4912 100644
--- a/api/internal/utils/consts/error.go
+++ b/api/internal/utils/consts/error.go
@@ -1,3 +1,19 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 package consts
 
 import "github.com/shiningrush/droplet/data"
diff --git a/api/route/base.go b/api/route/base.go
index 2afd68e..e40bcc6 100644
--- a/api/route/base.go
+++ b/api/route/base.go
@@ -17,6 +17,8 @@
 package route
 
 import (
+	"github.com/apisix/manager-api/internal/handler/service"
+	"github.com/apisix/manager-api/internal/handler/upstream"
 	"github.com/gin-contrib/pprof"
 	"github.com/gin-contrib/sessions"
 	"github.com/gin-contrib/sessions/cookie"
@@ -44,9 +46,9 @@ func SetUpRouter() *gin.Engine {
 	AppendHealthCheck(r)
 	AppendAuthentication(r)
 	//AppendRoute(r)
-	AppendSsl(r)
+	//AppendSsl(r)
 	AppendPlugin(r)
-	AppendUpstream(r)
+	//AppendUpstream(r)
 	//AppendConsumer(r)
 	AppendRouteGroup(r)
 
@@ -54,6 +56,8 @@ func SetUpRouter() *gin.Engine {
 		route.NewHandler,
 		ssl.NewHandler,
 		consumer.NewHandler,
+		upstream.NewHandler,
+		service.NewHandler,
 	}
 	for i := range factories {
 		h, err := factories[i]()