623 lines
23 KiB
TypeScript
623 lines
23 KiB
TypeScript
import * as plugins from '../plugins.js';
|
|
import * as shared from './shared/index.js';
|
|
import * as appstate from '../appstate.js';
|
|
import * as interfaces from '../../ts_interfaces/index.js';
|
|
import { BackendExecutionEnvironment } from '../environments/backend-environment.js';
|
|
import {
|
|
DeesElement,
|
|
customElement,
|
|
html,
|
|
state,
|
|
css,
|
|
cssManager,
|
|
type TemplateResult,
|
|
} from '@design.estate/dees-element';
|
|
|
|
// ============================================================================
|
|
// Data transformation helpers
|
|
// Maps backend data shapes to @serve.zone/catalog component interfaces
|
|
// ============================================================================
|
|
|
|
function formatBytes(bytes: number): string {
|
|
if (!bytes || bytes === 0) return '0 B';
|
|
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
const k = 1024;
|
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
const value = bytes / Math.pow(k, i);
|
|
return `${value.toFixed(1)} ${units[i]}`;
|
|
}
|
|
|
|
function parseImageString(image: string): { repository: string; tag: string } {
|
|
const lastColon = image.lastIndexOf(':');
|
|
const lastSlash = image.lastIndexOf('/');
|
|
if (lastColon > lastSlash && lastColon > 0) {
|
|
return {
|
|
repository: image.substring(0, lastColon),
|
|
tag: image.substring(lastColon + 1),
|
|
};
|
|
}
|
|
return { repository: image, tag: 'latest' };
|
|
}
|
|
|
|
function mapStatus(status: string): 'running' | 'stopped' | 'starting' | 'error' {
|
|
switch (status) {
|
|
case 'running': return 'running';
|
|
case 'starting': return 'starting';
|
|
case 'failed': return 'error';
|
|
case 'stopped':
|
|
case 'stopping':
|
|
default: return 'stopped';
|
|
}
|
|
}
|
|
|
|
function toServiceDetail(service: interfaces.data.IService) {
|
|
const parsed = parseImageString(service.image);
|
|
return {
|
|
name: service.name,
|
|
status: mapStatus(service.status),
|
|
image: service.image,
|
|
port: service.port,
|
|
domain: service.domain || null,
|
|
containerId: service.containerID || '',
|
|
created: service.createdAt ? new Date(service.createdAt).toLocaleString() : '-',
|
|
updated: service.updatedAt ? new Date(service.updatedAt).toLocaleString() : '-',
|
|
registry: service.useOneboxRegistry ? 'Onebox Registry' : (service.registry || 'Docker Hub'),
|
|
repository: service.registryRepository || parsed.repository,
|
|
tag: service.registryImageTag || parsed.tag,
|
|
};
|
|
}
|
|
|
|
function toServiceStats(stats: interfaces.data.IContainerStats) {
|
|
return {
|
|
cpu: stats.cpuPercent,
|
|
memory: formatBytes(stats.memoryUsed),
|
|
memoryLimit: formatBytes(stats.memoryLimit),
|
|
networkIn: formatBytes(stats.networkRx),
|
|
networkOut: formatBytes(stats.networkTx),
|
|
};
|
|
}
|
|
|
|
function parseLogs(logs: any): Array<{ timestamp: string; message: string; level?: string }> {
|
|
if (Array.isArray(logs)) {
|
|
return logs.map((entry: any) => {
|
|
const ts = entry.timestamp
|
|
? (typeof entry.timestamp === 'number' ? new Date(entry.timestamp).toISOString() : String(entry.timestamp))
|
|
: new Date().toISOString();
|
|
const message = entry.message || String(entry);
|
|
const level = entry.level || 'info';
|
|
return { timestamp: ts, message, level };
|
|
});
|
|
}
|
|
if (typeof logs === 'string' && logs.trim()) {
|
|
return logs.split('\n').filter((line: string) => line.trim()).map((line: string) => {
|
|
const match = line.match(/^(\d{4}-\d{2}-\d{2}T[\d:.]+Z?)\s+(.*)/);
|
|
const timestamp = match ? match[1] : new Date().toISOString();
|
|
const message = match ? match[2] : line;
|
|
const msgLower = message.toLowerCase();
|
|
const level = msgLower.includes('error') || msgLower.includes('fatal')
|
|
? 'error'
|
|
: msgLower.includes('warn')
|
|
? 'warn'
|
|
: 'info';
|
|
return { timestamp, message, level };
|
|
});
|
|
}
|
|
return [];
|
|
}
|
|
|
|
const defaultStats = { cpu: 0, memory: '0 B', memoryLimit: '0 B', networkIn: '0 B', networkOut: '0 B' };
|
|
|
|
@customElement('ob-view-services')
|
|
export class ObViewServices extends DeesElement {
|
|
@state()
|
|
accessor servicesState: appstate.IServicesState = {
|
|
services: [],
|
|
currentService: null,
|
|
currentServiceLogs: [],
|
|
currentServiceStats: null,
|
|
platformServices: [],
|
|
currentPlatformService: null,
|
|
currentPlatformServiceStats: null,
|
|
currentPlatformServiceLogs: [],
|
|
};
|
|
|
|
@state()
|
|
accessor backupsState: appstate.IBackupsState = {
|
|
backups: [],
|
|
schedules: [],
|
|
};
|
|
|
|
@state()
|
|
accessor currentView: 'list' | 'create' | 'detail' | 'backups' | 'platform-detail' = 'list';
|
|
|
|
@state()
|
|
accessor selectedServiceName: string = '';
|
|
|
|
@state()
|
|
accessor selectedPlatformType: string = '';
|
|
|
|
@state()
|
|
accessor workspaceOpen: boolean = false;
|
|
|
|
@state()
|
|
accessor pendingTemplate: any = null;
|
|
|
|
constructor() {
|
|
super();
|
|
|
|
const servicesSub = appstate.servicesStatePart
|
|
.select((s) => s)
|
|
.subscribe((newState) => {
|
|
this.servicesState = newState;
|
|
});
|
|
this.rxSubscriptions.push(servicesSub);
|
|
|
|
const backupsSub = appstate.backupsStatePart
|
|
.select((s) => s)
|
|
.subscribe((newState) => {
|
|
this.backupsState = newState;
|
|
});
|
|
this.rxSubscriptions.push(backupsSub);
|
|
}
|
|
|
|
public static styles = [
|
|
cssManager.defaultStyles,
|
|
shared.viewHostCss,
|
|
css`
|
|
.page-actions {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.deploy-button {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 10px 20px;
|
|
background: ${cssManager.bdTheme('#18181b', '#fafafa')};
|
|
color: ${cssManager.bdTheme('#fafafa', '#18181b')};
|
|
border: none;
|
|
border-radius: 6px;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
cursor: pointer;
|
|
transition: opacity 200ms ease;
|
|
}
|
|
|
|
.deploy-button:hover {
|
|
opacity: 0.9;
|
|
}
|
|
|
|
.deploy-button svg {
|
|
width: 16px;
|
|
height: 16px;
|
|
}
|
|
|
|
:host(.workspace-mode) {
|
|
max-width: none;
|
|
padding: 0;
|
|
height: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
:host(.workspace-mode) ob-sectionheading {
|
|
display: none;
|
|
}
|
|
`,
|
|
];
|
|
|
|
async connectedCallback() {
|
|
super.connectedCallback();
|
|
await Promise.all([
|
|
appstate.servicesStatePart.dispatchAction(appstate.fetchServicesAction, null),
|
|
appstate.servicesStatePart.dispatchAction(appstate.fetchPlatformServicesAction, null),
|
|
]);
|
|
|
|
// If a platform service was selected from the dashboard, navigate to its detail
|
|
const state = appstate.servicesStatePart.getState();
|
|
if (state.currentPlatformService) {
|
|
const type = state.currentPlatformService.type;
|
|
appstate.servicesStatePart.setState({
|
|
...appstate.servicesStatePart.getState(),
|
|
currentPlatformService: null,
|
|
});
|
|
this.navigateToPlatformDetail(type);
|
|
}
|
|
|
|
// If an app template was selected from the App Store, switch to create view
|
|
const uiState = appstate.uiStatePart.getState();
|
|
if (uiState.pendingAppTemplate) {
|
|
this.pendingTemplate = uiState.pendingAppTemplate;
|
|
appstate.uiStatePart.setState({
|
|
...appstate.uiStatePart.getState(),
|
|
pendingAppTemplate: undefined,
|
|
});
|
|
this.currentView = 'create';
|
|
}
|
|
}
|
|
|
|
public render(): TemplateResult {
|
|
switch (this.currentView) {
|
|
case 'create':
|
|
return this.renderCreateView();
|
|
case 'detail':
|
|
return this.renderDetailView();
|
|
case 'backups':
|
|
return this.renderBackupsView();
|
|
case 'platform-detail':
|
|
return this.renderPlatformDetailView();
|
|
default:
|
|
return this.renderListView();
|
|
}
|
|
}
|
|
|
|
private renderListView(): TemplateResult {
|
|
const mappedServices = this.servicesState.services.map((s) => ({
|
|
name: s.name,
|
|
image: s.image,
|
|
domain: s.domain || null,
|
|
status: mapStatus(s.status),
|
|
}));
|
|
const displayStatus = (status: string) => {
|
|
switch (status) {
|
|
case 'running': return 'Running';
|
|
case 'stopped': return 'Stopped';
|
|
case 'starting': return 'Starting...';
|
|
case 'stopping': return 'Stopping...';
|
|
case 'failed': return 'Failed';
|
|
case 'not-deployed': return 'Not Deployed';
|
|
default: return status;
|
|
}
|
|
};
|
|
const mappedPlatformServices = this.servicesState.platformServices.map((ps) => ({
|
|
name: ps.displayName,
|
|
status: displayStatus(ps.status),
|
|
running: ps.status === 'running',
|
|
type: ps.type,
|
|
}));
|
|
return html`
|
|
<ob-sectionheading>Services</ob-sectionheading>
|
|
<div class="page-actions">
|
|
<button class="deploy-button" @click=${() => { this.currentView = 'create'; }}>
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<line x1="12" y1="5" x2="12" y2="19"></line>
|
|
<line x1="5" y1="12" x2="19" y2="12"></line>
|
|
</svg>
|
|
Deploy Service
|
|
</button>
|
|
</div>
|
|
<sz-services-list-view
|
|
.services=${mappedServices}
|
|
@service-click=${(e: CustomEvent) => {
|
|
this.selectedServiceName = e.detail.name || e.detail.service?.name;
|
|
appstate.servicesStatePart.dispatchAction(appstate.fetchServiceAction, {
|
|
name: this.selectedServiceName,
|
|
});
|
|
appstate.servicesStatePart.dispatchAction(appstate.fetchServiceLogsAction, {
|
|
name: this.selectedServiceName,
|
|
});
|
|
appstate.servicesStatePart.dispatchAction(appstate.fetchServiceStatsAction, {
|
|
name: this.selectedServiceName,
|
|
});
|
|
this.currentView = 'detail';
|
|
}}
|
|
@service-action=${(e: CustomEvent) => this.handleServiceAction(e)}
|
|
></sz-services-list-view>
|
|
<ob-sectionheading style="margin-top: 32px;">Platform Services</ob-sectionheading>
|
|
<div style="max-width: 500px;">
|
|
<sz-platform-services-card
|
|
.services=${mappedPlatformServices}
|
|
@service-click=${(e: CustomEvent) => {
|
|
const type = e.detail.type || this.servicesState.platformServices.find(
|
|
(ps) => ps.displayName === e.detail.name,
|
|
)?.type;
|
|
if (type) {
|
|
this.navigateToPlatformDetail(type);
|
|
}
|
|
}}
|
|
></sz-platform-services-card>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
private async deployFromTemplate(template: any): Promise<void> {
|
|
const name = template.id || template.name.toLowerCase().replace(/[^a-z0-9-]/g, '-');
|
|
const envVars: Record<string, string> = {};
|
|
if (template.envVars) {
|
|
for (const ev of template.envVars) {
|
|
if (ev.key && ev.value) envVars[ev.key] = ev.value;
|
|
}
|
|
}
|
|
const serviceConfig: interfaces.data.IServiceCreate = {
|
|
name,
|
|
image: template.image,
|
|
port: template.port || 80,
|
|
envVars,
|
|
enableMongoDB: template.enableMongoDB || false,
|
|
enableS3: template.enableS3 || false,
|
|
enableClickHouse: template.enableClickHouse || false,
|
|
};
|
|
await appstate.servicesStatePart.dispatchAction(appstate.createServiceAction, {
|
|
config: serviceConfig,
|
|
});
|
|
this.pendingTemplate = null;
|
|
this.currentView = 'list';
|
|
}
|
|
|
|
private renderCreateView(): TemplateResult {
|
|
// If we have a pending app template from the App Store, show a quick-deploy confirmation
|
|
if (this.pendingTemplate) {
|
|
const t = this.pendingTemplate;
|
|
return html`
|
|
<ob-sectionheading>Deploy ${t.name}</ob-sectionheading>
|
|
<div style="max-width: 600px; margin: 0 auto;">
|
|
<div style="background: var(--ci-shade-1, #09090b); border: 1px solid var(--ci-shade-2, #27272a); border-radius: 8px; padding: 24px; margin-bottom: 16px;">
|
|
<h3 style="margin: 0 0 8px 0; font-size: 18px;">${t.name}</h3>
|
|
<p style="margin: 0 0 16px 0; color: var(--ci-shade-5, #a1a1aa); font-size: 14px;">${t.description}</p>
|
|
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px; font-size: 13px;">
|
|
<div><span style="color: var(--ci-shade-5, #a1a1aa);">Image:</span> <strong>${t.image}</strong></div>
|
|
<div><span style="color: var(--ci-shade-5, #a1a1aa);">Port:</span> <strong>${t.port}</strong></div>
|
|
<div><span style="color: var(--ci-shade-5, #a1a1aa);">Service Name:</span> <strong>${t.id}</strong></div>
|
|
<div><span style="color: var(--ci-shade-5, #a1a1aa);">Category:</span> <strong>${t.category}</strong></div>
|
|
</div>
|
|
${t.enableMongoDB || t.enableS3 || t.enableClickHouse ? html`
|
|
<div style="margin-top: 12px; font-size: 13px; color: var(--ci-shade-5, #a1a1aa);">
|
|
Platform Services:
|
|
${t.enableMongoDB ? html`<span style="margin-right: 8px;">MongoDB</span>` : ''}
|
|
${t.enableS3 ? html`<span style="margin-right: 8px;">S3</span>` : ''}
|
|
${t.enableClickHouse ? html`<span>ClickHouse</span>` : ''}
|
|
</div>
|
|
` : ''}
|
|
</div>
|
|
<div style="display: flex; gap: 12px; justify-content: flex-end;">
|
|
<button class="deploy-button" style="background: transparent; border: 1px solid var(--ci-shade-2, #27272a); color: inherit;" @click=${() => { this.pendingTemplate = null; this.currentView = 'list'; }}>Cancel</button>
|
|
<button class="deploy-button" @click=${() => this.deployFromTemplate(t)}>Deploy ${t.name}</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
return html`
|
|
<ob-sectionheading>Create Service</ob-sectionheading>
|
|
<sz-service-create-view
|
|
.registries=${[]}
|
|
@create-service=${async (e: CustomEvent) => {
|
|
const formConfig = e.detail;
|
|
const serviceConfig: interfaces.data.IServiceCreate = {
|
|
name: formConfig.name,
|
|
image: formConfig.image,
|
|
port: formConfig.ports?.[0]?.containerPort
|
|
? parseInt(formConfig.ports[0].containerPort, 10)
|
|
: 80,
|
|
envVars: formConfig.envVars?.reduce(
|
|
(acc: Record<string, string>, ev: { key: string; value: string }) => {
|
|
if (ev.key) acc[ev.key] = ev.value;
|
|
return acc;
|
|
},
|
|
{} as Record<string, string>,
|
|
),
|
|
enableMongoDB: formConfig.enableMongoDB || false,
|
|
enableS3: formConfig.enableS3 || false,
|
|
enableClickHouse: formConfig.enableClickHouse || false,
|
|
};
|
|
await appstate.servicesStatePart.dispatchAction(appstate.createServiceAction, {
|
|
config: serviceConfig,
|
|
});
|
|
this.currentView = 'list';
|
|
}}
|
|
@cancel=${() => {
|
|
this.currentView = 'list';
|
|
}}
|
|
></sz-service-create-view>
|
|
`;
|
|
}
|
|
|
|
private renderDetailView(): TemplateResult {
|
|
const service = this.servicesState.currentService;
|
|
const transformedService = service ? toServiceDetail(service) : null;
|
|
const transformedStats = this.servicesState.currentServiceStats
|
|
? toServiceStats(this.servicesState.currentServiceStats)
|
|
: defaultStats;
|
|
const transformedLogs = parseLogs(this.servicesState.currentServiceLogs);
|
|
|
|
return html`
|
|
<ob-sectionheading>Service Details</ob-sectionheading>
|
|
<sz-service-detail-view
|
|
.service=${transformedService}
|
|
.logs=${transformedLogs}
|
|
.stats=${transformedStats}
|
|
@back=${() => {
|
|
this.currentView = 'list';
|
|
}}
|
|
@service-action=${(e: CustomEvent) => this.handleServiceAction(e)}
|
|
@request-workspace=${async (e: CustomEvent) => {
|
|
const name = e.detail?.service?.name || this.selectedServiceName;
|
|
const identity = appstate.loginStatePart.getState().identity;
|
|
if (!name || !identity) return;
|
|
try {
|
|
const env = new BackendExecutionEnvironment(name, identity);
|
|
await env.init();
|
|
const detailView = this.shadowRoot?.querySelector('sz-service-detail-view') as any;
|
|
if (detailView) {
|
|
detailView.workspaceEnvironment = env;
|
|
}
|
|
this.workspaceOpen = true;
|
|
this.classList.add('workspace-mode');
|
|
} catch (err) {
|
|
console.error('Failed to open workspace:', err);
|
|
}
|
|
}}
|
|
@back=${() => {
|
|
this.workspaceOpen = false;
|
|
this.classList.remove('workspace-mode');
|
|
this.currentView = 'list';
|
|
}}
|
|
></sz-service-detail-view>
|
|
`;
|
|
}
|
|
|
|
private renderBackupsView(): TemplateResult {
|
|
return html`
|
|
<ob-sectionheading>Backups</ob-sectionheading>
|
|
<sz-services-backups-view
|
|
.schedules=${this.backupsState.schedules}
|
|
.backups=${this.backupsState.backups}
|
|
@create-schedule=${(e: CustomEvent) => {
|
|
appstate.backupsStatePart.dispatchAction(appstate.createScheduleAction, {
|
|
config: e.detail,
|
|
});
|
|
}}
|
|
@run-now=${(e: CustomEvent) => {
|
|
appstate.backupsStatePart.dispatchAction(appstate.triggerScheduleAction, {
|
|
scheduleId: e.detail.scheduleId,
|
|
});
|
|
}}
|
|
@delete-backup=${(e: CustomEvent) => {
|
|
appstate.backupsStatePart.dispatchAction(appstate.deleteBackupAction, {
|
|
backupId: e.detail.backupId,
|
|
});
|
|
}}
|
|
></sz-services-backups-view>
|
|
`;
|
|
}
|
|
|
|
private navigateToPlatformDetail(type: string): void {
|
|
// Reset to list first to force fresh DOM for dees-chart-log
|
|
this.currentView = 'list';
|
|
this.selectedPlatformType = type;
|
|
|
|
// Clear previous stats/logs before fetching new ones
|
|
appstate.servicesStatePart.setState({
|
|
...appstate.servicesStatePart.getState(),
|
|
currentPlatformServiceStats: null,
|
|
currentPlatformServiceLogs: [],
|
|
});
|
|
|
|
// Fetch stats and logs for this platform service
|
|
const serviceType = type as interfaces.data.TPlatformServiceType;
|
|
appstate.servicesStatePart.dispatchAction(appstate.fetchPlatformServiceStatsAction, { serviceType });
|
|
appstate.servicesStatePart.dispatchAction(appstate.fetchPlatformServiceLogsAction, { serviceType });
|
|
|
|
// Switch to detail view on next microtask (ensures fresh DOM)
|
|
requestAnimationFrame(() => {
|
|
this.currentView = 'platform-detail';
|
|
});
|
|
}
|
|
|
|
private renderPlatformDetailView(): TemplateResult {
|
|
const platformService = this.servicesState.platformServices.find(
|
|
(ps) => ps.type === this.selectedPlatformType,
|
|
);
|
|
const stats = this.servicesState.currentPlatformServiceStats;
|
|
const metrics = {
|
|
cpu: stats ? Math.round(stats.cpuPercent) : 0,
|
|
memory: stats ? Math.round(stats.memoryPercent) : 0,
|
|
storage: 0,
|
|
connections: undefined as number | undefined,
|
|
};
|
|
|
|
// Real service info per platform type
|
|
const serviceInfo: Record<string, { host: string; port: number; version: string; config: Record<string, any> }> = {
|
|
mongodb: { host: 'onebox-mongodb', port: 27017, version: '4.4', config: { engine: 'WiredTiger', authEnabled: true } },
|
|
minio: { host: 'onebox-minio', port: 9000, version: 'latest', config: { consolePort: 9001, region: 'us-east-1' } },
|
|
clickhouse: { host: 'onebox-clickhouse', port: 8123, version: 'latest', config: { nativePort: 9000, httpPort: 8123 } },
|
|
caddy: { host: 'onebox-caddy', port: 80, version: '2-alpine', config: { httpsPort: 443, adminApi: 2019 } },
|
|
};
|
|
const info = platformService
|
|
? serviceInfo[platformService.type] || { host: 'unknown', port: 0, version: '', config: {} }
|
|
: { host: '', port: 0, version: '', config: {} };
|
|
|
|
// Map backend status to catalog-compatible status
|
|
const mapPlatformStatus = (status: string): 'running' | 'stopped' | 'error' => {
|
|
switch (status) {
|
|
case 'running': return 'running';
|
|
case 'failed': return 'error';
|
|
case 'starting':
|
|
case 'stopping':
|
|
case 'stopped':
|
|
case 'not-deployed':
|
|
default: return 'stopped';
|
|
}
|
|
};
|
|
|
|
return html`
|
|
<ob-sectionheading>Platform Service</ob-sectionheading>
|
|
<div class="page-actions" style="justify-content: flex-start;">
|
|
<button class="deploy-button" style="background: transparent; border: 1px solid var(--ci-shade-2, #27272a); color: inherit;" @click=${() => { this.currentView = 'list'; }}>
|
|
← Back to Services
|
|
</button>
|
|
</div>
|
|
<sz-platform-service-detail-view
|
|
.service=${platformService
|
|
? {
|
|
id: platformService.type,
|
|
name: platformService.displayName,
|
|
type: platformService.type,
|
|
status: mapPlatformStatus(platformService.status),
|
|
version: info.version,
|
|
host: info.host,
|
|
port: info.port,
|
|
config: info.config,
|
|
metrics,
|
|
}
|
|
: null}
|
|
.logs=${this.servicesState.currentPlatformServiceLogs.map((log) => ({
|
|
timestamp: new Date(log.timestamp).toISOString(),
|
|
level: log.level,
|
|
message: log.message,
|
|
}))}
|
|
@back=${() => {
|
|
this.currentView = 'list';
|
|
}}
|
|
@start=${async () => {
|
|
await appstate.servicesStatePart.dispatchAction(appstate.startPlatformServiceAction, {
|
|
serviceType: this.selectedPlatformType as interfaces.data.TPlatformServiceType,
|
|
});
|
|
// Refresh stats after starting
|
|
appstate.servicesStatePart.dispatchAction(appstate.fetchPlatformServiceStatsAction, {
|
|
serviceType: this.selectedPlatformType as interfaces.data.TPlatformServiceType,
|
|
});
|
|
}}
|
|
@stop=${async () => {
|
|
await appstate.servicesStatePart.dispatchAction(appstate.stopPlatformServiceAction, {
|
|
serviceType: this.selectedPlatformType as interfaces.data.TPlatformServiceType,
|
|
});
|
|
}}
|
|
@restart=${async () => {
|
|
await appstate.servicesStatePart.dispatchAction(appstate.stopPlatformServiceAction, {
|
|
serviceType: this.selectedPlatformType as interfaces.data.TPlatformServiceType,
|
|
});
|
|
await appstate.servicesStatePart.dispatchAction(appstate.startPlatformServiceAction, {
|
|
serviceType: this.selectedPlatformType as interfaces.data.TPlatformServiceType,
|
|
});
|
|
appstate.servicesStatePart.dispatchAction(appstate.fetchPlatformServiceStatsAction, {
|
|
serviceType: this.selectedPlatformType as interfaces.data.TPlatformServiceType,
|
|
});
|
|
}}
|
|
></sz-platform-service-detail-view>
|
|
`;
|
|
}
|
|
|
|
private async handleServiceAction(e: CustomEvent) {
|
|
const action = e.detail.action;
|
|
const name = e.detail.service?.name || e.detail.name || this.selectedServiceName;
|
|
switch (action) {
|
|
case 'start':
|
|
await appstate.servicesStatePart.dispatchAction(appstate.startServiceAction, { name });
|
|
break;
|
|
case 'stop':
|
|
await appstate.servicesStatePart.dispatchAction(appstate.stopServiceAction, { name });
|
|
break;
|
|
case 'restart':
|
|
await appstate.servicesStatePart.dispatchAction(appstate.restartServiceAction, { name });
|
|
break;
|
|
case 'delete':
|
|
await appstate.servicesStatePart.dispatchAction(appstate.deleteServiceAction, { name });
|
|
this.currentView = 'list';
|
|
break;
|
|
}
|
|
}
|
|
}
|