refactor: 对话秘钥校验 (#1430)

This commit is contained in:
shaohuzhang1 2024-10-22 18:29:13 +08:00 committed by GitHub
parent 5973957037
commit 23c7269231
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 455 additions and 378 deletions

View File

@ -36,7 +36,7 @@ from common.db.sql_execute import select_list
from common.exception.app_exception import AppApiException, NotFound404, AppUnauthorizedFailed
from common.field.common import UploadedImageField
from common.models.db_model_manage import DBModelManage
from common.util.common import valid_license
from common.util.common import valid_license, password_encrypt
from common.util.field_message import ErrMessage
from common.util.file_util import get_file_content
from dataset.models import DataSet, Document, Image
@ -264,7 +264,9 @@ class ApplicationSerializer(serializers.Serializer):
if work_flow is not None:
for node in work_flow.get('nodes', []):
if node['id'] == 'base-node':
input_field_list = node.get('properties', {}).get('api_input_field_list', node.get('properties', {}).get('input_field_list', []))
input_field_list = node.get('properties', {}).get('api_input_field_list',
node.get('properties', {}).get(
'input_field_list', []))
if input_field_list is not None:
for field in input_field_list:
if field['assignment_method'] == 'api_input' and field['variable'] in params:
@ -352,6 +354,8 @@ class ApplicationSerializer(serializers.Serializer):
class Authentication(serializers.Serializer):
access_token = serializers.CharField(required=True, error_messages=ErrMessage.char("access_token"))
authentication_value = serializers.JSONField(required=False, allow_null=True,
error_messages=ErrMessage.char("认证信息"))
def auth(self, request, with_valid=True):
token = request.META.get('HTTP_AUTHORIZATION')
@ -366,21 +370,47 @@ class ApplicationSerializer(serializers.Serializer):
self.is_valid(raise_exception=True)
access_token = self.data.get("access_token")
application_access_token = QuerySet(ApplicationAccessToken).filter(access_token=access_token).first()
authentication_value = self.data.get('authentication_value', None)
authentication = {}
if application_access_token is not None and application_access_token.is_active:
if token_details is not None and 'client_id' in token_details and token_details.get(
'client_id') is not None:
client_id = token_details.get('client_id')
authentication = {'type': token_details.get('type'),
'value': token_details.get('value')}
else:
client_id = str(uuid.uuid1())
if authentication_value is not None:
# 认证用户token
self.auth_authentication_value(authentication_value, str(application_access_token.application_id))
authentication = {'type': authentication_value.get('type'),
'value': password_encrypt(authentication_value.get('value'))}
token = signing.dumps({'application_id': str(application_access_token.application_id),
'user_id': str(application_access_token.application.user.id),
'access_token': application_access_token.access_token,
'type': AuthenticationType.APPLICATION_ACCESS_TOKEN.value,
'client_id': client_id})
'client_id': client_id
, **authentication})
return token
else:
raise NotFound404(404, "无效的access_token")
def auth_authentication_value(self, authentication_value, application_id):
application_setting_model = DBModelManage.get_model('application_setting')
xpack_cache = DBModelManage.get_model('xpack_cache')
X_PACK_LICENSE_IS_VALID = False if xpack_cache is None else xpack_cache.get('XPACK_LICENSE_IS_VALID', False)
if application_setting_model is not None and X_PACK_LICENSE_IS_VALID:
application_setting = QuerySet(application_setting_model).filter(application_id=application_id).first()
if application_setting.authentication and authentication_value is not None:
if authentication_value.get('type') == 'password':
if not self.auth_password(authentication_value, application_setting.authentication_value):
raise AppApiException(1005, "密码错误")
return True
@staticmethod
def auth_password(source_authentication_value, authentication_value):
return source_authentication_value.get('value') == authentication_value.get('value')
class Edit(serializers.Serializer):
name = serializers.CharField(required=False, max_length=64, min_length=1,
error_messages=ErrMessage.char("应用名称"))
@ -762,6 +792,8 @@ class ApplicationSerializer(serializers.Serializer):
'avatar': application_setting.avatar,
'float_icon': application_setting.float_icon,
'authentication': application_setting.authentication,
'authentication_type': application_setting.authentication_value.get(
'type', 'password'),
'disclaimer': application_setting.disclaimer,
'disclaimer_value': application_setting.disclaimer_value,
'custom_theme': application_setting.custom_theme,

