Files
portfolio/docs/DYNAMIC_ACTIVITY_MANAGEMENT.md
2026-01-07 14:30:00 +01:00

15 KiB

🎛️ Dynamic Activity Management - No Rebuild Required!

Übersicht

Dieses System erlaubt dir, alle Aktivitäten dynamisch zu steuern ohne die Website neu zu bauen. Alle Änderungen werden in Echtzeit über die Datenbank und n8n gesteuert.


🎯 Konzept: Zentrales Management

┌─────────────────┐
│  n8n Dashboard  │ ← Du steuerst hier alles
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│   PostgreSQL    │ ← Daten werden hier gespeichert
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│   API Route     │ ← Website liest alle 30s
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│ ActivityFeed UI │ ← Besucher sehen live updates
└─────────────────┘

Vorteile:

  • Keine Website-Rebuild notwendig
  • Echtzeit-Updates (30 Sekunden)
  • Volle Kontrolle via n8n
  • Historische Daten verfügbar
  • Multiple Steuerungsmöglichkeiten

🎮 Management Optionen

Option 1: n8n Dashboard UI EMPFOHLEN

Erstelle ein simples n8n Workflow-Dashboard mit Webhook-Buttons:

Workflow: "Activity Manager Dashboard"

{
  "nodes": [
    {
      "name": "HTTP Server",
      "type": "n8n-nodes-base.webhook",
      "parameters": {
        "path": "activity-dashboard",
        "method": "GET",
        "responseMode": "responseNode",
        "options": {}
      }
    },
    {
      "name": "HTML Dashboard",
      "type": "n8n-nodes-base.respondToWebhook",
      "parameters": {
        "responseBody": "=<html>\n<head>\n  <title>Activity Manager</title>\n  <style>\n    body { font-family: system-ui; max-width: 800px; margin: 50px auto; padding: 20px; }\n    .activity-section { background: #f5f5f5; padding: 20px; margin: 20px 0; border-radius: 8px; }\n    button { background: #333; color: white; padding: 10px 20px; margin: 5px; border: none; border-radius: 5px; cursor: pointer; }\n    button:hover { background: #555; }\n    input, select, textarea { padding: 8px; margin: 5px 0; border: 1px solid #ddd; border-radius: 4px; width: 100%; }\n    .status { display: inline-block; width: 12px; height: 12px; border-radius: 50%; }\n    .active { background: #4ade80; }\n    .inactive { background: #ef4444; }\n  </style>\n</head>\n<body>\n  <h1>🎛️ Activity Manager</h1>\n  \n  <div class=\"activity-section\">\n    <h2>🎵 Music Control</h2>\n    <p>Status: <span class=\"status active\"></span> Auto-syncing from Spotify</p>\n    <button onclick=\"fetch('/webhook/stop-music', {method:'POST'})\">Stop Music Display</button>\n  </div>\n\n  <div class=\"activity-section\">\n    <h2>💻 Coding Activity</h2>\n    <input type=\"text\" id=\"project\" placeholder=\"Project name\">\n    <input type=\"text\" id=\"language\" placeholder=\"Language (e.g., TypeScript)\">\n    <input type=\"text\" id=\"repo\" placeholder=\"GitHub Repo URL\">\n    <button onclick=\"updateCoding()\">Update Coding Status</button>\n    <button onclick=\"clearCoding()\">Clear</button>\n  </div>\n\n  <div class=\"activity-section\">\n    <h2>🎮 Gaming</h2>\n    <input type=\"text\" id=\"game\" placeholder=\"Game name\">\n    <select id=\"platform\">\n      <option>steam</option>\n      <option>playstation</option>\n      <option>xbox</option>\n    </select>\n    <button onclick=\"updateGaming()\">Start Gaming</button>\n    <button onclick=\"stopGaming()\">Stop Gaming</button>\n  </div>\n\n  <div class=\"activity-section\">\n    <h2>😊 Mood & Status</h2>\n    <input type=\"text\" id=\"mood\" placeholder=\"Emoji (e.g., 😊, 💻, 🎮)\" maxlength=\"2\">\n    <textarea id=\"message\" placeholder=\"Custom message\" rows=\"2\"></textarea>\n    <button onclick=\"updateStatus()\">Update Status</button>\n  </div>\n\n  <div class=\"activity-section\">\n    <h2>🏃 Manual Activities</h2>\n    <select id=\"activity-type\">\n      <option value=\"running\">Running</option>\n      <option value=\"reading\">Reading</option>\n      <option value=\"watching\">Watching</option>\n    </select>\n    <input type=\"text\" id=\"activity-details\" placeholder=\"Details\">\n    <button onclick=\"updateActivity()\">Start Activity</button>\n    <button onclick=\"clearActivity()\">Clear</button>\n  </div>\n\n  <div class=\"activity-section\">\n    <h2>🧹 Quick Actions</h2>\n    <button onclick=\"clearAll()\">Clear All Activities</button>\n    <button onclick=\"setAFK()\">Set AFK</button>\n    <button onclick=\"setFocusMode()\">Focus Mode (DND)</button>\n  </div>\n\n  <script>\n    function updateCoding() {\n      fetch('/webhook/update-activity', {\n        method: 'POST',\n        headers: {'Content-Type': 'application/json'},\n        body: JSON.stringify({\n          type: 'coding',\n          project: document.getElementById('project').value,\n          language: document.getElementById('language').value,\n          repo: document.getElementById('repo').value\n        })\n      }).then(() => alert('✅ Updated!'));\n    }\n\n    function updateGaming() {\n      fetch('/webhook/update-activity', {\n        method: 'POST',\n        headers: {'Content-Type': 'application/json'},\n        body: JSON.stringify({\n          type: 'gaming',\n          game: document.getElementById('game').value,\n          platform: document.getElementById('platform').value\n        })\n      }).then(() => alert('✅ Gaming status updated!'));\n    }\n\n    function updateStatus() {\n      fetch('/webhook/update-status', {\n        method: 'POST',\n        headers: {'Content-Type': 'application/json'},\n        body: JSON.stringify({\n          mood: document.getElementById('mood').value,\n          message: document.getElementById('message').value\n        })\n      }).then(() => alert('✅ Status updated!'));\n    }\n\n    function clearAll() {\n      if(confirm('Clear all activities?')) {\n        fetch('/webhook/clear-all', {method: 'POST'})\n          .then(() => alert('✅ All cleared!'));\n      }\n    }\n\n    function setAFK() {\n      fetch('/webhook/update-status', {\n        method: 'POST',\n        headers: {'Content-Type': 'application/json'},\n        body: JSON.stringify({mood: '💤', message: 'AFK - Be right back'})\n      }).then(() => alert('✅ AFK mode activated!'));\n    }\n  </script>\n</body>\n</html>"
      }
    }
  ]
}

