homelab_automation/app/container_customizations_manager.js
Bruno Charest 8affa0f8b7
Some checks failed
Tests / Backend Tests (Python) (3.10) (push) Has been cancelled
Tests / Backend Tests (Python) (3.11) (push) Has been cancelled
Tests / Backend Tests (Python) (3.12) (push) Has been cancelled
Tests / Frontend Tests (JS) (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / All Tests Passed (push) Has been cancelled
feat: Implement container customization and add Portainer installation/removal playbooks.
2025-12-27 11:02:24 -05:00

126 lines
3.5 KiB
JavaScript

const containerCustomizationsManager = {
apiBase: window.location.origin,
_initialized: false,
_loadPromise: null,
byKey: new Map(),
listeners: new Set(),
getAuthHeaders() {
const token = localStorage.getItem('accessToken');
const apiKey = localStorage.getItem('apiKey');
const headers = {
'Content-Type': 'application/json'
};
if (token) {
headers['Authorization'] = `Bearer ${token}`;
} else if (apiKey) {
headers['X-API-Key'] = apiKey;
}
return headers;
},
async fetchAPI(endpoint, options = {}) {
const response = await fetch(`${this.apiBase}${endpoint}`, {
...options,
headers: {
...this.getAuthHeaders(),
...(options.headers || {})
}
});
if (!response.ok) {
if (response.status === 401) {
if (window.dashboard?.logout) {
window.dashboard.logout();
}
return null;
}
throw new Error(`API Error: ${response.status}`);
}
if (response.status === 204) return null;
return response.json();
},
_key(hostId, containerId) {
return `${hostId}::${containerId}`;
},
setData(items) {
this.byKey.clear();
(items || []).forEach((c) => {
if (!c?.host_id || !c?.container_id) return;
this.byKey.set(this._key(c.host_id, c.container_id), c);
});
this._emitChange();
},
async load() {
const res = await this.fetchAPI('/api/docker/container-customizations');
this.setData(res?.customizations || []);
},
async ensureInit() {
if (this._initialized) return;
if (this._loadPromise) {
await this._loadPromise;
return;
}
this._loadPromise = (async () => {
try {
await this.load();
} finally {
this._initialized = true;
}
})();
await this._loadPromise;
},
onChange(callback) {
this.listeners.add(callback);
return () => this.listeners.delete(callback);
},
_emitChange() {
for (const cb of this.listeners) {
try {
cb();
} catch (e) {
console.error('containerCustomizationsManager listener error:', e);
}
}
},
get(hostId, containerId) {
return this.byKey.get(this._key(hostId, containerId)) || null;
},
async upsert(hostId, containerId, { icon_key = null, icon_color = null, bg_color = null }) {
const payload = { icon_key, icon_color, bg_color };
const res = await this.fetchAPI(`/api/docker/container-customizations/${encodeURIComponent(hostId)}/${encodeURIComponent(containerId)}`, {
method: 'PUT',
body: JSON.stringify(payload)
});
if (!res) return null;
this.byKey.set(this._key(hostId, containerId), res);
this._emitChange();
return res;
},
async remove(hostId, containerId) {
await this.fetchAPI(`/api/docker/container-customizations/${encodeURIComponent(hostId)}/${encodeURIComponent(containerId)}`, {
method: 'DELETE'
});
this.byKey.delete(this._key(hostId, containerId));
this._emitChange();
}
};
window.containerCustomizationsManager = containerCustomizationsManager;