View File

@ -376,7 +376,9 @@ class Application(APIView):
security=[])
def post(self, request: Request):
return result.success(
ApplicationSerializer.Authentication(data={'access_token': request.data.get("access_token")}).auth(
ApplicationSerializer.Authentication(data={'access_token': request.data.get("access_token"),
'authentication_value': request.data.get(
'authentication_value')}).auth(
request),
headers={"Access-Control-Allow-Origin": "*", "Access-Control-Allow-Credentials": "true",
"Access-Control-Allow-Methods": "POST",
@ -539,12 +541,13 @@ class Application(APIView):
authentication_classes = [TokenAuth]
@action(methods=['POST'], detail=False)
@has_permissions(ViewPermission([RoleConstants.ADMIN, RoleConstants.USER, RoleConstants.APPLICATION_ACCESS_TOKEN],
[lambda r, keywords: Permission(group=Group.APPLICATION,
operate=Operate.USE,
dynamic_tag=keywords.get(
'application_id'))],
compare=CompareConstants.AND))
@has_permissions(
ViewPermission([RoleConstants.ADMIN, RoleConstants.USER, RoleConstants.APPLICATION_ACCESS_TOKEN],
[lambda r, keywords: Permission(group=Group.APPLICATION,
operate=Operate.USE,
dynamic_tag=keywords.get(
'application_id'))],
compare=CompareConstants.AND))
def post(self, request: Request, application_id: str):
return result.success(
ApplicationSerializer.Operate(data={'application_id': application_id, 'user_id': request.user.id})
@ -554,31 +557,33 @@ class Application(APIView):
authentication_classes = [TokenAuth]
@action(methods=['POST'], detail=False)
@has_permissions(ViewPermission([RoleConstants.ADMIN, RoleConstants.USER, RoleConstants.APPLICATION_ACCESS_TOKEN],
[lambda r, keywords: Permission(group=Group.APPLICATION,
operate=Operate.USE,
dynamic_tag=keywords.get(
'application_id'))],
compare=CompareConstants.AND))
@has_permissions(
ViewPermission([RoleConstants.ADMIN, RoleConstants.USER, RoleConstants.APPLICATION_ACCESS_TOKEN],
[lambda r, keywords: Permission(group=Group.APPLICATION,
operate=Operate.USE,
dynamic_tag=keywords.get(
'application_id'))],
compare=CompareConstants.AND))
def post(self, request: Request, application_id: str):
byte_data = ApplicationSerializer.Operate(
data={'application_id': application_id, 'user_id': request.user.id}).text_to_speech(
request.data.get('text'))
return HttpResponse(byte_data, status=200, headers={'Content-Type': 'audio/mp3',
'Content-Disposition': 'attachment; filename="abc.mp3"'})
'Content-Disposition': 'attachment; filename="abc.mp3"'})
class PlayDemoText(APIView):
authentication_classes = [TokenAuth]
@action(methods=['POST'], detail=False)
@has_permissions(ViewPermission([RoleConstants.ADMIN, RoleConstants.USER, RoleConstants.APPLICATION_ACCESS_TOKEN],
[lambda r, keywords: Permission(group=Group.APPLICATION,
operate=Operate.USE,
dynamic_tag=keywords.get(
'application_id'))],
compare=CompareConstants.AND))
@has_permissions(
ViewPermission([RoleConstants.ADMIN, RoleConstants.USER, RoleConstants.APPLICATION_ACCESS_TOKEN],
[lambda r, keywords: Permission(group=Group.APPLICATION,
operate=Operate.USE,
dynamic_tag=keywords.get(
'application_id'))],
compare=CompareConstants.AND))
def post(self, request: Request, application_id: str):
byte_data = ApplicationSerializer.Operate(
data={'application_id': application_id, 'user_id': request.user.id}).play_demo_text(request.data)
return HttpResponse(byte_data, status=200, headers={'Content-Type': 'audio/mp3',
'Content-Disposition': 'attachment; filename="abc.mp3"'})
'Content-Disposition': 'attachment; filename="abc.mp3"'})

