Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions backend/alembic/versions/060_platform_token_ddl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""060_platform_token_ddl

Revision ID: b40e41c67db3
Revises: db1a95567cbb
Create Date: 2026-01-04 15:50:31.550287

"""
from alembic import op
import sqlalchemy as sa
import sqlmodel.sql.sqltypes
from sqlalchemy.dialects import postgresql

# revision identifiers, used by Alembic.
revision = 'b40e41c67db3'
down_revision = 'db1a95567cbb'
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('sys_platform_token',
sa.Column('id', sa.BigInteger(), nullable=False),
sa.Column('token', sqlmodel.sql.sqltypes.AutoString(length=255), nullable=False),
sa.Column('create_time', sa.BigInteger(), nullable=False),
sa.Column('exp_time', sa.BigInteger(), nullable=False),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_sys_platform_token_id'), 'sys_platform_token', ['id'], unique=False)
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f('ix_sys_platform_token_id'), table_name='sys_platform_token')
op.drop_table('sys_platform_token')
# ### end Alembic commands ###
44 changes: 26 additions & 18 deletions backend/apps/db/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import platform
import urllib.parse
from decimal import Decimal
from datetime import timedelta
from typing import Optional

import oracledb
Expand Down Expand Up @@ -447,6 +448,19 @@ def get_fields(ds: CoreDatasource, table_name: str = None):
return res_list


def convert_value(value):
"""转换值为JSON可序列化的类型"""
if isinstance(value, timedelta):
# 将 timedelta 转换为秒数(整数)或字符串
return str(value) # 或 value.total_seconds()
elif isinstance(value, Decimal):
return float(value)
elif hasattr(value, 'isoformat'): # 处理 datetime/date/time
return value.isoformat()
else:
return value


def exec_sql(ds: CoreDatasource | AssistantOutDsSchema, sql: str, origin_column=False):
while sql.endswith(';'):
sql = sql[:-1]
Expand All @@ -459,9 +473,8 @@ def exec_sql(ds: CoreDatasource | AssistantOutDsSchema, sql: str, origin_column=
columns = result.keys()._keys if origin_column else [item.lower() for item in result.keys()._keys]
res = result.fetchall()
result_list = [
{str(columns[i]): float(value) if isinstance(value, Decimal) else value for i, value in
enumerate(tuple_item)}
for tuple_item in res
{str(columns[i]): convert_value(value) for i, value in enumerate(tuple_item)} for tuple_item in
res
]
return {"fields": columns, "data": result_list,
"sql": bytes.decode(base64.b64encode(bytes(sql, 'utf-8')))}
Expand All @@ -480,9 +493,8 @@ def exec_sql(ds: CoreDatasource | AssistantOutDsSchema, sql: str, origin_column=
field in
cursor.description]
result_list = [
{str(columns[i]): float(value) if isinstance(value, Decimal) else value for i, value in
enumerate(tuple_item)}
for tuple_item in res
{str(columns[i]): convert_value(value) for i, value in enumerate(tuple_item)} for tuple_item in
res
]
return {"fields": columns, "data": result_list,
"sql": bytes.decode(base64.b64encode(bytes(sql, 'utf-8')))}
Expand All @@ -499,9 +511,8 @@ def exec_sql(ds: CoreDatasource | AssistantOutDsSchema, sql: str, origin_column=
field in
cursor.description]
result_list = [
{str(columns[i]): float(value) if isinstance(value, Decimal) else value for i, value in
enumerate(tuple_item)}
for tuple_item in res
{str(columns[i]): convert_value(value) for i, value in enumerate(tuple_item)} for tuple_item in
res
]
return {"fields": columns, "data": result_list,
"sql": bytes.decode(base64.b64encode(bytes(sql, 'utf-8')))}
Expand All @@ -518,9 +529,8 @@ def exec_sql(ds: CoreDatasource | AssistantOutDsSchema, sql: str, origin_column=
field in
cursor.description]
result_list = [
{str(columns[i]): float(value) if isinstance(value, Decimal) else value for i, value in
enumerate(tuple_item)}
for tuple_item in res
{str(columns[i]): convert_value(value) for i, value in enumerate(tuple_item)} for tuple_item in
res
]
return {"fields": columns, "data": result_list,
"sql": bytes.decode(base64.b64encode(bytes(sql, 'utf-8')))}
Expand All @@ -538,9 +548,8 @@ def exec_sql(ds: CoreDatasource | AssistantOutDsSchema, sql: str, origin_column=
field in
cursor.description]
result_list = [
{str(columns[i]): float(value) if isinstance(value, Decimal) else value for i, value in
enumerate(tuple_item)}
for tuple_item in res
{str(columns[i]): convert_value(value) for i, value in enumerate(tuple_item)} for tuple_item in
res
]
return {"fields": columns, "data": result_list,
"sql": bytes.decode(base64.b64encode(bytes(sql, 'utf-8')))}
Expand All @@ -553,9 +562,8 @@ def exec_sql(ds: CoreDatasource | AssistantOutDsSchema, sql: str, origin_column=
field in
columns]
result_list = [
{str(columns[i]): float(value) if isinstance(value, Decimal) else value for i, value in
enumerate(tuple(tuple_item))}
for tuple_item in res
{str(columns[i]): convert_value(value) for i, value in enumerate(tuple_item)} for tuple_item in
res
]
return {"fields": columns, "data": result_list,
"sql": bytes.decode(base64.b64encode(bytes(sql, 'utf-8')))}
Expand Down
2 changes: 2 additions & 0 deletions backend/common/utils/whitelist.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
"/system/authentication/platform/status",
"/system/authentication/login/*",
"/system/authentication/sso/*",
"/system/platform/sso/*",
"/system/platform/client/*",
"/system/parameter/login"
]

Expand Down
2 changes: 1 addition & 1 deletion backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ dependencies = [
"pyyaml (>=6.0.2,<7.0.0)",
"fastapi-mcp (>=0.3.4,<0.4.0)",
"tabulate>=0.9.0",
"sqlbot-xpack>=0.0.4.0,<0.0.5.0",
"sqlbot-xpack>=0.0.5.0,<0.0.6.0",
"fastapi-cache2>=0.2.2",
"sqlparse>=0.5.3",
"redis>=6.2.0",
Expand Down
64 changes: 64 additions & 0 deletions frontend/src/assets/svg/logo_wechat-work.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
79 changes: 79 additions & 0 deletions frontend/src/views/login/xpack/DingtalkQr.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<template>
<div id="de2-dingtalk-qr" :class="{ 'de2-dingtalk-qr': !isBind }" />
</template>

<script lang="ts" setup>
import { loadScript } from '@/utils/RemoteJs'
import { propTypes } from '@/utils/propTypes'
import { getSQLBotAddr } from '@/utils/utils'
import { ref } from 'vue'
import { queryClientInfo } from './platformUtils'
interface DingtalkQrInfo {
client_id: string
state: string
redirect_uri: string
}

const props = defineProps({
isBind: propTypes.bool.def(false),
})
const origin = ref(7)
const remoteJsUrl = 'https://g.alicdn.com/dingding/h5-dingtalk-login/0.21.0/ddlogin.js'
const jsId = 'de-dingtalk-qr-id'
const init = () => {
loadScript(remoteJsUrl, jsId).then(() => {
queryClientInfo(origin.value).then((res: any) => {
const data = formatQrResult(res)
loadQr(data.client_id, data.state, data.redirect_uri)
})
})
}

const formatQrResult = (data: any): DingtalkQrInfo => {
const result = { client_id: null, state: null, redirect_uri: null } as unknown as DingtalkQrInfo
result.client_id = data.client_id
result.state = 'fit2cloud-dingtalk-qr'
result.redirect_uri = data.redirect_uri || getSQLBotAddr()
if (props.isBind) {
result.state += '_de_bind'
}
return result
}

const loadQr = (client_id: string, STATE: string, REDIRECT_URI: string) => {
// eslint-disable-next-line
// @ts-ignore
window.DTFrameLogin(
{
id: 'de2-dingtalk-qr',
width: 280,
height: 300,
},
{
redirect_uri: encodeURIComponent(REDIRECT_URI),
client_id: client_id,
scope: 'openid',
response_type: 'code',
state: STATE,
prompt: 'consent',
},
(loginResult: any) => {
const { redirectUrl, authCode } = loginResult
// 这里可以直接进行重定向
window.location.href = redirectUrl
// 也可以在不跳转页面的情况下,使用code进行授权
console.log(authCode)
},
(errorMsg: any) => {
// 这里一般需要展示登录失败的具体原因,可以使用toast等轻提示
console.error(`errorMsg of errorCbk: ${errorMsg}`)
}
)
}
init()
</script>
<style lang="less" scoped>
.de2-dingtalk-qr {
margin-top: -36px;
}
</style>
Loading
Loading