Zugriff:

https://your-n8n-instance.com/webhook/activity-dashboard

Option 2: Discord Bot Commands

Erstelle einen Discord Bot für schnelle Updates:

Commands:

!status 💻 Working on new features
!coding Portfolio Next.js
!music <automatic from spotify>
!gaming Elden Ring
!clear
!afk

n8n Workflow:

{
  "nodes": [
    {
      "name": "Discord Webhook",
      "type": "n8n-nodes-base.webhook",
      "parameters": {
        "path": "discord-bot"
      }
    },
    {
      "name": "Parse Command",
      "type": "n8n-nodes-base.function",
      "parameters": {
        "functionCode": "const message = items[0].json.content;\nconst [command, ...args] = message.split(' ');\n\nswitch(command) {\n  case '!status':\n    return [{\n      json: {\n        action: 'update_status',\n        mood: args[0],\n        message: args.slice(1).join(' ')\n      }\n    }];\n  \n  case '!coding':\n    return [{\n      json: {\n        action: 'update_activity',\n        type: 'coding',\n        details: args.join(' ')\n      }\n    }];\n  \n  case '!clear':\n    return [{\n      json: { action: 'clear_all' }\n    }];\n}\n\nreturn [{ json: {} }];"
      }
    },
    {
      "name": "Update Database",
      "type": "n8n-nodes-base.postgres"
    }
  ]
}

Option 3: Mobile App / Shortcut

iOS Shortcuts:

