Skip to content

Commit

Permalink
openapi: support reset port (#907)
Browse files Browse the repository at this point in the history
  • Loading branch information
Ehco1996 committed Nov 17, 2023
1 parent 800e5ee commit 19f5e66
Show file tree
Hide file tree
Showing 11 changed files with 128 additions and 58 deletions.
8 changes: 0 additions & 8 deletions apps/mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,6 @@
from django.core.exceptions import ValidationError
from django.db import connection, models, transaction
from django.db.models.signals import post_delete, post_save, pre_save
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt


class CSRFExemptMixin:
@method_decorator(csrf_exempt)
def dispatch(self, *args, **kwargs):
return super(CSRFExemptMixin, self).dispatch(*args, **kwargs)


class StaffRequiredMixin(LoginRequiredMixin):
Expand Down
14 changes: 14 additions & 0 deletions apps/openapi/serializer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from rest_framework import serializers

from apps.proxy.models import ProxyNode


class ProxyNodeSerializer(serializers.ModelSerializer):
class Meta:
model = ProxyNode
fields = "__all__"

multi_user_port = serializers.SerializerMethodField()

def get_multi_user_port(self, node: ProxyNode):
return node.get_user_port()
11 changes: 5 additions & 6 deletions apps/openapi/urls.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
from django.urls import path
from rest_framework import routers

from . import views
from apps.openapi import views

app_name = "openapi"
urlpatterns = [
path("proxy_nodes/search/", views.ProxyNodeSearchView.as_view()),
path("proxy_nodes/<int:node_id>/", views.ProxyNodeDetailView.as_view()),
]

router = routers.DefaultRouter()
router.register(r"proxy_nodes", views.ProxyNodeViewSet, basename="proxy_nodes")
25 changes: 9 additions & 16 deletions apps/openapi/utils.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,19 @@
from functools import wraps

from django.http import JsonResponse
from rest_framework import authentication, exceptions

from apps.openapi.models import UserOpenAPIKey


def gen_common_error_response(msg: str, status=400) -> JsonResponse:
return JsonResponse({"error_msg": msg}, status=status)


def openapi_authorized(view_func):
@wraps(view_func)
def wrapper(request, *args, **kwargs):
class OpenAPIAuthentication(authentication.TokenAuthentication):
def authenticate(self, request):
key = request.META.get("HTTP_X_API_KEY", "")
if not key:
return gen_common_error_response(
"x-api-key in header not found", status=401
)
raise exceptions.NotAuthenticated("api key is required")
user_key = UserOpenAPIKey.get_by_key(key)
if not user_key:
return gen_common_error_response("x-api-key is invalid", status=401)
request.user = user_key.user
return view_func(request, *args, **kwargs)
raise exceptions.AuthenticationFailed("api key is wrong", code=401)
return (user_key.user, None)


return wrapper
def gen_common_error_response(msg: str, status=400) -> JsonResponse:
return JsonResponse({"error_msg": msg}, status=status)
53 changes: 28 additions & 25 deletions apps/openapi/views.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
from django.forms import model_to_dict
from django.http import JsonResponse
from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from rest_framework.decorators import action
from rest_framework.viewsets import ModelViewSet

from apps.openapi.utils import gen_common_error_response, openapi_authorized
from apps.openapi.serializer import ProxyNodeSerializer
from apps.openapi.utils import OpenAPIAuthentication, gen_common_error_response
from apps.proxy.models import ProxyNode
from apps.utils import handle_json_request


class ProxyNodeSearchView(View):
@csrf_exempt
@method_decorator(openapi_authorized)
def dispatch(self, *args, **kwargs):
return super(ProxyNodeSearchView, self).dispatch(*args, **kwargs)
class BaseOpenAPIViewSet(ModelViewSet):
authentication_classes = [OpenAPIAuthentication]

def get(self, request):

class ProxyNodeViewSet(BaseOpenAPIViewSet):
serializer_class = ProxyNodeSerializer
queryset = ProxyNode.objects.all()

@action(detail=False, methods=["get"])
def search(self, request):
ip = request.GET.get("ip")
if not ip:
return gen_common_error_response("ip in query is required")
Expand All @@ -25,23 +26,25 @@ def get(self, request):
return gen_common_error_response(
f"node with ip:{ip} not found", status=404
)
return JsonResponse(model_to_dict(node))

return JsonResponse(self.serializer_class(node).data)

class ProxyNodeDetailView(View):
@csrf_exempt
@method_decorator(openapi_authorized)
@method_decorator(handle_json_request)
def dispatch(self, *args, **kwargs):
return super(ProxyNodeDetailView, self).dispatch(*args, **kwargs)
@action(detail=True, methods=["post"])
def reset_multi_user_port(self, request, pk):
node = ProxyNode.get_by_id(pk)
if not node:
return gen_common_error_response(
f"node with id:{pk} not found", status=404
)
node.reset_random_multi_user_port()
return JsonResponse(self.serializer_class(node).data)

def patch(self, request, node_id):
node = ProxyNode.get_by_id(node_id)
def partial_update(self, request, pk):
node = ProxyNode.get_by_id(pk)
if not node:
return gen_common_error_response(
f"node with id:{node_id} not found", status=404
f"node with id:{pk} not found", status=404
)
enable = request.json.get("enable")
enable = request.data.get("enable")
node.enable = enable
node.save()
return JsonResponse(model_to_dict(node))
return JsonResponse(self.serializer_class(node).data)
3 changes: 2 additions & 1 deletion apps/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from django.urls import include, path

from apps.custom_views import AsyncPasswordResetView
from apps.openapi.urls import router as openapi_router

urlpatterns = [
path(
Expand Down Expand Up @@ -36,7 +37,7 @@

# append openapi urls
urlpatterns.append(
path("openapi/v1/", include("apps.openapi.urls", namespace="openapi"))
path("openapi/v1/", include(openapi_router.urls)),
)


Expand Down
2 changes: 2 additions & 0 deletions apps/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ def wrapper(request, *args, **kwargs):
def handle_json_request(view_func):
@wraps(view_func)
def wrapper(request, *args, **kw):
if request.headers.get("Content-Type") != "application/json":
return
try:
request.json = json.loads(request.body)
except Exception:
Expand Down
1 change: 1 addition & 0 deletions configs/default/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"rest_framework",
"corsheaders",
"django_prometheus",
"django_telegram_login",
Expand Down
30 changes: 28 additions & 2 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ sentry-sdk = "^1.10.1"
short_url = "^1.2.2"
tomd = "^0.1.3"
uWSGI = "^2.0.22"
djangorestframework = "^3.14.0"

[tool.poetry.group.dev.dependencies]
autoflake = "^2.2.1"
Expand Down
38 changes: 38 additions & 0 deletions spec/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ components:
- current_used_upload_bandwidth_bytes
- download_bandwidth_bytes
- upload_bandwidth_bytes
- multi_user_port
CommonErrorResp:
type: object
properties:
Expand Down Expand Up @@ -158,3 +159,40 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/CommonErrorResp"
/proxy_nodes/{node_id}/reset_multi_user_port/:
post:
summary: reset port for proxy node
tags:
- ProxyNode
parameters:
- name: node_id
in: path
required: true
schema:
type: integer
description: ID of the ProxyNode to update
responses:
200:
description: Successful update
content:
application/json:
schema:
$ref: "#/components/schemas/ProxyNode"
400:
description: Bad request
content:
application/json:
schema:
$ref: "#/components/schemas/CommonErrorResp"
404:
description: ProxyNode not found
content:
application/json:
schema:
$ref: "#/components/schemas/CommonErrorResp"
500:
description: Internal server error
content:
application/json:
schema:
$ref: "#/components/schemas/CommonErrorResp"

0 comments on commit 19f5e66

Please sign in to comment.