Skip to content

Commit 9112d2f

Browse files
feat: Wecom and dingtalk support
1 parent d362df9 commit 9112d2f

File tree

10 files changed

+503
-28
lines changed

10 files changed

+503
-28
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"""060_platform_token_ddl
2+
3+
Revision ID: b40e41c67db3
4+
Revises: db1a95567cbb
5+
Create Date: 2026-01-04 15:50:31.550287
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
import sqlmodel.sql.sqltypes
11+
from sqlalchemy.dialects import postgresql
12+
13+
# revision identifiers, used by Alembic.
14+
revision = 'b40e41c67db3'
15+
down_revision = 'db1a95567cbb'
16+
branch_labels = None
17+
depends_on = None
18+
19+
20+
def upgrade():
21+
# ### commands auto generated by Alembic - please adjust! ###
22+
op.create_table('sys_platform_token',
23+
sa.Column('id', sa.BigInteger(), nullable=False),
24+
sa.Column('token', sqlmodel.sql.sqltypes.AutoString(length=255), nullable=False),
25+
sa.Column('create_time', sa.BigInteger(), nullable=False),
26+
sa.Column('exp_time', sa.BigInteger(), nullable=False),
27+
sa.PrimaryKeyConstraint('id')
28+
)
29+
op.create_index(op.f('ix_sys_platform_token_id'), 'sys_platform_token', ['id'], unique=False)
30+
# ### end Alembic commands ###
31+
32+
33+
def downgrade():
34+
# ### commands auto generated by Alembic - please adjust! ###
35+
op.drop_index(op.f('ix_sys_platform_token_id'), table_name='sys_platform_token')
36+
op.drop_table('sys_platform_token')
37+
# ### end Alembic commands ###

backend/common/utils/whitelist.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
"/system/authentication/platform/status",
3737
"/system/authentication/login/*",
3838
"/system/authentication/sso/*",
39+
"/system/platform/sso/*",
40+
"/system/platform/client/*",
3941
"/system/parameter/login"
4042
]
4143

backend/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ dependencies = [
3939
"pyyaml (>=6.0.2,<7.0.0)",
4040
"fastapi-mcp (>=0.3.4,<0.4.0)",
4141
"tabulate>=0.9.0",
42-
"sqlbot-xpack>=0.0.4.0,<0.0.5.0",
42+
"sqlbot-xpack>=0.0.5.0,<0.0.6.0",
4343
"fastapi-cache2>=0.2.2",
4444
"sqlparse>=0.5.3",
4545
"redis>=6.2.0",
Lines changed: 64 additions & 0 deletions
Loading
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<template>
2+
<div id="de2-dingtalk-qr" :class="{ 'de2-dingtalk-qr': !isBind }" />
3+
</template>
4+
5+
<script lang="ts" setup>
6+
import { loadScript } from '@/utils/RemoteJs'
7+
import { propTypes } from '@/utils/propTypes'
8+
import { getSQLBotAddr } from '@/utils/utils'
9+
import { ref } from 'vue'
10+
import { queryClientInfo } from './platformUtils'
11+
interface DingtalkQrInfo {
12+
client_id: string
13+
state: string
14+
redirect_uri: string
15+
}
16+
17+
const props = defineProps({
18+
isBind: propTypes.bool.def(false),
19+
})
20+
const origin = ref(7)
21+
const remoteJsUrl = 'https://g.alicdn.com/dingding/h5-dingtalk-login/0.21.0/ddlogin.js'
22+
const jsId = 'de-dingtalk-qr-id'
23+
const init = () => {
24+
loadScript(remoteJsUrl, jsId).then(() => {
25+
queryClientInfo(origin.value).then((res: any) => {
26+
const data = formatQrResult(res)
27+
loadQr(data.client_id, data.state, data.redirect_uri)
28+
})
29+
})
30+
}
31+
32+
const formatQrResult = (data: any): DingtalkQrInfo => {
33+
const result = { client_id: null, state: null, redirect_uri: null } as unknown as DingtalkQrInfo
34+
result.client_id = data.client_id
35+
result.state = 'fit2cloud-dingtalk-qr'
36+
result.redirect_uri = data.redirect_uri || getSQLBotAddr()
37+
if (props.isBind) {
38+
result.state += '_de_bind'
39+
}
40+
return result
41+
}
42+
43+
const loadQr = (client_id: string, STATE: string, REDIRECT_URI: string) => {
44+
// eslint-disable-next-line
45+
// @ts-ignore
46+
window.DTFrameLogin(
47+
{
48+
id: 'de2-dingtalk-qr',
49+
width: 280,
50+
height: 300,
51+
},
52+
{
53+
redirect_uri: encodeURIComponent(REDIRECT_URI),
54+
client_id: client_id,
55+
scope: 'openid',
56+
response_type: 'code',
57+
state: STATE,
58+
prompt: 'consent',
59+
},
60+
(loginResult: any) => {
61+
const { redirectUrl, authCode } = loginResult
62+
// 这里可以直接进行重定向
63+
window.location.href = redirectUrl
64+
// 也可以在不跳转页面的情况下,使用code进行授权
65+
console.log(authCode)
66+
},
67+
(errorMsg: any) => {
68+
// 这里一般需要展示登录失败的具体原因,可以使用toast等轻提示
69+
console.error(`errorMsg of errorCbk: ${errorMsg}`)
70+
}
71+
)
72+
}
73+
init()
74+
</script>
75+
<style lang="less" scoped>
76+
.de2-dingtalk-qr {
77+
margin-top: -36px;
78+
}
79+
</style>

frontend/src/views/login/xpack/Handler.vue

Lines changed: 76 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
<template>
2-
<!-- <div v-if="loginCategory.qrcode" :class="{ 'de-qr-hidden': !qrStatus }">
2+
<div v-if="loginCategory.qrcode" :class="{ 'de-qr-hidden': !qrStatus }">
33
<QrTab
44
v-if="qrStatus"
55
:wecom="loginCategory.wecom"
66
:dingtalk="loginCategory.dingtalk"
77
:lark="loginCategory.lark"
88
:larksuite="loginCategory.larksuite"
99
/>
10-
</div> -->
10+
</div>
1111
<LdapLoginForm v-if="isLdap" />
1212
<el-divider v-if="anyEnable" class="de-other-login-divider">{{
1313
t('login.other_login')
@@ -54,13 +54,11 @@
5454
import { ref, onMounted, nextTick, computed } from 'vue'
5555
import QrcodeLdap from './QrcodeLdap.vue'
5656
import LdapLoginForm from './LdapLoginForm.vue'
57-
/* import Oidc from './Oidc.vue'
58-
import Oauth2 from './Oauth2.vue'
59-
import Saml2 from './Saml2.vue' */
57+
6058
import Oidc from './Oidc.vue'
6159
import Cas from './Cas.vue'
6260
import Oauth2 from './Oauth2.vue'
63-
// import QrTab from './QrTab.vue'
61+
import QrTab from './QrTab.vue'
6462
import { request } from '@/utils/request'
6563
import { useCache } from '@/utils/useCache'
6664
@@ -197,6 +195,8 @@ const init = (cb?: () => void) => {
197195
.then((res) => {
198196
if (res) {
199197
const list: any[] = res as any[]
198+
/* list.push({ name: 'qrcode', enable: true })
199+
list.push({ name: 'wecom', enable: true }) */
200200
list.forEach((item: { name: keyof LoginCategory; enable: boolean }) => {
201201
loginCategory.value[item.name] = item.enable
202202
if (item.enable) {
@@ -416,6 +416,72 @@ const oidcLogin = () => {
416416
}, 1500)
417417
})
418418
}
419+
const wecomLogin = () => {
420+
const urlParams = getUrlParams()
421+
request
422+
.post('/system/platform/sso/6', urlParams)
423+
.then((res: any) => {
424+
const token = res.access_token
425+
// const platform_info = res.platform_info
426+
if (token && isPlatformClient()) {
427+
wsCache.set('de-platform-client', true)
428+
}
429+
userStore.setToken(token)
430+
userStore.setExp(res.exp)
431+
userStore.setTime(Date.now())
432+
userStore.setPlatformInfo({
433+
flag: 'wecom',
434+
// data: platform_info ? JSON.stringify(platform_info) : '',
435+
origin: 6,
436+
})
437+
const queryRedirectPath = getCurLocation()
438+
router.push({ path: queryRedirectPath })
439+
})
440+
.catch((e: any) => {
441+
userStore.setToken('')
442+
setTimeout(() => {
443+
// logoutHandler(true, true)
444+
platformLoginMsg.value = e?.message || e
445+
setTimeout(() => {
446+
window.location.href =
447+
window.location.origin + window.location.pathname + window.location.hash
448+
}, 2000)
449+
}, 1500)
450+
})
451+
}
452+
const dingtalkLogin = () => {
453+
const urlParams = getUrlParams()
454+
request
455+
.post('/system/platform/sso/7', urlParams)
456+
.then((res: any) => {
457+
const token = res.access_token
458+
// const platform_info = res.platform_info
459+
if (token && isPlatformClient()) {
460+
wsCache.set('de-platform-client', true)
461+
}
462+
userStore.setToken(token)
463+
userStore.setExp(res.exp)
464+
userStore.setTime(Date.now())
465+
userStore.setPlatformInfo({
466+
flag: 'dingtalk',
467+
// data: platform_info ? JSON.stringify(platform_info) : '',
468+
origin: 7,
469+
})
470+
const queryRedirectPath = getCurLocation()
471+
router.push({ path: queryRedirectPath })
472+
})
473+
.catch((e: any) => {
474+
userStore.setToken('')
475+
setTimeout(() => {
476+
// logoutHandler(true, true)
477+
platformLoginMsg.value = e?.message || e
478+
setTimeout(() => {
479+
window.location.href =
480+
window.location.origin + window.location.pathname + window.location.hash
481+
}, 2000)
482+
}, 1500)
483+
})
484+
}
419485
/* const platformLogin = (origin: number) => {
420486
const url = '/system/authentication/sso/cas'
421487
request
@@ -591,6 +657,10 @@ onMounted(() => {
591657
oauth2Login()
592658
} else if (state?.includes('oidc')) {
593659
oidcLogin()
660+
} else if (state?.includes('wecom')) {
661+
wecomLogin()
662+
} else if (state?.includes('dingtalk')) {
663+
dingtalkLogin()
594664
} else {
595665
auto2Platform()
596666
}

0 commit comments

Comments
 (0)