Skip to content

Commit

Permalink
Merge pull request #17 from OVINC-CN/feat_tcloud_ci_callback
Browse files Browse the repository at this point in the history
feat(tcloud): support for ci callback
  • Loading branch information
OrenZhang authored Nov 29, 2024
2 parents db8aa87 + 1502053 commit 7c63c3e
Show file tree
Hide file tree
Showing 15 changed files with 364 additions and 1 deletion.
Empty file added apps/tcloud/__init__.py
Empty file.
21 changes: 21 additions & 0 deletions apps/tcloud/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from django.contrib import admin

from apps.tcloud.models import AuditCallback


@admin.register(AuditCallback)
class AuditCallbackAdmin(admin.ModelAdmin):
list_display = [
"id",
"bucket_region",
"bucket_id",
"event_name",
"audit_id",
"is_sensitive",
"creation_time",
"callback_time",
"updated_time",
"is_handled",
]
list_filter = ["bucket_region", "bucket_id", "event_name", "is_sensitive", "is_handled"]
list_editable = ["is_handled"]
8 changes: 8 additions & 0 deletions apps/tcloud/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django.apps import AppConfig
from django.utils.translation import gettext_lazy


class TCloudConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "apps.tcloud"
verbose_name = gettext_lazy("TCloud")
11 changes: 11 additions & 0 deletions apps/tcloud/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from django.utils.translation import gettext_lazy
from ovinc_client.core.models import TextChoices


class CICallbackEventName(TextChoices):
REVIEW_IMAGE = "ReviewImage", gettext_lazy("Review Image")
REVIEW_DOCUMENT = "ReviewDocument", gettext_lazy("Review Document")
REVIEW_VIDEO = "ReviewVideo", gettext_lazy("Review Video")
REVIEW_AUDIO = "ReviewAudio", gettext_lazy("Review Audio")
REVIEW_TEXT = "ReviewText", gettext_lazy("Review Text")
REVIEW_HTML = "ReviewHtml", gettext_lazy("Review HTML")
50 changes: 50 additions & 0 deletions apps/tcloud/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# pylint: disable=C0301,C0103
# Generated by Django 4.2.16 on 2024-11-29 12:00

from django.db import migrations, models


class Migration(migrations.Migration):
initial = True

dependencies = []

operations = [
migrations.CreateModel(
name="AuditCallback",
fields=[
("id", models.BigAutoField(primary_key=True, serialize=False, verbose_name="ID")),
("bucket_region", models.CharField(max_length=32, verbose_name="Bucket Region")),
("bucket_id", models.CharField(db_index=True, max_length=255, verbose_name="Bucket ID")),
(
"event_name",
models.CharField(
choices=[
("ReviewImage", "Review Image"),
("ReviewDocument", "Review Document"),
("ReviewVideo", "Review Video"),
("ReviewAudio", "Review Audio"),
("ReviewText", "Review Text"),
("ReviewHtml", "Review HTML"),
],
db_index=True,
max_length=32,
verbose_name="Event Name",
),
),
("audit_id", models.CharField(db_index=True, max_length=255, verbose_name="Audit ID")),
("is_sensitive", models.BooleanField(db_index=True, verbose_name="Result")),
("detail", models.JSONField(verbose_name="Detail")),
("creation_time", models.DateTimeField(db_index=True, verbose_name="Audit Time")),
("callback_time", models.DateTimeField(auto_now_add=True, db_index=True, verbose_name="Callback Time")),
("updated_time", models.DateTimeField(auto_now=True, db_index=True, verbose_name="Updated Time")),
("is_handled", models.BooleanField(db_index=True, default=False, verbose_name="Handled")),
],
options={
"verbose_name": "Audit Callback",
"verbose_name_plural": "Audit Callback",
"ordering": ["-id"],
"unique_together": {("bucket_region", "bucket_id", "event_name", "audit_id")},
},
),
]
18 changes: 18 additions & 0 deletions apps/tcloud/migrations/0002_alter_auditcallback_creation_time.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# pylint: disable=C0301,C0103
# Generated by Django 4.2.16 on 2024-11-29 13:05

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("tcloud", "0001_initial"),
]

