120 lines
3.5 KiB
TypeScript
120 lines
3.5 KiB
TypeScript
import { provisioningStates, type ProvisioningAction } from './provisioning.js';
|
|
import type { ProvisioningDeployment } from './provisioning-detail.js';
|
|
import type { ProvisioningSource } from './provisioning-detail.js';
|
|
|
|
export const provisioningListStatuses = [
|
|
...provisioningStates,
|
|
// Retain the combined filter for existing links and API clients.
|
|
'pending',
|
|
'attention'
|
|
] as const;
|
|
export const provisioningListSources = [
|
|
'manual',
|
|
'campaign',
|
|
'beta',
|
|
'maintenance',
|
|
'recovery',
|
|
'autoscaling',
|
|
'rebalance',
|
|
'system'
|
|
] as const;
|
|
export type ProvisioningListSource = (typeof provisioningListSources)[number];
|
|
export const provisioningSourceLabels: Record<ProvisioningListSource, string> = {
|
|
manual: 'Manual',
|
|
campaign: 'Campaign',
|
|
beta: 'Beta',
|
|
maintenance: 'Maintenance',
|
|
recovery: 'Recovery',
|
|
autoscaling: 'Autoscaling',
|
|
rebalance: 'Rebalance',
|
|
system: 'System'
|
|
};
|
|
export const provisioningSourceKinds = {
|
|
campaign: ['campaign'],
|
|
beta: ['beta'],
|
|
maintenance: ['maintenance'],
|
|
recovery: ['watchdog'],
|
|
autoscaling: ['autoscaling'],
|
|
rebalance: ['rebalance']
|
|
} as const satisfies Record<
|
|
Exclude<ProvisioningListSource, 'manual' | 'system'>,
|
|
readonly ProvisioningSource['kind'][]
|
|
>;
|
|
|
|
/** Record titles and incident summaries belong in expanded details, not the source column. */
|
|
export function provisioningSourceLabel(
|
|
row: Pick<ProvisioningListRow, 'sources' | 'triggered_by'>
|
|
): string {
|
|
const kind = row.sources?.[0]?.kind;
|
|
return provisioningSourceLabels[
|
|
kind === 'watchdog'
|
|
? 'recovery'
|
|
: (kind ?? (row.triggered_by === 'ui' ? 'manual' : 'system'))
|
|
];
|
|
}
|
|
|
|
export function provisioningMatchesSource(
|
|
row: Pick<ProvisioningListRow, 'sources' | 'triggered_by'>,
|
|
source: ProvisioningListSource
|
|
): boolean {
|
|
const sources = row.sources ?? [];
|
|
if (source === 'manual' || source === 'system')
|
|
return (
|
|
!sources.length &&
|
|
(source === 'manual' ? row.triggered_by === 'ui' : row.triggered_by !== 'ui')
|
|
);
|
|
const kinds: readonly ProvisioningSource['kind'][] = provisioningSourceKinds[source];
|
|
return sources.some((item) => kinds.includes(item.kind));
|
|
}
|
|
|
|
export type ProvisioningListRow = Pick<
|
|
ProvisioningDeployment,
|
|
| 'id'
|
|
| 'server'
|
|
| 'server_deleted_at'
|
|
| 'action'
|
|
| 'state'
|
|
| 'stage'
|
|
| 'started_at'
|
|
| 'finished_at'
|
|
| 'triggered_by'
|
|
| 'requested_by'
|
|
| 'error'
|
|
| 'warnings'
|
|
| 'server_exists'
|
|
> & {
|
|
canCancelRemoval: number;
|
|
canCancel?: number;
|
|
sources?: ProvisioningSource[];
|
|
phase?: string | null;
|
|
phaseStartedAt?: string | null;
|
|
waitCode?: string | null;
|
|
waitMessage?: string | null;
|
|
};
|
|
|
|
export interface ProvisioningListOptions {
|
|
view?: 'active' | 'history' | 'all';
|
|
action?: ProvisioningAction;
|
|
status?: (typeof provisioningListStatuses)[number];
|
|
source?: ProvisioningListSource;
|
|
server?: string;
|
|
limit?: number;
|
|
offset?: number;
|
|
}
|
|
|
|
export interface ProvisioningListPage {
|
|
deployments: ProvisioningListRow[];
|
|
total: number;
|
|
needsReview: number;
|
|
limit: number;
|
|
offset: number;
|
|
}
|
|
|
|
export function provisioningPageSize(value: number): number {
|
|
return Number.isFinite(value) ? Math.min(100, Math.max(1, Math.floor(value) || 25)) : 25;
|
|
}
|
|
|
|
export function provisioningPageOffset(value: number): number {
|
|
return Number.isFinite(value) ? Math.min(1_000_000, Math.max(0, Math.floor(value))) : 0;
|
|
}
|