mirror of
https://github.com/hi2shark/nazhua.git
synced 2026-01-12 15:20:43 +08:00
✨ 增加自动适配版本,无需指定nezhaVersion,自动适配v1和v0版本
This commit is contained in:
parent
b001277932
commit
557951ede3
68
.github/workflows/release.yml
vendored
68
.github/workflows/release.yml
vendored
@ -52,33 +52,44 @@ jobs:
|
||||
draft: false
|
||||
prerelease: false
|
||||
|
||||
- name: 构建v0版本 - 完整引用版本
|
||||
- name: 构建自动版 - 完整引用版本
|
||||
run: npm run build
|
||||
|
||||
- name: 打包v0-${{ steps.determine_version.outputs.version }}-all.zip
|
||||
run: zip -r v0-${{ steps.determine_version.outputs.version }}-all.zip dist
|
||||
- name: 打包v${{ steps.determine_version.outputs.version }}-all.zip
|
||||
run: zip -r v${{ steps.determine_version.outputs.version }}-all.zip dist
|
||||
|
||||
- name: 构建v0版本 - JSDeliver引用版本
|
||||
- name: 构建自动版 - JSDeliver引用版本
|
||||
env:
|
||||
VITE_SARASA_TERM_SC_USE_CDN: '1'
|
||||
VITE_USE_CDN: '1'
|
||||
VITE_CDN_LIB_TYPE: 'jsdelivr'
|
||||
run: npm run build
|
||||
|
||||
- name: 打包v0-${{ steps.determine_version.outputs.version }}-cdn-jsdelivr.zip
|
||||
run: zip -r v0-${{ steps.determine_version.outputs.version }}-cdn-jsdelivr.zip dist
|
||||
- name: 打包v${{ steps.determine_version.outputs.version }}-cdn-jsdelivr.zip
|
||||
run: zip -r v${{ steps.determine_version.outputs.version }}-cdn-jsdelivr.zip dist
|
||||
|
||||
- name: 构建v0版本 - loli(CDNJS)引用版本
|
||||
- name: 构建自动版 - loli(CDNJS)引用版本
|
||||
env:
|
||||
VITE_SARASA_TERM_SC_USE_CDN: '1'
|
||||
VITE_USE_CDN: '1'
|
||||
VITE_CDN_LIB_TYPE: 'loli'
|
||||
run: npm run build
|
||||
|
||||
- name: 打包v0-${{ steps.determine_version.outputs.version }}-cdn-loli.zip
|
||||
run: zip -r v0-${{ steps.determine_version.outputs.version }}-cdn-loli.zip dist
|
||||
- name: 打包v${{ steps.determine_version.outputs.version }}-cdn-loli.zip
|
||||
run: zip -r v${{ steps.determine_version.outputs.version }}-cdn-loli.zip dist
|
||||
|
||||
- name: 构建哪吒v1内置版本
|
||||
- name: 构建哪吒v0版本
|
||||
env:
|
||||
VITE_NEZHA_VERSION: 'v0'
|
||||
VITE_SARASA_TERM_SC_USE_CDN: '1'
|
||||
VITE_USE_CDN: '1'
|
||||
VITE_CDN_LIB_TYPE: 'jsdelivr'
|
||||
run: npm run build
|
||||
|
||||
- name: 打包v0-dist.zip
|
||||
run: zip -r v0-dist.zip dist
|
||||
|
||||
- name: 构建哪吒v1版本
|
||||
env:
|
||||
VITE_NEZHA_VERSION: 'v1'
|
||||
VITE_SARASA_TERM_SC_USE_CDN: '1'
|
||||
@ -89,34 +100,44 @@ jobs:
|
||||
- name: 打包dist.zip
|
||||
run: zip -r dist.zip dist
|
||||
|
||||
- name: Upload v0-${{ steps.determine_version.outputs.version }}-all.zip
|
||||
- name: Upload v${{ steps.determine_version.outputs.version }}-all.zip
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./v0-${{ steps.determine_version.outputs.version }}-all.zip
|
||||
asset_name: v0-${{ steps.determine_version.outputs.version }}-all.zip
|
||||
asset_path: ./v${{ steps.determine_version.outputs.version }}-all.zip
|
||||
asset_name: v${{ steps.determine_version.outputs.version }}-all.zip
|
||||
asset_content_type: application/zip
|
||||
|
||||
- name: Upload v0-${{ steps.determine_version.outputs.version }}-cdn-jsdelivr.zip
|
||||
- name: Upload v${{ steps.determine_version.outputs.version }}-cdn-jsdelivr.zip
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./v0-${{ steps.determine_version.outputs.version }}-cdn-jsdelivr.zip
|
||||
asset_name: v0-${{ steps.determine_version.outputs.version }}-cdn-jsdelivr.zip
|
||||
asset_path: ./v${{ steps.determine_version.outputs.version }}-cdn-jsdelivr.zip
|
||||
asset_name: v${{ steps.determine_version.outputs.version }}-cdn-jsdelivr.zip
|
||||
asset_content_type: application/zip
|
||||
|
||||
- name: Upload v0-${{ steps.determine_version.outputs.version }}-cdn-loli.zip
|
||||
- name: Upload v${{ steps.determine_version.outputs.version }}-cdn-loli.zip
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./v0-${{ steps.determine_version.outputs.version }}-cdn-loli.zip
|
||||
asset_name: v0-${{ steps.determine_version.outputs.version }}-cdn-loli.zip
|
||||
asset_path: ./v${{ steps.determine_version.outputs.version }}-cdn-loli.zip
|
||||
asset_name: v${{ steps.determine_version.outputs.version }}-cdn-loli.zip
|
||||
asset_content_type: application/zip
|
||||
|
||||
- name: Upload v0-dist.zip
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./v0-dist.zip
|
||||
asset_name: v0-dist.zip
|
||||
asset_content_type: application/zip
|
||||
|
||||
- name: Upload dist.zip
|
||||
@ -131,14 +152,17 @@ jobs:
|
||||
|
||||
- name: Add release notes
|
||||
run: |
|
||||
# 获取最近一次提交的变更内容
|
||||
git log $(git describe --tags --abbrev=0)..HEAD --pretty=format:"%s%n%b" > change.txt
|
||||
|
||||
# 获取现有的发布说明
|
||||
gh release view v${{ steps.determine_version.outputs.version }} --json body -q .body > body.txt
|
||||
|
||||
# 获取最近一次提交的变更内容
|
||||
git log $(git describe --tags --abbrev=0)..HEAD --pretty=format:"%s%n%b" >> body.txt
|
||||
# 添加最近一次提交的变更内容
|
||||
echo -e "\n## 变更内容\n$(cat change.txt)" >> body.txt
|
||||
|
||||
# 添加其他发布说明
|
||||
echo -e "\n哪吒V1请下载dist.zip\n哪吒V0请下载v0-${{ steps.determine_version.outputs.version }}-cdn-jsdelivr.zip\n -all是全量包,-cdn-jsdelivr是jsdelivr引用版,-cdn-loli是cdnjs的loli.net引用版\nv0版本构建物通过修改./config.js指定nezhaVersion的版本号为'v1',v1也可以正常使用" >> body.txt
|
||||
echo -e "\n哪吒V1请下载dist.zip\n哪吒V0请下载v0-dist.zip\nv${{ steps.determine_version.outputs.version }}-all.zip是全量包\nv${{ steps.determine_version.outputs.version }}-cdn-jsdelivr.zip是jsdelivr引用版\nv${{ steps.determine_version.outputs.version }}-cdn-loli.zip是cdnjs的loli.net引用版" >> body.txt
|
||||
|
||||
# 更新发布说明
|
||||
gh release edit v${{ steps.determine_version.outputs.version }} --notes-file body.txt
|
||||
|
||||
@ -14,7 +14,9 @@ import {
|
||||
} from 'vue';
|
||||
import { useStore } from 'vuex';
|
||||
import { useRoute } from 'vue-router';
|
||||
import config from '@/config';
|
||||
import config, {
|
||||
init as initConfig,
|
||||
} from '@/config';
|
||||
import sleep from '@/utils/sleep';
|
||||
import LayoutMain from './layout/main.vue';
|
||||
|
||||
@ -76,6 +78,11 @@ async function wsReconnect() {
|
||||
onMounted(async () => {
|
||||
refreshTime();
|
||||
|
||||
// 如果没有配置哪吒版本,尝试载入 v1 版本配置
|
||||
if (!config.init) {
|
||||
await initConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化服务器信息
|
||||
*/
|
||||
|
||||
@ -1,16 +1,20 @@
|
||||
import {
|
||||
reactive,
|
||||
} from 'vue';
|
||||
import {
|
||||
loadProfile as loadNezhaV1Profile,
|
||||
} from '@/utils/load-nezha-v1-config';
|
||||
|
||||
const defaultNezhaVersion = import.meta.env.VITE_NEZHA_VERSION;
|
||||
|
||||
const config = reactive({
|
||||
init: false,
|
||||
nazhua: {
|
||||
title: '哪吒监控',
|
||||
// 如果打包禁用 Sarasa Term SC 字体,默认为禁用该字体的配置
|
||||
disableSarasaTermSC: import.meta.env.VITE_DISABLE_SARASA_TERM_SC === '1',
|
||||
|
||||
nezhaVersion: ['v0', 'v1'].includes(defaultNezhaVersion) ? defaultNezhaVersion : 'v0',
|
||||
nezhaVersion: ['v0', 'v1'].includes(defaultNezhaVersion) ? defaultNezhaVersion : null,
|
||||
apiMonitorPath: '/api/v1/monitor/{id}',
|
||||
wsPath: '/ws',
|
||||
nezhaPath: '/nezha/',
|
||||
@ -25,6 +29,10 @@ const config = reactive({
|
||||
},
|
||||
});
|
||||
|
||||
if (config.nazhua.nezhaVersion) {
|
||||
config.init = true;
|
||||
}
|
||||
|
||||
export function mergeNazhuaConfig(customConfig) {
|
||||
Object.keys(customConfig).forEach((key) => {
|
||||
config.nazhua[key] = customConfig[key];
|
||||
@ -34,3 +42,10 @@ export function mergeNazhuaConfig(customConfig) {
|
||||
window.$mergeNazhuaConfig = mergeNazhuaConfig;
|
||||
|
||||
export default config;
|
||||
|
||||
export const init = async () => {
|
||||
await loadNezhaV1Profile(true).then((res) => {
|
||||
config.nazhua.nezhaVersion = res ? 'v1' : 'v0';
|
||||
});
|
||||
config.init = true;
|
||||
};
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
<a
|
||||
ref="nofollow"
|
||||
href="https://nezha.wiki"
|
||||
:title="'当前为哪吒监控' + $config.nazhua.nezhaVersion"
|
||||
target="_blank"
|
||||
>哪吒监控</a>
|
||||
</span>
|
||||
|
||||
@ -1,126 +1,11 @@
|
||||
/**
|
||||
* V1版数据加载
|
||||
*/
|
||||
import store from '@/store';
|
||||
import config from '@/config';
|
||||
import request from '@/utils/request';
|
||||
import { Mapping } from '@/utils/object-mapping';
|
||||
|
||||
/**
|
||||
* 字段映射
|
||||
*/
|
||||
export const SERVER_FIELD_MAPS = {
|
||||
ID: 'id',
|
||||
CreatedAt: undefined,
|
||||
UpdatedAt: undefined,
|
||||
DeletedAt: undefined,
|
||||
Name: 'name',
|
||||
Tag: '_$function|queryGroup|id',
|
||||
DisplayIndex: 'display_index',
|
||||
HideForGuest: undefined,
|
||||
EnableDDNS: undefined,
|
||||
Host: '_$mapping|HOST_FIELD_MAPS',
|
||||
State: '_$mapping|STATE_FIELD_MAPS',
|
||||
LastActive: 'last_active',
|
||||
};
|
||||
export const HOST_FIELD_MAPS = {
|
||||
Platform: 'host.platform',
|
||||
PlatformVersion: 'host.platform_version',
|
||||
CPU: 'host.cpu',
|
||||
MemTotal: 'host.mem_total',
|
||||
DiskTotal: 'host.disk_total',
|
||||
SwapTotal: 'host.swap_total',
|
||||
Arch: 'host.arch',
|
||||
Virtualization: 'host.virtualization',
|
||||
BootTime: 'host.boot_time',
|
||||
CountryCode: 'country_code',
|
||||
Version: 'host.version',
|
||||
GPU: 'host.gpu',
|
||||
};
|
||||
export const STATE_FIELD_MAPS = {
|
||||
CPU: 'state.cpu',
|
||||
MemUsed: 'state.mem_used',
|
||||
SwapUsed: 'state.swap_used',
|
||||
DiskUsed: 'state.disk_used',
|
||||
NetInTransfer: 'state.net_in_transfer',
|
||||
NetOutTransfer: 'state.net_out_transfer',
|
||||
NetInSpeed: 'state.net_in_speed',
|
||||
NetOutSpeed: 'state.net_out_speed',
|
||||
Uptime: 'state.uptime',
|
||||
Load1: 'state.load_1',
|
||||
Load5: 'state.load_5',
|
||||
Load15: 'state.load_15',
|
||||
TcpConnCount: 'state.tcp_conn_count',
|
||||
UdpConnCount: 'state.udp_conn_count',
|
||||
ProcessCount: 'state.process_count',
|
||||
Temperatures: 'state.temperatures',
|
||||
GPU: 'state.gpu',
|
||||
};
|
||||
|
||||
/**
|
||||
* 魔法方法
|
||||
*/
|
||||
const magics = {
|
||||
HOST_FIELD_MAPS,
|
||||
STATE_FIELD_MAPS,
|
||||
queryGroup: (id) => {
|
||||
const groupItem = store.state.serverGroup?.find?.((i) => {
|
||||
if (i.servers) {
|
||||
return i.servers.includes(id);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return groupItem?.name;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理V1版数据
|
||||
* @param {Object} v1Data V1版数据
|
||||
* @return {Object} V0版数据
|
||||
*/
|
||||
export const handelV1toV0 = (v1Data) => {
|
||||
const v0Data = {};
|
||||
Object.keys(SERVER_FIELD_MAPS).forEach((key) => {
|
||||
if (SERVER_FIELD_MAPS[key] === undefined) {
|
||||
return;
|
||||
}
|
||||
if (SERVER_FIELD_MAPS[key].includes('_$')) {
|
||||
const $magic = SERVER_FIELD_MAPS[key].split('|');
|
||||
switch ($magic[0]) {
|
||||
case '_$function':
|
||||
if ($magic.length >= 3 && magics[$magic[1]]) {
|
||||
v0Data[key] = magics[$magic[1]](
|
||||
Mapping.mapping(v1Data, $magic[2]),
|
||||
);
|
||||
} else {
|
||||
v0Data[key] = undefined;
|
||||
}
|
||||
break;
|
||||
case '_$mapping':
|
||||
v0Data[key] = Mapping.each(magics[$magic[1]], v1Data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
v0Data[key] = Mapping.mapping(v1Data, SERVER_FIELD_MAPS[key]);
|
||||
});
|
||||
if (v1Data.public_note) {
|
||||
try {
|
||||
v0Data.PublicNote = JSON.parse(v1Data.public_note);
|
||||
} catch (e) {
|
||||
v1Data.PublicNote = null;
|
||||
}
|
||||
} else {
|
||||
v1Data.PublicNote = null;
|
||||
}
|
||||
return v0Data;
|
||||
};
|
||||
|
||||
export const loadServerGroup = async () => request({
|
||||
// TODO: v1GroupPath 兼容 v1ApiGroupPath 到v0.6.0
|
||||
// DELETE: v1GroupPath 兼容 v1ApiGroupPath 到v0.6.0
|
||||
url: config.nazhua.v1GroupPath || config.nazhua.v1ApiGroupPath,
|
||||
type: 'GET',
|
||||
}).then((res) => {
|
||||
@ -156,10 +41,13 @@ export const loadSetting = async () => request({
|
||||
/**
|
||||
* 加载个人信息
|
||||
*/
|
||||
export const loadProfile = async () => request({
|
||||
export const loadProfile = async (check) => request({
|
||||
url: config.nazhua.v1ApiProfilePath,
|
||||
type: 'GET',
|
||||
}).then((res) => {
|
||||
if (check) {
|
||||
return res.status === 200;
|
||||
}
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
return res.data?.data || {};
|
||||
}
|
||||
|
||||
@ -13,11 +13,15 @@ const requestTagMap = {};
|
||||
* @return {Promise}
|
||||
*/
|
||||
async function axiosRequest(options) {
|
||||
return axios(options).then((res) => {
|
||||
if (res.status === 200) {
|
||||
return res;
|
||||
return axios(options).then((res) => res).catch((err) => {
|
||||
if (err.response) {
|
||||
return err.response;
|
||||
}
|
||||
throw new CustomError(`网络错误${res.status}`, res.status);
|
||||
if (err.request) {
|
||||
// 请求已经成功发起,但没有收到响应
|
||||
return null;
|
||||
}
|
||||
throw new CustomError(err.message);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
118
src/utils/transform-v1-2-v0.js
Normal file
118
src/utils/transform-v1-2-v0.js
Normal file
@ -0,0 +1,118 @@
|
||||
/**
|
||||
* V1版数据加载
|
||||
*/
|
||||
import store from '@/store';
|
||||
import { Mapping } from '@/utils/object-mapping';
|
||||
|
||||
/**
|
||||
* 字段映射
|
||||
*/
|
||||
export const SERVER_FIELD_MAPS = {
|
||||
ID: 'id',
|
||||
CreatedAt: undefined,
|
||||
UpdatedAt: undefined,
|
||||
DeletedAt: undefined,
|
||||
Name: 'name',
|
||||
Tag: '_$function|queryGroup|id',
|
||||
DisplayIndex: 'display_index',
|
||||
HideForGuest: undefined,
|
||||
EnableDDNS: undefined,
|
||||
Host: '_$mapping|HOST_FIELD_MAPS',
|
||||
State: '_$mapping|STATE_FIELD_MAPS',
|
||||
LastActive: 'last_active',
|
||||
};
|
||||
export const HOST_FIELD_MAPS = {
|
||||
Platform: 'host.platform',
|
||||
PlatformVersion: 'host.platform_version',
|
||||
CPU: 'host.cpu',
|
||||
MemTotal: 'host.mem_total',
|
||||
DiskTotal: 'host.disk_total',
|
||||
SwapTotal: 'host.swap_total',
|
||||
Arch: 'host.arch',
|
||||
Virtualization: 'host.virtualization',
|
||||
BootTime: 'host.boot_time',
|
||||
CountryCode: 'country_code',
|
||||
Version: 'host.version',
|
||||
GPU: 'host.gpu',
|
||||
};
|
||||
export const STATE_FIELD_MAPS = {
|
||||
CPU: 'state.cpu',
|
||||
MemUsed: 'state.mem_used',
|
||||
SwapUsed: 'state.swap_used',
|
||||
DiskUsed: 'state.disk_used',
|
||||
NetInTransfer: 'state.net_in_transfer',
|
||||
NetOutTransfer: 'state.net_out_transfer',
|
||||
NetInSpeed: 'state.net_in_speed',
|
||||
NetOutSpeed: 'state.net_out_speed',
|
||||
Uptime: 'state.uptime',
|
||||
Load1: 'state.load_1',
|
||||
Load5: 'state.load_5',
|
||||
Load15: 'state.load_15',
|
||||
TcpConnCount: 'state.tcp_conn_count',
|
||||
UdpConnCount: 'state.udp_conn_count',
|
||||
ProcessCount: 'state.process_count',
|
||||
Temperatures: 'state.temperatures',
|
||||
GPU: 'state.gpu',
|
||||
};
|
||||
|
||||
/**
|
||||
* 魔法方法
|
||||
*/
|
||||
const magics = {
|
||||
HOST_FIELD_MAPS,
|
||||
STATE_FIELD_MAPS,
|
||||
queryGroup: (id) => {
|
||||
const groupItem = store.state.serverGroup?.find?.((i) => {
|
||||
if (i.servers) {
|
||||
return i.servers.includes(id);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return groupItem?.name;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理V1版数据
|
||||
* @param {Object} v1Data V1版数据
|
||||
* @return {Object} V0版数据
|
||||
*/
|
||||
export default function (v1Data) {
|
||||
const v0Data = {};
|
||||
Object.keys(SERVER_FIELD_MAPS).forEach((key) => {
|
||||
if (SERVER_FIELD_MAPS[key] === undefined) {
|
||||
return;
|
||||
}
|
||||
if (SERVER_FIELD_MAPS[key].includes('_$')) {
|
||||
const $magic = SERVER_FIELD_MAPS[key].split('|');
|
||||
switch ($magic[0]) {
|
||||
case '_$function':
|
||||
if ($magic.length >= 3 && magics[$magic[1]]) {
|
||||
v0Data[key] = magics[$magic[1]](
|
||||
Mapping.mapping(v1Data, $magic[2]),
|
||||
);
|
||||
} else {
|
||||
v0Data[key] = undefined;
|
||||
}
|
||||
break;
|
||||
case '_$mapping':
|
||||
v0Data[key] = Mapping.each(magics[$magic[1]], v1Data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
v0Data[key] = Mapping.mapping(v1Data, SERVER_FIELD_MAPS[key]);
|
||||
});
|
||||
if (v1Data.public_note) {
|
||||
try {
|
||||
v0Data.PublicNote = JSON.parse(v1Data.public_note);
|
||||
} catch (e) {
|
||||
v1Data.PublicNote = null;
|
||||
}
|
||||
} else {
|
||||
v1Data.PublicNote = null;
|
||||
}
|
||||
return v0Data;
|
||||
}
|
||||
@ -1,8 +1,6 @@
|
||||
import config from '@/config';
|
||||
import MessageSubscribe from '@/utils/subscribe';
|
||||
import {
|
||||
handelV1toV0,
|
||||
} from '@/utils/load-nezha-v1-config';
|
||||
import v1TransformV0 from '@/utils/transform-v1-2-v0';
|
||||
|
||||
import WSService from './service';
|
||||
|
||||
@ -10,9 +8,9 @@ import WSService from './service';
|
||||
* 获取不同版本的WebSocket路径
|
||||
*/
|
||||
function getWsApiPath() {
|
||||
let url = config.nazhua.wsPath;
|
||||
if (config.nazhua.nezhaVersion === 'v1') {
|
||||
url = config.nazhua.v1WsPath;
|
||||
let url = config?.nazhua?.wsPath;
|
||||
if (config?.nazhua?.nezhaVersion === 'v1') {
|
||||
url = config?.nazhua?.v1WsPath;
|
||||
}
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
@ -38,7 +36,7 @@ const wsService = new WSService({
|
||||
msg.emit('servers', {
|
||||
now: data.now,
|
||||
servers: data?.servers?.map?.((server) => {
|
||||
const item = handelV1toV0(server);
|
||||
const item = v1TransformV0(server);
|
||||
return item;
|
||||
}) || [],
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user