@ithinkdt/cloud
Version:
iThinkDT Cloud
257 lines (232 loc) • 9.93 kB
JavaScript
import { HttpError, merge, qsParse } from '@ithinkdt/common'
import { $fetch } from '@ithinkdt/core'
import { sourceApp } from './ctx'
import { moduleTypeMap, MODULE_PATH_SPLIT, MODULE_APP_SPLIT } from './module'
export let _token
function logout(ctx, token) {
return fetch(`${ctx}logout`, {
method: 'post',
redirect: 'error',
credentials: 'omit',
headers: new Headers({
Authorization: `Bearer ${token}`,
}),
}).catch((error) => {
console.debug('调用登出接口错误', error)
})
}
export function getDictApi(ctx) {
return {
async fetchDicts(types) {
const api = types.length > 1 ? 'Types' : 'Type'
const res = await $fetch.get(`${ctx}cli/dict/value/getBy${api}`, {
[`dict${api}`]: types.join(','),
})
return api === 'Types' ? res : { [types[0]]: res }
},
labelGetter: (dict, locale) => (locale === 'zh-CN' ? dict.dictValue : dict.dictValueEn) ?? dict.dictValue,
valueGetter: (dict) => dict.dictKey,
}
}
let context
export function loadFrontConfig(appCode, { _BASE, SYS, YIDA }, apps) {
return Promise.all([
apps?.length
? Promise.resolve({ data: apps })
: fetch(`${_BASE}${SYS}pub/app/all`, { credentials: 'omit' })
.then((resp) => resp.json())
.catch((error) => {
console.log(`未能获取到前端应用信息 (${error.message}),使用默认配置。`)
return {
data: [],
}
}),
fetch(`${_BASE}${SYS}pub/busConfig/getFrontConfig`, { credentials: 'omit' })
.then((resp) => resp.json())
.catch((error) => {
console.log(`未能获取到前端配置 (${error.message}),使用默认配置。`)
return {
data: [],
}
}),
YIDA
? fetch(`${_BASE}${YIDA}/pub/yida/app/personalize/${appCode}`)
.then((resp) => resp.json())
.catch((error) => {
console.log(`未能获取到前端配置 (${error.message}),使用默认配置。`)
return {
data: {},
}
})
: Promise.resolve({}),
]).then(async ([{ data: apps }, { data: configs }, { data: personalize = {} }]) => {
const map = {}
for (const it of configs) {
map[it.configKey] = it.configValue
}
context = {}
for (let { appCode, url = '' } of apps) {
if (!url.endsWith('/')) url = url + '/'
context[appCode] = url
context[appCode.replaceAll('-', '_').toUpperCase()] = url
}
const logo = await fetch(
`${_BASE}${SYS}pub/logo/${apps.find((app) => app.appCode === appCode)?.logo || map.FRONT_LOGO_ID}`,
).then((resp) => resp.text())
return {
...map,
context,
apps,
appName(appCode, locale) {
const app = apps.find((app) => app.appCode === appCode)
return (
app?.[locale.startsWith('zh') ? 'appName' : 'appNameEn'] ?? app?.appName ?? app?.appNameEn ?? '系统'
)
},
logo,
multiTab: personalize.multiTab ?? map.FRONT_MULTI_TAB === 'true',
i18n: map.FRONT_I18N === 'true',
accordionMenu: personalize.accordionMenu ?? map.FRONT_ACCORDION_MENU === 'true',
collpaseBtnPlacement: personalize.collpaseBtnPlacement ?? (map.FRONT_COLLPASE_BTN_PLACEMENT || 'default'),
menuPlacement: personalize.menuPlacement ?? (map.FRONT_MENU_PLACEMENT || 'sidebar'),
appearence: personalize.showAppearence ?? map.FRONT_APPEARENCE === 'true',
theme: {
light: JSON.parse(map.FRONT_THEME_LIGHT ?? '{}'),
dark: merge({}, JSON.parse(map.FRONT_THEME_LIGHT ?? '{}'), JSON.parse(map.FRONT_THEME_DARK ?? '{}')),
},
locale: map.FRONT_LOCALE || undefined,
watermark: personalize.watermark ?? map.FRONT_WATERMARK === 'true',
renderWatermark: new Function('user', `return (${map.FRONT_WATERMARK_RENDER})`),
footer: map.FRONT_FOOTER.trim() || undefined,
timeout: Number.parseFloat(map.FRONT_REQUEST_TIMEOUT) || undefined,
domain: map.FRONT_COOKIE_DOMAIN || undefined,
sso:
map.FRONT_SSO_ENABLED === 'false'
? false
: {
selfLogin: map.FRONT_LOCAL_LOGIN === 'true',
login: (redirect) => {
return new Function('redirect', `return \`${map.FRONT_SSO_LOGIN_URL}\``)(
encodeURIComponent(redirect),
)
},
logout: async (redirect, { appCode, username, token }) => {
await logout(_BASE + SYS, token)
return new Function(
'redirect',
'appCode',
'username',
`return \`${map.FRONT_SSO_LOGOUT_URL}\``,
)(encodeURIComponent(redirect), encodeURIComponent(appCode), encodeURIComponent(username))
},
parseToken: (fromUrl) => {
const url = new URL(fromUrl)
return new Function('url', 'query', map.FRONT_SSO_PARSE_URL)(
fromUrl,
qsParse(url.search || '?' + (url.hash.split('?')[1] || '')),
)
},
},
}
})
}
function mapModules(modules, appCode, locale) {
return (modules ?? []).map((it) => {
const children = mapModules(it.children, appCode, locale)
let type = it.asMenu
? children.some((it) => it.type !== 'action' && it.hidden !== true)
? 'group'
: (it.moduleType && moduleTypeMap[it.moduleType]) || 'module'
: 'action'
let moduleUrl = it.moduleUrl
if (type === 'module' && appCode !== sourceApp) {
type = 'federate'
moduleUrl = appCode + MODULE_PATH_SPLIT + moduleUrl
}
let lcKey
let remote
if (type === 'federate') {
remote = moduleUrl?.split(MODULE_PATH_SPLIT)[0]
if (!remote || remote === sourceApp) {
type = 'module'
remote = undefined
moduleUrl = moduleUrl?.split(MODULE_PATH_SPLIT)[1]
} else {
remote = context[remote] + 'routes.js?_t=' + Date.now()
}
} else if (type === 'lowcode') {
const [prefix] = moduleUrl?.split(MODULE_PATH_SPLIT) ?? []
lcKey = prefix?.split(MODULE_APP_SPLIT)[0]
remote = prefix?.split(MODULE_APP_SPLIT)[1]
}
return {
key: it.id,
type,
label: (locale === 'zh-CN' ? it.moduleName : it.moduleNameEn) ?? it.moduleName ?? it.text,
hidden: it.visiable === false,
path: ['federate', 'lowcode'].includes(type) ? moduleUrl.split(MODULE_PATH_SPLIT)[1] : moduleUrl,
remote,
lcKey,
level: it.moduleLevel,
icon: it.icon,
children: children,
parentKey: it.parentId,
actions:
type === 'action'
? moduleUrl?.split(',').filter((it) => !!it)
: children.map((it) => it.actions).flat(Number.POSITIVE_INFINITY),
}
})
}
export function getAuthApi(appCode, ctx, base) {
return {
login(user) {
return $fetch.post(`${ctx}login`, user, { alert: false, redirect: 'error' })
},
logout({ token }) {
return logout(base + ctx, token)
},
async loadAuthInfo(token, locale) {
_token = token
const [user, modules] = await Promise.allSettled([
$fetch.get(`${ctx}cli/userinfo`, undefined, { alert: false }),
$fetch.get(`${ctx}cli/modules`, appCode ? { appCode } : undefined, { alert: false }),
])
if (user?.reason instanceof HttpError) {
if (user?.reason.code === 403) {
throw new Error('forbidden')
}
if (user?.reason.code === 408) {
throw new Error('timeout')
}
throw user?.reason
}
const _ms = appCode ? modules.value[0]?.children : modules.value
if (_ms)
for (const m of _ms) {
m.asMenu = true
}
return {
user: {
...user.value,
nickname: user.value.personName,
},
modules: mapModules(_ms, appCode, locale),
}
},
changePwd({ old: curPassword, new: newPassword }) {
return $fetch.post(
`${ctx}cli/user/password/change`,
{ curPassword, newPassword },
{ reqType: 'urlencoded' },
)
},
jwt: {
headerField: 'Authorization',
cookieField: 'access_token',
tokenFormatter: (token) => `Bearer ${token}`,
tokenParser: (str) => str?.slice(7),
unauthCodes: [401, 604, 606],
},
}
}