1. "Start Coding" → POST to n8n webhook
2. "Finished Work" → Clear activity
3. "Set Mood" → Update status

Android Tasker:

  • Similar webhooks
  • Location-based triggers
  • Time-based automation

Option 4: CLI Tool

Erstelle ein simples CLI Tool:

#!/bin/bash
# activity.sh

N8N_URL="https://your-n8n-instance.com"

case "$1" in
  status)
    curl -X POST "$N8N_URL/webhook/update-status" \
      -H "Content-Type: application/json" \
      -d "{\"mood\":\"$2\",\"message\":\"$3\"}"
    ;;
  coding)
    curl -X POST "$N8N_URL/webhook/update-activity" \
      -H "Content-Type: application/json" \
      -d "{\"type\":\"coding\",\"project\":\"$2\",\"language\":\"$3\"}"
    ;;
  clear)
    curl -X POST "$N8N_URL/webhook/clear-all"
    ;;
  *)
    echo "Usage: activity.sh [status|coding|clear] [args]"
    ;;
esac

Usage:

./activity.sh status 💻 "Deep work mode"
./activity.sh coding "Portfolio" "TypeScript"
./activity.sh clear

🔄 Automatische Sync-Workflows

Musik geht weg wenn nicht mehr läuft

n8n Workflow: "Spotify Auto-Clear"

{
  "nodes": [
    {
      "name": "Check Every 30s",
      "type": "n8n-nodes-base.cron",
      "parameters": {
        "cronExpression": "*/30 * * * * *"
      }
    },
    {
      "name": "Get Spotify Status",
      "type": "n8n-nodes-base.httpRequest",
      "parameters": {
        "url": "https://api.spotify.com/v1/me/player/currently-playing"
      }
    },
    {
      "name": "Check If Playing",
      "type": "n8n-nodes-base.if",
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{$json.is_playing}}",
              "value2": false
            }
          ]
        }
      }
    },
    {
      "name": "Clear Music from Database",
      "type": "n8n-nodes-base.postgres",
      "parameters": {
        "operation": "executeQuery",
        "query": "UPDATE activity_status SET music_playing = FALSE, music_track = NULL, music_artist = NULL, music_album = NULL, music_album_art = NULL, music_progress = NULL WHERE id = 1"
      }
    }
  ]
}

Auto-Clear nach Zeit

n8n Workflow: "Activity Timeout"

// Function Node: Check Activity Age
const lastUpdate = new Date(items[0].json.updated_at);
const now = new Date();
const hoursSinceUpdate = (now - lastUpdate) / (1000 * 60 * 60);

// Clear activity if older than 2 hours
if (hoursSinceUpdate > 2) {
  return [{
    json: {
      should_clear: true,
      reason: `Activity too old (${hoursSinceUpdate.toFixed(1)} hours)`
    }
  }];
}

return [{ json: { should_clear: false } }];

Smart Activity Detection

Workflow: "Detect Coding from Git Commits"

// When you push to GitHub
const commit = items[0].json;
const repo = commit.repository.name;
const message = commit.head_commit.message;

// Detect language from files
const files = commit.head_commit.modified;
const language = files[0]?.split('.').pop(); // Get extension

return [{
  json: {
    activity_type: 'coding',
    activity_details: message,
    activity_project: repo,
    activity_language: language,
    activity_repo: commit.repository.html_url,
    link: commit.head_commit.url
  }
}];

📊 Activity Analytics Dashboard

Workflow: "Activity History & Stats"

Speichere Historie in separater Tabelle:

CREATE TABLE activity_history (
  id SERIAL PRIMARY KEY,
  activity_type VARCHAR(50),
  details TEXT,
  duration INTEGER, -- in minutes
  started_at TIMESTAMP,
  ended_at TIMESTAMP,
  created_at TIMESTAMP DEFAULT NOW()
);

-- View für Statistiken
CREATE VIEW activity_stats AS
SELECT 
  activity_type,
  COUNT(*) as count,
  SUM(duration) as total_minutes,
  AVG(duration) as avg_duration,
  DATE(created_at) as date
FROM activity_history
GROUP BY activity_type, DATE(created_at)
ORDER BY date DESC;

