自定义源变更
parent
0c6cedf879
commit
b1f2a27eac
|
@ -1,3 +1,17 @@
|
|||
### 自定义源的不兼容变更与新增内容(源开发者需要看)
|
||||
|
||||
自定义源的调用方式已改变:
|
||||
|
||||
- 为了与移动端的调用方式统一,不再推荐使用 `window.lx` 对象(移动端无`window`对象),改用 `globalThis.lx`
|
||||
- `inited` 事件不再需要传递 `status` 属性,脚本运行过程中,在成功调用 `inited` 事件之前的任何首次未捕获的错误都将视为初始化失败,所以现在若想人为让脚本初始化失败,直接抛出一个错误即可
|
||||
- 新增 `globalThis.lx.env` 属性,桌面端环境固定为 `desktop`,移动端环境固定为 `mobile`
|
||||
- 新增 `globalThis.lx.currentScriptInfo` 对象,可以从这里获取解析后的脚本头部注释信息及脚本原始内容,具体可用属性看文档说明
|
||||
- `globalThis.lx.version` 属性更新到 `2.0.0`
|
||||
|
||||
### 新增
|
||||
|
||||
- 若自定义源初始化失败,将会出现弹窗提示初始化失败的详情
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复备份文件无法导入json格式的问题
|
||||
|
|
|
@ -19,6 +19,9 @@ declare namespace LX {
|
|||
description: string
|
||||
script: string
|
||||
allowShowUpdateAlert: boolean
|
||||
author?: string
|
||||
homepage?: string
|
||||
version?: string
|
||||
sources?: UserApiSources
|
||||
}
|
||||
|
||||
|
|
|
@ -684,6 +684,7 @@
|
|||
"user_api__btn_import": "Import",
|
||||
"user_api__btn_remove": "Remove",
|
||||
"user_api__import_file": "Select music API script file",
|
||||
"user_api__init_failed_alert": "Custom source [{name}] failed to initialize:",
|
||||
"user_api__max_tip": "There can only be a maximum of 20 sources at the same time🤪\nIf you want to continue importing, please remove some old sources to make room",
|
||||
"user_api__noitem": "There is nothing here...😲",
|
||||
"user_api__note": "Tip: Although we have isolated the script's running environment as much as possible, importing scripts containing malicious behaviors may still affect your system. Please import them carefully.",
|
||||
|
|
|
@ -683,6 +683,7 @@
|
|||
"user_api__btn_import": "导入",
|
||||
"user_api__btn_remove": "移除",
|
||||
"user_api__import_file": "选择音乐API脚本文件",
|
||||
"user_api__init_failed_alert": "自定义源 [{name}] 初始化失败:",
|
||||
"user_api__max_tip": "最多只能同时存在20个源哦🤪\n想要继续导入的话,请先移除一些旧的源腾出位置吧",
|
||||
"user_api__noitem": "这里竟然是空的 😲",
|
||||
"user_api__note": "提示:虽然我们已经尽可能地隔离了脚本的运行环境,但导入包含恶意行为的脚本仍可能会影响你的系统,请谨慎导入。",
|
||||
|
|
|
@ -683,6 +683,7 @@
|
|||
"user_api__btn_import": "導入",
|
||||
"user_api__btn_remove": "移除",
|
||||
"user_api__import_file": "選擇音樂API腳本文件",
|
||||
"user_api__init_failed_alert": "自訂來源 [{name}] 初始化失敗:",
|
||||
"user_api__max_tip": "最多只能同時存在20個源哦🤪\n想要繼續導入的話,請先移除一些舊的源騰出位置吧",
|
||||
"user_api__noitem": "這裡竟然是空的 😲",
|
||||
"user_api__note": "提示:雖然我們已經盡可能地隔離了腳本的運行環境,但導入包含惡意行為的腳本仍可能會影響你的系統,請謹慎導入。",
|
||||
|
|
|
@ -4,6 +4,7 @@ import fs from 'fs'
|
|||
import path from 'node:path'
|
||||
import { openDevTools as handleOpenDevTools } from '@main/utils'
|
||||
import { encodePath } from '@common/utils/electron'
|
||||
import USER_API_RENDERER_EVENT_NAME from './rendererEvent/name'
|
||||
|
||||
let browserWindow: Electron.BrowserWindow | null = null
|
||||
|
||||
|
@ -87,12 +88,12 @@ export const createWindow = async(userApi: LX.UserApi.UserApiInfo) => {
|
|||
winEvent()
|
||||
|
||||
// console.log(html.replace('</body>', `<script>${userApi.script}</script></body>`))
|
||||
const randomNum = Math.random().toString().substring(2, 10)
|
||||
await browserWindow.loadURL(
|
||||
'data:text/html;charset=UTF-8,' + encodeURIComponent(html
|
||||
.replace('<meta http-equiv="Content-Security-Policy" content="default-src \'none\'">',
|
||||
`<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'nonce-${randomNum}';">`)
|
||||
.replace('</body>', `<script nonce="${randomNum}">${userApi.script}</script></body>`)))
|
||||
// const randomNum = Math.random().toString().substring(2, 10)
|
||||
await browserWindow.loadURL('data:text/html;charset=UTF-8,' + encodeURIComponent(html))
|
||||
|
||||
browserWindow.on('ready-to-show', () => {
|
||||
sendEvent(USER_API_RENDERER_EVENT_NAME.initEnv, userApi)
|
||||
})
|
||||
|
||||
// global.modules.userApiWindow.loadFile(join(dir, 'renderer/user-api.html'))
|
||||
// global.modules.userApiWindow.webContents.openDevTools()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { contextBridge, ipcRenderer } from 'electron'
|
||||
import { contextBridge, ipcRenderer, webFrame } from 'electron'
|
||||
import needle from 'needle'
|
||||
import zlib from 'zlib'
|
||||
import { createCipheriv, publicEncrypt, constants, randomBytes, createHash } from 'crypto'
|
||||
|
@ -74,7 +74,6 @@ const handleRequest = (context, { requestKey, data }) => {
|
|||
* @param {*} context
|
||||
* @param {*} info {
|
||||
* openDevTools: false,
|
||||
* status: true,
|
||||
* message: 'xxx',
|
||||
* sources: {
|
||||
* kw: ['128k', '320k', 'flac', 'flac24bit'],
|
||||
|
@ -87,18 +86,18 @@ const handleRequest = (context, { requestKey, data }) => {
|
|||
*/
|
||||
const handleInit = (context, info) => {
|
||||
if (!info) {
|
||||
sendMessage(USER_API_RENDERER_EVENT_NAME.init, null, false, 'Init failed')
|
||||
sendMessage(USER_API_RENDERER_EVENT_NAME.init, null, false, 'Missing required parameter init info')
|
||||
// sendMessage(USER_API_RENDERER_EVENT_NAME.init, false, null, typeof info.message === 'string' ? info.message.substring(0, 100) : '')
|
||||
return
|
||||
}
|
||||
if (info.openDevTools === true) {
|
||||
sendMessage(USER_API_RENDERER_EVENT_NAME.openDevTools)
|
||||
}
|
||||
if (!info.status) {
|
||||
sendMessage(USER_API_RENDERER_EVENT_NAME.init, null, false, 'Init failed')
|
||||
// sendMessage(USER_API_RENDERER_EVENT_NAME.init, false, null, typeof info.message === 'string' ? info.message.substring(0, 100) : '')
|
||||
return
|
||||
}
|
||||
// if (!info.status) {
|
||||
// sendMessage(USER_API_RENDERER_EVENT_NAME.init, null, false, 'Missing required parameter init info')
|
||||
// // sendMessage(USER_API_RENDERER_EVENT_NAME.init, false, null, typeof info.message === 'string' ? info.message.substring(0, 100) : '')
|
||||
// return
|
||||
// }
|
||||
const sourceInfo = {
|
||||
sources: {},
|
||||
}
|
||||
|
@ -138,140 +137,181 @@ const handleShowUpdateAlert = (data, resolve, reject) => {
|
|||
resolve()
|
||||
}
|
||||
|
||||
contextBridge.exposeInMainWorld('lx', {
|
||||
EVENT_NAMES,
|
||||
request(url, { method = 'get', timeout, headers, body, form, formData }, callback) {
|
||||
let options = { headers }
|
||||
let data
|
||||
if (body) {
|
||||
data = body
|
||||
} else if (form) {
|
||||
data = form
|
||||
// data.content_type = 'application/x-www-form-urlencoded'
|
||||
options.json = false
|
||||
} else if (formData) {
|
||||
data = formData
|
||||
// data.content_type = 'multipart/form-data'
|
||||
options.json = false
|
||||
}
|
||||
options.response_timeout = timeout
|
||||
|
||||
let request = needle.request(method, url, data, options, (err, resp, body) => {
|
||||
if (!err) {
|
||||
body = resp.body = resp.raw.toString()
|
||||
try {
|
||||
resp.body = JSON.parse(resp.body)
|
||||
} catch (_) {}
|
||||
body = resp.body
|
||||
const initEnv = (userApi) => {
|
||||
contextBridge.exposeInMainWorld('lx', {
|
||||
EVENT_NAMES,
|
||||
request(url, { method = 'get', timeout, headers, body, form, formData }, callback) {
|
||||
let options = { headers }
|
||||
let data
|
||||
if (body) {
|
||||
data = body
|
||||
} else if (form) {
|
||||
data = form
|
||||
// data.content_type = 'application/x-www-form-urlencoded'
|
||||
options.json = false
|
||||
} else if (formData) {
|
||||
data = formData
|
||||
// data.content_type = 'multipart/form-data'
|
||||
options.json = false
|
||||
}
|
||||
callback(err, {
|
||||
statusCode: resp.statusCode,
|
||||
statusMessage: resp.statusMessage,
|
||||
headers: resp.headers,
|
||||
bytes: resp.bytes,
|
||||
raw: resp.raw,
|
||||
body,
|
||||
}, body)
|
||||
}).request
|
||||
options.response_timeout = timeout
|
||||
|
||||
return () => {
|
||||
if (!request.aborted) request.abort()
|
||||
request = null
|
||||
}
|
||||
},
|
||||
send(eventName, data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!eventNames.includes(eventName)) return reject(new Error('The event is not supported: ' + eventName))
|
||||
let request = needle.request(method, url, data, options, (err, resp, body) => {
|
||||
// console.log(err, resp, body)
|
||||
if (err) {
|
||||
callback(err, null, null)
|
||||
} else {
|
||||
body = resp.body = resp.raw.toString()
|
||||
try {
|
||||
resp.body = JSON.parse(resp.body)
|
||||
} catch (_) {}
|
||||
body = resp.body
|
||||
callback(err, {
|
||||
statusCode: resp.statusCode,
|
||||
statusMessage: resp.statusMessage,
|
||||
headers: resp.headers,
|
||||
bytes: resp.bytes,
|
||||
raw: resp.raw,
|
||||
body,
|
||||
}, body)
|
||||
}
|
||||
}).request
|
||||
|
||||
return () => {
|
||||
if (!request.aborted) request.abort()
|
||||
request = null
|
||||
}
|
||||
},
|
||||
send(eventName, data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!eventNames.includes(eventName)) return reject(new Error('The event is not supported: ' + eventName))
|
||||
switch (eventName) {
|
||||
case EVENT_NAMES.inited:
|
||||
if (isInitedApi) return reject(new Error('Script is inited'))
|
||||
isInitedApi = true
|
||||
handleInit(this, data)
|
||||
resolve()
|
||||
break
|
||||
case EVENT_NAMES.updateAlert:
|
||||
if (isShowedUpdateAlert) return reject(new Error('The update alert can only be called once.'))
|
||||
isShowedUpdateAlert = true
|
||||
handleShowUpdateAlert(data, resolve, reject)
|
||||
break
|
||||
default:
|
||||
reject(new Error('Unknown event name: ' + eventName))
|
||||
}
|
||||
})
|
||||
},
|
||||
on(eventName, handler) {
|
||||
if (!eventNames.includes(eventName)) return Promise.reject(new Error('The event is not supported: ' + eventName))
|
||||
switch (eventName) {
|
||||
case EVENT_NAMES.inited:
|
||||
if (isInitedApi) return reject(new Error('Script is inited'))
|
||||
isInitedApi = true
|
||||
handleInit(this, data)
|
||||
resolve()
|
||||
case EVENT_NAMES.request:
|
||||
events.request = handler
|
||||
break
|
||||
case EVENT_NAMES.updateAlert:
|
||||
if (isShowedUpdateAlert) return reject(new Error('The update alert can only be called once.'))
|
||||
isShowedUpdateAlert = true
|
||||
handleShowUpdateAlert(data, resolve, reject)
|
||||
break
|
||||
default:
|
||||
reject(new Error('Unknown event name: ' + eventName))
|
||||
default: return Promise.reject(new Error('The event is not supported: ' + eventName))
|
||||
}
|
||||
})
|
||||
},
|
||||
on(eventName, handler) {
|
||||
if (!eventNames.includes(eventName)) return Promise.reject(new Error('The event is not supported: ' + eventName))
|
||||
switch (eventName) {
|
||||
case EVENT_NAMES.request:
|
||||
events.request = handler
|
||||
break
|
||||
default: return Promise.reject(new Error('The event is not supported: ' + eventName))
|
||||
}
|
||||
return Promise.resolve()
|
||||
},
|
||||
utils: {
|
||||
crypto: {
|
||||
aesEncrypt(buffer, mode, key, iv) {
|
||||
const cipher = createCipheriv(mode, key, iv)
|
||||
return Buffer.concat([cipher.update(buffer), cipher.final()])
|
||||
},
|
||||
rsaEncrypt(buffer, key) {
|
||||
buffer = Buffer.concat([Buffer.alloc(128 - buffer.length), buffer])
|
||||
return publicEncrypt({ key, padding: constants.RSA_NO_PADDING }, buffer)
|
||||
},
|
||||
randomBytes(size) {
|
||||
return randomBytes(size)
|
||||
},
|
||||
md5(str) {
|
||||
return createHash('md5').update(str).digest('hex')
|
||||
},
|
||||
return Promise.resolve()
|
||||
},
|
||||
buffer: {
|
||||
from(...args) {
|
||||
return Buffer.from(...args)
|
||||
utils: {
|
||||
crypto: {
|
||||
aesEncrypt(buffer, mode, key, iv) {
|
||||
const cipher = createCipheriv(mode, key, iv)
|
||||
return Buffer.concat([cipher.update(buffer), cipher.final()])
|
||||
},
|
||||
rsaEncrypt(buffer, key) {
|
||||
buffer = Buffer.concat([Buffer.alloc(128 - buffer.length), buffer])
|
||||
return publicEncrypt({ key, padding: constants.RSA_NO_PADDING }, buffer)
|
||||
},
|
||||
randomBytes(size) {
|
||||
return randomBytes(size)
|
||||
},
|
||||
md5(str) {
|
||||
return createHash('md5').update(str).digest('hex')
|
||||
},
|
||||
},
|
||||
bufToString(buf, format) {
|
||||
return Buffer.from(buf, 'binary').toString(format)
|
||||
buffer: {
|
||||
from(...args) {
|
||||
return Buffer.from(...args)
|
||||
},
|
||||
bufToString(buf, format) {
|
||||
return Buffer.from(buf, 'binary').toString(format)
|
||||
},
|
||||
},
|
||||
},
|
||||
zlib: {
|
||||
inflate(buf) {
|
||||
return new Promise((resolve, reject) => {
|
||||
zlib.inflate(buf, (err, data) => {
|
||||
if (err) reject(new Error(err.message))
|
||||
else resolve(data)
|
||||
zlib: {
|
||||
inflate(buf) {
|
||||
return new Promise((resolve, reject) => {
|
||||
zlib.inflate(buf, (err, data) => {
|
||||
if (err) reject(new Error(err.message))
|
||||
else resolve(data)
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
deflate(data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
zlib.deflate(data, (err, buf) => {
|
||||
if (err) reject(new Error(err.message))
|
||||
else resolve(buf)
|
||||
},
|
||||
deflate(data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
zlib.deflate(data, (err, buf) => {
|
||||
if (err) reject(new Error(err.message))
|
||||
else resolve(buf)
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
version: '1.3.0',
|
||||
// removeEvent(eventName, handler) {
|
||||
// if (!eventNames.includes(eventName)) return Promise.reject(new Error('The event is not supported: ' + eventName))
|
||||
// let handlers
|
||||
// switch (eventName) {
|
||||
// case EVENT_NAMES.request:
|
||||
// handlers = events.request
|
||||
// break
|
||||
// }
|
||||
// for (let index = 0; index < handlers.length; index++) {
|
||||
// if (handlers[index] === handler) {
|
||||
// handlers.splice(index, 1)
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// removeAllEvents() {
|
||||
// for (const handlers of Object.values(events)) {
|
||||
// handlers.splice(0, handlers.length)
|
||||
// }
|
||||
// },
|
||||
currentScriptInfo: {
|
||||
name: userApi.name,
|
||||
description: userApi.description,
|
||||
version: userApi.version,
|
||||
author: userApi.author,
|
||||
homepage: userApi.homepage,
|
||||
rawScript: userApi.script,
|
||||
},
|
||||
version: '2.0.0',
|
||||
env: 'desktop',
|
||||
// removeEvent(eventName, handler) {
|
||||
// if (!eventNames.includes(eventName)) return Promise.reject(new Error('The event is not supported: ' + eventName))
|
||||
// let handlers
|
||||
// switch (eventName) {
|
||||
// case EVENT_NAMES.request:
|
||||
// handlers = events.request
|
||||
// break
|
||||
// }
|
||||
// for (let index = 0; index < handlers.length; index++) {
|
||||
// if (handlers[index] === handler) {
|
||||
// handlers.splice(index, 1)
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// removeAllEvents() {
|
||||
// for (const handlers of Object.values(events)) {
|
||||
// handlers.splice(0, handlers.length)
|
||||
// }
|
||||
// },
|
||||
})
|
||||
|
||||
contextBridge.exposeInMainWorld('__lx_init_error_handler__', {
|
||||
sendError(errorMessage) {
|
||||
if (isInitedApi) return
|
||||
isInitedApi = true
|
||||
if (errorMessage.length > 1024) errorMessage = errorMessage.substring(0, 1024) + '...'
|
||||
sendMessage(USER_API_RENDERER_EVENT_NAME.init, null, false, errorMessage)
|
||||
},
|
||||
})
|
||||
|
||||
webFrame.executeJavaScript(`(() => {
|
||||
window.addEventListener('error', (event) => {
|
||||
if (event.isTrusted) globalThis.__lx_init_error_handler__.sendError(event.message.replace(/^Uncaught\\sError:\\s/, ''))
|
||||
})
|
||||
window.addEventListener('unhandledrejection', (event) => {
|
||||
if (!event.isTrusted) return
|
||||
const message = typeof event.reason === 'string' ? event.reason : event.reason?.message ?? String(event.reason)
|
||||
globalThis.__lx_init_error_handler__.sendError(message.replace(/^Error:\\s/, ''))
|
||||
})
|
||||
})()`)
|
||||
|
||||
webFrame.executeJavaScript(userApi.script).catch(_ => _)
|
||||
}
|
||||
|
||||
|
||||
ipcRenderer.on(USER_API_RENDERER_EVENT_NAME.initEnv, (event, data) => {
|
||||
initEnv(data)
|
||||
})
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const names = {
|
||||
initEnv: '',
|
||||
init: '',
|
||||
request: '',
|
||||
response: '',
|
||||
|
|
|
@ -18,18 +18,44 @@ export const getUserApis = (): LX.UserApi.UserApiInfo[] => {
|
|||
return userApis
|
||||
}
|
||||
|
||||
const INFO_NAMES = {
|
||||
name: 24,
|
||||
description: 36,
|
||||
author: 56,
|
||||
homepage: 1024,
|
||||
version: 36,
|
||||
} as const
|
||||
type INFO_NAMES_Type = typeof INFO_NAMES
|
||||
const matchInfo = (scriptInfo: string) => {
|
||||
const infoArr = scriptInfo.split(/\r?\n/)
|
||||
const rxp = /^\s?\*\s?@(\w+)\s(.+)$/
|
||||
const infos: Partial<Record<keyof typeof INFO_NAMES, string>> = {}
|
||||
for (const info of infoArr) {
|
||||
const result = rxp.exec(info)
|
||||
if (!result) continue
|
||||
const key = result[1] as keyof typeof INFO_NAMES
|
||||
if (INFO_NAMES[key] == null) continue
|
||||
infos[key] = result[2].trim()
|
||||
}
|
||||
|
||||
for (const [key, len] of Object.entries(INFO_NAMES) as Array<{ [K in keyof INFO_NAMES_Type]: [K, INFO_NAMES_Type[K]] }[keyof INFO_NAMES_Type]>) {
|
||||
infos[key] ||= ''
|
||||
if (infos[key] == null) infos[key] = ''
|
||||
else if (infos[key]!.length > len) infos[key] = infos[key]!.substring(0, len) + '...'
|
||||
}
|
||||
|
||||
return infos as Record<keyof typeof INFO_NAMES, string>
|
||||
}
|
||||
export const importApi = (script: string): LX.UserApi.UserApiInfo => {
|
||||
let scriptInfo = script.split(/\r?\n/)
|
||||
let name = scriptInfo[1] || ''
|
||||
let description = scriptInfo[2] || ''
|
||||
name = name.startsWith(' * @name ') ? name.replace(' * @name ', '').trim() : `user_api_${new Date().toLocaleString()}`
|
||||
if (name.length > 24) name = name.substring(0, 24) + '...'
|
||||
description = description.startsWith(' * @description ') ? description.replace(' * @description ', '').trim() : ''
|
||||
if (description.length > 36) description = description.substring(0, 36) + '...'
|
||||
const result = /^\/\*[\S|\s]+?\*\//.exec(script)
|
||||
if (!result) throw new Error('无效的自定义源文件')
|
||||
|
||||
let scriptInfo = matchInfo(result[0])
|
||||
|
||||
scriptInfo.name ||= `user_api_${new Date().toLocaleString()}`
|
||||
const apiInfo = {
|
||||
id: `user_api_${Math.random().toString().substring(2, 5)}_${Date.now()}`,
|
||||
name,
|
||||
description,
|
||||
...scriptInfo,
|
||||
script,
|
||||
allowShowUpdateAlert: true,
|
||||
}
|
||||
|
|
|
@ -16,8 +16,9 @@ export default () => {
|
|||
userApi.status = status
|
||||
userApi.message = message
|
||||
|
||||
if (status && apiInfo?.sources) {
|
||||
if (apiInfo.id === appSetting['common.apiSource']) {
|
||||
if (!apiInfo || apiInfo.id !== appSetting['common.apiSource']) return
|
||||
if (status) {
|
||||
if (apiInfo.sources) {
|
||||
let apis: any = {}
|
||||
let qualitys: LX.QualityList = {}
|
||||
for (const [source, { actions, type, qualitys: sourceQualitys }] of Object.entries(apiInfo.sources)) {
|
||||
|
@ -42,7 +43,7 @@ export default () => {
|
|||
musicInfo: songInfo,
|
||||
},
|
||||
},
|
||||
// eslint-disable-next-line @typescript-eslint/promise-function-async
|
||||
// eslint-disable-next-line @typescript-eslint/promise-function-async
|
||||
}).then(res => {
|
||||
// console.log(res)
|
||||
if (!/^https?:/.test(res.data.url)) return Promise.reject(new Error('Get url failed'))
|
||||
|
@ -64,6 +65,14 @@ export default () => {
|
|||
qualityList.value = qualitys
|
||||
userApi.apis = apis
|
||||
}
|
||||
} else {
|
||||
if (message) {
|
||||
void dialog({
|
||||
message: `${t('user_api__init_failed_alert', { name: apiInfo.name })}\n${message}`,
|
||||
selection: true,
|
||||
confirmButtonText: t('ok'),
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -231,7 +231,7 @@ export default {
|
|||
let status
|
||||
if (userApi.status) status = t('setting__basic_source_status_success')
|
||||
else if (userApi.message == 'initing') status = t('setting__basic_source_status_initing')
|
||||
else status = `${t('setting__basic_source_status_failed')} - ${userApi.message}`
|
||||
else status = `${t('setting__basic_source_status_failed')}`
|
||||
|
||||
return status
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue