From c437bdee6f8277f33512d2bb1bfe4d88cb9215ca Mon Sep 17 00:00:00 2001 From: Deploymentcenter Bot Date: Thu, 6 Aug 2026 21:53:30 +0200 Subject: [PATCH] feat(bugtracker, tokens): Add project search, push_id, target_agent, tags, edit modal, extended severities (idea, wishlist), token actions (copy/toggle/delete), and agent integration guide --- docs/BUGTRACKER_INTEGRATION_GUIDE.md | 254 ++++++++------ public/api/bugtracker/v1/manage/index.php | 31 ++ public/api/bugtracker/v1/report.php | 3 +- public/index.php | 314 +++++++++++++++--- public/install_db.php | 28 ++ .../005_bugtracker_push_id_and_severities.sql | 55 +++ sql/schema.sql | 8 +- src/Core/TokenManager.php | 9 + src/Modules/Bugtracker/BugRepo.php | 157 +++++++-- 9 files changed, 682 insertions(+), 177 deletions(-) create mode 100644 sql/migrations/005_bugtracker_push_id_and_severities.sql diff --git a/docs/BUGTRACKER_INTEGRATION_GUIDE.md b/docs/BUGTRACKER_INTEGRATION_GUIDE.md index 8e67a75..c016152 100644 --- a/docs/BUGTRACKER_INTEGRATION_GUIDE.md +++ b/docs/BUGTRACKER_INTEGRATION_GUIDE.md @@ -1,156 +1,214 @@ -# Deploymentcenter — Bugtracker, Feature-Tracker & Token Provisioning Guide +# 🤖 AI Agent Integration Guide: Deployment Center Bugtracker & Provisioning API -> **Zielgruppe**: KI-Agenten & Softwareentwickler -> **Zweck**: Anleitung zur automatisierten Registrierung, Fehlererfassung, Feature-Einreichung und Behebung über das Deployment Center. +This guide defines the standardized protocol and API specifications for autonomous AI Developer Agents interacting with the **Deployment Center Bugtracker & Token Provisioning System**. --- -## 1. Token-Architektur & Autonome Sub-Token-Erstellung +## 📌 Executive Overview for AI Agents -Das Deployment Center nutzt eine hierarchische Token-Struktur: -- **Master-Token**: Wird im Deployment Center Admin-Dashboard pro Identität (z. B. Entwickler-Agent, Client-Anwendung, Host-Server) erstellt. -- **Sub-Token**: Wird von Anwendungen oder KI-Agenten autonom über die Provisioning-API angefordert und für API-Aufrufe genutzt. +As an autonomous AI agent working on software repositories, you MUST actively track all runtime errors, stack traces, planned features, and backlog items in the central Deployment Center. -### Sub-Token Anfordern (`POST /api/tokens/v1/provision`) -Sendet einen HTTP POST Request mit dem Master-Token im `X-Master-Token` Header. +### Core Capabilities: +1. **Sub-Token Auto-Provisioning**: Generate restricted sub-tokens for scoped agent tasks. +2. **Automated Error Ingestion**: Report unhandled exceptions with stack traces & automatic SHA-256 deduplication. +3. **Feature & Idea Backlog**: Submit roadmap ideas (`severity: "idea"`) or backlog items (`severity: "wishlist"`). +4. **Active Workflow Management**: Fetch active bugs assigned to your agent ID, update status (`in_progress`, `resolved`), and append diagnostic comments. -```http -POST /api/tokens/v1/provision HTTP/1.1 -Host: dc.mhdf.de -Content-Type: application/json -X-Master-Token: dc_master_myapp_dev_agent_001 +--- +## 🔑 1. Token Provisioning API + +Agents authenticate using a **Master Token** or auto-provisioned **Sub-Token**. + +### Endpoint: `POST /api/tokens/v1/provision` + +Header: `Authorization: Bearer ` + +#### Request Payload: +```json { - "name": "Dev Workstation Agent #4", - "instance_id": "DEV-WORKSTATION-01", + "parent_token": "dc_master_myapp_dev_agent_001", + "name": "Codebase Refactoring Agent Token", + "environment": "development", "scopes": ["bugtracker:report", "bugtracker:manage"], - "environment": "development" + "expires_in_hours": 24 } ``` -#### JSON Antwort: +#### Response: ```json { "status": "success", - "sub_token": "dc_sub_5f8a2c1...", - "token_id": "tok_s_89a1b2c3", - "name": "Dev Workstation Agent #4", + "token_id": "tok_s_8912ab", + "raw_token": "dc_sub_myapp_refactor_agent_991", "scopes": ["bugtracker:report", "bugtracker:manage"], - "environment": "development" + "environment": "development", + "expires_at": "2026-08-07 21:00:00" } ``` --- -## 2. Bug & Feature Ingest API (`POST /api/bugtracker/v1/report`) +## 🐛 2. Reporting Bugs, Features & Ideas -Wird von Überwachungs-Agenten im laufenden Betrieb oder Entwickler-Agenten auf der Workstation genutzt. +### Endpoint: `POST /api/bugtracker/v1/report.php` -### Request Schema: +Header: `Authorization: Bearer ` + +### A. Reporting an Unhandled Exception / Bug ```json { "project_slug": "myapp", - "type": "bug", // "bug" oder "feature_request" - "environment": "development", // "production", "development", "staging", "testing" - "severity": "high", // "low", "medium", "high", "critical" + "type": "bug", "title": "NullReferenceException in UserAuthService.cs line 42", - "description": "Beim Login ohne gesetzte Session ist ein Unerwarteter Nullpointer-Fehler aufgetreten.", + "description": "Triggered when user logs in without an active session object.", "error_message": "NullReferenceException: Object reference not set to an instance of an object.", - "stack_trace": "at MyApp.Core.UserAuthService.ValidateToken(String token)...", + "stack_trace": "at MyApp.Core.UserAuthService.ValidateToken(String token) in UserAuthService.cs:line 42\nat MyApp.Controllers.AuthController.Login() in AuthController.cs:line 18", "build_version": "v1.4.2-dev", - "created_by": "agent:dev-monitor-01" + "environment": "development", + "severity": "high", + "push_id": "push_wf_8912", + "target_agent": "agent:code-fixer-01", + "tags": "auth, security, csharp", + "created_by": "agent:watchdog-monitor" } ``` -#### Besonderheiten: -- **Deduplizierung**: Bei Bugs berechnet das System automatisch einen Error-Hash. Tritt derselbe Fehler in derselben Umgebung erneut auf, wird kein neuer Bug angelegt, sondern `occurrence_count` hochgezählt. +### B. Submitting a Feature Request or Quick Reminder Idea (`severity: "idea"`) +```json +{ + "project_slug": "myapp", + "type": "feature_request", + "title": "Automatische Datenbank-Backups vor FTP Deployments", + "description": "Gedanke für später: Vor jedem FTP-Deployment automatisch mysqldump ausführen und im Server-Archiv ablegen.", + "build_version": "v1.6.0-roadmap", + "environment": "development", + "severity": "idea", + "push_id": "push_wf_9910", + "target_agent": "agent:db-optimizer", + "tags": "database, automation, backup", + "created_by": "agent:planner" +} +``` + +#### Response: +```json +{ + "status": "success", + "item_id": 4, + "is_new": true, + "occurrence_count": 1, + "error_hash": "e2c918a514d89a42f", + "type": "bug", + "environment": "development", + "push_id": "push_wf_8912", + "message": "New bug reported successfully." +} +``` --- -## 3. Geschützte Management & Resolution API (`/api/bugtracker/v1/manage`) +## 📌 3. Managing Items (Fetching, Updating & Commenting) -Schnittstelle für KI-Behebungs-Agenten zum Abrufen offener Bugs, Schreiben von Diagnose-Notizen und Markieren als gelöst. +### Base Endpoint: `/api/bugtracker/v1/manage/index.php` -**Header**: `Authorization: Bearer ` (Benötigt Scope `bugtracker:manage`). +Header: `Authorization: Bearer ` -### 3.1 Offene Items Abfragen (`GET /api/bugtracker/v1/manage/index.php`) -Filter-Parameter: `environment` (`development`/`production`), `type` (`bug`/`feature_request`), `status` (`open`/`in_progress`/`resolved`). +### A. Fetching Open Items Assigned to an Agent +```http +GET /api/bugtracker/v1/manage/index.php?project_slug=myapp&status=open&agent=agent:code-fixer-01 +``` -### 3.2 Item-Details & Kommentar-Historie (`GET /api/bugtracker/v1/manage/index.php?id=123`) - -### 3.3 Ermittlungsschritt / Kommentar Hinzufügen (`POST /api/bugtracker/v1/manage/index.php?action=comment&id=123`) +### B. Updating Status & Details (`POST ?action=update`) ```json { - "comment": "Log-Analyse gestartet. Der Fehler tritt auf, wenn $_SESSION['dc_user_id'] nicht gesetzt ist.", - "author": "agent:code-fixer-01", - "action_taken": "investigated" + "id": 4, + "status": "in_progress", + "severity": "high", + "push_id": "push_wf_8912", + "target_agent": "agent:code-fixer-01", + "tags": "auth, fixed_pending_test", + "author": "agent:code-fixer-01" } ``` -### 3.4 Item als Gelöst / Umgesetzt Markieren (`POST /api/bugtracker/v1/manage/index.php?action=resolve&id=123`) +### C. Appending Diagnostic Timeline Comments (`POST ?action=comment`) ```json { - "resolved_in_build": "v1.4.3", - "resolution_notes": "Null-Check für Session-Variable hinzugefügt.", + "id": 4, + "comment": "Ursache identifiziert: $_SESSION['user'] war Null in line 42. Null-Check und Safe Navigation Operator wurden hinzugefügt.", + "action_taken": "code_patched", + "author": "agent:code-fixer-01" +} +``` + +### D. Marking as Resolved (`POST ?action=resolve`) +```json +{ + "id": 4, + "resolved_in_build": "v1.4.3-dev", + "resolution_notes": "Unit tests hinzugefügt und Null-Check in ValidateToken() integriert.", "author": "agent:code-fixer-01" } ``` --- -## 4. C# (.NET 8) Implementierungsbeispiel für KI-Agenten +## 💻 4. Code Implementation Examples for Agents -```csharp -using System; -using System.Net.Http; -using System.Text; -using System.Text.Json; -using System.Threading.Tasks; +### Python Example: Automatic Error Reporter Decorator +```python +import requests +import traceback +import sys -public class DeploymentCenterBugtrackerClient -{ - private static readonly HttpClient HttpClient = new HttpClient(); - private const string BaseUrl = "https://dc.mhdf.de"; +DC_API_URL = "https://dc.mhdf.de/api/bugtracker/v1/report.php" +AGENT_TOKEN = "dc_sub_myapp_agent_live_001" - public static async Task IngestDevBugAsync(string masterToken, string title, string errorMessage, string stackTrace) - { - // 1. Sub-Token anfordern - var provisionReq = new HttpRequestMessage(HttpMethod.Post, $"{BaseUrl}/api/tokens/v1/provision") - { - Content = new StringContent(JsonSerializer.Serialize(new - { - client_name = "Dev Agent Client", - scopes = new[] { "bugtracker:report" }, - environment = "development" - }), Encoding.UTF8, "application/json") - }; - provisionReq.Headers.Add("X-Master-Token", masterToken); - - var provResponse = await HttpClient.SendAsync(provisionReq); - var provJson = await provResponse.Content.ReadAsStringAsync(); - using var doc = JsonDocument.Parse(provJson); - string subToken = doc.RootElement.GetProperty("sub_token").GetString(); - - // 2. Bug melden - var reportReq = new HttpRequestMessage(HttpMethod.Post, $"{BaseUrl}/api/bugtracker/v1/report") - { - Content = new StringContent(JsonSerializer.Serialize(new - { - project_slug = "myapp", - type = "bug", - environment = "development", - severity = "high", - title = title, - error_message = errorMessage, - stack_trace = stackTrace, - build_version = "v1.4.2-dev", - created_by = "agent:dev-agent-01" - }), Encoding.UTF8, "application/json") - }; - reportReq.Headers.Add("Authorization", $"Bearer {subToken}"); - - var reportResponse = await HttpClient.SendAsync(reportReq); - Console.WriteLine($"Report Response: {reportResponse.StatusCode}"); +def report_exception_to_dc(project_slug: str, exc: Exception, env: str = "production", push_id: str = None): + payload = { + "project_slug": project_slug, + "type": "bug", + "title": f"{type(exc).__name__}: {str(exc)}", + "error_message": str(exc), + "stack_trace": traceback.format_exc(), + "build_version": "v1.4.2", + "environment": env, + "severity": "high", + "push_id": push_id, + "created_by": "agent:python-runner" } -} + headers = { + "Content-Type": "application/json", + "Authorization": f"Bearer {AGENT_TOKEN}" + } + try: + r = requests.post(DC_API_URL, json=payload, headers=headers, timeout=5) + return r.json() + except Exception as e: + print(f"Failed to report to Deployment Center: {e}", file=sys.stderr) ``` + +### cURL Example: Submit Feature Request / Idea +```bash +curl -X POST "https://dc.mhdf.de/api/bugtracker/v1/report.php" \ + -H "Authorization: Bearer dc_sub_myapp_agent_live_001" \ + -H "Content-Type: application/json" \ + -d '{ + "project_slug": "myapp", + "type": "feature_request", + "title": "Erweiterte Filterung im WebUI Dashboard", + "severity": "idea", + "push_id": "push_task_1029", + "tags": "ui, dashboard", + "created_by": "agent:dev-assistant" + }' +``` + +--- + +## 🎯 Best Practices for Developer Agents + +1. **Always set `push_id`**: When executing automated pipelines, pass a `push_id` so all updates can be traced back to the specific execution run. +2. **Use `severity: "idea"` for thoughts**: When noticing potential refactorings or future improvements during coding, log them immediately as ideas. +3. **Comment before resolving**: Before calling `action=resolve`, write a diagnostic comment explaining **why** and **how** the fix was performed. diff --git a/public/api/bugtracker/v1/manage/index.php b/public/api/bugtracker/v1/manage/index.php index 55df494..a2016d0 100644 --- a/public/api/bugtracker/v1/manage/index.php +++ b/public/api/bugtracker/v1/manage/index.php @@ -157,6 +157,35 @@ try { exit; } + if ($action === 'update' || str_contains($path, '/update')) { + if ($method !== 'POST') { + http_response_code(405); + echo json_encode(['status' => 'error', 'message' => 'POST required for update']); + exit; + } + + if (!$itemId && !empty($input['id'])) { + $itemId = (int)$input['id']; + } + + if (!$itemId) { + http_response_code(400); + echo json_encode(['status' => 'error', 'message' => 'Missing item ID']); + exit; + } + + $author = !empty($input['author']) ? trim($input['author']) : $authorName; + $ok = $repo->updateItemDetails($itemId, $input, $author); + + if ($ok) { + echo json_encode(['status' => 'success', 'message' => "Item #{$itemId} updated successfully"]); + } else { + http_response_code(400); + echo json_encode(['status' => 'error', 'message' => 'Failed to update item']); + } + exit; + } + // Detail View of a single item if ($itemId > 0 && $method === 'GET') { $details = $repo->getItemDetails($itemId); @@ -176,6 +205,8 @@ try { 'type' => $_GET['type'] ?? 'all', 'status' => $_GET['status'] ?? 'all', 'severity' => $_GET['severity'] ?? 'all', + 'push_id' => $_GET['push_id'] ?? '', + 'target_agent' => $_GET['target_agent'] ?? $_GET['agent'] ?? '', 'search' => $_GET['search'] ?? $_GET['q'] ?? '', ]; diff --git a/public/api/bugtracker/v1/report.php b/public/api/bugtracker/v1/report.php index 7a18825..b65ea16 100644 --- a/public/api/bugtracker/v1/report.php +++ b/public/api/bugtracker/v1/report.php @@ -71,8 +71,9 @@ try { 'error_hash' => $result['error_hash'], 'type' => $result['type'], 'environment' => $result['environment'], + 'push_id' => $result['push_id'] ?? null, 'message' => $result['is_new'] - ? ($result['type'] === 'bug' ? 'New bug reported successfully.' : 'New feature request submitted.') + ? ($result['type'] === 'bug' ? 'New bug reported successfully.' : 'New feature request / idea submitted.') : 'Recurring bug count updated.', ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); diff --git a/public/index.php b/public/index.php index d0050f4..69f3ae2 100644 --- a/public/index.php +++ b/public/index.php @@ -437,6 +437,15 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { } } + if ($action === 'delete_core_token') { + $tokenId = trim($_POST['token_id'] ?? ''); + if ($tokenId) { + $coreTokenMgr = new CoreTokenManager($pdo); + $coreTokenMgr->deleteToken($tokenId); + $msg = "Token '{$tokenId}' und abgeleitete Sub-Tokens wurden dauerhaft gelöscht."; + } + } + // Bugtracker & Feature-Tracker Actions if ($action === 'bt_add_comment') { $itemId = (int)($_POST['item_id'] ?? 0); @@ -474,6 +483,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $build = trim($_POST['build_version'] ?? 'v1.0.0'); $env = $_POST['environment'] ?? 'production'; $sev = $_POST['severity'] ?? 'medium'; + $pushId = trim($_POST['push_id'] ?? ''); + $agent = trim($_POST['target_agent'] ?? ''); + $tags = trim($_POST['tags'] ?? ''); $createdBy = trim($_POST['created_by'] ?? $_SESSION['dc_username'] ?? 'admin'); if ($title) { @@ -488,6 +500,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { 'build_version' => $build, 'environment' => $env, 'severity' => $sev, + 'push_id' => $pushId, + 'target_agent' => $agent, + 'tags' => $tags, 'created_by' => $createdBy, ]); $msg = $res['is_new'] @@ -508,6 +523,32 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $msg = "Status für Item #{$itemId} geändert auf {$status}."; } } + + if ($action === 'bt_update_item') { + $itemId = (int)($_POST['item_id'] ?? 0); + $status = $_POST['status'] ?? 'open'; + $sev = $_POST['severity'] ?? 'medium'; + $pushId = trim($_POST['push_id'] ?? ''); + $agent = trim($_POST['target_agent'] ?? ''); + $tags = trim($_POST['tags'] ?? ''); + $notes = trim($_POST['resolution_notes'] ?? ''); + $build = trim($_POST['resolved_in_build'] ?? ''); + $author = trim($_POST['author'] ?? $_SESSION['dc_username'] ?? 'admin'); + + if ($itemId > 0) { + $bugRepo = new BugRepo($pdo); + $bugRepo->updateItemDetails($itemId, [ + 'status' => $status, + 'severity' => $sev, + 'push_id' => $pushId, + 'target_agent' => $agent, + 'tags' => $tags, + 'resolution_notes' => $notes, + 'resolved_in_build' => $build, + ], $author); + $msg = "Item #{$itemId} wurde erfolgreich aktualisiert."; + } + } } // Fetch All Data @@ -1838,7 +1879,7 @@ $baseUrl = $protocol . '://' . $host;
-
+
Dev Bugs (Entwicklung)
@@ -1851,6 +1892,10 @@ $baseUrl = $protocol . '://' . $host;
Offene Feature Requests
+
+
💡 Ideen & Gedanken
+
+
Gelöst / Umgesetzt
@@ -1858,26 +1903,46 @@ $baseUrl = $protocol . '://' . $host;
-
-

