444 lines
12 KiB
Vue
444 lines
12 KiB
Vue
<template>
|
|
<div>
|
|
<DismissableBanner
|
|
v-if="$releaseGroup.pg.eol_versions.includes($releaseGroup.pg.version)"
|
|
class="col-span-1 lg:col-span-2"
|
|
title="您的站点使用的是已终止支持的版本。请升级到最新版本以获取最新功能和安全更新。"
|
|
:id="`${$releaseGroup.name}-eol`"
|
|
type="gray"
|
|
>
|
|
<Button
|
|
class="ml-auto"
|
|
variant="outline"
|
|
link="https://jingrow.com/docs/sites/version-upgrade"
|
|
>
|
|
立即升级
|
|
</Button>
|
|
</DismissableBanner>
|
|
<ObjectList class="mt-3" :options="listOptions" />
|
|
<Dialog
|
|
v-model="showAppVersionDialog"
|
|
:options="{
|
|
title: `${$releaseGroup.getAppVersions.params?.args.bench} 中的应用`,
|
|
size: '6xl',
|
|
}"
|
|
>
|
|
<template #body-content>
|
|
<ObjectList :options="appVersionOptions" />
|
|
</template>
|
|
</Dialog>
|
|
</div>
|
|
</template>
|
|
<script lang="jsx">
|
|
import Badge from '@/components/global/Badge.vue';
|
|
import { createResource, getCachedDocumentResource, Tooltip } from 'jingrow-ui';
|
|
import { defineAsyncComponent, h } from 'vue';
|
|
import { toast } from 'vue-sonner';
|
|
import ActionButton from '../components/ActionButton.vue';
|
|
import SSHCertificateDialog from '../components/group/SSHCertificateDialog.vue';
|
|
import ObjectList from '../components/ObjectList.vue';
|
|
import {
|
|
getSitesTabColumns,
|
|
sitesTabRoute,
|
|
siteTabFilterControls,
|
|
} from '../objects/common';
|
|
import { confirmDialog, icon, renderDialog } from '../utils/components';
|
|
import { getToastErrorMessage } from '../utils/toast';
|
|
import DismissableBanner from '../components/DismissableBanner.vue';
|
|
|
|
export default {
|
|
name: 'ReleaseGroupBenchSites',
|
|
props: ['releaseGroup'],
|
|
components: { ObjectList, DismissableBanner },
|
|
data() {
|
|
return {
|
|
showAppVersionDialog: false,
|
|
sitesGroupedByBench: [],
|
|
};
|
|
},
|
|
resources: {
|
|
benches() {
|
|
return {
|
|
type: 'list',
|
|
pagetype: 'Bench',
|
|
filters: {
|
|
group: this.$releaseGroup.name,
|
|
skip_team_filter_for_system_user_and_support_agent: true,
|
|
},
|
|
fields: ['name', 'status'],
|
|
orderBy: 'creation desc',
|
|
pageLength: 99999,
|
|
auto: true,
|
|
onSuccess() {
|
|
this.$resources.sites.fetch();
|
|
},
|
|
};
|
|
},
|
|
sites() {
|
|
return {
|
|
type: 'list',
|
|
pagetype: 'Site',
|
|
filters: {
|
|
group: this.$releaseGroup.name,
|
|
skip_team_filter_for_system_user_and_support_agent: true,
|
|
},
|
|
fields: [
|
|
'name',
|
|
'status',
|
|
'bench',
|
|
'host_name',
|
|
'plan.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',
|
|
],
|
|
orderBy: 'creation desc, bench desc',
|
|
pageLength: 99999,
|
|
transform(data) {
|
|
return this.groupSitesByBench(data);
|
|
},
|
|
auto: false,
|
|
};
|
|
},
|
|
},
|
|
computed: {
|
|
listOptions() {
|
|
return {
|
|
list: this.$resources.sites,
|
|
groupHeader: ({ group: bench }) => {
|
|
if (!bench?.status) return;
|
|
|
|
const options = this.benchOptions(bench);
|
|
const IconHash = icon('hash', 'w-3 h-3');
|
|
const IconStar = icon('star', 'w-3 h-3');
|
|
return (
|
|
<div class="flex items-center">
|
|
<Tooltip text="查看工作台详情">
|
|
<a
|
|
class="cursor-pointer text-base font-medium leading-6 text-gray-900"
|
|
href={`/dashboard/benches/${bench.name}`}
|
|
>
|
|
{bench.group}
|
|
</a>
|
|
</Tooltip>
|
|
{bench.status != 'Active' ? (
|
|
<Badge class="ml-4" label={bench.status} />
|
|
) : null}
|
|
{bench.has_app_patch_applied && (
|
|
<Tooltip text="此工作台中的应用可能已被修补">
|
|
<a
|
|
class="ml-2 rounded bg-gray-100 p-1 text-gray-700"
|
|
href="https://jingrow.com/docs/benches/app-patches"
|
|
target="_blank"
|
|
>
|
|
<IconHash />
|
|
</a>
|
|
</Tooltip>
|
|
)}
|
|
{bench.has_updated_inplace && (
|
|
<Tooltip text="此工作台已就地更新">
|
|
<a
|
|
class="ml-2 rounded bg-gray-100 p-1 text-gray-700"
|
|
href="https://jingrow.com/docs/in-place-updates"
|
|
target="_blank"
|
|
>
|
|
<IconStar />
|
|
</a>
|
|
</Tooltip>
|
|
)}
|
|
<ActionButton class="ml-auto" options={options} />
|
|
</div>
|
|
);
|
|
},
|
|
emptyStateMessage: this.$releaseGroup.pg.deploy_information.last_deploy
|
|
? '未找到站点'
|
|
: '请先创建部署以开始创建站点',
|
|
columns: getSitesTabColumns(false),
|
|
filterControls: siteTabFilterControls,
|
|
route: sitesTabRoute,
|
|
primaryAction: () => {
|
|
return {
|
|
label: '新建站点',
|
|
slots: {
|
|
prefix: icon('plus', 'w-4 h-4'),
|
|
},
|
|
disabled: !this.$releaseGroup.pg?.deploy_information?.last_deploy,
|
|
route: {
|
|
name: 'Release Group New Site',
|
|
params: { bench: this.releaseGroup },
|
|
},
|
|
};
|
|
},
|
|
};
|
|
},
|
|
appVersionOptions() {
|
|
return {
|
|
columns: [
|
|
{
|
|
label: '应用',
|
|
fieldname: 'app',
|
|
},
|
|
{
|
|
label: '仓库',
|
|
fieldname: 'repository',
|
|
format(value, row) {
|
|
return `${row.repository_owner}/${row.repository}`;
|
|
},
|
|
link: (value, row) => {
|
|
return row.repository_url;
|
|
},
|
|
},
|
|
{
|
|
label: '分支',
|
|
fieldname: 'branch',
|
|
type: 'Badge',
|
|
},
|
|
{
|
|
label: '提交',
|
|
fieldname: 'hash',
|
|
type: 'Badge',
|
|
format(value, row) {
|
|
return value.slice(0, 7);
|
|
},
|
|
link: (value, row) => {
|
|
return `https://github.com/${row.repository_owner}/${row.repository}/commit/${value}`;
|
|
},
|
|
},
|
|
{
|
|
label: '标签',
|
|
fieldname: 'tag',
|
|
type: 'Badge',
|
|
},
|
|
],
|
|
data: () => this.$releaseGroup.getAppVersions.data,
|
|
};
|
|
},
|
|
$releaseGroup() {
|
|
return getCachedDocumentResource('Release Group', this.releaseGroup);
|
|
},
|
|
},
|
|
methods: {
|
|
groupSitesByBench(data) {
|
|
if (!this.$resources.benches.data) return [];
|
|
return this.$resources.benches.data.map((bench) => {
|
|
let sites = (data || []).filter((site) => site.bench === bench.name);
|
|
return {
|
|
...bench,
|
|
collapsed: false,
|
|
group: bench.name,
|
|
rows: sites,
|
|
};
|
|
});
|
|
},
|
|
benchOptions(bench) {
|
|
if (!bench) return [];
|
|
|
|
return [
|
|
{
|
|
label: '在桌面查看',
|
|
condition: () => this.$team?.pg?.is_desk_user,
|
|
onClick: () =>
|
|
window.open(
|
|
`${window.location.protocol}//${window.location.host}/app/bench/${bench.name}`,
|
|
'_blank',
|
|
),
|
|
},
|
|
{
|
|
label: '显示应用',
|
|
onClick: () => {
|
|
toast.promise(
|
|
this.$releaseGroup.getAppVersions
|
|
.submit({ bench: bench.name })
|
|
.then(() => {
|
|
this.showAppVersionDialog = true;
|
|
}),
|
|
{
|
|
loading: '正在获取应用...',
|
|
success: '已获取应用及其版本',
|
|
error: '获取应用失败',
|
|
duration: 1000,
|
|
},
|
|
);
|
|
},
|
|
},
|
|
{
|
|
label: 'SSH 访问',
|
|
condition: () => bench.status === 'Active',
|
|
onClick: () => {
|
|
renderDialog(
|
|
h(SSHCertificateDialog, {
|
|
bench: bench.name,
|
|
releaseGroup: this.$releaseGroup.name,
|
|
}),
|
|
);
|
|
},
|
|
},
|
|
{
|
|
label: '查看日志',
|
|
condition: () => bench.status === 'Active',
|
|
onClick: () => {
|
|
let BenchLogsDialog = defineAsyncComponent(
|
|
() => import('../components/group/BenchLogsDialog.vue'),
|
|
);
|
|
|
|
renderDialog(
|
|
h(BenchLogsDialog, {
|
|
bench: bench.name,
|
|
}),
|
|
);
|
|
},
|
|
},
|
|
{
|
|
label: '更新所有站点',
|
|
condition: () => bench.status === 'Active' && bench.rows.length > 0,
|
|
onClick: () => {
|
|
confirmDialog({
|
|
title: '更新所有站点',
|
|
message: `您确定要将工作台 <b>${bench.name}</b> 中的所有站点更新到最新版本吗?`,
|
|
primaryAction: {
|
|
label: '更新',
|
|
variant: 'solid',
|
|
onClick: ({ hide }) => {
|
|
toast.promise(
|
|
this.runBenchMethod(bench.name, 'update_all_sites'),
|
|
{
|
|
loading: '正在为站点安排更新...',
|
|
success: () => {
|
|
hide();
|
|
return '站点已安排更新';
|
|
},
|
|
error: (e) => {
|
|
hide();
|
|
return getToastErrorMessage(
|
|
e,
|
|
'更新站点失败',
|
|
);
|
|
},
|
|
duration: 1000,
|
|
},
|
|
);
|
|
},
|
|
},
|
|
});
|
|
},
|
|
},
|
|
{
|
|
label: '重启工作台',
|
|
condition: () => bench.status === 'Active',
|
|
onClick: () => {
|
|
confirmDialog({
|
|
title: '重启工作台',
|
|
message: `您确定要重启工作台 <b>${bench.name}</b> 吗?`,
|
|
primaryAction: {
|
|
label: '重启',
|
|
variant: 'solid',
|
|
theme: 'red',
|
|
onClick: ({ hide }) => {
|
|
toast.promise(this.runBenchMethod(bench.name, 'restart'), {
|
|
loading: '正在重启工作台...',
|
|
success: () => {
|
|
hide();
|
|
return '工作台将很快重启';
|
|
},
|
|
error: (e) => {
|
|
hide();
|
|
return getToastErrorMessage(e, '重启工作台失败');
|
|
},
|
|
duration: 1000,
|
|
});
|
|
},
|
|
},
|
|
});
|
|
},
|
|
},
|
|
{
|
|
label: '重建资源',
|
|
condition: () =>
|
|
bench.status === 'Active' &&
|
|
(Number(this.$releaseGroup.pg.version.split(' ')[1]) > 13 ||
|
|
this.$releaseGroup.pg.version === 'v0.1'),
|
|
onClick: () => {
|
|
confirmDialog({
|
|
title: '重建资源',
|
|
message: `您确定要为工作台 <b>${bench.name}</b> 重建资源吗?`,
|
|
primaryAction: {
|
|
label: '重建',
|
|
variant: 'solid',
|
|
theme: 'red',
|
|
onClick: ({ hide }) => {
|
|
toast.promise(this.runBenchMethod(bench.name, 'rebuild'), {
|
|
loading: '正在重建资源...',
|
|
success: () => {
|
|
hide();
|
|
return '资源将在后台重建。这可能需要几分钟时间。';
|
|
},
|
|
error: (e) => {
|
|
hide();
|
|
return getToastErrorMessage(
|
|
e,
|
|
'重建资源失败',
|
|
);
|
|
},
|
|
duration: 1000,
|
|
});
|
|
},
|
|
},
|
|
});
|
|
},
|
|
},
|
|
{
|
|
label: '归档工作台',
|
|
onClick: () => {
|
|
confirmDialog({
|
|
title: '归档工作台',
|
|
message: `您确定要归档工作台 <b>${bench.name}</b> 吗?`,
|
|
primaryAction: {
|
|
label: '归档',
|
|
variant: 'solid',
|
|
theme: 'red',
|
|
onClick: ({ hide }) => {
|
|
toast.promise(this.runBenchMethod(bench.name, 'archive'), {
|
|
loading: '正在安排工作台归档...',
|
|
success: () => {
|
|
hide();
|
|
return '工作台已安排归档';
|
|
},
|
|
error: (e) =>
|
|
getToastErrorMessage(e, '归档工作台失败'),
|
|
});
|
|
},
|
|
},
|
|
});
|
|
},
|
|
},
|
|
{
|
|
label: '查看进程',
|
|
condition: () => bench.status === 'Active',
|
|
onClick: () => {
|
|
let SupervisorProcessesDialog = defineAsyncComponent(
|
|
() => import('../components/group/SupervisorProcessesDialog.vue'),
|
|
);
|
|
|
|
renderDialog(
|
|
h(SupervisorProcessesDialog, {
|
|
bench: bench.name,
|
|
}),
|
|
);
|
|
},
|
|
},
|
|
];
|
|
},
|
|
runBenchMethod(name, methodName) {
|
|
const method = createResource({
|
|
url: 'jcloud.api.client.run_pg_method',
|
|
});
|
|
return method.submit({
|
|
dt: 'Bench',
|
|
dn: name,
|
|
method: methodName,
|
|
});
|
|
},
|
|
},
|
|
};
|
|
</script> |