View File

@ -12,7 +12,9 @@ from application.models.api_key_model import ApplicationAccessToken
from common.auth.handle.auth_base_handle import AuthBaseHandle
from common.constants.authentication_type import AuthenticationType
from common.constants.permission_constants import RoleConstants, Permission, Group, Operate, Auth
from common.exception.app_exception import AppAuthenticationFailed
from common.exception.app_exception import AppAuthenticationFailed, ChatException
from common.models.db_model_manage import DBModelManage
from common.util.common import password_encrypt
class PublicAccessToken(AuthBaseHandle):
@ -29,6 +31,20 @@ class PublicAccessToken(AuthBaseHandle):
auth_details = get_token_details()
application_access_token = QuerySet(ApplicationAccessToken).filter(
application_id=auth_details.get('application_id')).first()
application_setting_model = DBModelManage.get_model('application_setting')
xpack_cache = DBModelManage.get_model('xpack_cache')
X_PACK_LICENSE_IS_VALID = False if xpack_cache is None else xpack_cache.get('XPACK_LICENSE_IS_VALID', False)
if application_setting_model is not None and X_PACK_LICENSE_IS_VALID:
application_setting = QuerySet(application_setting_model).filter(application_id=str(
application_access_token.application_id)).first()
if application_setting.authentication:
authentication = auth_details.get('authentication', {})
if authentication is None:
authentication = {}
if application_setting.authentication_value.get('type') != authentication.get(
'type') or password_encrypt(
application_setting.authentication_value.get('value')) != authentication.get('value'):
raise ChatException(1002, "身份验证信息不正确")
if application_access_token is None:
raise AppAuthenticationFailed(1002, "身份验证信息不正确")
if not application_access_token.is_active:

View File

