|
|
|
@@ -10,6 +10,8 @@ import { bindNativeOperation } from './native.js';
|
|
|
|
|
import { workerActivity } from '../pipeline/activity.js';
|
|
|
|
|
import { canCancelCreation } from './cancel-creation.js';
|
|
|
|
|
import { nativeDeployment } from '../backends/native.js';
|
|
|
|
|
import { serverTitle } from '../../ui/server-labels.js';
|
|
|
|
|
import { provisioningPageSize, provisioningPageOffset, type ProvisioningListOptions, type ProvisioningListPage, type ProvisioningListRow } from '../../provisioning-list.js';
|
|
|
|
|
|
|
|
|
|
export function beginDeployment(server: string, action: ProvisioningAction, triggeredBy = 'ui'): string {
|
|
|
|
|
const id = randomUUID();
|
|
|
|
@@ -250,10 +252,54 @@ function preserveOutcome(id: string): void {
|
|
|
|
|
details: { state: row.state, finishedAt: row.finished_at, error: row.error, warnings: warningMessages(row.warnings) } } });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function listDeployments() {
|
|
|
|
|
return db().prepare(`SELECT d.*, (d.server_deleted_at IS NULL AND EXISTS(SELECT 1 FROM servers s WHERE s.name = d.server AND s.destroyed_at IS NULL)) AS server_exists,
|
|
|
|
|
const activeDeployment = "d.server_deleted_at IS NULL AND d.state IN ('queued', 'awaiting-health', 'running', 'waiting')";
|
|
|
|
|
const reviewDeployment = "d.state IN ('failed', 'interrupted', 'warning')";
|
|
|
|
|
const deploymentListColumns = `d.id, d.server, d.server_deleted_at, d.action, d.state, d.stage,
|
|
|
|
|
d.started_at, d.finished_at, d.triggered_by, d.error, d.warnings,
|
|
|
|
|
(d.server_deleted_at IS NULL AND EXISTS(SELECT 1 FROM servers s WHERE s.name = d.server AND s.destroyed_at IS NULL)) AS server_exists,
|
|
|
|
|
(d.server_deleted_at IS NULL AND d.action = 'destroy' AND d.state = 'waiting' AND d.stage != ?) AS canCancelRemoval
|
|
|
|
|
FROM server_deployments d ORDER BY started_at DESC, rowid DESC`).all(REMOVING_SERVER);
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
function deploymentListQuery(options: ProvisioningListOptions) {
|
|
|
|
|
const conditions = [options.view === 'active' ? `(${activeDeployment})`
|
|
|
|
|
: options.view === 'history' ? `NOT (${activeDeployment})` : '1'];
|
|
|
|
|
const parameters: string[] = [];
|
|
|
|
|
if (options.action) { conditions.push('d.action = ?'); parameters.push(options.action); }
|
|
|
|
|
if (options.status === 'attention') conditions.push(reviewDeployment);
|
|
|
|
|
const search = (options.server ?? '').slice(0, 100).toLowerCase();
|
|
|
|
|
if (search) {
|
|
|
|
|
// Read only distinct names to preserve friendly map/lobby searches, including deleted servers.
|
|
|
|
|
const names = (db().prepare('SELECT DISTINCT server FROM server_deployments').all() as { server: string }[])
|
|
|
|
|
.map(row => row.server).filter(name => `${name} ${serverTitle(name)}`.toLowerCase().includes(search));
|
|
|
|
|
conditions.push('d.server IN (SELECT value FROM json_each(?))');
|
|
|
|
|
parameters.push(JSON.stringify(names));
|
|
|
|
|
}
|
|
|
|
|
return { where: conditions.join(' AND '), parameters,
|
|
|
|
|
limit: provisioningPageSize(options.limit ?? 25), offset: provisioningPageOffset(options.offset ?? 0) };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function deploymentListRows(query: ReturnType<typeof deploymentListQuery>, active: boolean): ProvisioningListRow[] {
|
|
|
|
|
return db().prepare(`SELECT ${deploymentListColumns} FROM server_deployments d WHERE ${query.where}
|
|
|
|
|
ORDER BY d.started_at DESC, d.rowid DESC ${active ? '' : 'LIMIT ? OFFSET ?'}`)
|
|
|
|
|
.all(REMOVING_SERVER, ...query.parameters, ...(active ? [] : [query.limit, query.offset])) as ProvisioningListRow[];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** All list reads omit observation snapshots; retained history is always bounded. */
|
|
|
|
|
export function listDeployments(options: ProvisioningListOptions = {}): ProvisioningListRow[] {
|
|
|
|
|
return deploymentListRows(deploymentListQuery(options), options.view === 'active');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function deploymentListPage(options: ProvisioningListOptions = {}): ProvisioningListPage {
|
|
|
|
|
return db().transaction(() => {
|
|
|
|
|
const query = deploymentListQuery(options);
|
|
|
|
|
const deployments = deploymentListRows(query, options.view === 'active');
|
|
|
|
|
const total = options.view === 'active' ? deployments.length : (db().prepare(
|
|
|
|
|
`SELECT COUNT(*) AS total FROM server_deployments d WHERE ${query.where}`
|
|
|
|
|
).get(...query.parameters) as { total: number }).total;
|
|
|
|
|
const { needsReview } = db().prepare(`SELECT COUNT(*) AS needsReview FROM server_deployments d WHERE ${reviewDeployment}`)
|
|
|
|
|
.get() as { needsReview: number };
|
|
|
|
|
return { deployments, total, needsReview, limit: query.limit, offset: query.offset };
|
|
|
|
|
})();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function interruptDeployments(): void {
|
|
|
|
|