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
226 lines
6.8 KiB
JavaScript
226 lines
6.8 KiB
JavaScript
const favoritesManager = {
|
|
apiBase: window.location.origin,
|
|
_initialized: false,
|
|
_loadPromise: null,
|
|
|
|
groups: [],
|
|
favoritesByKey: new Map(),
|
|
favoritesById: 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();
|
|
},
|
|
|
|
setData(groups, favorites) {
|
|
this.groups = Array.isArray(groups) ? groups : [];
|
|
this.favoritesByKey.clear();
|
|
this.favoritesById.clear();
|
|
|
|
(favorites || []).forEach(f => {
|
|
const dc = f?.docker_container;
|
|
if (!dc?.host_id || !dc?.container_id) return;
|
|
const key = `${dc.host_id}::${dc.container_id}`;
|
|
this.favoritesByKey.set(key, f);
|
|
this.favoritesById.set(f.id, f);
|
|
});
|
|
|
|
this.refreshStarsInDom();
|
|
this._emitChange();
|
|
},
|
|
|
|
async load() {
|
|
const [groupsRes, favsRes] = await Promise.all([
|
|
this.fetchAPI('/api/favorites/groups').catch(() => ({ groups: [] })),
|
|
this.fetchAPI('/api/favorites/containers').catch(() => ({ containers: [] })),
|
|
]);
|
|
|
|
this.setData(groupsRes?.groups || [], favsRes?.containers || []);
|
|
},
|
|
|
|
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('favoritesManager listener error:', e);
|
|
}
|
|
}
|
|
},
|
|
|
|
_key(hostId, containerId) {
|
|
return `${hostId}::${containerId}`;
|
|
},
|
|
|
|
isFavorite(hostId, containerId) {
|
|
return this.favoritesByKey.has(this._key(hostId, containerId));
|
|
},
|
|
|
|
getFavoriteId(hostId, containerId) {
|
|
const fav = this.favoritesByKey.get(this._key(hostId, containerId));
|
|
return fav ? fav.id : null;
|
|
},
|
|
|
|
async addOrUpdateFavorite(hostId, containerId, groupId = null) {
|
|
const created = await this.fetchAPI('/api/favorites/containers', {
|
|
method: 'POST',
|
|
body: JSON.stringify({
|
|
host_id: hostId,
|
|
container_id: containerId,
|
|
group_id: groupId
|
|
})
|
|
});
|
|
|
|
if (!created) return null;
|
|
|
|
const key = this._key(hostId, containerId);
|
|
this.favoritesByKey.set(key, created);
|
|
this.favoritesById.set(created.id, created);
|
|
this.refreshStarsInDom();
|
|
this._emitChange();
|
|
return created;
|
|
},
|
|
|
|
async moveFavoriteToGroup(hostId, containerId, groupId = null) {
|
|
return await this.addOrUpdateFavorite(hostId, containerId, groupId);
|
|
},
|
|
|
|
async listGroups() {
|
|
const res = await this.fetchAPI('/api/favorites/groups');
|
|
this.groups = res?.groups || [];
|
|
this._emitChange();
|
|
return this.groups;
|
|
},
|
|
|
|
async createGroup({ name, sort_order = 0, color = null, icon_key = null }) {
|
|
const created = await this.fetchAPI('/api/favorites/groups', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ name, sort_order, color, icon_key })
|
|
});
|
|
await this.listGroups();
|
|
return created;
|
|
},
|
|
|
|
async updateGroup(groupId, { name = null, sort_order = null, color = null, icon_key = null }) {
|
|
const updated = await this.fetchAPI(`/api/favorites/groups/${groupId}`, {
|
|
method: 'PATCH',
|
|
body: JSON.stringify({ name, sort_order, color, icon_key })
|
|
});
|
|
await this.listGroups();
|
|
return updated;
|
|
},
|
|
|
|
async deleteGroup(groupId) {
|
|
await this.fetchAPI(`/api/favorites/groups/${groupId}`, { method: 'DELETE' });
|
|
await this.listGroups();
|
|
await this.load();
|
|
},
|
|
|
|
async removeFavoriteById(favoriteId) {
|
|
await this.fetchAPI(`/api/favorites/containers/${favoriteId}`, { method: 'DELETE' });
|
|
|
|
const existing = this.favoritesById.get(favoriteId);
|
|
if (existing?.docker_container?.host_id && existing?.docker_container?.container_id) {
|
|
const key = this._key(existing.docker_container.host_id, existing.docker_container.container_id);
|
|
this.favoritesByKey.delete(key);
|
|
}
|
|
this.favoritesById.delete(favoriteId);
|
|
|
|
this.refreshStarsInDom();
|
|
this._emitChange();
|
|
},
|
|
|
|
async toggleFavorite(hostId, containerId) {
|
|
const key = this._key(hostId, containerId);
|
|
const existing = this.favoritesByKey.get(key);
|
|
|
|
if (existing?.id) {
|
|
await this.removeFavoriteById(existing.id);
|
|
return { favorited: false };
|
|
}
|
|
|
|
await this.addOrUpdateFavorite(hostId, containerId, null);
|
|
return { favorited: true };
|
|
},
|
|
|
|
refreshStarsInDom() {
|
|
const buttons = document.querySelectorAll('[data-fav-key]');
|
|
buttons.forEach(btn => {
|
|
const key = btn.getAttribute('data-fav-key');
|
|
const isFav = this.favoritesByKey.has(key);
|
|
|
|
const icon = btn.querySelector('i');
|
|
if (icon) {
|
|
icon.classList.toggle('fas', isFav);
|
|
icon.classList.toggle('far', !isFav);
|
|
}
|
|
|
|
btn.classList.toggle('text-purple-400', isFav);
|
|
btn.classList.toggle('text-gray-400', !isFav);
|
|
btn.setAttribute('title', isFav ? 'Retirer des favoris' : 'Ajouter aux favoris');
|
|
btn.setAttribute('aria-pressed', isFav ? 'true' : 'false');
|
|
});
|
|
},
|
|
};
|
|
|
|
window.favoritesManager = favoritesManager;
|