@ -6,6 +6,7 @@
@date2023/10/16 16:42
@desc:
"""
import hashlib
import importlib
from functools import reduce
from typing import Dict, List
@ -62,6 +63,18 @@ def flat_map(array: List[List]):
return result
def password_encrypt(raw_password):
"""
密码 md5加密
:param raw_password: 密码
:return: 加密后密码
"""
md5 = hashlib.md5() # 2实例化md5() 方法
md5.update(raw_password.encode()) # 3对字符串的字节类型加密
result = md5.hexdigest() # 4加密
return result
def post(post_function):
def inner(func):
def run(*args, **kwargs):

View File

@ -122,11 +122,17 @@ const putAccessToken: (
"access_token": "string"
}
*/
const postAppAuthentication: (access_token: string, loading?: Ref<boolean>) => Promise<any> = (
access_token,
loading
) => {
return post(`${prefix}/authentication`, { access_token }, undefined, loading)
const postAppAuthentication: (
access_token: string,
loading?: Ref<boolean>,
authentication_value?: any
) => Promise<any> = (access_token, loading, authentication_value) => {
return post(
`${prefix}/authentication`,
{ access_token: access_token, authentication_value },
undefined,
loading
)
}
/**

View File

@ -365,8 +365,7 @@ function handleInputFieldList() {
?.filter((v: any) => v.id === 'base-node')
.map((v: any) => {
inputFieldList.value = v.properties.user_input_field_list
? v.properties.user_input_field_list
.map((v: any) => {
? v.properties.user_input_field_list.map((v: any) => {
switch (v.type) {
case 'input':
return {
@ -404,51 +403,51 @@ function handleInputFieldList() {
return v
}
})
: v.properties.input_field_list ? v.properties.input_field_list
.filter((v: any) => v.assignment_method === 'user_input')
.map((v: any) => {
switch (v.type) {
case 'input':
return {
field: v.variable,
input_type: 'TextInput',
label: v.name,
default_value: default_value[v.variable],
required: v.is_required
}
case 'select':
return {
field: v.variable,
input_type: 'SingleSelect',
label: v.name,
default_value: default_value[v.variable],
required: v.is_required,
option_list: v.optionList.map((o: any) => {
return { key: o, value: o }
})
}
case 'date':
return {
field: v.variable,
input_type: 'DatePicker',
label: v.name,
default_value: default_value[v.variable],
required: v.is_required,
attrs: {
format: 'YYYY-MM-DD HH:mm:ss',
'value-format': 'YYYY-MM-DD HH:mm:ss',
type: 'datetime'
: v.properties.input_field_list
? v.properties.input_field_list
.filter((v: any) => v.assignment_method === 'user_input')
.map((v: any) => {
switch (v.type) {
case 'input':
return {
field: v.variable,
input_type: 'TextInput',
label: v.name,
default_value: default_value[v.variable],
required: v.is_required
}
}
default:
break
}
})
case 'select':
return {
field: v.variable,
input_type: 'SingleSelect',
label: v.name,
default_value: default_value[v.variable],
required: v.is_required,
option_list: v.optionList.map((o: any) => {
return { key: o, value: o }
})
}
case 'date':
return {
field: v.variable,
input_type: 'DatePicker',
label: v.name,
default_value: default_value[v.variable],
required: v.is_required,
attrs: {
format: 'YYYY-MM-DD HH:mm:ss',
'value-format': 'YYYY-MM-DD HH:mm:ss',
type: 'datetime'
}
}
default:
break
}
})
: []
apiInputFieldList.value = v.properties.api_input_field_list
? v.properties.api_input_field_list
.map((v: any) => {
? v.properties.api_input_field_list.map((v: any) => {
switch (v.type) {
case 'input':
return {
@ -488,45 +487,45 @@ function handleInputFieldList() {
})
: v.properties.input_field_list
? v.properties.input_field_list
.filter((v: any) => v.assignment_method === 'api_input')
.map((v: any) => {
switch (v.type) {
case 'input':
return {
field: v.variable,
input_type: 'TextInput',
label: v.name,
default_value: default_value[v.variable],
required: v.is_required
}
case 'select':
return {
field: v.variable,
input_type: 'SingleSelect',
label: v.name,
default_value: default_value[v.variable],
required: v.is_required,
option_list: v.optionList.map((o: any) => {
return { key: o, value: o }
})
}
case 'date':
return {
field: v.variable,
input_type: 'DatePicker',
label: v.name,
default_value: default_value[v.variable],
required: v.is_required,
attrs: {
format: 'YYYY-MM-DD HH:mm:ss',
'value-format': 'YYYY-MM-DD HH:mm:ss',
type: 'datetime'
.filter((v: any) => v.assignment_method === 'api_input')
.map((v: any) => {
switch (v.type) {
case 'input':
return {
field: v.variable,
input_type: 'TextInput',
label: v.name,
default_value: default_value[v.variable],
required: v.is_required
}
}
default:
break
}
})
case 'select':
return {
field: v.variable,
input_type: 'SingleSelect',
label: v.name,
default_value: default_value[v.variable],
required: v.is_required,
option_list: v.optionList.map((o: any) => {
return { key: o, value: o }
})
}
case 'date':
return {
field: v.variable,
input_type: 'DatePicker',
label: v.name,
default_value: default_value[v.variable],
required: v.is_required,
attrs: {
format: 'YYYY-MM-DD HH:mm:ss',
'value-format': 'YYYY-MM-DD HH:mm:ss',
type: 'datetime'
}
}
default:
break
}
})
: []
})
}
@ -912,7 +911,7 @@ const mediaRecorderStatus = ref(true)
const startRecording = async () => {
try {
//
Recorder.CLog=function(){}
Recorder.CLog = function () {}
mediaRecorderStatus.value = false
handleTimeChange()
mediaRecorder.value = new Recorder({

View File

@ -40,6 +40,9 @@ instance.interceptors.response.use(
(response: any) => {
if (response.data) {
if (response.data.code !== 200 && !(response.data instanceof Blob)) {
if (response.config.url.includes('/application/authentication')) {
return Promise.reject(response.data)
}
if (
!response.config.url.includes('/valid') &&
!response.config.url.includes('/function_lib/debug')

View File

@ -89,10 +89,14 @@ const useApplicationStore = defineStore({
})
},
async asyncAppAuthentication(token: string, loading?: Ref<boolean>) {
async asyncAppAuthentication(
token: string,
loading?: Ref<boolean>,
authentication_value?: any
) {
return new Promise((resolve, reject) => {
applicationApi
.postAppAuthentication(token, loading)
.postAppAuthentication(token, loading, authentication_value)
.then((res) => {
localStorage.setItem('accessToken', res.data)
sessionStorage.setItem('accessToken', res.data)

View File

@ -0,0 +1,83 @@
<template>
<el-dialog
:modelValue="show"
modal-class="positioned-mask"
width="300"
title="请输入密码打开链接"
custom-class="no-close-button"
:close-on-click-modal="false"
:close-on-press-escape="false"
:show-close="false"
top="25vh"
center
:modal="true"
>
<el-form ref="FormRef" :model="form">
<el-form-item prop="value" :rules="rules.value">
<el-input show-password v-model="form.value" />
</el-form-item>
<el-button class="w-full mt-8" type="primary" @click="validator" :loading="loading"
>确定</el-button
>
</el-form>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useRoute } from 'vue-router'
import useStore from '@/stores'
const route = useRoute()
const FormRef = ref()
const {
params: { accessToken }
} = route as any
const { application } = useStore()
const props = defineProps<{ applicationProfile: any; modelValue: boolean }>()
const loading = ref<boolean>(false)
const show = computed(() => {
if (props.applicationProfile) {
if (props.modelValue) {
return false
}
return props.applicationProfile.authentication
}
return false
})
const emit = defineEmits(['update:modelValue'])
const auth = () => {
return application.asyncAppAuthentication(accessToken, loading, form.value).then(() => {
emit('update:modelValue', true)
})
}
const validator_auth = (rule: any, value: string, callback: any) => {
if (value === '') {
callback(new Error('密码不能为空'))
} else {
auth().catch(() => {
callback(new Error('密码错误'))
})
}
}
const validator = () => {
FormRef.value.validate()
}
const rules = {
value: [{ required: true, validator: validator_auth, trigger: 'blur' }]
}
const form = ref({
type: 'password',
value: ''
})
</script>
<style lang="scss">
.positioned-mask {
top: var(--app-header-height);
height: calc(100% - var(--app-header-height));
.el-overlay-dialog {
top: var(--app-header-height);
height: calc(100% - var(--app-header-height));
}
}
</style>

View File

@ -0,0 +1,56 @@
<template>
<div class="chat-pc__header">
<div class="flex align-center">
<div class="mr-12 ml-24 flex">
<AppAvatar
v-if="isAppIcon(application_profile?.icon)"
shape="square"
:size="32"
style="background: none"
>
<img :src="application_profile?.icon" alt="" />
</AppAvatar>
<AppAvatar
v-else-if="application_profile?.name"
:name="application_profile?.name"
pinyinColor
shape="square"
:size="32"
/>
</div>
<h4>{{ application_profile?.name }}</h4>
</div>
</div>
<div>
<component
:is="auth_components[`/src/views/chat/auth/component/${auth_type}.vue`].default"
v-model="is_auth"
:applicationProfile="application_profile"
/>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { isAppIcon } from '@/utils/application'
const auth_components: any = import.meta.glob('@/views/chat/auth/component/*.vue', {
eager: true
})
const emit = defineEmits(['update:modelValue'])
const props = withDefaults(
defineProps<{ modelValue: boolean; application_profile: any; auth_type?: string }>(),
{
auth_type: 'password'
}
)
const is_auth = computed({
get: () => {
return props.modelValue
},
set: (v) => {
emit('update:modelValue', v)
}
})
</script>
<style lang="scss"></style>

View File

@ -34,49 +34,27 @@
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, computed } from 'vue'
import { useRoute } from 'vue-router'
import { ref, computed } from 'vue'
import { isAppIcon } from '@/utils/application'
import useStore from '@/stores'
const route = useRoute()
const {
params: { accessToken }
} = route as any
const { application, user } = useStore()
const { user } = useStore()
const isDefaultTheme = computed(() => {
return user.isDefaultTheme()
})
const loading = ref(false)
const applicationDetail = ref<any>({})
const applicationAvailable = ref<boolean>(true)
function getAccessToken(token: string) {
application
.asyncAppAuthentication(token, loading)
.then(() => {
getAppProfile()
})
.catch(() => {
applicationAvailable.value = false
})
}
function getAppProfile() {
application
.asyncGetAppProfile(loading)
.then((res: any) => {
applicationDetail.value = res.data
})
.catch(() => {
applicationAvailable.value = false
})
}
onMounted(() => {
user.changeUserType(2)
getAccessToken(accessToken)
const props = defineProps<{
application_profile: any
applicationAvailable: boolean
}>()
const applicationDetail = computed({
get: () => {
return props.application_profile
},
set: (v) => {}
})
</script>
<style lang="scss">

View File

@ -1,31 +1,5 @@
<template>
<div class="chat-embed layout-bg" v-loading="loading">
<el-dialog
v-model="isPasswordDialogVisible"
width="300"
title="请输入密码打开链接"
custom-class="no-close-button"
:close-on-click-modal="false"
:close-on-press-escape="false"
:show-close="false"
align-center
center
:modal="true"
>
<el-form ref="FormRef" :model="form" :rules="rules" v-loading="validateLoading">
<el-form-item prop="password">
<el-input
v-model="form.password"
:placeholder="$t('login.ldap.passwordPlaceholder')"
show-password
/>
</el-form-item>
<el-button class="w-full mt-8" type="primary" @click="submitHandle(FormRef)"
>确定</el-button
>
</el-form>
</el-dialog>
<div class="chat-embed__header" :class="!isDefaultTheme ? 'custom-header' : ''">
<div class="chat-width flex align-center">
<div class="mr-12 ml-24 flex">
@ -49,7 +23,7 @@
<h4>{{ applicationDetail?.name }}</h4>
</div>
</div>
<div v-if="isAuthenticated">
<div>
<div class="chat-embed__main">
<AiChat
ref="AiChatRef"
@ -128,16 +102,11 @@
</template>
<script setup lang="ts">
import { ref, onMounted, reactive, nextTick, computed } from 'vue'
import { useRoute } from 'vue-router'
import { isAppIcon } from '@/utils/application'
import type { FormInstance, FormRules } from 'element-plus'
import useStore from '@/stores'
const route = useRoute()
const {
params: { accessToken }
} = route as any
const { application, user, log } = useStore()
import { isAppIcon } from '@/utils/application'
import useStore from '@/stores'
const { user, log } = useStore()
const isDefaultTheme = computed(() => {
return user.isDefaultTheme()
@ -146,47 +115,19 @@ const isDefaultTheme = computed(() => {
const AiChatRef = ref()
const loading = ref(false)
const left_loading = ref(false)
const applicationDetail = ref<any>({})
const applicationAvailable = ref<boolean>(true)
const chatLogeData = ref<any[]>([])
const show = ref(false)
const FormRef = ref()
const isPasswordDialogVisible = ref(false)
const validateLoading = ref(false)
const form = ref({
password: ''
const props = defineProps<{
application_profile: any
applicationAvailable: boolean
}>()
const applicationDetail = computed({
get: () => {
return props.application_profile
},
set: (v) => {}
})
const isAuthenticated = ref(false)
const validateName = (rule: any, value: string, callback: any) => {
if (value === '') {
callback(new Error('密码不能为空'))
} else {
application
.validatePassword(applicationDetail?.value.id, form.value.password, validateLoading)
.then((res: any) => {
if (res?.data.is_valid) {
isAuthenticated.value = true
isPasswordDialogVisible.value = false
} else {
callback(new Error('密码错误'))
}
})
}
}
const rules = reactive({
password: [{ required: true, validator: validateName, trigger: 'blur' }]
})
const submitHandle = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate((valid) => {})
}
const paginationConfig = reactive({
current_page: 1,
page_size: 20,
@ -237,38 +178,6 @@ function newChat() {
currentChatId.value = 'new'
}
function getAccessToken(token: string) {
application
.asyncAppAuthentication(token, loading)
.then(() => {
setTimeout(() => {
getAppProfile()
}, 500)
})
.catch(() => {
applicationAvailable.value = false
})
}
function getAppProfile() {
application
.asyncGetAppProfile(loading)
.then((res: any) => {
applicationDetail.value = res.data
if (user.isEnterprise()) {
isPasswordDialogVisible.value = applicationDetail?.value.authentication
}
if (!isPasswordDialogVisible.value) {
isAuthenticated.value = true
}
if (res.data?.show_history || !user.isEnterprise()) {
getChatLog(applicationDetail.value.id)
}
})
.catch(() => {
applicationAvailable.value = false
})
}
function getChatLog(id: string) {
const page = {
current_page: 1,
@ -324,10 +233,16 @@ function refresh(id: string) {
getChatLog(applicationDetail.value.id)
currentChatId.value = id
}
/**
*初始化历史对话记录
*/
const init = () => {
if (applicationDetail.value.show_history || !user.isEnterprise()) {
getChatLog(applicationDetail.value.id)
}
}
onMounted(() => {
user.changeUserType(2)
getAccessToken(accessToken)
init()
})
</script>
<style lang="scss">

View File

@ -1,20 +1,36 @@
<template>
<component :is="currentTemplate" :key="route.fullPath" />
<component
v-if="chat_show"
:applicationAvailable="applicationAvailable"
:is="currentTemplate"
:application_profile="application_profile"
:key="route.fullPath"
v-loading="loading"
/>
<Auth
v-else
:application_profile="application_profile"
:auth_type="application_profile.authentication_type"
v-model="is_auth"
></Auth>
</template>
<script setup lang="ts">
import { ref, onMounted, computed } from 'vue'
import { useRoute } from 'vue-router'
import useStore from '@/stores'
import Auth from '@/views/chat/auth/index.vue'
const route = useRoute()
const { application, user } = useStore()
const components: any = import.meta.glob('@/views/chat/**/index.vue', {
eager: true
})
const route = useRoute()
const {
query: { mode }
} = route as any
const {
query: { mode },
params: { accessToken }
} = route as any
const is_auth = ref<boolean>(false)
const currentTemplate = computed(() => {
let modeName = ''
if (mode && mode === 'embed') {
@ -22,26 +38,57 @@ const currentTemplate = computed(() => {
} else {
modeName = show_history.value || !user.isEnterprise() ? 'pc' : 'base'
}
const name = `/src/views/chat/${modeName}/index.vue`
return components[name].default
})
/**
* 是否显示对话
*/
const chat_show = computed(() => {
if (init_data_end.value) {
if (!applicationAvailable.value) {
return true
}
if (application_profile.value) {
if (application_profile.value.authentication && is_auth.value) {
return true
} else if (!application_profile.value.authentication) {
return true
}
}
}
return false
})
const loading = ref(false)
const show_history = ref(false)
const application_profile = ref<any>({})
/**
* 初始化结束
*/
const init_data_end = ref<boolean>(false)
const applicationAvailable = ref<boolean>(true)
function getAppProfile() {
application.asyncGetAppProfile(loading).then((res: any) => {
return application.asyncGetAppProfile(loading).then((res: any) => {
show_history.value = res.data?.show_history
application_profile.value = res.data
})
}
onMounted(() => {
user.asyncGetProfile().then(() => {
if (user.isEnterprise()) {
getAppProfile()
}
function getAccessToken(token: string) {
return application.asyncAppAuthentication(token, loading).then(() => {
getAppProfile()
})
}
onMounted(() => {
Promise.all([user.changeUserType(2), getAccessToken(accessToken)])
.catch(() => {
applicationAvailable.value = false
})
.finally(() => (init_data_end.value = true))
user.asyncGetProfile()
})
</script>
<style lang="scss"></style>

View File

@ -1,30 +1,5 @@
<template>
<div class="chat-pc layout-bg" :class="classObj" v-loading="loading">
<el-dialog
v-model="isPasswordDialogVisible"
width="480"
title="请输入密码打开链接"
custom-class="no-close-button"
:close-on-click-modal="false"
:close-on-press-escape="false"
:show-close="false"
align-center
center
:modal="true"
>
<el-form ref="FormRef" :model="form" :rules="rules" v-loading="validateLoading">
<el-form-item prop="password">
<el-input
v-model="form.password"
:placeholder="$t('login.ldap.passwordPlaceholder')"
show-password
/>
</el-form-item>
<el-button class="w-full mt-8" type="primary" @click="submitHandle(FormRef)"
>确定</el-button
>
</el-form>
</el-dialog>
<div class="chat-pc__header" :class="!isDefaultTheme ? 'custom-header' : ''">
<div class="flex align-center">
<div class="mr-12 ml-24 flex">
@ -47,7 +22,7 @@
<h4>{{ applicationDetail?.name }}</h4>
</div>
</div>
<div v-if="isAuthenticated">
<div>
<div class="flex">
<div class="chat-pc__left border-r">
<div class="p-24 pb-0">
@ -149,65 +124,22 @@
</template>
<script setup lang="ts">
import { reactive, ref, onMounted, nextTick, computed } from 'vue'
import { useRoute } from 'vue-router'
import { ref, onMounted, nextTick, computed } from 'vue'
import { marked } from 'marked'
import { saveAs } from 'file-saver'
import { isAppIcon } from '@/utils/application'
import useStore from '@/stores'
import useResize from '@/layout/hooks/useResize'
import type { FormInstance, FormRules } from 'element-plus'
import { t } from '@/locales'
useResize()
const route = useRoute()
const {
params: { accessToken }
} = route as any
const { application, user, log, common } = useStore()
const { user, log, common } = useStore()
const isDefaultTheme = computed(() => {
return user.isDefaultTheme()
})
const FormRef = ref()
const isCollapse = ref(false)
const isPasswordDialogVisible = ref(false)
const validateLoading = ref(false)
const form = ref({
password: ''
})
const isAuthenticated = ref(false)
const validateName = (rule: any, value: string, callback: any) => {
if (value === '') {
callback(new Error('密码不能为空'))
} else {
application
.validatePassword(applicationDetail?.value.id, form.value.password, validateLoading)
.then((res: any) => {
if (res?.data.is_valid) {
isAuthenticated.value = true
isPasswordDialogVisible.value = false
} else {
callback(new Error('密码错误'))
}
})
}
}
const rules = reactive({
password: [{ required: true, validator: validateName, trigger: 'blur' }]
})
const submitHandle = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate((valid) => {})
}
const classObj = computed(() => {
return {
@ -221,12 +153,21 @@ const newObj = {
id: 'new',
abstract: '新建对话'
}
const props = defineProps<{
application_profile: any
applicationAvailable: boolean
}>()
const AiChatRef = ref()
const loading = ref(false)
const left_loading = ref(false)
const applicationDetail = ref<any>({})
const applicationAvailable = ref<boolean>(true)
const applicationDetail = computed({
get: () => {
return props.application_profile
},
set: (v) => {}
})
const chatLogeData = ref<any[]>([])
const paginationConfig = ref({
@ -270,37 +211,6 @@ function handleScroll(event: any) {
}
}
function getAccessToken(token: string) {
application
.asyncAppAuthentication(token, loading)
.then(() => {
getAppProfile()
})
.catch(() => {
applicationAvailable.value = false
})
}
function getAppProfile() {
application
.asyncGetAppProfile(loading)
.then((res: any) => {
applicationDetail.value = res.data
if (user.isEnterprise()) {
isPasswordDialogVisible.value = applicationDetail?.value.authentication
}
if (!isPasswordDialogVisible.value) {
isAuthenticated.value = true
}
if (res.data?.show_history || !user.isEnterprise()) {
getChatLog(applicationDetail.value.id)
}
})
.catch(() => {
applicationAvailable.value = false
})
}
function newChat() {
if (!chatLogeData.value.some((v) => v.id === 'new')) {
paginationConfig.value.current_page = 1
@ -403,9 +313,19 @@ async function exportHTML(): Promise<void> {
saveAs(blob, suggestedName)
}
/**
*初始化历史对话记录
*/
const init = () => {
if (
(applicationDetail.value.show_history || !user.isEnterprise()) &&
props.applicationAvailable
) {
getChatLog(applicationDetail.value.id)
}
}
onMounted(() => {
user.changeUserType(2)
getAccessToken(accessToken)
init()
})
</script>
<style lang="scss">