🐛 Bugs & Feature Requests

-
+
+

🐛 Bugs, Features & Ideen

+
+ + +
@@ -1908,6 +1973,7 @@ $baseUrl = $protocol . '://' . $host; $statusBadge = match($item['status']) { 'open' => 'OFFEN', + 'planned' => 'GEPLANT', 'in_progress' => 'IN BEARBEITUNG', 'resolved' => 'GELÖST / UMGESETZT', 'rejected' => 'ABGELEHNT', @@ -1915,22 +1981,38 @@ $baseUrl = $protocol . '://' . $host; }; $sevBadge = match($item['severity']) { + 'idea' => '💡 IDEE', + 'wishlist' => '⭐ WUNSCHLISTE', 'critical' => '🔥 KRITISCH', 'high' => 'HOCH', 'medium' => 'MITTEL', default => 'NIEDRIG' }; + + $jsonItem = htmlspecialchars(json_encode($item), ENT_QUOTES, 'UTF-8'); ?> @@ -1942,10 +2024,13 @@ $baseUrl = $protocol . '://' . $host; @@ -1957,7 +2042,7 @@ $baseUrl = $protocol . '://' . $host;
-

➕ Bug oder Feature Request Manuell Erfassen

+

➕ Bug, Feature Request oder Idee Erfassen