operations = [
migrations.AlterField(
model_name="auditcallback",
name="creation_time",
field=models.CharField(blank=True, db_index=True, max_length=32, null=True, verbose_name="Audit Time"),
),
]
Empty file.
62 changes: 62 additions & 0 deletions apps/tcloud/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
from django.contrib.auth import get_user_model
from django.db import models
from django.utils.translation import gettext_lazy
from ovinc_client.core.constants import MAX_CHAR_LENGTH, SHORT_CHAR_LENGTH
from ovinc_client.core.models import BaseModel

from apps.tcloud.constants import CICallbackEventName
from apps.tcloud.parsers import TCICallbackParser, TCIDocumentCallbackParser

USER_MODEL = get_user_model()


class AuditCallback(BaseModel):
"""
Audit Callback
"""

id = models.BigAutoField(verbose_name=gettext_lazy("ID"), primary_key=True)
bucket_region = models.CharField(verbose_name=gettext_lazy("Bucket Region"), max_length=SHORT_CHAR_LENGTH)
bucket_id = models.CharField(verbose_name=gettext_lazy("Bucket ID"), max_length=MAX_CHAR_LENGTH, db_index=True)
event_name = models.CharField(
verbose_name=gettext_lazy("Event Name"),
max_length=SHORT_CHAR_LENGTH,
db_index=True,
choices=CICallbackEventName.choices,
)
audit_id = models.CharField(verbose_name=gettext_lazy("Audit ID"), max_length=MAX_CHAR_LENGTH, db_index=True)
is_sensitive = models.BooleanField(verbose_name=gettext_lazy("Is Sensitive"), db_index=True)
detail = models.JSONField(verbose_name=gettext_lazy("Detail"))
creation_time = models.CharField(
verbose_name=gettext_lazy("Audit Time"), max_length=SHORT_CHAR_LENGTH, db_index=True, null=True, blank=True
)
callback_time = models.DateTimeField(verbose_name=gettext_lazy("Callback Time"), db_index=True, auto_now_add=True)
updated_time = models.DateTimeField(verbose_name=gettext_lazy("Updated Time"), db_index=True, auto_now=True)
is_handled = models.BooleanField(verbose_name=gettext_lazy("Handled"), db_index=True, default=False)

class Meta:
verbose_name = gettext_lazy("Audit Callback")
verbose_name_plural = verbose_name
ordering = ["-id"]
unique_together = [["bucket_region", "bucket_id", "event_name", "audit_id"]]

def __str__(self):
return f"{self.bucket_region}:{self.bucket_id}:{self.event_name}:{self.audit_id}"

@classmethod
def add_callback(cls, data: dict) -> "AuditCallback":
event_name = data["EventName"]
match event_name:
case CICallbackEventName.REVIEW_DOCUMENT:
parser = TCIDocumentCallbackParser(data)
case _:
parser = TCICallbackParser(data)
return cls.objects.create(
bucket_region=parser.bucket_region,
bucket_id=parser.bucket_id,
event_name=event_name,
audit_id=parser.audit_id,
is_sensitive=parser.is_sensitive,
detail=parser.detail,
creation_time=parser.creation_time,
)
41 changes: 41 additions & 0 deletions apps/tcloud/parsers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
class TCICallbackParser:
"""
callback parser
"""

def __init__(self, data: dict):
self.data = data

@property
def is_sensitive(self) -> bool:
return self.data["JobsDetail"]["Result"] != 0

@property
def audit_id(self) -> str:
return self.data["JobsDetail"]["JobId"]

@property
def detail(self) -> dict:
return self.data

@property
def bucket_id(self) -> str:
return self.data["JobsDetail"]["BucketId"]

@property
def bucket_region(self) -> str:
return self.data["JobsDetail"]["Region"]

@property
def creation_time(self) -> str:
return self.data["JobsDetail"]["CreationTime"]


class TCIDocumentCallbackParser(TCICallbackParser):
"""
document callback parser
"""

@property
def is_sensitive(self) -> bool:
return self.data["JobsDetail"]["Suggestion"] != 0
9 changes: 9 additions & 0 deletions apps/tcloud/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from adrf.serializers import Serializer
from rest_framework import serializers

