forked from getarcaneapp/arcane
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathenvironment.store.svelte.ts
More file actions
155 lines (135 loc) · 5.15 KB
/
environment.store.svelte.ts
File metadata and controls
155 lines (135 loc) · 5.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import { PersistedState } from 'runed';
import { goto, invalidateAll } from '$app/navigation';
import { page } from '$app/state';
import type { Environment } from '$lib/types/environment.type';
import { isEnvironmentOnline } from '$lib/utils/environment-status';
export const LOCAL_DOCKER_ENVIRONMENT_ID = '0';
function getResourceListPage(): string | null {
const routeId = page.route?.id;
if (!routeId) return null;
// Check if route has a dynamic segment (contains [...] pattern)
// and is a resource detail page (not settings, environments management, etc.)
const resourcePrefixes = ['/containers', '/images', '/projects', '/networks', '/volumes'];
for (const prefix of resourcePrefixes) {
// Match routes like /containers/[containerId] or /(app)/containers/[containerId]
// but not /containers or /containers/components/...
const pattern = prefix + '/[';
if (routeId.includes(pattern) && !routeId.includes('/components/')) {
return prefix;
}
}
return null;
}
function createEnvironmentManagementStore() {
const selectedEnvironmentId = new PersistedState<string | null>('selectedEnvironmentId', null);
let _selectedEnvironment = $state<Environment | null>(null);
let _availableEnvironments = $state<Environment[]>([]);
let _initialized = false;
let _initializedWithData = false;
let _resolveReadyPromiseFunction: () => void;
const _readyPromise = new Promise<void>((resolve) => {
_resolveReadyPromiseFunction = resolve;
});
function _updateAvailable(environments: Environment[]): Environment[] {
const sorted = [...environments].sort((a, b) => {
if (a.id === LOCAL_DOCKER_ENVIRONMENT_ID) return -1;
if (b.id === LOCAL_DOCKER_ENVIRONMENT_ID) return 1;
return 0;
});
_availableEnvironments = sorted;
return sorted;
}
function _isAutoSelectableEnvironment(environment: Environment): boolean {
if (!environment.enabled) return false;
if (environment.id === LOCAL_DOCKER_ENVIRONMENT_ID) return true;
return isEnvironmentOnline(environment);
}
function _setSelectedEnvironment(environment: Environment): Environment {
_selectedEnvironment = environment;
selectedEnvironmentId.current = environment.id;
return environment;
}
function _selectInitialEnvironment(available: Environment[]): Environment | null {
const savedId = selectedEnvironmentId.current;
if (savedId) {
const found = available.find((env) => env.id === savedId);
if (found && _isAutoSelectableEnvironment(found)) {
return _setSelectedEnvironment(found);
}
}
const localEnv = available.find((env) => env.id === LOCAL_DOCKER_ENVIRONMENT_ID);
if (localEnv && _isAutoSelectableEnvironment(localEnv)) {
return _setSelectedEnvironment(localEnv);
}
const firstReachable = available.find((env) => _isAutoSelectableEnvironment(env));
if (firstReachable) {
return _setSelectedEnvironment(firstReachable);
}
_selectedEnvironment = null;
return null;
}
return {
get selected(): Environment | null {
return _selectedEnvironment;
},
get available(): Environment[] {
return _availableEnvironments;
},
initialize: async (environmentsData: Environment[]) => {
const available = _updateAvailable(environmentsData);
const hasRealEnvironments = environmentsData.length > 0;
if (!_initialized) {
_selectInitialEnvironment(available);
_initialized = true;
if (hasRealEnvironments) {
_initializedWithData = true;
}
_resolveReadyPromiseFunction();
} else if (hasRealEnvironments && !_initializedWithData) {
_selectInitialEnvironment(available);
_initializedWithData = true;
} else {
// Update the selected environment's data if it exists
if (_selectedEnvironment) {
const updated = available.find((env) => env.id === _selectedEnvironment!.id);
if (updated) {
_selectedEnvironment = updated;
// If the current environment was disabled, switch to an enabled one
if (!updated.enabled) {
_selectInitialEnvironment(available);
}
} else {
// Environment no longer exists, select a new one
_selectInitialEnvironment(available);
}
} else if (available.length > 0) {
_selectInitialEnvironment(available);
}
}
},
setEnvironment: async (environment: Environment) => {
if (!environment.enabled) return;
if (_selectedEnvironment?.id !== environment.id) {
_selectedEnvironment = environment;
selectedEnvironmentId.current = environment.id;
// Check if we're on a resource detail page (e.g., /containers/abc123)
// These pages show environment-specific resources that won't exist in the new environment
// Navigate to the corresponding list page to avoid 500 errors
const listPage = getResourceListPage();
if (listPage) {
await goto(listPage);
} else {
await invalidateAll();
}
}
},
isInitialized: () => _initialized,
getLocalEnvironment: () => _availableEnvironments.find((env) => env.id === LOCAL_DOCKER_ENVIRONMENT_ID) || null,
ready: _readyPromise,
getCurrentEnvironmentId: async (): Promise<string> => {
await _readyPromise;
return _selectedEnvironment ? _selectedEnvironment.id : LOCAL_DOCKER_ENVIRONMENT_ID;
}
};
}
export const environmentStore = createEnvironmentManagementStore();