@@ -1973,7 +2058,7 @@ $baseUrl = $protocol . '://' . $host;
@@ -1988,6 +2073,8 @@ $baseUrl = $protocol . '://' . $host;
+
+
+ + +
+
+ + +
+
+ + +
+
- +
@@ -2005,14 +2106,14 @@ $baseUrl = $protocol . '://' . $host;
- +
- +
- +
@@ -2068,22 +2169,29 @@ $baseUrl = $protocol . '://' . $host; Sub-Tokens
@@ -2103,8 +2211,10 @@ $baseUrl = $protocol . '://' . $host; + + @@ -2112,6 +2222,8 @@ $baseUrl = $protocol . '://' . $host; $subTokens = array_filter($coreAllTokens, fn($t) => $t['type'] === 'sub'); foreach ($subTokens as $sTok): $scopesArr = json_decode($sTok['scopes'], true) ?: []; + $sRaw = !empty($sTok['raw_token']) ? $sTok['raw_token'] : $sTok['token_id']; + $sMasked = (strlen($sRaw) > 12) ? substr($sRaw, 0, 12) . '••••••••••••••••' : $sRaw; ?> @@ -2125,8 +2237,31 @@ $baseUrl = $protocol . '://' . $host; + + @@ -2393,16 +2528,23 @@ $baseUrl = $protocol . '://' . $host; // Bugtracker Table Filter JS function filterBugtrackerTable() { + const project = document.getElementById('btFilterProject').value; const env = document.getElementById('btFilterEnv').value; const type = document.getElementById('btFilterType').value; + const severity = document.getElementById('btFilterSeverity').value; const status = document.getElementById('btFilterStatus').value; + const query = (document.getElementById('btFilterSearch').value || '').toLowerCase().trim(); document.querySelectorAll('#btTableBody .bt-row').forEach(row => { + const matchProject = (project === 'all' || row.getAttribute('data-project') === project); const matchEnv = (env === 'all' || row.getAttribute('data-env') === env); const matchType = (type === 'all' || row.getAttribute('data-type') === type); + const matchSeverity = (severity === 'all' || row.getAttribute('data-severity') === severity); const matchStatus = (status === 'all' || row.getAttribute('data-status') === status); + const textContent = row.innerText.toLowerCase(); + const matchQuery = (!query || textContent.includes(query)); - if (matchEnv && matchType && matchStatus) { + if (matchProject && matchEnv && matchType && matchSeverity && matchStatus && matchQuery) { row.style.display = ''; } else { row.style.display = 'none'; @@ -2410,6 +2552,24 @@ $baseUrl = $protocol . '://' . $host; }); } + // Open Bugtracker Edit Modal + function openEditBugtrackerModal(item) { + document.getElementById('btEditItemId').value = item.id; + document.getElementById('btEditTitleDisplay').innerText = '#' + item.id + ': ' + item.title; + document.getElementById('btEditStatus').value = item.status; + document.getElementById('btEditSeverity').value = item.severity; + document.getElementById('btEditPushId').value = item.push_id || ''; + document.getElementById('btEditTargetAgent').value = item.target_agent || ''; + document.getElementById('btEditTags').value = item.tags || ''; + document.getElementById('btEditResolvedInBuild').value = item.resolved_in_build || ''; + document.getElementById('btEditResolutionNotes').value = item.resolution_notes || ''; + document.getElementById('btEditModal').style.display = 'flex'; + } + + function closeEditBugtrackerModal() { + document.getElementById('btEditModal').style.display = 'none'; + } + // Open Bugtracker Item Details & Timeline Modal function openBugtrackerModal(itemId) { const modal = document.getElementById('btDetailModal'); @@ -2640,9 +2800,10 @@ $baseUrl = $protocol . '://' . $host; document.getElementById('editExpiresAt').value = opt.getAttribute('data-exp') || ''; } - // Token Masking Toggle + // Token Masking Toggle & Copy function toggleTokenMask(tokId) { - const el = document.getElementById('tok-text-' + tokId); + const el = document.getElementById(tokId) || document.getElementById('tok-text-' + tokId) || document.getElementById('tok-core-' + tokId) || document.getElementById('tok-sub-' + tokId); + if (!el) return; if (el.innerText.includes('••••')) { el.innerText = el.getAttribute('data-full'); } else { @@ -2651,10 +2812,18 @@ $baseUrl = $protocol . '://' . $host; } function copyTokenValue(tokId) { - const el = document.getElementById('tok-text-' + tokId); + const el = document.getElementById(tokId) || document.getElementById('tok-text-' + tokId) || document.getElementById('tok-core-' + tokId) || document.getElementById('tok-sub-' + tokId); + if (!el) return; const fullVal = el.getAttribute('data-full'); - navigator.clipboard.writeText(fullVal); - alert('Token in Zwischenablage kopiert!'); + if (navigator.clipboard && navigator.clipboard.writeText) { + navigator.clipboard.writeText(fullVal).then(() => { + alert('Token in Zwischenablage kopiert!'); + }).catch(() => { + prompt('Token kopieren:', fullVal); + }); + } else { + prompt('Token kopieren:', fullVal); + } } function generateOfflinePayload() { @@ -2709,28 +2878,73 @@ $baseUrl = $protocol . '://' . $host; - -
#
-
- Projekt: | Von: +
+ Projekt: + Von: + + 📲 Push: + + + 🤖 + + + 🏷️ +
- - - - +
+ + + + + +
- + - - + + - - - +
+ + + + + + + + WIDERUFEN + +
+ - +
- - WIDERUFEN - +
Bezeichnung / Client Umgebung ScopesToken Key Zuletzt Genutzt StatusAktionen
+ + + + + + +
+ +
+ + + +
+ +
+ + + +
+
+