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