523 lines
12 KiB
JavaScript
523 lines
12 KiB
JavaScript
import { defineAsyncComponent, h } from 'vue';
|
|
import LucideAppWindow from '~icons/lucide/app-window';
|
|
import ServerActions from '../components/server/ServerActions.vue';
|
|
import { getTeam } from '../data/team';
|
|
import router from '../router';
|
|
import { icon } from '../utils/components';
|
|
import { duration, planTitle, userCurrency } from '../utils/format';
|
|
import { trialDays } from '../utils/site';
|
|
import { getJobsTab } from './common/jobs';
|
|
import { tagTab } from './common/tags';
|
|
|
|
export default {
|
|
pagetype: 'Server',
|
|
whitelistedMethods: {
|
|
increaseDiskSize: 'increase_disk_size_for_server',
|
|
configureAutoAddStorage: 'configure_auto_add_storage',
|
|
changePlan: 'change_plan',
|
|
reboot: 'reboot',
|
|
rename: 'rename',
|
|
dropServer: 'drop_server',
|
|
addTag: 'add_resource_tag',
|
|
removeTag: 'remove_resource_tag'
|
|
},
|
|
list: {
|
|
route: '/servers',
|
|
title: '服务器',
|
|
fields: [
|
|
'title',
|
|
'database_server',
|
|
'plan.title as plan_title',
|
|
'plan.price_usd as price_usd',
|
|
'plan.price_cny as price_cny',
|
|
'cluster.image as cluster_image',
|
|
'cluster.title as cluster_title'
|
|
],
|
|
filterControls() {
|
|
return [
|
|
{
|
|
type: 'select',
|
|
label: '状态',
|
|
fieldname: 'status',
|
|
options: [
|
|
{ label: '', value: '' },
|
|
{ label: '激活', value: 'Active' },
|
|
{ label: '待定', value: 'Pending' }
|
|
]
|
|
},
|
|
{
|
|
type: 'select',
|
|
label: '区域',
|
|
fieldname: 'cluster',
|
|
options: [
|
|
'',
|
|
'中国大陆',
|
|
'中国香港',
|
|
'美国-洛杉矶',
|
|
'新加坡',
|
|
'英国-伦敦',
|
|
'德国-法兰克福',
|
|
'阿联酋-迪拜',
|
|
]
|
|
}
|
|
];
|
|
},
|
|
orderBy: 'creation desc',
|
|
columns: [
|
|
{
|
|
label: '服务器',
|
|
fieldname: 'name',
|
|
width: 1.5,
|
|
class: 'font-medium',
|
|
format(value, row) {
|
|
return row.title || value;
|
|
}
|
|
},
|
|
{ label: '状态', fieldname: 'status', type: 'Badge', width: 0.8 },
|
|
{
|
|
label: '应用服务器计划',
|
|
format(value, row) {
|
|
return planTitle(row);
|
|
}
|
|
},
|
|
{
|
|
label: '数据库服务器计划',
|
|
fieldname: 'db_plan',
|
|
format(value) {
|
|
if (!value) return '';
|
|
return planTitle(value);
|
|
}
|
|
},
|
|
{
|
|
label: '区域',
|
|
fieldname: 'cluster',
|
|
format(value, row) {
|
|
return row.cluster_title || value;
|
|
},
|
|
prefix(row) {
|
|
return h('img', {
|
|
src: row.cluster_image,
|
|
class: 'w-4 h-4',
|
|
alt: row.cluster_title
|
|
});
|
|
}
|
|
}
|
|
],
|
|
primaryAction({ listResource: servers }) {
|
|
return {
|
|
label: '新建服务器',
|
|
variant: 'solid',
|
|
slots: {
|
|
prefix: icon('plus')
|
|
},
|
|
onClick() {
|
|
router.push({ name: 'New Server' });
|
|
}
|
|
};
|
|
}
|
|
},
|
|
detail: {
|
|
titleField: 'name',
|
|
route: '/servers/:name',
|
|
statusBadge({ documentResource: server }) {
|
|
return {
|
|
label: server.pg.status
|
|
};
|
|
},
|
|
breadcrumbs({ documentResource: server }) {
|
|
return [
|
|
{
|
|
label: '服务器',
|
|
route: '/servers'
|
|
},
|
|
{
|
|
label: server.pg.title || server.pg.name,
|
|
route: `/servers/${server.pg.name}`
|
|
}
|
|
];
|
|
},
|
|
actions({ documentResource: server }) {
|
|
let $team = getTeam();
|
|
|
|
return [
|
|
{
|
|
label: '模拟服务器所有者',
|
|
title: '模拟服务器所有者', // for label to pop-up on hover
|
|
slots: {
|
|
icon: defineAsyncComponent(() =>
|
|
import('~icons/lucide/venetian-mask')
|
|
)
|
|
},
|
|
condition: () =>
|
|
$team.pg?.is_desk_user && server.pg.team !== $team.name,
|
|
onClick() {
|
|
switchToTeam(server.pg.team);
|
|
}
|
|
},
|
|
{
|
|
label: '选项',
|
|
button: {
|
|
label: '选项',
|
|
slots: {
|
|
icon: icon('more-horizontal')
|
|
}
|
|
},
|
|
options: [
|
|
{
|
|
label: '在 Desk 中查看',
|
|
icon: icon('external-link'),
|
|
condition: () => $team.pg?.is_desk_user,
|
|
onClick() {
|
|
window.open(
|
|
`${window.location.protocol}//${
|
|
window.location.host
|
|
}/app/${server.pagetype.replace(' ', '-').toLowerCase()}/${
|
|
server.pg.name
|
|
}`,
|
|
'_blank'
|
|
);
|
|
}
|
|
},
|
|
{
|
|
label: '访问服务器',
|
|
icon: icon('external-link'),
|
|
condition: () =>
|
|
server.pg.status === 'Active' && $team.pg?.is_desk_user,
|
|
onClick() {
|
|
window.open(`https://${server.pg.name}`, '_blank');
|
|
}
|
|
}
|
|
]
|
|
}
|
|
];
|
|
},
|
|
tabs: [
|
|
{
|
|
label: '概览',
|
|
icon: icon('home'),
|
|
route: 'overview',
|
|
type: 'Component',
|
|
component: defineAsyncComponent(() =>
|
|
import('../components/server/ServerOverview.vue')
|
|
),
|
|
props: server => {
|
|
return { server: server.pg.name };
|
|
}
|
|
},
|
|
{
|
|
label: '分析',
|
|
icon: icon('bar-chart-2'),
|
|
route: 'analytics',
|
|
type: 'Component',
|
|
component: defineAsyncComponent(() =>
|
|
import('../components/server/ServerCharts.vue')
|
|
),
|
|
props: server => {
|
|
return {
|
|
serverName: server.pg.name
|
|
};
|
|
}
|
|
},
|
|
{
|
|
label: '站点',
|
|
icon: icon(LucideAppWindow),
|
|
route: 'sites',
|
|
type: 'list',
|
|
list: {
|
|
pagetype: 'Site',
|
|
filters: server => {
|
|
return { server: server.pg.name };
|
|
},
|
|
fields: [
|
|
'plan.plan_title as plan_title',
|
|
'plan.price_usd as price_usd',
|
|
'plan.price_cny as price_cny',
|
|
'group.title as group_title',
|
|
'group.public as group_public',
|
|
'group.team as group_team',
|
|
'group.version as version',
|
|
'trial_end_date'
|
|
],
|
|
orderBy: 'creation desc',
|
|
searchField: 'host_name',
|
|
route(row) {
|
|
return { name: '站点详情', params: { name: row.name } };
|
|
},
|
|
filterControls() {
|
|
return [
|
|
{
|
|
type: 'select',
|
|
label: '状态',
|
|
fieldname: 'status',
|
|
options: [
|
|
{ label: '', value: '' },
|
|
{ label: '激活', value: 'Active' },
|
|
{ label: '未激活', value: 'Inactive' },
|
|
{ label: '已暂停', value: 'Suspended' },
|
|
{ label: '损坏', value: 'Broken' }
|
|
]
|
|
},
|
|
{
|
|
type: 'link',
|
|
label: '版本',
|
|
fieldname: 'group.version',
|
|
options: {
|
|
pagetype: 'Jingrow Version'
|
|
}
|
|
},
|
|
{
|
|
type: 'link',
|
|
label: '站点分组',
|
|
fieldname: 'group',
|
|
options: {
|
|
pagetype: 'Release Group'
|
|
}
|
|
},
|
|
{
|
|
type: 'link',
|
|
label: '标签',
|
|
fieldname: 'tags.tag',
|
|
options: {
|
|
pagetype: 'Jcloud Tag',
|
|
filters: {
|
|
pagetype_name: 'Site'
|
|
}
|
|
}
|
|
}
|
|
];
|
|
},
|
|
columns: [
|
|
{
|
|
label: '站点',
|
|
fieldname: 'host_name',
|
|
width: 1.5,
|
|
class: 'font-medium',
|
|
format(value, row) {
|
|
return value || row.name;
|
|
}
|
|
},
|
|
{ label: '状态', fieldname: 'status', type: 'Badge', width: 0.6 },
|
|
{
|
|
label: '计划',
|
|
fieldname: 'plan',
|
|
width: 0.85,
|
|
format(value, row) {
|
|
if (row.trial_end_date) {
|
|
return trialDays(row.trial_end_date);
|
|
}
|
|
let $team = getTeam();
|
|
if (row.price_usd > 0) {
|
|
let china = $team.pg.country == 'china';
|
|
let formattedValue = userCurrency(
|
|
china ? row.price_cny : row.price_usd,
|
|
0
|
|
);
|
|
return `${formattedValue}/月`;
|
|
}
|
|
return row.plan_title;
|
|
}
|
|
},
|
|
{
|
|
label: '站点分组',
|
|
fieldname: 'group_title',
|
|
width: '15rem'
|
|
},
|
|
{
|
|
label: '版本',
|
|
fieldname: 'version',
|
|
width: 0.5
|
|
}
|
|
]
|
|
}
|
|
},
|
|
{
|
|
label: '站点分组',
|
|
icon: icon('package'),
|
|
route: 'groups',
|
|
type: 'list',
|
|
list: {
|
|
pagetype: 'Release Group',
|
|
filters: server => {
|
|
return { server: server.pg.name };
|
|
},
|
|
fields: [{ apps: ['app'] }, { servers: ['server'] }],
|
|
columns: [
|
|
{ label: '标题', fieldname: 'title' },
|
|
{
|
|
label: '状态',
|
|
fieldname: 'active_benches',
|
|
type: 'Badge',
|
|
width: 0.5,
|
|
format: (value, row) => {
|
|
if (!value) return '等待部署';
|
|
else return '激活';
|
|
}
|
|
},
|
|
{
|
|
label: '版本',
|
|
fieldname: 'version',
|
|
width: 0.5
|
|
},
|
|
{
|
|
label: '应用',
|
|
fieldname: 'app',
|
|
format: (value, row) => {
|
|
return (row.apps || []).map(d => d.app).join(', ');
|
|
},
|
|
width: '25rem'
|
|
},
|
|
{
|
|
label: '站点',
|
|
fieldname: 'site_count',
|
|
width: 0.25
|
|
}
|
|
],
|
|
filterControls() {
|
|
return [
|
|
{
|
|
type: 'link',
|
|
label: '版本',
|
|
fieldname: 'version',
|
|
options: {
|
|
pagetype: 'Jingrow Version'
|
|
}
|
|
},
|
|
{
|
|
type: 'link',
|
|
label: '标签',
|
|
fieldname: 'tags.tag',
|
|
options: {
|
|
pagetype: 'Jcloud Tag',
|
|
filters: {
|
|
pagetype_name: 'Release Group'
|
|
}
|
|
}
|
|
}
|
|
];
|
|
},
|
|
route(row) {
|
|
return {
|
|
name: 'Release Group Detail',
|
|
params: { name: row.name }
|
|
};
|
|
},
|
|
primaryAction({ listResource: benches, documentResource: server }) {
|
|
return {
|
|
label: '新建站点分组',
|
|
slots: {
|
|
prefix: icon('plus')
|
|
},
|
|
onClick() {
|
|
router.push({
|
|
name: 'Server New Release Group',
|
|
params: { server: server.pg.name }
|
|
});
|
|
}
|
|
};
|
|
}
|
|
}
|
|
},
|
|
getJobsTab('Server'),
|
|
{
|
|
label: '执行',
|
|
icon: icon('play'),
|
|
childrenRoutes: ['Server Play'],
|
|
route: 'plays',
|
|
type: 'list',
|
|
list: {
|
|
pagetype: 'Ansible Play',
|
|
filterControls({ documentResource: server }) {
|
|
return [
|
|
{
|
|
type: 'select',
|
|
label: '服务器',
|
|
fieldname: 'server',
|
|
options: [
|
|
server.pg.name,
|
|
server.pg.database_server,
|
|
server.pg.replication_server
|
|
].filter(Boolean)
|
|
}
|
|
];
|
|
},
|
|
filters: server => {
|
|
return {
|
|
server: [
|
|
'in',
|
|
[
|
|
server.pg.name,
|
|
server.pg.database_server,
|
|
server.pg.replication_server
|
|
].filter(Boolean)
|
|
]
|
|
};
|
|
},
|
|
route(row) {
|
|
return {
|
|
name: 'Server Play',
|
|
params: { id: row.name }
|
|
};
|
|
},
|
|
orderBy: 'creation desc',
|
|
fields: ['server', 'end'],
|
|
columns: [
|
|
{
|
|
label: '执行',
|
|
fieldname: 'play',
|
|
width: 2
|
|
},
|
|
{
|
|
label: '状态',
|
|
fieldname: 'status',
|
|
type: 'Badge',
|
|
width: 0.5
|
|
},
|
|
{
|
|
label: '服务器',
|
|
fieldname: 'server',
|
|
width: 2
|
|
},
|
|
{
|
|
label: '持续时间',
|
|
fieldname: 'duration',
|
|
width: 0.5,
|
|
format(value, row) {
|
|
if (row.job_id === 0 || !row.end) return;
|
|
return duration(value);
|
|
}
|
|
},
|
|
{
|
|
label: '',
|
|
fieldname: 'creation',
|
|
type: 'Timestamp',
|
|
align: 'right'
|
|
}
|
|
]
|
|
}
|
|
},
|
|
{
|
|
label: '操作',
|
|
icon: icon('sliders'),
|
|
route: 'actions',
|
|
type: 'Component',
|
|
component: ServerActions,
|
|
props: server => {
|
|
return { server: server.pg.name };
|
|
}
|
|
},
|
|
tagTab()
|
|
]
|
|
},
|
|
routes: [
|
|
{
|
|
name: 'Server Job',
|
|
path: 'jobs/:id',
|
|
component: () => import('../pages/JobPage.vue')
|
|
},
|
|
{
|
|
name: 'Server Play',
|
|
path: 'plays/:id',
|
|
component: () => import('../pages/PlayPage.vue')
|
|
}
|
|
]
|
|
}; |