Dashboard Queries:

-- Heute
SELECT * FROM activity_stats WHERE date = CURRENT_DATE;

-- Diese Woche
SELECT activity_type, SUM(total_minutes) as minutes
FROM activity_stats 
WHERE date >= CURRENT_DATE - INTERVAL '7 days'
GROUP BY activity_type;

-- Most Coded Languages
SELECT activity_language, COUNT(*) 
FROM activity_history 
WHERE activity_type = 'coding'
GROUP BY activity_language
ORDER BY COUNT(*) DESC;

🎨 Custom Activity Types

Erweitere das System mit eigenen Activity-Types:

-- Add custom columns
ALTER TABLE activity_status 
ADD COLUMN custom_activity_type VARCHAR(100),
ADD COLUMN custom_activity_data JSONB;

-- Example: Workout tracking
UPDATE activity_status SET 
  custom_activity_type = 'workout',
  custom_activity_data = '{
    "exercise": "Push-ups",
    "reps": 50,
    "icon": "💪",
    "color": "orange"
  }'::jsonb
WHERE id = 1;

Frontend Support:

// In ActivityFeed.tsx
interface CustomActivity {
  type: string;
  data: {
    icon: string;
    color: string;
    [key: string]: any;
  };
}

// Render custom activities dynamically
if (data.customActivity) {
  return (
    <motion.div
      className={`bg-${data.customActivity.data.color}/20`}
    >
      <span>{data.customActivity.data.icon}</span>
      <span>{data.customActivity.type}</span>
      {/* Render data fields dynamically */}
    </motion.div>
  );
}

🔐 Security & Best Practices

1. Webhook Authentication

// In n8n webhook
const secret = $credentials.webhookSecret;
const providedSecret = $node["Webhook"].json.headers["x-webhook-secret"];

if (secret !== providedSecret) {
  return [{
    json: { error: "Unauthorized" },
    statusCode: 401
  }];
}

2. Rate Limiting

-- Track requests
CREATE TABLE webhook_requests (
  ip_address VARCHAR(45),
  endpoint VARCHAR(100),
  requested_at TIMESTAMP DEFAULT NOW()
);

-- Check rate limit (max 10 requests per minute)
SELECT COUNT(*) FROM webhook_requests 
WHERE ip_address = $1 
AND requested_at > NOW() - INTERVAL '1 minute';

3. Input Validation

// In n8n Function node
const validateInput = (data) => {
  if (!data.type || typeof data.type !== 'string') {
    throw new Error('Invalid activity type');
  }
  
  if (data.type === 'coding' && !data.project) {
    throw new Error('Project name required for coding activity');
  }
  
  return true;
};

🚀 Quick Deploy Checklist

  • Datenbank Table erstellt (setup_activity_status.sql)
  • n8n Workflows importiert
  • Spotify OAuth konfiguriert
  • GitHub Webhooks eingerichtet
  • Dashboard-URL getestet
  • API Routes deployed
  • Environment Variables gesetzt
  • Frontend ActivityFeed getestet
  • Auto-Clear Workflows aktiviert

💡 Pro-Tipps

  1. Backup System: Exportiere n8n Workflows regelmäßig
  2. Monitoring: Setup alerts wenn Workflows fehlschlagen
  3. Testing: Nutze n8n's Test-Modus vor Produktion
  4. Logging: Speichere alle Aktivitäten für Analyse
  5. Fallbacks: Zeige Placeholder wenn keine Daten vorhanden

📞 Quick Support Commands

# Check database status
psql -d portfolio_dev -c "SELECT * FROM activity_status WHERE id = 1;"

# Clear all activities
psql -d portfolio_dev -c "UPDATE activity_status SET activity_type = NULL, music_playing = FALSE WHERE id = 1;"

# View recent history
psql -d portfolio_dev -c "SELECT * FROM activity_history ORDER BY created_at DESC LIMIT 10;"

# Test n8n webhook
curl -X POST https://your-n8n.com/webhook/update-activity \
  -H "Content-Type: application/json" \
  -d '{"type":"coding","details":"Testing","project":"Portfolio"}'

Happy automating! 🎉