2025-04-12 17:39:38 +08:00

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')
}
]
};