feat: volume scopes redesign — replace shared/isolated with 6 scopes
Replace confusing shared/isolated volume modes with explicit scopes: - instance: per-deploy isolated directory - stage: shared within a stage across deploys - project: shared across all stages - project_named: named group within a project - named: global named volume across projects - ephemeral: tmpfs in-memory mount Includes schema migration (shared→project, isolated→instance), backward-compatible deployer resolution, scope metadata API endpoint, and redesigned volume editor UI with scope guide cards and hints.
This commit is contained in:
+8
-3
@@ -20,7 +20,8 @@ import type {
|
||||
StageEnv,
|
||||
StandaloneProxy,
|
||||
ValidationResult,
|
||||
Volume
|
||||
Volume,
|
||||
VolumeScopeInfo
|
||||
} from './types';
|
||||
|
||||
// ── Helpers ─────────────────────────────────────────────────────────
|
||||
@@ -327,7 +328,7 @@ export function listVolumes(projectId: string): Promise<Volume[]> {
|
||||
|
||||
export function createVolume(
|
||||
projectId: string,
|
||||
data: { source: string; target: string; mode?: string }
|
||||
data: { source: string; target: string; scope: string; name?: string; mode?: string }
|
||||
): Promise<Volume> {
|
||||
return post<Volume>(`/api/projects/${projectId}/volumes`, data);
|
||||
}
|
||||
@@ -335,11 +336,15 @@ export function createVolume(
|
||||
export function updateVolume(
|
||||
projectId: string,
|
||||
volId: string,
|
||||
data: { source?: string; target?: string; mode?: string }
|
||||
data: { source?: string; target?: string; scope?: string; name?: string; mode?: string }
|
||||
): Promise<Volume> {
|
||||
return put<Volume>(`/api/projects/${projectId}/volumes/${volId}`, data);
|
||||
}
|
||||
|
||||
export function listVolumeScopes(): Promise<VolumeScopeInfo[]> {
|
||||
return get<VolumeScopeInfo[]>('/api/volumes/scopes');
|
||||
}
|
||||
|
||||
export function deleteVolume(
|
||||
projectId: string,
|
||||
volId: string
|
||||
|
||||
@@ -120,15 +120,16 @@
|
||||
},
|
||||
"volumeEditor": {
|
||||
"title": "Volume Mounts",
|
||||
"description": "Configure volume mounts for containers.",
|
||||
"sharedDesc": "Shared mode uses the source path as-is for all instances.",
|
||||
"isolatedDesc": "Isolated mode appends /{stage}-{tag}/ to the source, giving each instance its own directory.",
|
||||
"description": "Configure volume mounts for containers. Choose a scope to control how volumes are shared between deploys.",
|
||||
"sourceHost": "Source (Host)",
|
||||
"targetContainer": "Target (Container)",
|
||||
"mode": "Mode",
|
||||
"scope": "Scope",
|
||||
"nameColumn": "Name",
|
||||
"namePlaceholder": "e.g. shared-db",
|
||||
"requiresName": "requires name",
|
||||
"noHostPath": "no host path",
|
||||
"tmpfs": "tmpfs (in-memory)",
|
||||
"actions": "Actions",
|
||||
"shared": "Shared",
|
||||
"isolated": "Isolated",
|
||||
"edit": "Edit",
|
||||
"delete": "Delete",
|
||||
"save": "Save",
|
||||
|
||||
@@ -120,15 +120,16 @@
|
||||
},
|
||||
"volumeEditor": {
|
||||
"title": "Тома",
|
||||
"description": "Настройка монтирования томов для контейнеров.",
|
||||
"sharedDesc": "Режим «Общий» использует путь источника как есть для всех экземпляров.",
|
||||
"isolatedDesc": "Режим «Изолированный» добавляет /{stage}-{tag}/ к источнику, создавая свою директорию для каждого экземпляра.",
|
||||
"description": "Настройка монтирования томов для контейнеров. Выберите область видимости для управления общим доступом между развёртываниями.",
|
||||
"sourceHost": "Источник (хост)",
|
||||
"targetContainer": "Цель (контейнер)",
|
||||
"mode": "Режим",
|
||||
"scope": "Область",
|
||||
"nameColumn": "Имя",
|
||||
"namePlaceholder": "напр. shared-db",
|
||||
"requiresName": "требуется имя",
|
||||
"noHostPath": "нет пути на хосте",
|
||||
"tmpfs": "tmpfs (в памяти)",
|
||||
"actions": "Действия",
|
||||
"shared": "Общий",
|
||||
"isolated": "Изолированный",
|
||||
"edit": "Изменить",
|
||||
"delete": "Удалить",
|
||||
"save": "Сохранить",
|
||||
|
||||
+14
-1
@@ -161,17 +161,30 @@ export interface EntityPickerItem {
|
||||
disabledHint?: string;
|
||||
}
|
||||
|
||||
/** Volume scope determines the sharing level. */
|
||||
export type VolumeScope = 'instance' | 'stage' | 'project' | 'project_named' | 'named' | 'ephemeral';
|
||||
|
||||
/** Volume mount configuration for a project. */
|
||||
export interface Volume {
|
||||
id: string;
|
||||
project_id: string;
|
||||
source: string;
|
||||
target: string;
|
||||
mode: 'shared' | 'isolated';
|
||||
mode?: string;
|
||||
scope: VolumeScope;
|
||||
name: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
/** Scope metadata returned by GET /api/volumes/scopes. */
|
||||
export interface VolumeScopeInfo {
|
||||
scope: VolumeScope;
|
||||
description: string;
|
||||
needs_name: boolean;
|
||||
path_example: string;
|
||||
}
|
||||
|
||||
/** Docker daemon health check result. */
|
||||
export interface DockerHealth {
|
||||
connected: boolean;
|
||||
|
||||
Reference in New Issue
Block a user