mirror of https://github.com/jumpserver/jumpserver
perf: Links in WeCom messages can be opened without re-logging in.
parent
cc1fcd2b98
commit
7c55c42582
|
@ -13,20 +13,17 @@ from authentication.const import ConfirmType
|
||||||
from authentication.mixins import AuthMixin
|
from authentication.mixins import AuthMixin
|
||||||
from authentication.permissions import UserConfirmation
|
from authentication.permissions import UserConfirmation
|
||||||
from common.sdk.im.wecom import URL
|
from common.sdk.im.wecom import URL
|
||||||
from common.sdk.im.wecom import WeCom
|
from common.sdk.im.wecom import WeCom, wecom_tool
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
from common.utils.common import get_request_ip
|
|
||||||
from common.utils.django import reverse, get_object_or_none, safe_next_url
|
from common.utils.django import reverse, get_object_or_none, safe_next_url
|
||||||
from common.utils.random import random_string
|
|
||||||
from common.views.mixins import UserConfirmRequiredExceptionMixin, PermissionsMixin
|
from common.views.mixins import UserConfirmRequiredExceptionMixin, PermissionsMixin
|
||||||
from users.models import User
|
from users.models import User
|
||||||
from users.views import UserVerifyPasswordView
|
from users.views import UserVerifyPasswordView
|
||||||
from .base import BaseLoginCallbackView, BaseBindCallbackView
|
from .base import BaseLoginCallbackView, BaseBindCallbackView
|
||||||
from .mixins import METAMixin, FlashMessageMixin
|
from .mixins import METAMixin, FlashMessageMixin
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
|
||||||
|
|
||||||
WECOM_STATE_SESSION_KEY = '_wecom_state'
|
logger = get_logger(__file__)
|
||||||
|
|
||||||
|
|
||||||
class WeComBaseMixin(UserConfirmRequiredExceptionMixin, PermissionsMixin, FlashMessageMixin, View):
|
class WeComBaseMixin(UserConfirmRequiredExceptionMixin, PermissionsMixin, FlashMessageMixin, View):
|
||||||
|
@ -45,7 +42,7 @@ class WeComBaseMixin(UserConfirmRequiredExceptionMixin, PermissionsMixin, FlashM
|
||||||
)
|
)
|
||||||
|
|
||||||
def verify_state(self):
|
def verify_state(self):
|
||||||
return self.verify_state_with_session_key(WECOM_STATE_SESSION_KEY)
|
return wecom_tool.check_state(self.request.GET.get('state'), self.request)
|
||||||
|
|
||||||
def get_already_bound_response(self, redirect_url):
|
def get_already_bound_response(self, redirect_url):
|
||||||
msg = _('WeCom is already bound')
|
msg = _('WeCom is already bound')
|
||||||
|
@ -56,13 +53,10 @@ class WeComBaseMixin(UserConfirmRequiredExceptionMixin, PermissionsMixin, FlashM
|
||||||
class WeComQRMixin(WeComBaseMixin, View):
|
class WeComQRMixin(WeComBaseMixin, View):
|
||||||
|
|
||||||
def get_qr_url(self, redirect_uri):
|
def get_qr_url(self, redirect_uri):
|
||||||
state = random_string(16)
|
|
||||||
self.request.session[WECOM_STATE_SESSION_KEY] = state
|
|
||||||
|
|
||||||
params = {
|
params = {
|
||||||
'appid': settings.WECOM_CORPID,
|
'appid': settings.WECOM_CORPID,
|
||||||
'agentid': settings.WECOM_AGENTID,
|
'agentid': settings.WECOM_AGENTID,
|
||||||
'state': state,
|
'state': wecom_tool.gen_state(request=self.request),
|
||||||
'redirect_uri': redirect_uri,
|
'redirect_uri': redirect_uri,
|
||||||
}
|
}
|
||||||
url = URL.QR_CONNECT + '?' + urlencode(params)
|
url = URL.QR_CONNECT + '?' + urlencode(params)
|
||||||
|
@ -74,13 +68,11 @@ class WeComOAuthMixin(WeComBaseMixin, View):
|
||||||
def get_oauth_url(self, redirect_uri):
|
def get_oauth_url(self, redirect_uri):
|
||||||
if not settings.AUTH_WECOM:
|
if not settings.AUTH_WECOM:
|
||||||
return reverse('authentication:login')
|
return reverse('authentication:login')
|
||||||
state = random_string(16)
|
|
||||||
self.request.session[WECOM_STATE_SESSION_KEY] = state
|
|
||||||
|
|
||||||
params = {
|
params = {
|
||||||
'appid': settings.WECOM_CORPID,
|
'appid': settings.WECOM_CORPID,
|
||||||
'agentid': settings.WECOM_AGENTID,
|
'agentid': settings.WECOM_AGENTID,
|
||||||
'state': state,
|
'state': wecom_tool.gen_state(request=self.request),
|
||||||
'redirect_uri': redirect_uri,
|
'redirect_uri': redirect_uri,
|
||||||
'response_type': 'code',
|
'response_type': 'code',
|
||||||
'scope': 'snsapi_base',
|
'scope': 'snsapi_base',
|
||||||
|
|
|
@ -16,12 +16,6 @@ def digest(corp_id, corp_secret):
|
||||||
return dist
|
return dist
|
||||||
|
|
||||||
|
|
||||||
def update_values(default: dict, others: dict):
|
|
||||||
for key in default.keys():
|
|
||||||
if key in others:
|
|
||||||
default[key] = others[key]
|
|
||||||
|
|
||||||
|
|
||||||
def set_default(data: dict, default: dict):
|
def set_default(data: dict, default: dict):
|
||||||
for key in default.keys():
|
for key in default.keys():
|
||||||
if key not in data:
|
if key not in data:
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
from typing import Iterable, AnyStr
|
from typing import Iterable, AnyStr
|
||||||
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.core.cache import cache
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from rest_framework.exceptions import APIException
|
from rest_framework.exceptions import APIException
|
||||||
|
|
||||||
from common.sdk.im.mixin import RequestMixin, BaseRequest
|
from common.sdk.im.mixin import RequestMixin, BaseRequest
|
||||||
from common.sdk.im.utils import digest, update_values
|
from common.sdk.im.utils import digest
|
||||||
from common.utils.common import get_logger
|
from common.utils import reverse, random_string, get_logger, lazyproperty
|
||||||
from users.utils import construct_user_email, flatten_dict, map_attributes
|
from users.utils import construct_user_email, flatten_dict, map_attributes
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
@ -107,15 +109,6 @@ class WeCom(RequestMixin):
|
||||||
对于业务代码,只需要关心由 用户id 或 消息不对 导致的错误,其他错误不予理会
|
对于业务代码,只需要关心由 用户id 或 消息不对 导致的错误,其他错误不予理会
|
||||||
"""
|
"""
|
||||||
users = tuple(users)
|
users = tuple(users)
|
||||||
|
|
||||||
extra_params = {
|
|
||||||
"safe": 0,
|
|
||||||
"enable_id_trans": 0,
|
|
||||||
"enable_duplicate_check": 0,
|
|
||||||
"duplicate_check_interval": 1800
|
|
||||||
}
|
|
||||||
update_values(extra_params, kwargs)
|
|
||||||
|
|
||||||
body = {
|
body = {
|
||||||
"touser": '|'.join(users),
|
"touser": '|'.join(users),
|
||||||
"msgtype": "text",
|
"msgtype": "text",
|
||||||
|
@ -123,7 +116,7 @@ class WeCom(RequestMixin):
|
||||||
"text": {
|
"text": {
|
||||||
"content": msg
|
"content": msg
|
||||||
},
|
},
|
||||||
**extra_params
|
**kwargs
|
||||||
}
|
}
|
||||||
if markdown:
|
if markdown:
|
||||||
body['msgtype'] = 'markdown'
|
body['msgtype'] = 'markdown'
|
||||||
|
@ -144,15 +137,15 @@ class WeCom(RequestMixin):
|
||||||
if 'invaliduser' not in data:
|
if 'invaliduser' not in data:
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
invaliduser = data['invaliduser']
|
invalid_user = data['invaliduser']
|
||||||
if not invaliduser:
|
if not invalid_user:
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
if isinstance(invaliduser, str):
|
if isinstance(invalid_user, str):
|
||||||
logger.error(f'WeCom send text 200, but invaliduser is not str: invaliduser={invaliduser}')
|
logger.error(f'WeCom send text 200, but invaliduser is not str: invaliduser={invalid_user}')
|
||||||
raise WeComError
|
raise WeComError
|
||||||
|
|
||||||
invalid_users = invaliduser.split('|')
|
invalid_users = invalid_user.split('|')
|
||||||
return invalid_users
|
return invalid_users
|
||||||
|
|
||||||
def get_user_id_by_code(self, code):
|
def get_user_id_by_code(self, code):
|
||||||
|
@ -167,13 +160,12 @@ class WeCom(RequestMixin):
|
||||||
|
|
||||||
self._requests.check_errcode_is_0(data)
|
self._requests.check_errcode_is_0(data)
|
||||||
|
|
||||||
USER_ID = 'UserId'
|
user_id = 'UserId'
|
||||||
OPEN_ID = 'OpenId'
|
open_id = 'OpenId'
|
||||||
|
if user_id in data:
|
||||||
if USER_ID in data:
|
return data[user_id], user_id
|
||||||
return data[USER_ID], USER_ID
|
elif open_id in data:
|
||||||
elif OPEN_ID in data:
|
return data[open_id], open_id
|
||||||
return data[OPEN_ID], OPEN_ID
|
|
||||||
else:
|
else:
|
||||||
logger.error(f'WeCom response 200 but get field from json error: fields=UserId|OpenId')
|
logger.error(f'WeCom response 200 but get field from json error: fields=UserId|OpenId')
|
||||||
raise WeComError
|
raise WeComError
|
||||||
|
@ -195,3 +187,37 @@ class WeCom(RequestMixin):
|
||||||
default_detail = self.default_user_detail(data, user_id)
|
default_detail = self.default_user_detail(data, user_id)
|
||||||
detail = map_attributes(default_detail, info, self.attributes)
|
detail = map_attributes(default_detail, info, self.attributes)
|
||||||
return detail
|
return detail
|
||||||
|
|
||||||
|
|
||||||
|
class WeComTool(object):
|
||||||
|
WECOM_STATE_SESSION_KEY = '_wecom_state'
|
||||||
|
WECOM_STATE_VALUE = 'wecom'
|
||||||
|
|
||||||
|
@lazyproperty
|
||||||
|
def qr_cb_url(self):
|
||||||
|
return reverse('authentication:wecom-qr-login-callback', external=True)
|
||||||
|
|
||||||
|
def gen_state(self, request=None):
|
||||||
|
state = random_string(16)
|
||||||
|
if not request:
|
||||||
|
cache.set(state, self.WECOM_STATE_VALUE, timeout=60 * 60 * 24)
|
||||||
|
else:
|
||||||
|
request.session[self.WECOM_STATE_SESSION_KEY] = state
|
||||||
|
return state
|
||||||
|
|
||||||
|
def check_state(self, state, request=None):
|
||||||
|
return cache.get(state) == self.WECOM_STATE_VALUE or \
|
||||||
|
request.session[self.WECOM_STATE_SESSION_KEY] == state
|
||||||
|
|
||||||
|
def wrap_redirect_url(self, next_url):
|
||||||
|
params = {
|
||||||
|
'appid': settings.WECOM_CORPID,
|
||||||
|
'agentid': settings.WECOM_AGENTID,
|
||||||
|
'state': self.gen_state(),
|
||||||
|
'redirect_uri': f'{self.qr_cb_url}?next={next_url}',
|
||||||
|
'response_type': 'code', 'scope': 'snsapi_base',
|
||||||
|
}
|
||||||
|
return URL.OAUTH_CONNECT + '?' + urlencode(params) + '#wechat_redirect'
|
||||||
|
|
||||||
|
|
||||||
|
wecom_tool = WeComTool()
|
||||||
|
|
|
@ -127,13 +127,16 @@ class Message(metaclass=MessageType):
|
||||||
def get_html_msg(self) -> dict:
|
def get_html_msg(self) -> dict:
|
||||||
return self.get_common_msg()
|
return self.get_common_msg()
|
||||||
|
|
||||||
def get_markdown_msg(self) -> dict:
|
@staticmethod
|
||||||
|
def html_to_markdown(html_msg):
|
||||||
h = HTML2Text()
|
h = HTML2Text()
|
||||||
h.body_width = 300
|
h.body_width = 0
|
||||||
msg = self.get_html_msg()
|
content = html_msg['message']
|
||||||
content = msg['message']
|
html_msg['message'] = h.handle(content)
|
||||||
msg['message'] = h.handle(content)
|
return html_msg
|
||||||
return msg
|
|
||||||
|
def get_markdown_msg(self) -> dict:
|
||||||
|
return self.html_to_markdown(self.get_html_msg())
|
||||||
|
|
||||||
def get_text_msg(self) -> dict:
|
def get_text_msg(self) -> dict:
|
||||||
h = HTML2Text()
|
h = HTML2Text()
|
||||||
|
|
|
@ -4,6 +4,7 @@ from django.conf import settings
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from common.sdk.im.wecom import wecom_tool
|
||||||
from common.utils import get_logger, reverse
|
from common.utils import get_logger, reverse
|
||||||
from common.utils import lazyproperty
|
from common.utils import lazyproperty
|
||||||
from common.utils.timezone import local_now_display
|
from common.utils.timezone import local_now_display
|
||||||
|
@ -75,53 +76,50 @@ class CommandWarningMessage(CommandAlertMixin, UserMessage):
|
||||||
super().__init__(user)
|
super().__init__(user)
|
||||||
self.command = command
|
self.command = command
|
||||||
|
|
||||||
def get_html_msg(self) -> dict:
|
def get_session_url(self, external=True):
|
||||||
command = self.command
|
session_id = self.command.get('session', '')
|
||||||
|
org_id = self.command['org_id']
|
||||||
command_input = command['input']
|
session_url = ''
|
||||||
user = command['user']
|
|
||||||
asset = command['asset']
|
|
||||||
account = command.get('_account', '')
|
|
||||||
cmd_acl = command.get('_cmd_filter_acl')
|
|
||||||
cmd_group = command.get('_cmd_group')
|
|
||||||
session_id = command.get('session', '')
|
|
||||||
risk_level = command['risk_level']
|
|
||||||
org_id = command['org_id']
|
|
||||||
org_name = command.get('_org_name') or org_id
|
|
||||||
|
|
||||||
if session_id:
|
if session_id:
|
||||||
session_url = reverse(
|
session_url = reverse(
|
||||||
'api-terminal:session-detail', kwargs={'pk': session_id},
|
'api-terminal:session-detail', kwargs={'pk': session_id},
|
||||||
external=True, api_to_ui=True
|
external=external, api_to_ui=True
|
||||||
) + '?oid={}'.format(org_id)
|
) + '?oid={}'.format(org_id)
|
||||||
session_url = session_url.replace('/terminal/sessions/', '/audit/sessions/sessions/')
|
session_url = session_url.replace('/terminal/sessions/', '/audit/sessions/sessions/')
|
||||||
else:
|
return session_url
|
||||||
session_url = ''
|
|
||||||
|
|
||||||
# Command ACL
|
def gen_html_string(self, **other_context):
|
||||||
cmd_acl_name = cmd_group_name = ''
|
command = self.command
|
||||||
if cmd_acl:
|
cmd_acl = command.get('_cmd_filter_acl')
|
||||||
cmd_acl_name = cmd_acl.name
|
cmd_group = command.get('_cmd_group')
|
||||||
if cmd_group:
|
org_id = command['org_id']
|
||||||
cmd_group_name = cmd_group.name
|
org_name = command.get('_org_name') or org_id
|
||||||
|
cmd_acl_name = cmd_acl.name if cmd_acl else ''
|
||||||
|
cmd_group_name = cmd_group.name if cmd_group else ''
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'command': command_input,
|
'command': command['input'],
|
||||||
'user': user,
|
'user': command['user'],
|
||||||
'asset': asset,
|
'asset': command['asset'],
|
||||||
'account': account,
|
'account': command.get('_account', ''),
|
||||||
'cmd_filter_acl': cmd_acl_name,
|
'cmd_filter_acl': cmd_acl_name,
|
||||||
'cmd_group': cmd_group_name,
|
'cmd_group': cmd_group_name,
|
||||||
'session_url': session_url,
|
'risk_level': RiskLevelChoices.get_label(command['risk_level']),
|
||||||
'risk_level': RiskLevelChoices.get_label(risk_level),
|
|
||||||
'org': org_name,
|
'org': org_name,
|
||||||
}
|
}
|
||||||
|
context.update(other_context)
|
||||||
message = render_to_string('terminal/_msg_command_warning.html', context)
|
message = render_to_string('terminal/_msg_command_warning.html', context)
|
||||||
return {
|
return {'subject': self.subject, 'message': message}
|
||||||
'subject': self.subject,
|
|
||||||
'message': message
|
def get_wecom_msg(self):
|
||||||
}
|
session_url = wecom_tool.wrap_redirect_url(
|
||||||
|
self.get_session_url(external=False)
|
||||||
|
)
|
||||||
|
message = self.gen_html_string(session_url=session_url)
|
||||||
|
return self.html_to_markdown(message)
|
||||||
|
|
||||||
|
def get_html_msg(self) -> dict:
|
||||||
|
return self.gen_html_string(session_url=self.get_session_url())
|
||||||
|
|
||||||
|
|
||||||
class CommandAlertMessage(CommandAlertMixin, SystemMessage):
|
class CommandAlertMessage(CommandAlertMixin, SystemMessage):
|
||||||
|
@ -141,15 +139,18 @@ class CommandAlertMessage(CommandAlertMixin, SystemMessage):
|
||||||
command['session'] = Session.objects.first().id
|
command['session'] = Session.objects.first().id
|
||||||
return cls(command)
|
return cls(command)
|
||||||
|
|
||||||
def get_html_msg(self) -> dict:
|
def get_session_url(self, external=True):
|
||||||
command = self.command
|
|
||||||
session_detail_url = reverse(
|
session_detail_url = reverse(
|
||||||
'api-terminal:session-detail', kwargs={'pk': command['session']},
|
'api-terminal:session-detail', api_to_ui=True,
|
||||||
external=True, api_to_ui=True
|
kwargs={'pk': self.command['session']}, external=external,
|
||||||
) + '?oid={}'.format(self.command['org_id'])
|
) + '?oid={}'.format(self.command['org_id'])
|
||||||
session_detail_url = session_detail_url.replace(
|
session_detail_url = session_detail_url.replace(
|
||||||
'/terminal/sessions/', '/audit/sessions/sessions/'
|
'/terminal/sessions/', '/audit/sessions/sessions/'
|
||||||
)
|
)
|
||||||
|
return session_detail_url
|
||||||
|
|
||||||
|
def gen_html_string(self, **other_context) -> dict:
|
||||||
|
command = self.command
|
||||||
level = RiskLevelChoices.get_label(command['risk_level'])
|
level = RiskLevelChoices.get_label(command['risk_level'])
|
||||||
items = {
|
items = {
|
||||||
_("Asset"): command['asset'],
|
_("Asset"): command['asset'],
|
||||||
|
@ -159,14 +160,21 @@ class CommandAlertMessage(CommandAlertMixin, SystemMessage):
|
||||||
}
|
}
|
||||||
context = {
|
context = {
|
||||||
'items': items,
|
'items': items,
|
||||||
'session_url': session_detail_url,
|
|
||||||
"command": command['input'],
|
"command": command['input'],
|
||||||
}
|
}
|
||||||
|
context.update(other_context)
|
||||||
message = render_to_string('terminal/_msg_command_alert.html', context)
|
message = render_to_string('terminal/_msg_command_alert.html', context)
|
||||||
return {
|
return {'subject': self.subject, 'message': message}
|
||||||
'subject': self.subject,
|
|
||||||
'message': message
|
def get_wecom_msg(self):
|
||||||
}
|
session_url = wecom_tool.wrap_redirect_url(
|
||||||
|
self.get_session_url(external=False)
|
||||||
|
)
|
||||||
|
message = self.gen_html_string(session_url=session_url)
|
||||||
|
return self.html_to_markdown(message)
|
||||||
|
|
||||||
|
def get_html_msg(self) -> dict:
|
||||||
|
return self.gen_html_string(session_url=self.get_session_url())
|
||||||
|
|
||||||
|
|
||||||
class CommandExecutionAlert(CommandAlertMixin, SystemMessage):
|
class CommandExecutionAlert(CommandAlertMixin, SystemMessage):
|
||||||
|
@ -189,16 +197,20 @@ class CommandExecutionAlert(CommandAlertMixin, SystemMessage):
|
||||||
}
|
}
|
||||||
return cls(cmd)
|
return cls(cmd)
|
||||||
|
|
||||||
def get_html_msg(self) -> dict:
|
def get_asset_urls(self, external=True, tran_func=None):
|
||||||
command = self.command
|
|
||||||
assets_with_url = []
|
assets_with_url = []
|
||||||
for asset in command['assets']:
|
for asset in self.command['assets']:
|
||||||
url = reverse(
|
url = reverse(
|
||||||
'assets:asset-detail', kwargs={'pk': asset.id},
|
'assets:asset-detail', kwargs={'pk': asset.id},
|
||||||
api_to_ui=True, external=True, is_console=True
|
api_to_ui=True, external=external, is_console=True
|
||||||
) + '?oid={}'.format(asset.org_id)
|
) + '?oid={}'.format(asset.org_id)
|
||||||
|
if tran_func:
|
||||||
|
url = tran_func(url)
|
||||||
assets_with_url.append([asset, url])
|
assets_with_url.append([asset, url])
|
||||||
|
return assets_with_url
|
||||||
|
|
||||||
|
def gen_html_string(self, **other_context):
|
||||||
|
command = self.command
|
||||||
level = RiskLevelChoices.get_label(command['risk_level'])
|
level = RiskLevelChoices.get_label(command['risk_level'])
|
||||||
|
|
||||||
items = {
|
items = {
|
||||||
|
@ -206,17 +218,23 @@ class CommandExecutionAlert(CommandAlertMixin, SystemMessage):
|
||||||
_("Level"): level,
|
_("Level"): level,
|
||||||
_("Date"): local_now_display(),
|
_("Date"): local_now_display(),
|
||||||
}
|
}
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'items': items,
|
'items': items,
|
||||||
'assets_with_url': assets_with_url,
|
|
||||||
'command': command['input'],
|
'command': command['input'],
|
||||||
}
|
}
|
||||||
|
context.update(other_context)
|
||||||
message = render_to_string('terminal/_msg_command_execute_alert.html', context)
|
message = render_to_string('terminal/_msg_command_execute_alert.html', context)
|
||||||
return {
|
return {'subject': self.subject, 'message': message}
|
||||||
'subject': self.subject,
|
|
||||||
'message': message
|
def get_wecom_msg(self):
|
||||||
}
|
assets_with_url = self.get_asset_urls(
|
||||||
|
external=False, tran_func=wecom_tool.wrap_redirect_url
|
||||||
|
)
|
||||||
|
message = self.gen_html_string(assets_with_url=assets_with_url)
|
||||||
|
return self.html_to_markdown(message)
|
||||||
|
|
||||||
|
def get_html_msg(self) -> dict:
|
||||||
|
return self.gen_html_string(assets_with_url=self.get_asset_urls())
|
||||||
|
|
||||||
|
|
||||||
class StorageConnectivityMessage(SystemMessage):
|
class StorageConnectivityMessage(SystemMessage):
|
||||||
|
|
|
@ -4,12 +4,12 @@ from urllib.parse import urljoin
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.forms import model_to_dict
|
from django.forms import model_to_dict
|
||||||
from django.shortcuts import reverse
|
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from common.db.encoder import ModelJSONFieldEncoder
|
from common.db.encoder import ModelJSONFieldEncoder
|
||||||
from common.utils import get_logger, random_string
|
from common.sdk.im.wecom import wecom_tool
|
||||||
|
from common.utils import get_logger, random_string, reverse
|
||||||
from notifications.notifications import UserMessage
|
from notifications.notifications import UserMessage
|
||||||
from . import const
|
from . import const
|
||||||
from .models import Ticket
|
from .models import Ticket
|
||||||
|
@ -22,16 +22,13 @@ class BaseTicketMessage(UserMessage):
|
||||||
ticket: Ticket
|
ticket: Ticket
|
||||||
content_title: str
|
content_title: str
|
||||||
|
|
||||||
@property
|
def get_ticket_detail_url(self, external=True):
|
||||||
def ticket_detail_url(self):
|
detail_url = const.TICKET_DETAIL_URL.format(
|
||||||
tp = self.ticket.type
|
id=str(self.ticket.id), type=self.ticket.type
|
||||||
return urljoin(
|
|
||||||
settings.SITE_URL,
|
|
||||||
const.TICKET_DETAIL_URL.format(
|
|
||||||
id=str(self.ticket.id),
|
|
||||||
type=tp
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
if not external:
|
||||||
|
return detail_url
|
||||||
|
return urljoin(settings.SITE_URL, detail_url)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def content_title(self):
|
def content_title(self):
|
||||||
|
@ -41,17 +38,31 @@ class BaseTicketMessage(UserMessage):
|
||||||
def subject(self):
|
def subject(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def get_html_msg(self) -> dict:
|
def get_html_context(self):
|
||||||
context = dict(
|
return {'ticket_detail_url': self.get_ticket_detail_url()}
|
||||||
title=self.content_title,
|
|
||||||
content=self.content,
|
def get_wecom_context(self):
|
||||||
ticket_detail_url=self.ticket_detail_url
|
ticket_detail_url = wecom_tool.wrap_redirect_url(
|
||||||
)
|
[self.get_ticket_detail_url(external=False)]
|
||||||
message = render_to_string('tickets/_msg_ticket.html', context)
|
)[0]
|
||||||
return {
|
return {'ticket_detail_url': ticket_detail_url}
|
||||||
'subject': self.subject,
|
|
||||||
'message': message
|
def gen_html_string(self, **other_context):
|
||||||
|
context = {
|
||||||
|
'title': self.content_title, 'content': self.content,
|
||||||
}
|
}
|
||||||
|
context.update(other_context)
|
||||||
|
message = render_to_string(
|
||||||
|
'tickets/_msg_ticket.html', context
|
||||||
|
)
|
||||||
|
return {'subject': self.subject, 'message': message}
|
||||||
|
|
||||||
|
def get_html_msg(self) -> dict:
|
||||||
|
return self.gen_html_string(**self.get_html_context())
|
||||||
|
|
||||||
|
def get_wecom_msg(self):
|
||||||
|
message = self.gen_html_string(**self.get_wecom_context())
|
||||||
|
return self.html_to_markdown(message)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def gen_test_msg(cls):
|
def gen_test_msg(cls):
|
||||||
|
@ -113,27 +124,21 @@ class TicketAppliedToAssigneeMessage(BaseTicketMessage):
|
||||||
)
|
)
|
||||||
return title
|
return title
|
||||||
|
|
||||||
def get_ticket_approval_url(self):
|
def get_ticket_approval_url(self, external=True):
|
||||||
url = reverse('tickets:direct-approve', kwargs={'token': self.token})
|
url = reverse('tickets:direct-approve', kwargs={'token': self.token})
|
||||||
|
if not external:
|
||||||
|
return url
|
||||||
return urljoin(settings.SITE_URL, url)
|
return urljoin(settings.SITE_URL, url)
|
||||||
|
|
||||||
def get_html_msg(self) -> dict:
|
def get_html_context(self):
|
||||||
context = dict(
|
context = super().get_html_context()
|
||||||
title=self.content_title,
|
context['ticket_approval_url'] = self.get_ticket_approval_url()
|
||||||
content=self.content,
|
data = {
|
||||||
ticket_detail_url=self.ticket_detail_url
|
'ticket_id': self.ticket.id,
|
||||||
)
|
'approver_id': self.user.id, 'content': self.content,
|
||||||
|
|
||||||
ticket_approval_url = self.get_ticket_approval_url()
|
|
||||||
context.update({'ticket_approval_url': ticket_approval_url})
|
|
||||||
message = render_to_string('tickets/_msg_ticket.html', context)
|
|
||||||
cache.set(self.token, {
|
|
||||||
'ticket_id': self.ticket.id, 'approver_id': self.user.id,
|
|
||||||
'content': self.content,
|
|
||||||
}, 3600)
|
|
||||||
return {
|
|
||||||
'subject': self.subject, 'message': message
|
|
||||||
}
|
}
|
||||||
|
cache.set(self.token, data, 3600)
|
||||||
|
return context
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def gen_test_msg(cls):
|
def gen_test_msg(cls):
|
||||||
|
|
Loading…
Reference in New Issue