from apps.tcloud.constants import CICallbackEventName


class TCICallbackSerializer(Serializer):
EventName = serializers.ChoiceField(choices=CICallbackEventName.choices)
JobsDetail = serializers.JSONField()
10 changes: 10 additions & 0 deletions apps/tcloud/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.conf import settings
from django.urls import include, path
from rest_framework.routers import SimpleRouter

from apps.tcloud.views import AuditCallbackViewSet

router = SimpleRouter()
router.register("", AuditCallbackViewSet, basename="audit_callback")

urlpatterns = [path(f"ci/callback/{settings.TCI_AUDIT_CALLBACK_PREFIX}", include(router.urls))]
57 changes: 57 additions & 0 deletions apps/tcloud/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from channels.db import database_sync_to_async
from django.conf import settings
from django.utils.translation import gettext
from ovinc_client.core.auth import SessionAuthenticate
from ovinc_client.core.viewsets import MainViewSet
from rest_framework.request import Request
from rest_framework.response import Response

from apps.cel.tasks import send_notice
from apps.notice.constants import NoticeWayChoices
from apps.tcloud.models import AuditCallback
from apps.tcloud.serializers import TCICallbackSerializer


class AuditCallbackViewSet(MainViewSet):
"""
callback view set
"""

authentication_classes = [SessionAuthenticate]

async def create(self, request: Request, *args, **kwargs) -> Response:
"""
callback
"""
# validate
req_slz = TCICallbackSerializer(data=request.data)
req_slz.is_valid(raise_exception=True)
# save
callback = await database_sync_to_async(AuditCallback.add_callback)(req_slz.validated_data)
if callback.is_sensitive:
send_notice.delay(
notice_type=NoticeWayChoices.ROBOT,
robots=[settings.NOTICE_AUDIT_SENSITIVE_ROBOT],
content={
"msgtype": "markdown",
"markdown": {
"content": gettext(
'<font color="warning">Sensitive Object Notification</font>\n'
"Bucket Region: %(region)s\n"
"Bucket ID: %(bucket)s\n"
"Event Name: %(event)s\n"
"Audit ID: %(audit_id)s\n"
"Creation Time: %(creation_time)s\n"
)
% {
"region": callback.bucket_region,
"bucket": callback.bucket_id,
"event": callback.get_event_name_display(),
"audit_id": callback.audit_id,
"creation_time": callback.creation_time,
}
},
},
)
# response
return Response()
5 changes: 5 additions & 0 deletions entry/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"apps.cel",
"apps.home",
"apps.notice",
"apps.tcloud",
"ovinc_client.tcaptcha",
"ovinc_client.trace",
]
Expand Down Expand Up @@ -230,6 +231,10 @@
)
NOTICE_SMS_ID_VERIFY_CODE = os.getenv("NOTICE_SMS_ID_VERIFY_CODE", "")
NOTICE_SMS_ID_VERIFY_CODE_GLOBAL = os.getenv("NOTICE_SMS_ID_VERIFY_CODE_GLOBAL", "")
NOTICE_AUDIT_SENSITIVE_ROBOT = os.getenv("NOTICE_AUDIT_SENSITIVE_ROBOT", "")

# TCI Callback
TCI_AUDIT_CALLBACK_PREFIX = os.getenv("TCI_AUDIT_CALLBACK_PREFIX", "")

# OAuth
OAUTH_CODE_TIMEOUT = int(os.getenv("OAUTH_CODE_TIMEOUT", str(60 * 5)))
Expand Down
1 change: 1 addition & 0 deletions entry/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def serve_static(request, path, insecure=True, **kwargs):
path("account/", include("apps.account.urls")),
path("application/", include("apps.application.urls")),
path("notice/", include("apps.notice.urls")),
path("tcloud/", include("apps.tcloud.urls")),
path("trace/", include("ovinc_client.trace.urls")),
path("tcaptcha/", include("ovinc_client.tcaptcha.urls")),
]
Expand Down
Loading

0 comments on commit 7c63c3e

Please sign in to comment.