Dashboard-Performance: API-Endpoints konsolidieren
Backend:
- GET /api/sessions: Git-Status inline integriert (parallel pro Session)
- Neue Hilfsmethoden GetGitStatusSummaryAsync (Counts) und
GetGitStatusDetailedAsync (Dateinamen, für Einzel-Endpoint)
- GetSessionsWithStats gibt typisierte SessionWithStats zurück
- Neue Models: SessionWithStats, GitStatusSummary
- GET /api/agents: Skills inline pro Agent integriert
- SkillService als Parameter in HandleList
- Skills als Array mit slug/name/description
Frontend:
- Dashboard: 1 paralleler Promise.all statt sequentieller Requests
- Vorher: agents → sessions → N×git-status → M×agent-skills → cronjobs → shares
- Nachher: Promise.all([agents, sessions, cronjobs, shares]) (4 Requests)
- Git-Status direkt aus Session-Response, Skills aus Agent-Response
- Session-Page: Agent-Skills aus inline-Daten statt N×apiGetAgentSkills
Reduziert Dashboard-Requests von ~4+N+M auf 4 (fix).
This commit is contained in:
1 parent
0dcc05ebf9
commit
11acfde081
7 files changed
+265
-119
No files matched your search
@@ -74,8 +74,8 @@ User 1──* Agent 1──* Session 1──* Chain 1──* Message 1──* At
|
||||
| Auth | `/api/auth/` | `POST login`, `POST logout`, `GET me`, `GET autologin` |
|
||||
| Chat | `/api/chat` | `POST` → SSE Stream, `POST stop` |
|
||||
| History | `/api/history` | GET history, poll, archive, marks, delete |
|
||||
| Sessions | `/api/sessions/` | CRUD + system-prompt, exec, git-status |
|
||||
| Agents | `/api/agents/` | CRUD |
|
||||
| Sessions | `/api/sessions/` | CRUD + system-prompt, exec, git-status (Liste inkl. inline Git-Status) |
|
||||
| Agents | `/api/agents/` | CRUD (Liste inkl. inline Skills) |
|
||||
| Admin | `/api/admin/` | User CRUD, impersonate, login-link (Admin-only) |
|
||||
| Workspace | `/api/workspace/` | tree, file read/write, mkdir, rmdir, browse, agent-files |
|
||||
| Telegram | `/api/telegram/` | webhook (unauth), config, registration |
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Gott.Extensions;
|
||||
@@ -20,11 +22,51 @@ public static class AgentEndpoint
|
||||
app.MapDelete("/api/agents/{agentId}", HandleDelete);
|
||||
}
|
||||
|
||||
private static async Task HandleList(HttpContext ctx, DatabaseService db)
|
||||
private static async Task HandleList(HttpContext ctx, DatabaseService db, SkillService skillService)
|
||||
{
|
||||
var userId = ctx.GetUserId();
|
||||
var agents = await db.GetAgents(userId);
|
||||
await ctx.Response.WriteAsJsonAsync(agents);
|
||||
var allSkills = skillService.GetAllSkills();
|
||||
var skillMap = allSkills.ToDictionary(s => s.Slug);
|
||||
|
||||
var results = new List<object>();
|
||||
foreach (var a in agents)
|
||||
{
|
||||
var agentSkillSlugs = await db.GetAgentSkills(a.AgentId);
|
||||
var skills = agentSkillSlugs
|
||||
.Where(slug => skillMap.ContainsKey(slug))
|
||||
.Select(slug => new { slug, name = skillMap[slug].Name, description = skillMap[slug].Description })
|
||||
.ToList();
|
||||
|
||||
results.Add(new
|
||||
{
|
||||
a.AgentId,
|
||||
a.Name,
|
||||
a.Avatar,
|
||||
a.Description,
|
||||
a.Model,
|
||||
a.ToolChoice,
|
||||
a.CustomCommands,
|
||||
a.AutoCommit,
|
||||
a.UseSoul,
|
||||
a.UseIdentity,
|
||||
a.UseUserInfo,
|
||||
a.UseMemory,
|
||||
a.UseWebSearch,
|
||||
a.WebSearchVersion,
|
||||
a.AllowUserChoice,
|
||||
a.TaskInstruction,
|
||||
a.AvatarScale,
|
||||
a.AvatarOffsetX,
|
||||
a.AvatarOffsetY,
|
||||
a.IsDeleted,
|
||||
a.CreatedAt,
|
||||
a.UpdatedAt,
|
||||
Skills = skills
|
||||
});
|
||||
}
|
||||
|
||||
await ctx.Response.WriteAsJsonAsync(results);
|
||||
}
|
||||
|
||||
private static async Task HandleGet(HttpContext ctx, DatabaseService db)
|
||||
|
||||
+159
-42
@@ -3,8 +3,10 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Gott.Extensions;
|
||||
using Gott.Models;
|
||||
using Gott.Services;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
@@ -31,8 +33,64 @@ public static class SessionEndpoint
|
||||
private static async Task HandleList(HttpContext ctx, DatabaseService db)
|
||||
{
|
||||
var userId = ctx.GetUserId();
|
||||
var sessions = await db.GetSessionsWithStats(userId);
|
||||
await ctx.Response.WriteAsJsonAsync(sessions);
|
||||
var user = await db.GetUser(userId);
|
||||
var sessionList = await db.GetSessionsWithStats(userId);
|
||||
|
||||
// Git-Status parallel für alle Sessions mit Workspace laden
|
||||
var gitTasks = new Dictionary<int, Task<GitStatusSummary>>();
|
||||
for (int i = 0; i < sessionList.Count; i++)
|
||||
{
|
||||
var resolvedWs = WorkspaceHelper.ResolveWorkspace(sessionList[i].Session, user);
|
||||
if (!string.IsNullOrEmpty(resolvedWs))
|
||||
gitTasks[i] = GetGitStatusSummaryAsync(resolvedWs);
|
||||
}
|
||||
|
||||
if (gitTasks.Count > 0)
|
||||
await Task.WhenAll(gitTasks.Values);
|
||||
|
||||
// Ergebnisse zusammenführen
|
||||
var results = new List<object>();
|
||||
for (int i = 0; i < sessionList.Count; i++)
|
||||
{
|
||||
var sw = sessionList[i];
|
||||
var s = sw.Session;
|
||||
object gitStatus = null;
|
||||
if (gitTasks.TryGetValue(i, out var task) && task.IsCompletedSuccessfully && task.Result != null)
|
||||
{
|
||||
var gs = task.Result;
|
||||
if (gs.Add > 0 || gs.Edit > 0 || gs.Delete > 0 || gs.Outgoing > 0)
|
||||
gitStatus = new { add = gs.Add, edit = gs.Edit, delete = gs.Delete, outgoing = gs.Outgoing };
|
||||
}
|
||||
results.Add(new
|
||||
{
|
||||
s.SessionId,
|
||||
s.Name,
|
||||
s.Description,
|
||||
s.Image,
|
||||
s.AgentId,
|
||||
s.Workspace,
|
||||
s.Context,
|
||||
s.CustomCommands,
|
||||
s.AvatarScale,
|
||||
s.AvatarOffsetX,
|
||||
s.AvatarOffsetY,
|
||||
s.ReviewAgentId,
|
||||
s.MaxReviewIterations,
|
||||
s.FinalizerAgentId,
|
||||
s.PreparerAgentId,
|
||||
s.IsDeleted,
|
||||
s.CreatedAt,
|
||||
s.UpdatedAt,
|
||||
sw.MessageCount,
|
||||
sw.ArchiveMessageCount,
|
||||
sw.TotalMessageCount,
|
||||
sw.TelegramConfigured,
|
||||
sw.TelegramLinked,
|
||||
GitStatus = gitStatus
|
||||
});
|
||||
}
|
||||
|
||||
await ctx.Response.WriteAsJsonAsync(results);
|
||||
}
|
||||
|
||||
private static async Task HandleGet(HttpContext ctx, DatabaseService db)
|
||||
@@ -396,6 +454,23 @@ public static class SessionEndpoint
|
||||
return;
|
||||
}
|
||||
|
||||
var result = await GetGitStatusDetailedAsync(workingDir);
|
||||
if (result == null)
|
||||
{
|
||||
ctx.Response.StatusCode = 500;
|
||||
await ctx.Response.WriteAsJsonAsync(new { error = "Git-Status konnte nicht ermittelt werden" });
|
||||
return;
|
||||
}
|
||||
|
||||
await ctx.Response.WriteAsJsonAsync(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ermittelt eine Git-Status-Zusammenfassung (nur Counts) für die Dashboard-Ansicht.
|
||||
/// Gibt null zurück bei Fehlern (kein Git-Repo, kein git installiert etc.).
|
||||
/// </summary>
|
||||
private static async Task<GitStatusSummary> GetGitStatusSummaryAsync(string workingDir)
|
||||
{
|
||||
try
|
||||
{
|
||||
var psi = new ProcessStartInfo
|
||||
@@ -410,24 +485,85 @@ public static class SessionEndpoint
|
||||
};
|
||||
|
||||
using var proc = Process.Start(psi);
|
||||
if (proc == null)
|
||||
{
|
||||
ctx.Response.StatusCode = 500;
|
||||
await ctx.Response.WriteAsJsonAsync(new { error = "Git-Prozess konnte nicht gestartet werden" });
|
||||
return;
|
||||
}
|
||||
if (proc == null) return null;
|
||||
|
||||
var stdout = await proc.StandardOutput.ReadToEndAsync();
|
||||
var stderr = await proc.StandardError.ReadToEndAsync();
|
||||
await proc.WaitForExitAsync();
|
||||
if (proc.ExitCode != 0) return null;
|
||||
|
||||
if (proc.ExitCode != 0)
|
||||
int addCount = 0, editCount = 0, deleteCount = 0;
|
||||
var lines = stdout.Split('\n', StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var line in lines)
|
||||
{
|
||||
ctx.Response.StatusCode = 500;
|
||||
await ctx.Response.WriteAsJsonAsync(new { error = stderr.Trim() });
|
||||
return;
|
||||
if (line.Length < 4) continue;
|
||||
var x = line[0];
|
||||
var y = line[1];
|
||||
|
||||
if (x == '?' && y == '?') addCount++;
|
||||
else if (x == 'D' || y == 'D') deleteCount++;
|
||||
else if (x == 'A') addCount++;
|
||||
else editCount++;
|
||||
}
|
||||
|
||||
// Outgoing commits
|
||||
int outgoing = 0;
|
||||
try
|
||||
{
|
||||
var outPsi = new ProcessStartInfo
|
||||
{
|
||||
FileName = "git",
|
||||
Arguments = "rev-list --count @{u}..HEAD",
|
||||
WorkingDirectory = workingDir,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true
|
||||
};
|
||||
|
||||
using var outProc = Process.Start(outPsi);
|
||||
if (outProc != null)
|
||||
{
|
||||
var outResult = (await outProc.StandardOutput.ReadToEndAsync()).Trim();
|
||||
await outProc.WaitForExitAsync();
|
||||
if (outProc.ExitCode == 0 && int.TryParse(outResult, out var c))
|
||||
outgoing = c;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
return new GitStatusSummary { Add = addCount, Edit = editCount, Delete = deleteCount, Outgoing = outgoing };
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ermittelt den detaillierten Git-Status (mit Dateinamen) für den Einzel-Endpoint.
|
||||
/// </summary>
|
||||
private static async Task<object> GetGitStatusDetailedAsync(string workingDir)
|
||||
{
|
||||
try
|
||||
{
|
||||
var psi = new ProcessStartInfo
|
||||
{
|
||||
FileName = "git",
|
||||
Arguments = "status --porcelain",
|
||||
WorkingDirectory = workingDir,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true
|
||||
};
|
||||
|
||||
using var proc = Process.Start(psi);
|
||||
if (proc == null) return null;
|
||||
|
||||
var stdout = await proc.StandardOutput.ReadToEndAsync();
|
||||
await proc.WaitForExitAsync();
|
||||
if (proc.ExitCode != 0) return null;
|
||||
|
||||
var add = new List<string>();
|
||||
var edit = new List<string>();
|
||||
var delete = new List<string>();
|
||||
@@ -436,35 +572,20 @@ public static class SessionEndpoint
|
||||
foreach (var line in lines)
|
||||
{
|
||||
if (line.Length < 4) continue;
|
||||
|
||||
var x = line[0]; // index status
|
||||
var y = line[1]; // working tree status
|
||||
var x = line[0];
|
||||
var y = line[1];
|
||||
var file = line.Substring(3);
|
||||
|
||||
// Bei Renames: "R old -> new" => nur den neuen Namen nehmen
|
||||
var arrowIdx = file.IndexOf(" -> ", StringComparison.Ordinal);
|
||||
if (arrowIdx >= 0)
|
||||
file = file.Substring(arrowIdx + 4);
|
||||
|
||||
if (x == '?' && y == '?')
|
||||
{
|
||||
add.Add(file);
|
||||
}
|
||||
else if (x == 'D' || y == 'D')
|
||||
{
|
||||
delete.Add(file);
|
||||
}
|
||||
else if (x == 'A')
|
||||
{
|
||||
add.Add(file);
|
||||
}
|
||||
else
|
||||
{
|
||||
edit.Add(file);
|
||||
}
|
||||
if (x == '?' && y == '?') add.Add(file);
|
||||
else if (x == 'D' || y == 'D') delete.Add(file);
|
||||
else if (x == 'A') add.Add(file);
|
||||
else edit.Add(file);
|
||||
}
|
||||
|
||||
// Outgoing commits zählen (nur wenn Remote existiert)
|
||||
int outgoing = 0;
|
||||
try
|
||||
{
|
||||
@@ -488,17 +609,13 @@ public static class SessionEndpoint
|
||||
outgoing = count;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Kein Remote oder kein Upstream – outgoing bleibt 0
|
||||
}
|
||||
catch { }
|
||||
|
||||
await ctx.Response.WriteAsJsonAsync(new { add, edit, delete, outgoing });
|
||||
return new { add, edit, delete, outgoing };
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch
|
||||
{
|
||||
ctx.Response.StatusCode = 500;
|
||||
await ctx.Response.WriteAsJsonAsync(new { error = ex.Message });
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,3 +27,21 @@ public class SessionRecord
|
||||
public string CreatedAt { get; set; }
|
||||
public string UpdatedAt { get; set; }
|
||||
}
|
||||
|
||||
public class SessionWithStats
|
||||
{
|
||||
public SessionRecord Session { get; set; }
|
||||
public int MessageCount { get; set; }
|
||||
public int ArchiveMessageCount { get; set; }
|
||||
public int TotalMessageCount { get; set; }
|
||||
public bool TelegramConfigured { get; set; }
|
||||
public bool TelegramLinked { get; set; }
|
||||
}
|
||||
|
||||
public class GitStatusSummary
|
||||
{
|
||||
public int Add { get; set; }
|
||||
public int Edit { get; set; }
|
||||
public int Delete { get; set; }
|
||||
public int Outgoing { get; set; }
|
||||
}
|
||||
@@ -419,13 +419,13 @@ public class DatabaseService
|
||||
"SELECT * FROM sessions WHERE user_id = @userId AND is_deleted = 0 ORDER BY updated_at DESC", new { userId });
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<object>> GetSessionsWithStats(string userId)
|
||||
public async Task<List<SessionWithStats>> GetSessionsWithStats(string userId)
|
||||
{
|
||||
using var conn = CreateConnection();
|
||||
var sessions = await conn.QueryAsync<SessionRecord>(
|
||||
"SELECT * FROM sessions WHERE user_id = @userId AND is_deleted = 0 ORDER BY updated_at DESC", new { userId });
|
||||
|
||||
var results = new List<object>();
|
||||
var results = new List<SessionWithStats>();
|
||||
foreach (var s in sessions)
|
||||
{
|
||||
var activeCount = await conn.ExecuteScalarAsync<int>(@"
|
||||
@@ -440,26 +440,9 @@ public class DatabaseService
|
||||
WHERE c.session_id = @sessionId AND m.type = 'text'",
|
||||
new { sessionId = s.SessionId });
|
||||
|
||||
results.Add(new
|
||||
results.Add(new SessionWithStats
|
||||
{
|
||||
s.SessionId,
|
||||
s.Name,
|
||||
s.Description,
|
||||
s.Image,
|
||||
s.AgentId,
|
||||
s.Workspace,
|
||||
s.Context,
|
||||
s.CustomCommands,
|
||||
s.AvatarScale,
|
||||
s.AvatarOffsetX,
|
||||
s.AvatarOffsetY,
|
||||
s.ReviewAgentId,
|
||||
s.MaxReviewIterations,
|
||||
s.FinalizerAgentId,
|
||||
s.PreparerAgentId,
|
||||
s.IsDeleted,
|
||||
s.CreatedAt,
|
||||
s.UpdatedAt,
|
||||
Session = s,
|
||||
MessageCount = activeCount,
|
||||
ArchiveMessageCount = totalCount - activeCount,
|
||||
TotalMessageCount = totalCount,
|
||||
|
||||
@@ -35,12 +35,13 @@ registerPage('dashboard', {
|
||||
|
||||
// Telegram-Config parallel pro Session laden (sessions werden gleich geladen)
|
||||
|
||||
const agents = await apiGetAgents();
|
||||
const [agents, sessions, cronjobs, shares] = await Promise.all([
|
||||
apiGetAgents(), apiGetSessions(), apiGetCronjobs(), apiListShares()
|
||||
]);
|
||||
const agentMap = {};
|
||||
for (const a of agents) agentMap[a.agentId] = a;
|
||||
|
||||
// Sessions
|
||||
const sessions = await apiGetSessions();
|
||||
const grid = document.getElementById('sessionCards');
|
||||
grid.innerHTML = '';
|
||||
|
||||
@@ -48,6 +49,37 @@ registerPage('dashboard', {
|
||||
const agent = s.agentId ? agentMap[s.agentId] : null;
|
||||
const card = buildSessionCard(s, agent);
|
||||
|
||||
// Git-Status aus der Session-Response rendern (inline vom Backend)
|
||||
if (s.gitStatus) {
|
||||
const gs = s.gitStatus;
|
||||
const metaEl = card.querySelector('.card-meta');
|
||||
if (metaEl) {
|
||||
const parts = [];
|
||||
if (gs.add > 0) {
|
||||
const tip = `${gs.add} ${gs.add === 1 ? 'Datei' : 'Dateien'} hinzugefügt`;
|
||||
parts.push(`<span class="git-badge git-add" title="${esc(tip)}">+${gs.add}</span>`);
|
||||
}
|
||||
if (gs.edit > 0) {
|
||||
const tip = `${gs.edit} ${gs.edit === 1 ? 'Datei' : 'Dateien'} geändert`;
|
||||
parts.push(`<span class="git-badge git-edit" title="${esc(tip)}">~${gs.edit}</span>`);
|
||||
}
|
||||
if (gs.delete > 0) {
|
||||
const tip = `${gs.delete} ${gs.delete === 1 ? 'Datei' : 'Dateien'} gelöscht`;
|
||||
parts.push(`<span class="git-badge git-delete" title="${esc(tip)}">-${gs.delete}</span>`);
|
||||
}
|
||||
if (gs.outgoing > 0) {
|
||||
const tip = `${gs.outgoing} ${gs.outgoing === 1 ? 'Commit muss' : 'Commits müssen'} noch gepusht werden`;
|
||||
parts.push(`<span class="git-badge git-outgoing" title="${esc(tip)}"><i class="fa fa-arrow-up"></i> ${gs.outgoing}</span>`);
|
||||
}
|
||||
if (parts.length) {
|
||||
const gitEl = document.createElement('span');
|
||||
gitEl.className = 'card-meta-item card-git-status';
|
||||
gitEl.innerHTML = parts.join(' ');
|
||||
metaEl.appendChild(gitEl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Menü zum title-row hinzufügen
|
||||
const titleRow = card.querySelector('.card-title-row');
|
||||
titleRow.insertAdjacentHTML('beforeend', `<div class="card-menu">
|
||||
@@ -105,44 +137,6 @@ registerPage('dashboard', {
|
||||
navigateTo('session', { id: s.sessionId });
|
||||
});
|
||||
grid.appendChild(card);
|
||||
|
||||
// Git-Status asynchron laden
|
||||
if (s.workspace) {
|
||||
apiGitStatus(s.sessionId).then(status => {
|
||||
if (!status || status.error) return;
|
||||
const addCount = (status.add || []).length;
|
||||
const editCount = (status.edit || []).length;
|
||||
const deleteCount = (status.delete || []).length;
|
||||
const outgoing = status.outgoing || 0;
|
||||
if (addCount + editCount + deleteCount + outgoing === 0) return;
|
||||
|
||||
const metaEl = card.querySelector('.card-meta');
|
||||
if (!metaEl) return;
|
||||
|
||||
const parts = [];
|
||||
if (addCount > 0) {
|
||||
const tip = `${addCount} ${addCount === 1 ? 'Datei' : 'Dateien'} hinzugefügt`;
|
||||
parts.push(`<span class="git-badge git-add" title="${esc(tip)}">+${addCount}</span>`);
|
||||
}
|
||||
if (editCount > 0) {
|
||||
const tip = `${editCount} ${editCount === 1 ? 'Datei' : 'Dateien'} geändert`;
|
||||
parts.push(`<span class="git-badge git-edit" title="${esc(tip)}">~${editCount}</span>`);
|
||||
}
|
||||
if (deleteCount > 0) {
|
||||
const tip = `${deleteCount} ${deleteCount === 1 ? 'Datei' : 'Dateien'} gelöscht`;
|
||||
parts.push(`<span class="git-badge git-delete" title="${esc(tip)}">-${deleteCount}</span>`);
|
||||
}
|
||||
if (outgoing > 0) {
|
||||
const tip = `${outgoing} ${outgoing === 1 ? 'Commit muss' : 'Commits müssen'} noch gepusht werden`;
|
||||
parts.push(`<span class="git-badge git-outgoing" title="${esc(tip)}"><i class="fa fa-arrow-up"></i> ${outgoing}</span>`);
|
||||
}
|
||||
|
||||
const gitEl = document.createElement('span');
|
||||
gitEl.className = 'card-meta-item card-git-status';
|
||||
gitEl.innerHTML = parts.join(' ');
|
||||
metaEl.appendChild(gitEl);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -154,14 +148,8 @@ registerPage('dashboard', {
|
||||
const agentGrid = document.getElementById('agentCards');
|
||||
agentGrid.innerHTML = '';
|
||||
|
||||
// Skills pro Agent parallel laden
|
||||
const agentSkillsMap = {};
|
||||
await Promise.all(agents.map(async a => {
|
||||
agentSkillsMap[a.agentId] = await apiGetAgentSkills(a.agentId);
|
||||
}));
|
||||
|
||||
for (const a of agents) {
|
||||
const skills = agentSkillsMap[a.agentId] || [];
|
||||
const skills = a.skills || [];
|
||||
const card = buildAgentCard(a, skills);
|
||||
|
||||
// Menü zum title-row hinzufügen
|
||||
@@ -213,7 +201,6 @@ registerPage('dashboard', {
|
||||
document.getElementById('newAgentBtn').addEventListener('click', () => navigateTo('agent'));
|
||||
|
||||
// Cronjobs
|
||||
const cronjobs = await apiGetCronjobs();
|
||||
const cronjobGrid = document.getElementById('cronjobCards');
|
||||
cronjobGrid.innerHTML = '';
|
||||
|
||||
@@ -303,7 +290,6 @@ registerPage('dashboard', {
|
||||
document.getElementById('newCronjobBtn').addEventListener('click', () => navigateTo('cronjob'));
|
||||
|
||||
// Shared Links
|
||||
const shares = await apiListShares();
|
||||
const sharedSection = document.getElementById('sharedLinksSection');
|
||||
const sharedGrid = document.getElementById('sharedLinksCards');
|
||||
if (shares.length > 0) {
|
||||
|
||||
@@ -89,9 +89,9 @@ registerPage('session', {
|
||||
for (const a of agents) agentMap[a.agentId] = a;
|
||||
|
||||
const agentSkillsMap = {};
|
||||
await Promise.all(agents.map(async a => {
|
||||
agentSkillsMap[a.agentId] = await apiGetAgentSkills(a.agentId);
|
||||
}));
|
||||
for (const a of agents) {
|
||||
agentSkillsMap[a.agentId] = a.skills || [];
|
||||
}
|
||||
|
||||
function renderAgentInCard(cardEl, agent, emptyIcon, emptyText) {
|
||||
cardEl.innerHTML = '';
|
||||
|
||||
Reference in new issue
Block a user