470 lines
15 KiB
Markdown
470 lines
15 KiB
Markdown
# 🎛️ 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"**
|
|
```json
|
|
{
|
|
"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:**
|
|
```json
|
|
{
|
|
"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:
|
|
|
|
```bash
|
|
#!/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:**
|
|
```bash
|
|
./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"**
|
|
```json
|
|
{
|
|
"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"**
|
|
```javascript
|
|
// 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"**
|
|
```javascript
|
|
// 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:
|
|
|
|
```sql
|
|
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:**
|
|
```sql
|
|
-- 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:
|
|
|
|
```sql
|
|
-- 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:**
|
|
```typescript
|
|
// 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
|
|
|
|
```javascript
|
|
// 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
|
|
|
|
```sql
|
|
-- 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
|
|
|
|
```javascript
|
|
// 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
|
|
|
|
```bash
|
|
# 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! 🎉 |