CLAUDE: Add productivity tools with n8n workflow automation
- Add CONTEXT.md with ADHD-optimized task management patterns - Add troubleshooting guide for productivity tools - Add n8n workflow documentation including Ko-fi integration - Document n8n at LXC 210 (10.10.0.210) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
8e74633ab3
commit
c8dcf2b5ee
317
productivity/CONTEXT.md
Normal file
317
productivity/CONTEXT.md
Normal file
@ -0,0 +1,317 @@
|
||||
# Productivity Tools - Technology Context
|
||||
|
||||
## Overview
|
||||
ADHD-optimized task management system designed to break the context-switching spiral. The system uses a persistent terminal dashboard with zero-friction brain dump capture to help maintain focus while capturing interrupting thoughts.
|
||||
|
||||
## Architecture Patterns
|
||||
|
||||
### Single-Focus with Brain Dump Pattern
|
||||
**Pattern**: One visible task at a time with instant thought capture
|
||||
- **Current Focus**: Single task display with time tracking
|
||||
- **Brain Dump**: Zero-friction capture without context switching
|
||||
- **Auto-Progression**: System-managed task transitions
|
||||
- **Priority Queue**: Automatic high→medium→low ordering
|
||||
|
||||
**When to Use**:
|
||||
- ADHD workflow challenges
|
||||
- Frequent context switching problems
|
||||
- "Meant to do" task accumulation
|
||||
- Need for always-visible task tracking
|
||||
- Terminal-based work environments
|
||||
|
||||
### Design Principles
|
||||
1. **Minimal Friction**: Two-second task capture without switching
|
||||
2. **Always Visible**: Persistent terminal dashboard prevents forgetting
|
||||
3. **Single Focus**: One task display reduces overwhelm
|
||||
4. **Auto-Progression**: System picks next task, reducing decision fatigue
|
||||
5. **Time Awareness**: Duration tracking without judgment
|
||||
|
||||
## Core Components
|
||||
|
||||
### Task Manager Core
|
||||
**Purpose**: Central task management with priority queuing and state management
|
||||
**Location**: `~/.claude/tools/task-manager/task_manager.py`
|
||||
|
||||
**Key Features**:
|
||||
- Priority-based task ordering (High/Medium/Low)
|
||||
- Task states: brain_dump, current, completed, snoozed
|
||||
- Auto-progression on task completion
|
||||
- Snooze functionality with time-based wake-up
|
||||
- JSON-based persistence
|
||||
|
||||
**Data Model**:
|
||||
```python
|
||||
class Task:
|
||||
id: str # Timestamp-based unique ID
|
||||
description: str # Task description
|
||||
priority: str # high, medium, low
|
||||
status: str # brain_dump, current, completed, snoozed
|
||||
created_at: str # ISO timestamp
|
||||
started_at: Optional[str] # When task became current
|
||||
completed_at: Optional[str] # Completion timestamp
|
||||
snoozed_until: Optional[str] # Wake-up time for snoozed tasks
|
||||
```
|
||||
|
||||
### Terminal Dashboard
|
||||
**Purpose**: Persistent visual display of current focus and brain dump queue
|
||||
**Location**: `~/.claude/tools/task-manager/dashboard.py`
|
||||
|
||||
**Display Components**:
|
||||
- Current focus with elapsed time
|
||||
- Brain dump queue (up to 8 tasks visible)
|
||||
- Completion statistics
|
||||
- Command reference
|
||||
|
||||
**Update Behavior**:
|
||||
- Auto-refreshes every 1 second
|
||||
- Reads from JSON file (detects CLI changes)
|
||||
- Uses rich library for clean TUI rendering
|
||||
|
||||
### CLI Interface
|
||||
**Purpose**: Zero-friction command-line task operations
|
||||
**Location**: `~/.claude/tools/task-manager/cli.py`
|
||||
|
||||
**Core Commands**:
|
||||
- `task dump "description"` - Brain dump capture (most important!)
|
||||
- `task done` - Complete current, auto-load next
|
||||
- `task next` - Skip to next task
|
||||
- `task snooze [hours]` - Snooze current task
|
||||
- `task add "description" --now` - Add and make current
|
||||
- `task` - Show current status
|
||||
|
||||
**Priority Flags**:
|
||||
- `--high` - High priority (🔴)
|
||||
- `--medium` - Medium priority (🟡) [default]
|
||||
- `--low` - Low priority (🟢)
|
||||
|
||||
## Implementation Patterns
|
||||
|
||||
### The Context-Switch Spiral Solution
|
||||
**Problem Pattern**:
|
||||
```
|
||||
Working on Task A → Remember Task B → Switch to Task B →
|
||||
Remember Task C → Task A forgotten → Task B becomes "meant to do" → Loop
|
||||
```
|
||||
|
||||
**Solution Pattern**:
|
||||
```
|
||||
Working on Task A → Remember Task B → `task dump "Task B"` →
|
||||
Continue Task A → Complete with `task done` → System auto-loads Task B
|
||||
```
|
||||
|
||||
### Brain Dump Workflow
|
||||
1. **Capture**: `task dump "thought"` (2 seconds, no context switch)
|
||||
2. **Visible**: Task appears in brain dump queue on dashboard
|
||||
3. **Continue**: Keep working on current focus
|
||||
4. **Auto-Load**: Task surfaces when current task completes
|
||||
|
||||
### Priority Management
|
||||
**Automatic Ordering**:
|
||||
1. High priority tasks (🔴) surface first
|
||||
2. Medium priority tasks (🟡) second
|
||||
3. Low priority tasks (🟢) last
|
||||
4. Within priority level, oldest tasks first
|
||||
|
||||
**Use Cases**:
|
||||
- `--high`: Urgent, time-sensitive, or blocking tasks
|
||||
- `--medium`: Normal daily work (default)
|
||||
- `--low`: "Nice to have", research, exploration
|
||||
|
||||
### Time Tracking Pattern
|
||||
**Purpose**: Awareness without judgment
|
||||
- Shows elapsed time on current task
|
||||
- No alerts or pressure
|
||||
- Helps identify time sinks naturally
|
||||
- User decides when to move on
|
||||
|
||||
## Common Workflows
|
||||
|
||||
### Morning Startup
|
||||
```bash
|
||||
# Brain dump everything you need to do
|
||||
task dump "Review PRs" --high
|
||||
task dump "Update documentation"
|
||||
task dump "Research new library" --low
|
||||
|
||||
# Load first task (highest priority)
|
||||
task next
|
||||
|
||||
# Start dashboard in persistent pane
|
||||
task-dashboard
|
||||
```
|
||||
|
||||
### During Work Day
|
||||
```bash
|
||||
# Working on current task... suddenly remember something
|
||||
task dump "email Staci about dinner"
|
||||
|
||||
# Thought is captured, visible in brain dump, keep working
|
||||
|
||||
# Finish current task
|
||||
task done
|
||||
# → Auto-loads next task from queue
|
||||
```
|
||||
|
||||
### Handling Interruptions
|
||||
```bash
|
||||
# Urgent task comes up
|
||||
task add "Fix production bug" --now --high
|
||||
# → Immediately becomes current focus
|
||||
# → Previous task moved back to brain dump
|
||||
|
||||
# Can't work on current task right now
|
||||
task snooze 2
|
||||
# → Snoozed for 2 hours
|
||||
# → Next task auto-loaded
|
||||
```
|
||||
|
||||
### End of Day
|
||||
```bash
|
||||
# Check what's left
|
||||
task
|
||||
|
||||
# Everything in brain dump waits for tomorrow
|
||||
# Completed tasks tracked in stats
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Dashboard Setup
|
||||
**Persistent Pane Strategy**:
|
||||
- Use tmux/screen split pane for dashboard
|
||||
- Keep visible on secondary monitor
|
||||
- Always-visible = can't forget to check
|
||||
- Alternative: Dedicated terminal window
|
||||
|
||||
**Terminal Multiplexer Example** (tmux):
|
||||
```bash
|
||||
# Split terminal horizontally
|
||||
tmux split-window -h
|
||||
|
||||
# Run dashboard in right pane
|
||||
task-dashboard
|
||||
|
||||
# Work in left pane
|
||||
```
|
||||
|
||||
### Brain Dump Discipline
|
||||
**Key Principles**:
|
||||
- Capture thoughts immediately when they appear
|
||||
- Don't evaluate or organize - just dump
|
||||
- Trust the system to surface them later
|
||||
- Priority can be added later if wrong
|
||||
|
||||
**Anti-Patterns to Avoid**:
|
||||
- Stopping work to add detailed task information
|
||||
- Trying to organize tasks while capturing
|
||||
- Skipping capture because task seems "small"
|
||||
- Context switching to "quickly do" the task
|
||||
|
||||
### Priority Usage
|
||||
**High Priority** - Use sparingly:
|
||||
- Urgent deadlines (today/this week)
|
||||
- Blocking other people's work
|
||||
- Production issues
|
||||
- Time-sensitive opportunities
|
||||
|
||||
**Medium Priority** - Default for most work:
|
||||
- Regular daily tasks
|
||||
- Normal project work
|
||||
- Scheduled activities
|
||||
|
||||
**Low Priority** - Background/optional:
|
||||
- Research and exploration
|
||||
- "Someday/maybe" items
|
||||
- Nice-to-have improvements
|
||||
|
||||
### System Maintenance
|
||||
**Daily**:
|
||||
- Complete tasks as you work
|
||||
- Brain dump new thoughts immediately
|
||||
- Trust auto-progression
|
||||
|
||||
**Weekly**:
|
||||
- Review low-priority items
|
||||
- Clean up stale tasks
|
||||
- Adjust priorities if needed
|
||||
|
||||
## Integration Patterns
|
||||
|
||||
### PATH Integration
|
||||
**Location**: `~/.bashrc`
|
||||
```bash
|
||||
export PATH="$HOME/.claude/tools/task-manager:$PATH"
|
||||
```
|
||||
|
||||
**Benefits**:
|
||||
- `task` and `task-dashboard` available globally
|
||||
- No full paths needed
|
||||
- Muscle memory development
|
||||
|
||||
### Data Storage
|
||||
**Location**: `~/.claude/tools/task-manager/tasks.json`
|
||||
**Format**: JSON with task array and metadata
|
||||
|
||||
**Backup Strategy**:
|
||||
- File-based storage enables easy backup
|
||||
- Can sync via Dropbox/Google Drive if desired
|
||||
- Simple JSON enables scripting/reporting
|
||||
|
||||
### Future Integration Points
|
||||
**Planned Enhancements**:
|
||||
- Notion MCP sync (Cal has integration available)
|
||||
- Discord notifications on task completion
|
||||
- Scheduled Jarvis check-ins (periodic reminders)
|
||||
- Voice capture integration
|
||||
- Pomodoro timer integration
|
||||
- Analytics and reporting
|
||||
|
||||
## Technical Details
|
||||
|
||||
### File Structure
|
||||
```
|
||||
~/.claude/tools/task-manager/
|
||||
├── task_manager.py # Core task management logic
|
||||
├── dashboard.py # TUI dashboard (persistent display)
|
||||
├── cli.py # Command-line interface
|
||||
├── task # Shell wrapper for CLI
|
||||
├── task-dashboard # Shell wrapper for dashboard
|
||||
├── tasks.json # Data storage (auto-created)
|
||||
└── README.md # User documentation
|
||||
```
|
||||
|
||||
### Dependencies
|
||||
- Python 3.7+
|
||||
- `rich` library for TUI rendering
|
||||
- No other external dependencies
|
||||
|
||||
### Installation
|
||||
**Already Installed** - Cal's system:
|
||||
- Scripts: `~/.claude/tools/task-manager/`
|
||||
- PATH: Added to `~/.bashrc`
|
||||
- Data: `~/.claude/tools/task-manager/tasks.json`
|
||||
|
||||
## ADHD-Specific Optimizations
|
||||
|
||||
### Why Traditional Systems Fail
|
||||
1. **Hidden in apps** - Out of sight, out of mind
|
||||
2. **Too much organization** - Analysis paralysis
|
||||
3. **Too many features** - Overwhelm and abandonment
|
||||
4. **No immediate feedback** - Delayed dopamine
|
||||
5. **Context switching required** - Defeats purpose
|
||||
|
||||
### How This System Addresses ADHD
|
||||
1. **Always visible** - Persistent terminal display
|
||||
2. **Minimal organization** - Just dump and go
|
||||
3. **Minimal features** - Only what's needed
|
||||
4. **Immediate feedback** - Instant completion confirmation
|
||||
5. **Zero context switch** - Capture without abandoning work
|
||||
|
||||
### The Psychology
|
||||
**Reduced Anxiety**: Thoughts are captured and visible
|
||||
**Maintained Focus**: No need to switch away from current work
|
||||
**Reduced Overwhelm**: Single focus display, not full list
|
||||
**Dopamine Feedback**: Immediate confirmation on completion
|
||||
**Decision Reduction**: System picks next task automatically
|
||||
|
||||
This productivity system is optimized specifically for ADHD workflows and terminal-based work environments, providing the minimal viable structure needed to maintain focus while capturing interrupting thoughts.
|
||||
408
productivity/n8n/CONTEXT.md
Normal file
408
productivity/n8n/CONTEXT.md
Normal file
@ -0,0 +1,408 @@
|
||||
# n8n Workflow Automation Platform
|
||||
|
||||
Self-hosted workflow automation platform for building integrations and automating tasks across your homelab infrastructure.
|
||||
|
||||
## Overview
|
||||
|
||||
n8n is a fair-code licensed workflow automation tool that allows you to connect various services and automate complex processes through a visual workflow editor.
|
||||
|
||||
**Deployment Date:** 2025-11-13
|
||||
**Version:** Latest (auto-updating via Docker)
|
||||
**License:** Fair-code (free for self-hosted)
|
||||
|
||||
## Architecture
|
||||
|
||||
### Infrastructure
|
||||
|
||||
**LXC Container:**
|
||||
- **VMID:** 210
|
||||
- **Hostname:** docker-n8n-lxc
|
||||
- **IP Address:** 10.10.0.210
|
||||
- **CPU:** 4 cores
|
||||
- **RAM:** 8GB
|
||||
- **Disk:** 128GB
|
||||
- **OS:** Ubuntu 20.04 LTS
|
||||
|
||||
**Services:**
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ https://n8n.manticorum.com │
|
||||
│ (Nginx Proxy Manager) │
|
||||
└──────────────┬──────────────────────┘
|
||||
│ SSL Termination
|
||||
↓
|
||||
┌─────────────────────────────────────┐
|
||||
│ LXC 210 (10.10.0.210) │
|
||||
│ │
|
||||
│ ┌──────────────┐ ┌─────────────┐ │
|
||||
│ │ n8n │ │ PostgreSQL │ │
|
||||
│ │ :5678 │→ │ :5432 │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ - Workflows │ │ - Database │ │
|
||||
│ │ - Webhooks │ │ - Creds │ │
|
||||
│ │ - Executions │ │ - History │ │
|
||||
│ └──────────────┘ └─────────────┘ │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Data Persistence
|
||||
|
||||
**Docker Volumes:**
|
||||
- `n8n_data` - Workflow files, credentials, settings
|
||||
- `n8n_postgres_data` - PostgreSQL database
|
||||
|
||||
**Configuration:**
|
||||
- `/opt/n8n/docker-compose.yml` - Container orchestration
|
||||
- `/opt/n8n/.env` - Environment variables and secrets
|
||||
|
||||
## Access & Authentication
|
||||
|
||||
### External Access (Production)
|
||||
```
|
||||
URL: https://n8n.manticorum.com/
|
||||
Username: admin
|
||||
Password: [stored in .env file]
|
||||
Protocol: HTTPS (via NPM with Let's Encrypt)
|
||||
```
|
||||
|
||||
### Internal Access (Development/Testing)
|
||||
```
|
||||
URL: http://10.10.0.210:5678
|
||||
Username: admin
|
||||
Password: [same as above]
|
||||
Protocol: HTTP (direct to container)
|
||||
```
|
||||
|
||||
### SSH Access
|
||||
```bash
|
||||
# Using homelab SSH key
|
||||
ssh -i ~/.ssh/homelab_rsa root@10.10.0.210
|
||||
|
||||
# Or with SSH config alias
|
||||
ssh n8n
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Key configuration settings in `/opt/n8n/.env`:
|
||||
|
||||
**Database:**
|
||||
```bash
|
||||
DB_TYPE=postgresdb
|
||||
DB_POSTGRESDB_HOST=postgres
|
||||
DB_POSTGRESDB_PORT=5432
|
||||
DB_POSTGRESDB_DATABASE=n8n
|
||||
DB_POSTGRESDB_USER=n8n
|
||||
DB_POSTGRESDB_PASSWORD=[secure-random-password]
|
||||
```
|
||||
|
||||
**n8n Settings:**
|
||||
```bash
|
||||
N8N_HOST=n8n.manticorum.com
|
||||
N8N_PROTOCOL=https
|
||||
WEBHOOK_URL=https://n8n.manticorum.com/
|
||||
GENERIC_TIMEZONE=America/Los_Angeles
|
||||
```
|
||||
|
||||
**Security:**
|
||||
```bash
|
||||
N8N_ENCRYPTION_KEY=[64-char-hex-key] # NEVER CHANGE AFTER FIRST RUN
|
||||
N8N_BASIC_AUTH_ACTIVE=true
|
||||
N8N_BASIC_AUTH_USER=admin
|
||||
N8N_BASIC_AUTH_PASSWORD=[secure-random-password]
|
||||
```
|
||||
|
||||
**Performance:**
|
||||
```bash
|
||||
NODE_ENV=production
|
||||
EXECUTIONS_PROCESS=main
|
||||
EXECUTIONS_MODE=regular
|
||||
```
|
||||
|
||||
### Nginx Proxy Manager Configuration
|
||||
|
||||
**Proxy Host Settings:**
|
||||
- **Domain:** n8n.manticorum.com
|
||||
- **Scheme:** http
|
||||
- **Forward Host:** 10.10.0.210
|
||||
- **Forward Port:** 5678
|
||||
- **Websockets:** ✅ Enabled (REQUIRED)
|
||||
- **Block Common Exploits:** ✅ Enabled
|
||||
- **SSL:** Let's Encrypt with HTTP/2 and HSTS
|
||||
|
||||
**Custom Nginx Config:**
|
||||
```nginx
|
||||
# Increase timeout for long-running workflows
|
||||
proxy_read_timeout 300;
|
||||
proxy_connect_timeout 300;
|
||||
proxy_send_timeout 300;
|
||||
|
||||
# WebSocket support for real-time workflow updates
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
# Headers for n8n
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
```
|
||||
|
||||
## Operations
|
||||
|
||||
### Starting/Stopping Services
|
||||
|
||||
**Start n8n:**
|
||||
```bash
|
||||
ssh root@10.10.0.210 "cd /opt/n8n && docker compose up -d"
|
||||
```
|
||||
|
||||
**Stop n8n:**
|
||||
```bash
|
||||
ssh root@10.10.0.210 "cd /opt/n8n && docker compose down"
|
||||
```
|
||||
|
||||
**Restart n8n (apply configuration changes):**
|
||||
```bash
|
||||
ssh root@10.10.0.210 "cd /opt/n8n && docker compose restart n8n"
|
||||
```
|
||||
|
||||
**View logs:**
|
||||
```bash
|
||||
ssh root@10.10.0.210 "cd /opt/n8n && docker compose logs -f"
|
||||
|
||||
# Just n8n logs
|
||||
ssh root@10.10.0.210 "cd /opt/n8n && docker compose logs -f n8n"
|
||||
|
||||
# Just PostgreSQL logs
|
||||
ssh root@10.10.0.210 "cd /opt/n8n && docker compose logs -f postgres"
|
||||
```
|
||||
|
||||
**Check status:**
|
||||
```bash
|
||||
ssh root@10.10.0.210 "cd /opt/n8n && docker compose ps"
|
||||
```
|
||||
|
||||
### Updates
|
||||
|
||||
**Update to latest n8n version:**
|
||||
```bash
|
||||
ssh root@10.10.0.210 "cd /opt/n8n && docker compose pull && docker compose up -d"
|
||||
```
|
||||
|
||||
**Note:** n8n handles database migrations automatically on startup.
|
||||
|
||||
### Backups
|
||||
|
||||
**Database Backup:**
|
||||
```bash
|
||||
# Create timestamped backup
|
||||
ssh root@10.10.0.210 "cd /opt/n8n && docker compose exec -T postgres pg_dump -U n8n n8n > /root/n8n-backup-\$(date +%Y%m%d-%H%M%S).sql"
|
||||
|
||||
# Download backup locally
|
||||
scp -i ~/.ssh/homelab_rsa root@10.10.0.210:/root/n8n-backup-*.sql ~/backups/n8n/
|
||||
```
|
||||
|
||||
**Full Backup (including volumes):**
|
||||
```bash
|
||||
ssh root@10.10.0.210 "
|
||||
cd /opt/n8n
|
||||
docker compose down
|
||||
tar -czf /root/n8n-full-backup-\$(date +%Y%m%d-%H%M%S).tar.gz \
|
||||
/opt/n8n \
|
||||
/var/lib/docker/volumes/n8n_data \
|
||||
/var/lib/docker/volumes/n8n_postgres_data
|
||||
docker compose up -d
|
||||
"
|
||||
```
|
||||
|
||||
**Restore from Backup:**
|
||||
```bash
|
||||
# Restore database only
|
||||
cat n8n-backup-20251113.sql | ssh root@10.10.0.210 "docker compose exec -T postgres psql -U n8n n8n"
|
||||
|
||||
# Full restore (requires downtime)
|
||||
ssh root@10.10.0.210 "
|
||||
cd /opt/n8n
|
||||
docker compose down
|
||||
tar -xzf /root/n8n-full-backup-20251113.tar.gz -C /
|
||||
docker compose up -d
|
||||
"
|
||||
```
|
||||
|
||||
### Monitoring
|
||||
|
||||
**Health Check:**
|
||||
```bash
|
||||
# Check if n8n is responding
|
||||
curl -s -o /dev/null -w "%{http_code}" http://10.10.0.210:5678
|
||||
# Expected: 200
|
||||
|
||||
# Check container health
|
||||
ssh root@10.10.0.210 "docker ps --filter name=n8n --format '{{.Status}}'"
|
||||
```
|
||||
|
||||
**Resource Usage:**
|
||||
```bash
|
||||
ssh root@10.10.0.210 "docker stats --no-stream n8n n8n-postgres"
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Encryption Key
|
||||
- **Critical:** The `N8N_ENCRYPTION_KEY` encrypts all stored credentials
|
||||
- **Never change** this key after initial setup - it will make existing credentials unreadable
|
||||
- **Backup securely** - store this key in a password manager or secrets vault
|
||||
|
||||
### Credentials Storage
|
||||
- All workflow credentials are encrypted in the PostgreSQL database
|
||||
- Credentials are never exposed in workflow definitions
|
||||
- Use environment variables for sensitive data when possible
|
||||
|
||||
### Network Security
|
||||
- Port 5678 exposed on container for internal access
|
||||
- External access only via NPM (HTTPS with SSL)
|
||||
- Consider using NPM's built-in access lists for IP whitelisting
|
||||
|
||||
### Authentication
|
||||
- Basic auth enabled by default
|
||||
- Consider integrating with SSO for multi-user scenarios
|
||||
- Regularly rotate admin password
|
||||
|
||||
## Active Workflows
|
||||
|
||||
### Ko-fi → Paper Dynasty Integration
|
||||
|
||||
**Purpose:** Automated pack distribution when users purchase on Ko-fi
|
||||
|
||||
**Documentation:** `workflows/kofi-paper-dynasty.md`
|
||||
|
||||
**Workflow Features:**
|
||||
- Receives Ko-fi shop order webhooks
|
||||
- Multi-method user identification (discord_userid or team abbrev)
|
||||
- Product mapping via custom variables
|
||||
- Paper Dynasty API integration for pack grants
|
||||
- Discord notifications for success/errors/manual review
|
||||
- Comprehensive error handling and retry logic
|
||||
|
||||
**Webhook URLs:**
|
||||
- Test: `https://n8n.manticorum.com/webhook-test/kofi-paperdy`
|
||||
- Production: `https://n8n.manticorum.com/webhook/kofi-paperdy`
|
||||
|
||||
**Related Files:**
|
||||
- `workflows/kofi-paper-dynasty.md` - Complete setup guide
|
||||
- `workflows/kofi-product-mapping-template.json` - Product configuration template
|
||||
- `workflows/kofi-testing-guide.md` - Testing procedures and sample payloads
|
||||
|
||||
**Credentials Required:**
|
||||
- Ko-fi Verification Token
|
||||
- Paper Dynasty API Key
|
||||
- Discord Webhook URL
|
||||
|
||||
**Custom Variables:**
|
||||
- `KOFI_PRODUCT_MAP` - JSON mapping of Ko-fi products to PD packs
|
||||
|
||||
---
|
||||
|
||||
## Integration Patterns
|
||||
|
||||
### Common Use Cases
|
||||
|
||||
**Homelab Automation:**
|
||||
- Monitor system health and send alerts
|
||||
- Backup automation workflows
|
||||
- Service restart orchestration
|
||||
- Log aggregation and analysis
|
||||
|
||||
**Infrastructure Integration:**
|
||||
- Proxmox API automation
|
||||
- Docker container management
|
||||
- Network device configuration
|
||||
- DNS record management
|
||||
|
||||
**Notification Workflows:**
|
||||
- Discord/Slack alerts
|
||||
- Email notifications
|
||||
- SMS via Twilio
|
||||
- Push notifications
|
||||
|
||||
**Data Processing:**
|
||||
- API data collection and transformation
|
||||
- Scheduled report generation
|
||||
- Database sync operations
|
||||
- File processing and organization
|
||||
|
||||
**E-commerce Integration:**
|
||||
- Ko-fi payment processing (Paper Dynasty packs)
|
||||
- Automated product fulfillment
|
||||
- Customer notification workflows
|
||||
- Transaction logging and analytics
|
||||
|
||||
### Webhook Configuration
|
||||
|
||||
**Webhook URLs follow this pattern:**
|
||||
```
|
||||
https://n8n.manticorum.com/webhook/{webhook-path}
|
||||
https://n8n.manticorum.com/webhook-test/{webhook-path}
|
||||
```
|
||||
|
||||
**Test webhooks before production:**
|
||||
- Use `/webhook-test/` for development
|
||||
- Switch to `/webhook/` for production
|
||||
- Configure URL in n8n workflow webhook node
|
||||
|
||||
## Performance Tuning
|
||||
|
||||
### Resource Allocation
|
||||
|
||||
Current allocation is suitable for:
|
||||
- Up to 50 concurrent workflow executions
|
||||
- 100+ saved workflows
|
||||
- Moderate webhook traffic (<100 req/min)
|
||||
|
||||
**If experiencing performance issues:**
|
||||
- Increase LXC RAM allocation
|
||||
- Add more CPU cores
|
||||
- Monitor PostgreSQL query performance
|
||||
- Consider workflow optimization
|
||||
|
||||
### Execution Modes
|
||||
|
||||
**Current:** `EXECUTIONS_MODE=regular` (main process)
|
||||
|
||||
**Alternative:** `EXECUTIONS_MODE=queue` (separate worker)
|
||||
- Better for high-volume workflows
|
||||
- Requires Redis
|
||||
- More complex setup
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
See [troubleshooting.md](troubleshooting.md) for common issues and solutions.
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Troubleshooting Guide](troubleshooting.md) - Common issues and fixes
|
||||
- [Examples](examples/) - Configuration examples and templates
|
||||
- [Docker Context](/docker/CONTEXT.md) - Container infrastructure patterns
|
||||
- [Networking Context](/networking/CONTEXT.md) - NPM and reverse proxy setup
|
||||
|
||||
## References
|
||||
|
||||
- **Official Documentation:** https://docs.n8n.io/
|
||||
- **Community Forum:** https://community.n8n.io/
|
||||
- **GitHub Repository:** https://github.com/n8n-io/n8n
|
||||
- **Docker Hub:** https://hub.docker.com/r/n8nio/n8n
|
||||
|
||||
## Change Log
|
||||
|
||||
### 2025-11-13 - Initial Deployment
|
||||
- Created LXC 210 (docker-n8n-lxc) with 4 cores, 8GB RAM, 128GB disk
|
||||
- Deployed n8n with PostgreSQL 15 backend
|
||||
- Configured domain: n8n.manticorum.com
|
||||
- Integrated with Nginx Proxy Manager for HTTPS access
|
||||
- Enabled basic authentication
|
||||
- Set timezone to America/Los_Angeles
|
||||
196
productivity/n8n/README.md
Normal file
196
productivity/n8n/README.md
Normal file
@ -0,0 +1,196 @@
|
||||
# n8n Workflow Automation - Quick Reference
|
||||
|
||||
Self-hosted n8n instance at **https://n8n.manticorum.com/**
|
||||
|
||||
## Quick Links
|
||||
|
||||
- **Web UI:** https://n8n.manticorum.com/
|
||||
- **Documentation:** [CONTEXT.md](CONTEXT.md) - Complete technical documentation
|
||||
- **Troubleshooting:** [troubleshooting.md](troubleshooting.md) - Common issues and solutions
|
||||
- **Examples:** [examples/](examples/) - Configuration templates
|
||||
|
||||
## At a Glance
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **URL** | https://n8n.manticorum.com/ |
|
||||
| **Internal IP** | 10.10.0.210 |
|
||||
| **Container** | LXC 210 (docker-n8n-lxc) |
|
||||
| **Resources** | 4 CPU, 8GB RAM, 128GB disk |
|
||||
| **Database** | PostgreSQL 15 |
|
||||
| **Deployment** | 2025-11-13 |
|
||||
|
||||
## Quick Commands
|
||||
|
||||
```bash
|
||||
# SSH into n8n host
|
||||
ssh -i ~/.ssh/homelab_rsa root@10.10.0.210
|
||||
|
||||
# View logs
|
||||
ssh root@10.10.0.210 "cd /opt/n8n && docker compose logs -f n8n"
|
||||
|
||||
# Restart n8n
|
||||
ssh root@10.10.0.210 "cd /opt/n8n && docker compose restart n8n"
|
||||
|
||||
# Backup database
|
||||
ssh root@10.10.0.210 "cd /opt/n8n && docker compose exec -T postgres pg_dump -U n8n n8n > /root/n8n-backup-\$(date +%Y%m%d).sql"
|
||||
|
||||
# Update to latest version
|
||||
ssh root@10.10.0.210 "cd /opt/n8n && docker compose pull && docker compose up -d"
|
||||
|
||||
# Check status
|
||||
ssh root@10.10.0.210 "cd /opt/n8n && docker compose ps"
|
||||
```
|
||||
|
||||
## File Locations
|
||||
|
||||
| Path | Description |
|
||||
|------|-------------|
|
||||
| `/opt/n8n/docker-compose.yml` | Container configuration |
|
||||
| `/opt/n8n/.env` | Environment variables and secrets |
|
||||
| `n8n_data` volume | Workflows, credentials, settings |
|
||||
| `n8n_postgres_data` volume | PostgreSQL database |
|
||||
|
||||
## Access Credentials
|
||||
|
||||
**Location:** `/opt/n8n/.env` on LXC 210
|
||||
|
||||
View current credentials:
|
||||
```bash
|
||||
ssh root@10.10.0.210 "cat /opt/n8n/.env | grep BASIC_AUTH"
|
||||
```
|
||||
|
||||
## Common Tasks
|
||||
|
||||
### Create a New Workflow
|
||||
1. Login to https://n8n.manticorum.com/
|
||||
2. Click "Add workflow"
|
||||
3. Add nodes by clicking "+"
|
||||
4. Connect nodes by dragging
|
||||
5. Test with "Execute Workflow"
|
||||
6. Activate with toggle switch
|
||||
|
||||
### Setup a Webhook
|
||||
1. Add "Webhook" node to workflow
|
||||
2. Choose HTTP Method (GET, POST, etc.)
|
||||
3. Set Path (e.g., `my-webhook`)
|
||||
4. Activate workflow
|
||||
5. Use URL: `https://n8n.manticorum.com/webhook/my-webhook`
|
||||
|
||||
### Backup Workflows
|
||||
```bash
|
||||
# Backup database (includes all workflows and credentials)
|
||||
ssh root@10.10.0.210 "cd /opt/n8n && docker compose exec -T postgres pg_dump -U n8n n8n" > n8n-backup.sql
|
||||
|
||||
# Download locally
|
||||
scp -i ~/.ssh/homelab_rsa root@10.10.0.210:/root/n8n-backup-*.sql ~/backups/n8n/
|
||||
```
|
||||
|
||||
### View Execution History
|
||||
1. Login to n8n UI
|
||||
2. Click "Executions" in top nav
|
||||
3. Filter by workflow, status, or date
|
||||
4. Click execution to view details
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Internet
|
||||
↓
|
||||
Nginx Proxy Manager (NPM)
|
||||
↓ HTTPS/SSL
|
||||
https://n8n.manticorum.com
|
||||
↓ HTTP (internal)
|
||||
LXC 210 (10.10.0.210:5678)
|
||||
├─ n8n Container
|
||||
│ └─ Workflow Engine
|
||||
└─ PostgreSQL Container
|
||||
└─ Database (workflows, credentials, history)
|
||||
```
|
||||
|
||||
## Integration Examples
|
||||
|
||||
### Discord Notifications
|
||||
1. Webhook node → Discord Webhook URL
|
||||
2. Message node → Format notification
|
||||
3. Trigger: Manual, Schedule, or Webhook
|
||||
|
||||
### Homelab Monitoring
|
||||
1. HTTP Request → Check service health
|
||||
2. IF node → Check response code
|
||||
3. Discord/Email → Send alert if down
|
||||
4. Schedule Trigger → Run every 5 minutes
|
||||
|
||||
### Automated Backups
|
||||
1. Execute Command → Run backup script
|
||||
2. HTTP Request → Upload to cloud storage
|
||||
3. Discord → Send completion notification
|
||||
4. Schedule Trigger → Daily at 2 AM
|
||||
|
||||
### API Data Collection
|
||||
1. HTTP Request → Fetch data from API
|
||||
2. Set node → Transform data
|
||||
3. PostgreSQL → Store in database
|
||||
4. Schedule Trigger → Hourly
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
- ✅ Basic authentication enabled
|
||||
- ✅ HTTPS via NPM with Let's Encrypt
|
||||
- ✅ Internal network only (no direct internet exposure)
|
||||
- ✅ Credentials encrypted with N8N_ENCRYPTION_KEY
|
||||
- ⚠️ Never share encryption key
|
||||
- ⚠️ Regularly rotate admin password
|
||||
- ⚠️ Review workflow permissions
|
||||
|
||||
## Troubleshooting Quick Checks
|
||||
|
||||
| Issue | Quick Check |
|
||||
|-------|-------------|
|
||||
| Can't access n8n.manticorum.com | `curl http://10.10.0.210:5678` |
|
||||
| Login fails | Check `.env` file for correct credentials |
|
||||
| Webhook not working | Verify workflow is **Active** (green toggle) |
|
||||
| Slow performance | `ssh root@10.10.0.210 "docker stats n8n"` |
|
||||
| Database errors | `ssh root@10.10.0.210 "docker compose logs postgres"` |
|
||||
|
||||
See [troubleshooting.md](troubleshooting.md) for detailed solutions.
|
||||
|
||||
## Resources
|
||||
|
||||
- **Official Docs:** https://docs.n8n.io/
|
||||
- **Community Forum:** https://community.n8n.io/
|
||||
- **Workflow Templates:** https://n8n.io/workflows/
|
||||
- **Discord:** https://discord.gg/n8n
|
||||
|
||||
## Maintenance Schedule
|
||||
|
||||
**Weekly:**
|
||||
- Review failed executions
|
||||
- Check disk space
|
||||
- Monitor container health
|
||||
|
||||
**Monthly:**
|
||||
- Backup database
|
||||
- Update n8n version
|
||||
- Clean up old execution data
|
||||
- Review and archive unused workflows
|
||||
|
||||
**Quarterly:**
|
||||
- Test disaster recovery
|
||||
- Audit credentials
|
||||
- Review webhook security
|
||||
- Performance optimization
|
||||
|
||||
## Support
|
||||
|
||||
For issues or questions:
|
||||
1. Check [troubleshooting.md](troubleshooting.md)
|
||||
2. Review [CONTEXT.md](CONTEXT.md) for configuration details
|
||||
3. Search n8n community forum
|
||||
4. Check n8n documentation
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2025-11-13
|
||||
**Deployed By:** Claude Code automation
|
||||
**Maintained By:** Cal Corum
|
||||
90
productivity/n8n/examples/docker-compose.yml
Normal file
90
productivity/n8n/examples/docker-compose.yml
Normal file
@ -0,0 +1,90 @@
|
||||
version: '3.8'
|
||||
|
||||
# n8n Production Deployment with PostgreSQL
|
||||
# Location: /opt/n8n/docker-compose.yml on LXC 210 (10.10.0.210)
|
||||
#
|
||||
# This is the complete docker-compose configuration for n8n.manticorum.com
|
||||
# Updated: 2025-11-13
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15-alpine
|
||||
container_name: n8n-postgres
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- POSTGRES_USER=${POSTGRES_USER}
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
||||
- POSTGRES_DB=${POSTGRES_DB}
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -h localhost -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
networks:
|
||||
- n8n-network
|
||||
|
||||
n8n:
|
||||
image: n8nio/n8n:latest
|
||||
container_name: n8n
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- "5678:5678"
|
||||
environment:
|
||||
# Database Configuration
|
||||
- DB_TYPE=postgresdb
|
||||
- DB_POSTGRESDB_HOST=postgres
|
||||
- DB_POSTGRESDB_PORT=5432
|
||||
- DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
|
||||
- DB_POSTGRESDB_USER=${POSTGRES_USER}
|
||||
- DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
|
||||
|
||||
# n8n Configuration
|
||||
- N8N_HOST=${N8N_HOST}
|
||||
- N8N_PORT=5678
|
||||
- N8N_PROTOCOL=${N8N_PROTOCOL}
|
||||
- WEBHOOK_URL=${WEBHOOK_URL}
|
||||
- GENERIC_TIMEZONE=${TIMEZONE}
|
||||
|
||||
# Security
|
||||
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
|
||||
- N8N_BASIC_AUTH_ACTIVE=true
|
||||
- N8N_BASIC_AUTH_USER=${N8N_BASIC_AUTH_USER}
|
||||
- N8N_BASIC_AUTH_PASSWORD=${N8N_BASIC_AUTH_PASSWORD}
|
||||
|
||||
# Performance
|
||||
- NODE_ENV=production
|
||||
- EXECUTIONS_PROCESS=main
|
||||
- EXECUTIONS_MODE=regular
|
||||
|
||||
# Logging
|
||||
- N8N_LOG_LEVEL=info
|
||||
- N8N_LOG_OUTPUT=console
|
||||
|
||||
# Execution Data Management (Optional)
|
||||
# Uncomment to enable automatic cleanup:
|
||||
# - EXECUTIONS_DATA_PRUNE=true
|
||||
# - EXECUTIONS_DATA_MAX_AGE=168 # Delete after 7 days
|
||||
# - EXECUTIONS_DATA_SAVE_ON_ERROR=all
|
||||
# - EXECUTIONS_DATA_SAVE_ON_SUCCESS=all
|
||||
# - EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS=true
|
||||
|
||||
volumes:
|
||||
- n8n_data:/home/node/.n8n
|
||||
networks:
|
||||
- n8n-network
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
name: n8n_postgres_data
|
||||
n8n_data:
|
||||
name: n8n_data
|
||||
|
||||
networks:
|
||||
n8n-network:
|
||||
name: n8n-network
|
||||
driver: bridge
|
||||
102
productivity/n8n/examples/env.template
Normal file
102
productivity/n8n/examples/env.template
Normal file
@ -0,0 +1,102 @@
|
||||
# n8n Environment Configuration Template
|
||||
# Copy this file to /opt/n8n/.env and customize values
|
||||
#
|
||||
# IMPORTANT: Generate secure random values for passwords and keys!
|
||||
# Never commit .env files to version control!
|
||||
|
||||
# ============================================================================
|
||||
# PostgreSQL Configuration
|
||||
# ============================================================================
|
||||
POSTGRES_USER=n8n
|
||||
POSTGRES_PASSWORD=CHANGE_ME_GENERATE_RANDOM_PASSWORD
|
||||
POSTGRES_DB=n8n
|
||||
|
||||
# Generate random password:
|
||||
# openssl rand -base64 32 | tr -d '/+='
|
||||
|
||||
# ============================================================================
|
||||
# n8n Configuration
|
||||
# ============================================================================
|
||||
|
||||
# Your domain name (used for webhooks and external access)
|
||||
N8N_HOST=n8n.manticorum.com
|
||||
|
||||
# Protocol (should be https if using NPM with SSL)
|
||||
N8N_PROTOCOL=https
|
||||
|
||||
# Webhook URL (MUST match your public URL exactly)
|
||||
# This is used for webhook callbacks from external services
|
||||
WEBHOOK_URL=https://n8n.manticorum.com/
|
||||
|
||||
# Timezone for executions and cron jobs
|
||||
# Find yours at: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
|
||||
TIMEZONE=America/Los_Angeles
|
||||
|
||||
# ============================================================================
|
||||
# Security Configuration
|
||||
# ============================================================================
|
||||
|
||||
# Encryption Key - CRITICAL!
|
||||
# This encrypts all credentials stored in the database
|
||||
# ⚠️ NEVER CHANGE THIS AFTER FIRST RUN - you'll lose access to encrypted credentials!
|
||||
# ⚠️ BACKUP THIS KEY SECURELY - store in password manager
|
||||
#
|
||||
# Generate with: openssl rand -hex 32
|
||||
N8N_ENCRYPTION_KEY=CHANGE_ME_GENERATE_64_CHAR_HEX
|
||||
|
||||
# Basic Authentication
|
||||
# Protects your n8n instance from unauthorized access
|
||||
N8N_BASIC_AUTH_ACTIVE=true
|
||||
N8N_BASIC_AUTH_USER=admin
|
||||
N8N_BASIC_AUTH_PASSWORD=CHANGE_ME_GENERATE_RANDOM_PASSWORD
|
||||
|
||||
# Generate random password:
|
||||
# openssl rand -base64 16 | tr -d '/+='
|
||||
|
||||
# ============================================================================
|
||||
# Optional: Execution Data Management
|
||||
# ============================================================================
|
||||
|
||||
# Automatically delete old execution data to save disk space
|
||||
# Uncomment and customize as needed:
|
||||
|
||||
# EXECUTIONS_DATA_PRUNE=true
|
||||
# EXECUTIONS_DATA_MAX_AGE=168 # Delete executions older than 7 days (in hours)
|
||||
# EXECUTIONS_DATA_SAVE_ON_ERROR=all # Save error data (all, none)
|
||||
# EXECUTIONS_DATA_SAVE_ON_SUCCESS=all # Save success data (all, none)
|
||||
# EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS=true # Save manually triggered executions
|
||||
|
||||
# ============================================================================
|
||||
# Optional: Advanced Configuration
|
||||
# ============================================================================
|
||||
|
||||
# Uncomment and customize as needed:
|
||||
|
||||
# Email Configuration (for alerts, sharing workflows, etc.)
|
||||
# N8N_EMAIL_MODE=smtp
|
||||
# N8N_SMTP_HOST=smtp.gmail.com
|
||||
# N8N_SMTP_PORT=465
|
||||
# N8N_SMTP_USER=your-email@gmail.com
|
||||
# N8N_SMTP_PASS=your-app-password
|
||||
# N8N_SMTP_SENDER=your-email@gmail.com
|
||||
# N8N_SMTP_SSL=true
|
||||
|
||||
# External Hooks (for custom event handling)
|
||||
# EXTERNAL_HOOK_FILES=/data/hooks/hooks.js
|
||||
|
||||
# Workflow Default Settings
|
||||
# N8N_DEFAULT_BINARY_DATA_MODE=filesystem
|
||||
# N8N_BINARY_DATA_TTL=60 # Keep binary data for 60 minutes
|
||||
|
||||
# Metrics (for monitoring)
|
||||
# N8N_METRICS=true
|
||||
# N8N_METRICS_PREFIX=n8n_
|
||||
|
||||
# ============================================================================
|
||||
# Deployment Information
|
||||
# ============================================================================
|
||||
|
||||
# Deployment Date: 2025-11-13
|
||||
# LXC Container: 210 (docker-n8n-lxc)
|
||||
# IP Address: 10.10.0.210
|
||||
# Location: /opt/n8n/.env
|
||||
577
productivity/n8n/troubleshooting.md
Normal file
577
productivity/n8n/troubleshooting.md
Normal file
@ -0,0 +1,577 @@
|
||||
# n8n Troubleshooting Guide
|
||||
|
||||
Common issues and solutions for the n8n deployment at n8n.manticorum.com.
|
||||
|
||||
## Quick Diagnostics
|
||||
|
||||
**First steps when n8n isn't working:**
|
||||
|
||||
```bash
|
||||
# Check container status
|
||||
ssh root@10.10.0.210 "docker ps --filter name=n8n"
|
||||
|
||||
# Check logs for errors
|
||||
ssh root@10.10.0.210 "cd /opt/n8n && docker compose logs --tail=50 n8n"
|
||||
|
||||
# Check if service is responding
|
||||
curl -I http://10.10.0.210:5678
|
||||
|
||||
# Check NPM proxy status (if external access fails)
|
||||
# Access NPM UI and check proxy host status
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Container Issues
|
||||
|
||||
### n8n Container Won't Start
|
||||
|
||||
**Symptoms:**
|
||||
- Container exits immediately after starting
|
||||
- `docker ps` shows no n8n container
|
||||
- Error in logs: "database connection failed"
|
||||
|
||||
**Diagnosis:**
|
||||
```bash
|
||||
ssh root@10.10.0.210 "cd /opt/n8n && docker compose logs n8n | tail -30"
|
||||
```
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. **Check PostgreSQL is healthy:**
|
||||
```bash
|
||||
ssh root@10.10.0.210 "docker compose ps postgres"
|
||||
# Status should show "healthy"
|
||||
```
|
||||
|
||||
2. **Verify database credentials in .env:**
|
||||
```bash
|
||||
ssh root@10.10.0.210 "cat /opt/n8n/.env | grep POSTGRES"
|
||||
```
|
||||
|
||||
3. **Restart both services:**
|
||||
```bash
|
||||
ssh root@10.10.0.210 "cd /opt/n8n && docker compose down && docker compose up -d"
|
||||
```
|
||||
|
||||
4. **Check database connectivity:**
|
||||
```bash
|
||||
ssh root@10.10.0.210 "docker compose exec postgres psql -U n8n -d n8n -c 'SELECT 1;'"
|
||||
```
|
||||
|
||||
### PostgreSQL Container Issues
|
||||
|
||||
**Symptoms:**
|
||||
- n8n fails to connect to database
|
||||
- PostgreSQL container shows "unhealthy" status
|
||||
|
||||
**Diagnosis:**
|
||||
```bash
|
||||
ssh root@10.10.0.210 "cd /opt/n8n && docker compose logs postgres | tail -50"
|
||||
```
|
||||
|
||||
**Common Causes:**
|
||||
|
||||
1. **Corrupted database:**
|
||||
```bash
|
||||
# Check database integrity
|
||||
ssh root@10.10.0.210 "docker compose exec postgres pg_isready -U n8n"
|
||||
```
|
||||
|
||||
2. **Disk space full:**
|
||||
```bash
|
||||
ssh root@10.10.0.210 "df -h /"
|
||||
# Should have >10GB free
|
||||
```
|
||||
|
||||
3. **Permission issues:**
|
||||
```bash
|
||||
ssh root@10.10.0.210 "docker volume inspect n8n_postgres_data"
|
||||
```
|
||||
|
||||
**Recovery:**
|
||||
```bash
|
||||
# Restore from backup
|
||||
ssh root@10.10.0.210 "
|
||||
cd /opt/n8n
|
||||
docker compose down
|
||||
docker volume rm n8n_postgres_data
|
||||
docker compose up -d postgres
|
||||
# Wait for healthy status
|
||||
cat /root/n8n-backup-YYYYMMDD.sql | docker compose exec -T postgres psql -U n8n n8n
|
||||
docker compose up -d n8n
|
||||
"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Access Issues
|
||||
|
||||
### Can't Access n8n.manticorum.com
|
||||
|
||||
**Symptoms:**
|
||||
- Browser shows "Connection timed out" or "Can't reach this page"
|
||||
- Works on http://10.10.0.210:5678 but not via domain
|
||||
|
||||
**Diagnosis Steps:**
|
||||
|
||||
1. **Check DNS resolution:**
|
||||
```bash
|
||||
nslookup n8n.manticorum.com
|
||||
# Should return your public IP
|
||||
```
|
||||
|
||||
2. **Test internal access:**
|
||||
```bash
|
||||
curl -I http://10.10.0.210:5678
|
||||
# Should return HTTP 200
|
||||
```
|
||||
|
||||
3. **Check NPM proxy host:**
|
||||
- Login to NPM UI
|
||||
- Verify proxy host for n8n.manticorum.com exists
|
||||
- Check if status shows "online"
|
||||
|
||||
4. **Test NPM connectivity:**
|
||||
```bash
|
||||
# From NPM host
|
||||
curl -I http://10.10.0.210:5678
|
||||
```
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. **DNS not configured:**
|
||||
- Add A record: `n8n.manticorum.com` → `[your-public-IP]`
|
||||
- Wait for DNS propagation (up to 48 hours)
|
||||
|
||||
2. **NPM proxy host misconfigured:**
|
||||
- Domain: `n8n.manticorum.com`
|
||||
- Scheme: `http` (not https)
|
||||
- Forward Host: `10.10.0.210`
|
||||
- Forward Port: `5678`
|
||||
- ✅ Enable WebSockets Support
|
||||
|
||||
3. **Firewall blocking:**
|
||||
- Ensure ports 80 and 443 open on firewall
|
||||
- Check Proxmox firewall rules
|
||||
- Check LXC firewall if enabled
|
||||
|
||||
### SSL Certificate Issues
|
||||
|
||||
**Symptoms:**
|
||||
- Browser shows "Your connection is not private"
|
||||
- Certificate error in browser
|
||||
- NPM shows "Certificate request failed"
|
||||
|
||||
**Diagnosis:**
|
||||
```bash
|
||||
# Test SSL
|
||||
openssl s_client -connect n8n.manticorum.com:443 -servername n8n.manticorum.com
|
||||
|
||||
# Check certificate expiry
|
||||
echo | openssl s_client -connect n8n.manticorum.com:443 2>/dev/null | openssl x509 -noout -dates
|
||||
```
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. **Request new certificate in NPM:**
|
||||
- Edit proxy host
|
||||
- SSL tab → Request new SSL Certificate
|
||||
- Ensure email is correct
|
||||
- Check Let's Encrypt rate limits (5 per week)
|
||||
|
||||
2. **DNS validation failing:**
|
||||
- Verify domain points to correct IP
|
||||
- Ensure port 80 is accessible (Let's Encrypt uses HTTP validation)
|
||||
|
||||
3. **Use DNS challenge instead:**
|
||||
- If port 80 is blocked, use DNS challenge method in NPM
|
||||
- Requires API credentials for your DNS provider
|
||||
|
||||
### Login/Authentication Issues
|
||||
|
||||
**Symptoms:**
|
||||
- Can access n8n but login fails
|
||||
- "Invalid credentials" error
|
||||
- Basic auth popup keeps appearing
|
||||
|
||||
**Diagnosis:**
|
||||
```bash
|
||||
# Check current credentials
|
||||
ssh root@10.10.0.210 "cat /opt/n8n/.env | grep BASIC_AUTH"
|
||||
```
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. **Reset admin password:**
|
||||
```bash
|
||||
ssh root@10.10.0.210 "
|
||||
cd /opt/n8n
|
||||
# Generate new password
|
||||
NEW_PASS=\$(openssl rand -base64 16 | tr -d '/+=')
|
||||
echo \"New password: \$NEW_PASS\"
|
||||
# Update .env
|
||||
sed -i \"s/N8N_BASIC_AUTH_PASSWORD=.*/N8N_BASIC_AUTH_PASSWORD=\$NEW_PASS/\" .env
|
||||
# Restart
|
||||
docker compose restart n8n
|
||||
"
|
||||
```
|
||||
|
||||
2. **Clear browser cache:**
|
||||
- Browser may cache old credentials
|
||||
- Try incognito/private window
|
||||
- Clear site data for n8n.manticorum.com
|
||||
|
||||
3. **Disable basic auth temporarily:**
|
||||
```bash
|
||||
ssh root@10.10.0.210 "
|
||||
cd /opt/n8n
|
||||
sed -i 's/N8N_BASIC_AUTH_ACTIVE=true/N8N_BASIC_AUTH_ACTIVE=false/' .env
|
||||
docker compose restart n8n
|
||||
"
|
||||
```
|
||||
**Warning:** Only do this for troubleshooting, re-enable immediately!
|
||||
|
||||
---
|
||||
|
||||
## Workflow Issues
|
||||
|
||||
### Webhooks Not Working
|
||||
|
||||
**Symptoms:**
|
||||
- External services can't trigger workflows
|
||||
- Webhook URL returns 404 or timeout
|
||||
- Test webhooks work but production ones don't
|
||||
|
||||
**Diagnosis:**
|
||||
```bash
|
||||
# Test webhook URL
|
||||
curl -X POST https://n8n.manticorum.com/webhook/test
|
||||
|
||||
# Check n8n logs for incoming requests
|
||||
ssh root@10.10.0.210 "docker compose logs -f n8n | grep webhook"
|
||||
```
|
||||
|
||||
**Common Causes:**
|
||||
|
||||
1. **Incorrect WEBHOOK_URL in configuration:**
|
||||
```bash
|
||||
ssh root@10.10.0.210 "cat /opt/n8n/.env | grep WEBHOOK_URL"
|
||||
# Should be: https://n8n.manticorum.com/
|
||||
```
|
||||
|
||||
2. **Workflow not activated:**
|
||||
- Check workflow is toggled "Active" in n8n UI
|
||||
- Look for green indicator on workflow
|
||||
|
||||
3. **NPM WebSocket support not enabled:**
|
||||
- Edit proxy host in NPM
|
||||
- Details tab → ✅ WebSockets Support
|
||||
|
||||
4. **Firewall blocking webhooks:**
|
||||
- Ensure external services can reach your public IP on port 443
|
||||
|
||||
**Solutions:**
|
||||
|
||||
```bash
|
||||
# Update webhook URL
|
||||
ssh root@10.10.0.210 "
|
||||
cd /opt/n8n
|
||||
sed -i 's|WEBHOOK_URL=.*|WEBHOOK_URL=https://n8n.manticorum.com/|' .env
|
||||
docker compose restart n8n
|
||||
"
|
||||
|
||||
# Test after restart
|
||||
curl -X POST https://n8n.manticorum.com/webhook/test
|
||||
```
|
||||
|
||||
### Executions Failing or Timing Out
|
||||
|
||||
**Symptoms:**
|
||||
- Workflows start but never complete
|
||||
- Timeout errors in execution logs
|
||||
- Memory errors
|
||||
|
||||
**Diagnosis:**
|
||||
```bash
|
||||
# Check resource usage
|
||||
ssh root@10.10.0.210 "docker stats --no-stream n8n"
|
||||
|
||||
# Check execution logs
|
||||
# Access n8n UI → Executions → View failed execution
|
||||
```
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. **Increase timeout in NPM:**
|
||||
- NPM proxy host → Advanced tab
|
||||
- Add: `proxy_read_timeout 300;`
|
||||
|
||||
2. **Increase LXC resources:**
|
||||
```bash
|
||||
# On Proxmox host
|
||||
ssh root@10.10.0.11 "
|
||||
pct set 210 --memory 16384 # Increase to 16GB
|
||||
pct set 210 --cores 8 # Increase to 8 cores
|
||||
pct reboot 210
|
||||
"
|
||||
```
|
||||
|
||||
3. **Optimize workflow:**
|
||||
- Break large workflows into smaller ones
|
||||
- Use pagination for API calls
|
||||
- Add delay nodes between heavy operations
|
||||
|
||||
4. **Check external service timeouts:**
|
||||
- API you're calling may be slow
|
||||
- Increase timeout in HTTP Request nodes
|
||||
|
||||
### Database/Credential Issues
|
||||
|
||||
**Symptoms:**
|
||||
- "Error loading credentials" in workflow
|
||||
- Saved credentials not appearing
|
||||
- "Credentials could not be decrypted"
|
||||
|
||||
**Critical Error - Encryption Key Changed:**
|
||||
|
||||
If you see "could not be decrypted," the encryption key was changed or is incorrect.
|
||||
|
||||
**This is UNRECOVERABLE without the original key!**
|
||||
|
||||
```bash
|
||||
# Check current encryption key
|
||||
ssh root@10.10.0.210 "cat /opt/n8n/.env | grep N8N_ENCRYPTION_KEY"
|
||||
|
||||
# If you have the old key, restore it:
|
||||
ssh root@10.10.0.210 "
|
||||
cd /opt/n8n
|
||||
sed -i 's/N8N_ENCRYPTION_KEY=.*/N8N_ENCRYPTION_KEY=YOUR_OLD_KEY/' .env
|
||||
docker compose restart n8n
|
||||
"
|
||||
```
|
||||
|
||||
**Prevention:**
|
||||
- Backup `.env` file regularly
|
||||
- Store encryption key in password manager
|
||||
- Never regenerate encryption key after initial setup
|
||||
|
||||
---
|
||||
|
||||
## Performance Issues
|
||||
|
||||
### n8n Running Slow
|
||||
|
||||
**Symptoms:**
|
||||
- UI sluggish or unresponsive
|
||||
- Workflows take longer than expected
|
||||
- High CPU/memory usage
|
||||
|
||||
**Diagnosis:**
|
||||
```bash
|
||||
# Check resource usage
|
||||
ssh root@10.10.0.210 "
|
||||
docker stats n8n n8n-postgres
|
||||
df -h /
|
||||
free -h
|
||||
"
|
||||
|
||||
# Check PostgreSQL performance
|
||||
ssh root@10.10.0.210 "
|
||||
docker compose exec postgres psql -U n8n -d n8n -c '
|
||||
SELECT query, calls, total_time, mean_time
|
||||
FROM pg_stat_statements
|
||||
ORDER BY total_time DESC
|
||||
LIMIT 10;'
|
||||
"
|
||||
```
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. **Clean up old executions:**
|
||||
```bash
|
||||
# In n8n UI: Settings → Executions
|
||||
# Set: "Delete executions older than X days"
|
||||
```
|
||||
|
||||
2. **Optimize database:**
|
||||
```bash
|
||||
ssh root@10.10.0.210 "
|
||||
docker compose exec postgres psql -U n8n -d n8n -c 'VACUUM ANALYZE;'
|
||||
"
|
||||
```
|
||||
|
||||
3. **Increase LXC resources** (see above)
|
||||
|
||||
4. **Check disk I/O:**
|
||||
```bash
|
||||
ssh root@10.10.0.210 "iostat -x 1 5"
|
||||
# If %util is consistently >80%, consider faster storage
|
||||
```
|
||||
|
||||
### Database Growing Too Large
|
||||
|
||||
**Symptoms:**
|
||||
- Disk space warning
|
||||
- n8n slowing down over time
|
||||
- Backup files becoming huge
|
||||
|
||||
**Diagnosis:**
|
||||
```bash
|
||||
# Check database size
|
||||
ssh root@10.10.0.210 "
|
||||
docker compose exec postgres psql -U n8n -d n8n -c '
|
||||
SELECT pg_size_pretty(pg_database_size(current_database()));'
|
||||
"
|
||||
|
||||
# Check table sizes
|
||||
ssh root@10.10.0.210 "
|
||||
docker compose exec postgres psql -U n8n -d n8n -c '
|
||||
SELECT tablename, pg_size_pretty(pg_total_relation_size(tablename::text))
|
||||
FROM pg_tables
|
||||
WHERE schemaname = '\''public'\''
|
||||
ORDER BY pg_total_relation_size(tablename::text) DESC;'
|
||||
"
|
||||
```
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. **Configure execution pruning:**
|
||||
- Settings → Executions
|
||||
- Enable: "Delete executions older than 7 days"
|
||||
- Set: "Max execution data to save"
|
||||
|
||||
2. **Manual cleanup:**
|
||||
```bash
|
||||
ssh root@10.10.0.210 "
|
||||
docker compose exec postgres psql -U n8n -d n8n -c '
|
||||
DELETE FROM execution_entity
|
||||
WHERE \"startedAt\" < NOW() - INTERVAL '\''30 days'\'';
|
||||
VACUUM FULL;'
|
||||
"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Emergency Procedures
|
||||
|
||||
### Complete Service Restart
|
||||
|
||||
```bash
|
||||
ssh root@10.10.0.210 "cd /opt/n8n && docker compose down && docker compose up -d"
|
||||
```
|
||||
|
||||
### Emergency Backup Before Changes
|
||||
|
||||
```bash
|
||||
ssh root@10.10.0.210 "
|
||||
cd /opt/n8n
|
||||
# Create emergency backup
|
||||
docker compose exec -T postgres pg_dump -U n8n n8n > /root/n8n-emergency-$(date +%Y%m%d-%H%M%S).sql
|
||||
# Copy .env
|
||||
cp .env /root/n8n-env-emergency-$(date +%Y%m%d-%H%M%S).env
|
||||
"
|
||||
```
|
||||
|
||||
### Complete Reset (DESTRUCTIVE)
|
||||
|
||||
**Only if all else fails and you're okay losing workflows:**
|
||||
|
||||
```bash
|
||||
ssh root@10.10.0.210 "
|
||||
cd /opt/n8n
|
||||
docker compose down
|
||||
docker volume rm n8n_data n8n_postgres_data
|
||||
docker compose up -d
|
||||
"
|
||||
```
|
||||
|
||||
**Note:** This deletes everything. Restore from backup immediately after!
|
||||
|
||||
---
|
||||
|
||||
## Prevention & Monitoring
|
||||
|
||||
### Regular Maintenance
|
||||
|
||||
**Weekly:**
|
||||
- Check disk space: `df -h /`
|
||||
- Review failed executions in n8n UI
|
||||
- Check log for errors: `docker compose logs --since 7d`
|
||||
|
||||
**Monthly:**
|
||||
- Backup database and .env file
|
||||
- Update n8n: `docker compose pull && docker compose up -d`
|
||||
- Vacuum database: `VACUUM ANALYZE;`
|
||||
- Review execution data retention settings
|
||||
|
||||
**Quarterly:**
|
||||
- Test disaster recovery procedure
|
||||
- Review and archive old workflows
|
||||
- Audit credentials and remove unused ones
|
||||
- Check for security updates
|
||||
|
||||
### Monitoring Setup
|
||||
|
||||
**Basic health check script:**
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# /opt/monitoring/check-n8n.sh
|
||||
|
||||
STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://10.10.0.210:5678)
|
||||
|
||||
if [ "$STATUS" != "200" ]; then
|
||||
echo "❌ n8n is down! Status: $STATUS"
|
||||
# Send alert (Discord, email, etc.)
|
||||
else
|
||||
echo "✅ n8n is healthy"
|
||||
fi
|
||||
```
|
||||
|
||||
**Add to cron:**
|
||||
```bash
|
||||
*/5 * * * * /opt/monitoring/check-n8n.sh >> /var/log/n8n-health.log 2>&1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Getting Help
|
||||
|
||||
### Log Collection for Support
|
||||
|
||||
```bash
|
||||
# Collect all relevant logs
|
||||
ssh root@10.10.0.210 "
|
||||
cd /opt/n8n
|
||||
mkdir -p /tmp/n8n-debug
|
||||
docker compose logs --tail=200 > /tmp/n8n-debug/docker-logs.txt
|
||||
docker compose ps > /tmp/n8n-debug/container-status.txt
|
||||
cat .env | sed 's/PASSWORD=.*/PASSWORD=***/' > /tmp/n8n-debug/env-redacted.txt
|
||||
df -h > /tmp/n8n-debug/disk-space.txt
|
||||
free -h > /tmp/n8n-debug/memory.txt
|
||||
docker stats --no-stream > /tmp/n8n-debug/container-stats.txt
|
||||
tar -czf /root/n8n-debug-$(date +%Y%m%d-%H%M%S).tar.gz /tmp/n8n-debug/
|
||||
"
|
||||
```
|
||||
|
||||
### Resources
|
||||
|
||||
- **n8n Community Forum:** https://community.n8n.io/
|
||||
- **Official Docs:** https://docs.n8n.io/
|
||||
- **GitHub Issues:** https://github.com/n8n-io/n8n/issues
|
||||
- **Discord:** https://discord.gg/n8n
|
||||
|
||||
### When to Escalate
|
||||
|
||||
Escalate to n8n community/support if:
|
||||
- Database corruption suspected
|
||||
- Consistent crashes with no clear cause
|
||||
- Performance issues persist after optimization
|
||||
- Security concerns
|
||||
- Bug suspected in n8n itself
|
||||
|
||||
Always provide:
|
||||
- n8n version: `docker inspect n8n | grep Image`
|
||||
- Error messages from logs
|
||||
- Steps to reproduce
|
||||
- What you've already tried
|
||||
322
productivity/n8n/workflows/QUICK-START.md
Normal file
322
productivity/n8n/workflows/QUICK-START.md
Normal file
@ -0,0 +1,322 @@
|
||||
# Ko-fi → Paper Dynasty Quick Start Guide
|
||||
|
||||
Get the Ko-fi integration running in 30 minutes.
|
||||
|
||||
## Prerequisites Checklist
|
||||
|
||||
Before you start, gather these items:
|
||||
|
||||
- [ ] Ko-fi verification token (from Ko-fi dashboard)
|
||||
- [ ] Paper Dynasty API key (production)
|
||||
- [ ] Discord webhook URL for notifications
|
||||
- [ ] List of Ko-fi product codes you'll sell
|
||||
- [ ] Access to n8n at https://n8n.manticorum.com
|
||||
|
||||
## Setup Steps
|
||||
|
||||
### Step 1: Get Ko-fi Verification Token (2 minutes)
|
||||
|
||||
1. Login to Ko-fi: https://ko-fi.com/
|
||||
2. Navigate to: Settings → API → Webhooks → Advanced (dropdown)
|
||||
3. Copy the **Verification Token** (looks like: `8c2a3835-4544-4f27-a53a-adfd1fcc5664`)
|
||||
4. Save it securely - you'll add it to n8n next
|
||||
|
||||
### Step 2: Create Discord Webhook (2 minutes)
|
||||
|
||||
1. Open Discord channel where you want notifications
|
||||
2. Channel Settings → Integrations → Webhooks → New Webhook
|
||||
3. Name it: "Ko-fi Paper Dynasty"
|
||||
4. Copy webhook URL (looks like: `https://discord.com/api/webhooks/...`)
|
||||
5. Save URL - you'll add it to n8n next
|
||||
|
||||
### Step 3: Configure n8n Credentials (5 minutes)
|
||||
|
||||
Login to n8n and create three credentials:
|
||||
|
||||
**Credential 1: Ko-fi Verification Token**
|
||||
1. n8n → Settings → Credentials → Add Credential
|
||||
2. Search for "Generic Credential"
|
||||
3. Name: `Ko-fi Verification Token`
|
||||
4. Add field: `verification_token`
|
||||
5. Value: `[paste your Ko-fi token here]`
|
||||
6. Save
|
||||
|
||||
**Credential 2: Paper Dynasty API**
|
||||
1. Add Credential → Search for "Header Auth"
|
||||
2. Name: `Paper Dynasty API`
|
||||
3. Header Name: `Authorization`
|
||||
4. Value: `Bearer [your-pd-api-key]`
|
||||
5. Save
|
||||
|
||||
**Credential 3: Discord Webhook**
|
||||
1. Add Credential → Search for "Discord Webhook"
|
||||
2. Name: `Discord Ko-fi Notifications`
|
||||
3. Webhook URL: `[paste Discord webhook URL]`
|
||||
4. Save
|
||||
|
||||
### Step 4: Create Product Mapping (5 minutes)
|
||||
|
||||
1. n8n → Settings → Variables → Add Variable
|
||||
2. Variable Name: `KOFI_PRODUCT_MAP`
|
||||
3. Value: Copy and customize this JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"pack-standard-5": {
|
||||
"name": "5-Pack Standard",
|
||||
"pack_type_id": 1,
|
||||
"quantity": 5,
|
||||
"pack_cardset_id": null
|
||||
},
|
||||
"pack-premium-10": {
|
||||
"name": "10-Pack Premium",
|
||||
"pack_type_id": 3,
|
||||
"quantity": 10,
|
||||
"pack_cardset_id": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Important:** Replace `pack-standard-5` and `pack-premium-10` with your actual Ko-fi product codes.
|
||||
|
||||
4. Save variable
|
||||
|
||||
### Step 5: Import Workflow (10 minutes)
|
||||
|
||||
**Option A: Manual Creation**
|
||||
|
||||
Follow the detailed node-by-node instructions in `kofi-paper-dynasty.md`
|
||||
|
||||
**Option B: JSON Import (if workflow JSON provided)**
|
||||
|
||||
1. n8n → Workflows → Add Workflow → Import from File
|
||||
2. Select workflow JSON file
|
||||
3. Review imported nodes
|
||||
4. Update credential references if needed
|
||||
5. Activate workflow
|
||||
|
||||
**Key Nodes to Verify:**
|
||||
- Webhook path: `/kofi-paperdy` (use `/webhook-test/kofi-paperdy` initially)
|
||||
- Token validation references correct credential
|
||||
- Product mapping loads from variable
|
||||
- API endpoints correct (`https://pd.manticorum.com/api/v2/`)
|
||||
- Discord notifications reference correct credential
|
||||
|
||||
### Step 6: Test the Workflow (5 minutes)
|
||||
|
||||
**Quick Test with cURL:**
|
||||
|
||||
```bash
|
||||
# Replace YOUR_KOFI_TOKEN_HERE with your actual token
|
||||
curl -X POST 'https://n8n.manticorum.com/webhook-test/kofi-paperdy' \
|
||||
-H 'Content-Type: application/x-www-form-urlencoded' \
|
||||
--data-urlencode 'data={
|
||||
"verification_token": "YOUR_KOFI_TOKEN_HERE",
|
||||
"message_id": "quick-test-001",
|
||||
"timestamp": "2025-11-13T12:00:00Z",
|
||||
"type": "Shop Order",
|
||||
"from_name": "Test User",
|
||||
"message": "Team: SKB",
|
||||
"amount": "5.00",
|
||||
"email": "[email protected]",
|
||||
"currency": "USD",
|
||||
"kofi_transaction_id": "quick-test-001",
|
||||
"shop_items": [
|
||||
{
|
||||
"direct_link_code": "pack-standard-5",
|
||||
"variation_name": "5-Pack Standard",
|
||||
"quantity": 1
|
||||
}
|
||||
],
|
||||
"discord_userid": null
|
||||
}'
|
||||
```
|
||||
|
||||
**Expected Results:**
|
||||
- ✅ n8n shows successful execution
|
||||
- ✅ Discord notification received
|
||||
- ✅ If using real API, 5 packs granted to team SKB
|
||||
|
||||
### Step 7: Configure Ko-fi Webhook (3 minutes)
|
||||
|
||||
1. Ko-fi → Settings → API → Webhooks
|
||||
2. **Webhook URL:** `https://n8n.manticorum.com/webhook-test/kofi-paperdy` (test first!)
|
||||
3. Save
|
||||
4. Click "Test Webhook" button
|
||||
5. Verify n8n receives webhook and processes successfully
|
||||
6. Check Discord for test notification
|
||||
|
||||
### Step 8: Go Live (2 minutes)
|
||||
|
||||
Once testing is successful:
|
||||
|
||||
1. **Update workflow webhook path:**
|
||||
- n8n → Edit Workflow → Webhook node
|
||||
- Change path from `webhook-test/kofi-paperdy` to `webhook/kofi-paperdy`
|
||||
- Save and activate workflow
|
||||
|
||||
2. **Update Ko-fi webhook URL:**
|
||||
- Ko-fi → Settings → API → Webhooks
|
||||
- Change URL to: `https://n8n.manticorum.com/webhook/kofi-paperdy`
|
||||
- Save
|
||||
|
||||
3. **Monitor first transactions:**
|
||||
- Watch Discord channel closely
|
||||
- Review n8n execution logs
|
||||
- Verify packs granted correctly in Paper Dynasty
|
||||
|
||||
Done! Your Ko-fi integration is now live.
|
||||
|
||||
---
|
||||
|
||||
## Post-Setup Tasks
|
||||
|
||||
### Create Ko-fi Products
|
||||
|
||||
For each pack type you want to sell:
|
||||
|
||||
1. Ko-fi → Shop → Add Product
|
||||
2. **Product Name:** "5-Pack Standard Bundle"
|
||||
3. **Price:** $5.00
|
||||
4. **Description:** "Five standard Paper Dynasty packs"
|
||||
5. **Advanced Settings → Product Code:** `pack-standard-5`
|
||||
- ⚠️ This MUST match your product mapping!
|
||||
6. Add product image
|
||||
7. Publish product
|
||||
|
||||
**Repeat for each pack type** (premium, team choice, etc.)
|
||||
|
||||
### Document Your Product Codes
|
||||
|
||||
Keep a master list of all Ko-fi product codes and their mappings:
|
||||
|
||||
| Ko-fi Code | Pack Type | Quantity | Price | PD pack_type_id |
|
||||
|------------|-----------|----------|-------|-----------------|
|
||||
| pack-standard-5 | Standard | 5 | $5.00 | 1 |
|
||||
| pack-standard-10 | Standard | 10 | $9.00 | 1 |
|
||||
| pack-premium-5 | Premium | 5 | $10.00 | 3 |
|
||||
| pack-premium-10 | Premium | 10 | $18.00 | 3 |
|
||||
| pack-team-choice | Team Choice | 1 | $3.00 | 8 |
|
||||
|
||||
Update this when adding new products!
|
||||
|
||||
### Set Up Monitoring
|
||||
|
||||
**Create a monitoring checklist:**
|
||||
- [ ] Check Discord notifications daily
|
||||
- [ ] Review n8n execution logs weekly
|
||||
- [ ] Verify pack distribution accuracy weekly
|
||||
- [ ] Update product mapping when adding Ko-fi products
|
||||
- [ ] Test disaster recovery monthly
|
||||
|
||||
### Backup n8n
|
||||
|
||||
**Create weekly backup:**
|
||||
```bash
|
||||
ssh root@10.10.0.210 "
|
||||
cd /opt/n8n && \
|
||||
docker compose exec -T postgres pg_dump -U n8n n8n > \
|
||||
/root/n8n-backup-\$(date +%Y%m%d-%H%M%S).sql
|
||||
"
|
||||
|
||||
# Download backup
|
||||
scp root@10.10.0.210:/root/n8n-backup-*.sql ~/backups/n8n/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Webhook Not Receiving Data
|
||||
|
||||
**Check:**
|
||||
```bash
|
||||
# Test webhook endpoint
|
||||
curl -I https://n8n.manticorum.com/webhook-test/kofi-paperdy
|
||||
|
||||
# Should return: 200 OK or 400 Bad Request (not 404)
|
||||
```
|
||||
|
||||
**Fix:**
|
||||
- Ensure workflow is Active (toggle in n8n UI)
|
||||
- Verify webhook path matches Ko-fi configuration
|
||||
- Check n8n logs: `ssh root@10.10.0.210 "cd /opt/n8n && docker compose logs -f n8n"`
|
||||
|
||||
### Token Validation Failing
|
||||
|
||||
**Check:**
|
||||
- Token in n8n credential matches Ko-fi exactly (no spaces!)
|
||||
- Credential name in IF node: `{{ $credentials.kofiVerificationToken.verification_token }}`
|
||||
|
||||
**Fix:**
|
||||
- Re-copy token from Ko-fi
|
||||
- Update n8n credential
|
||||
- Test again
|
||||
|
||||
### Team Not Found
|
||||
|
||||
**Check:**
|
||||
- User has discord_userid linked in Paper Dynasty
|
||||
- OR user included team abbrev in message (e.g., "Team: SKB")
|
||||
- Team abbreviation exists in PD database
|
||||
|
||||
**Fix:**
|
||||
- Manual review Discord notification will be sent
|
||||
- Admin grants packs manually using transaction ID
|
||||
|
||||
### Packs Not Granted
|
||||
|
||||
**Check:**
|
||||
- n8n execution log shows 200 OK from PD API
|
||||
- Product code exists in `KOFI_PRODUCT_MAP` variable
|
||||
- pack_type_id is valid
|
||||
|
||||
**Verify in Paper Dynasty:**
|
||||
```python
|
||||
from api_client import PaperDynastyAPI
|
||||
api = PaperDynastyAPI(environment='prod')
|
||||
api.list_packs(team_id=69, opened=False, limit=20)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
Now that your integration is running:
|
||||
|
||||
1. **Read full documentation:** `kofi-paper-dynasty.md`
|
||||
2. **Review test scenarios:** `kofi-testing-guide.md`
|
||||
3. **Customize product mapping:** Add more pack types
|
||||
4. **Set up monitoring alerts:** Create health check workflow
|
||||
5. **Document your process:** Add notes for future reference
|
||||
|
||||
---
|
||||
|
||||
## Support Resources
|
||||
|
||||
**Documentation:**
|
||||
- Full Setup: `kofi-paper-dynasty.md`
|
||||
- Testing Guide: `kofi-testing-guide.md`
|
||||
- Product Mapping: `kofi-product-mapping-template.json`
|
||||
- n8n Context: `/productivity/n8n/CONTEXT.md`
|
||||
|
||||
**External Resources:**
|
||||
- Ko-fi Webhook Docs: https://help.ko-fi.com/hc/en-us/articles/360004162298
|
||||
- n8n Documentation: https://docs.n8n.io/
|
||||
- Paper Dynasty API: See `/home/cal/.claude/skills/paper-dynasty/SKILL.md`
|
||||
|
||||
**Need Help?**
|
||||
- Review n8n execution logs for detailed error messages
|
||||
- Check Discord notifications for manual review requests
|
||||
- Test with cURL to isolate issues
|
||||
- Consult troubleshooting guide in main documentation
|
||||
|
||||
---
|
||||
|
||||
## Change Log
|
||||
|
||||
### 2025-11-13 - Initial Version
|
||||
- Created quick start guide
|
||||
- Defined 30-minute setup process
|
||||
- Added troubleshooting quick reference
|
||||
- Included post-setup tasks and monitoring
|
||||
333
productivity/n8n/workflows/README.md
Normal file
333
productivity/n8n/workflows/README.md
Normal file
@ -0,0 +1,333 @@
|
||||
# n8n Workflows Documentation
|
||||
|
||||
Collection of production n8n workflows and integration guides.
|
||||
|
||||
## Available Workflows
|
||||
|
||||
### Ko-fi → Paper Dynasty Integration
|
||||
|
||||
**Status:** Production Ready
|
||||
**Purpose:** Automated pack distribution for Ko-fi shop purchases
|
||||
|
||||
**Quick Links:**
|
||||
- 🚀 **[QUICK-START.md](QUICK-START.md)** - Get running in 30 minutes
|
||||
- 📖 **[kofi-paper-dynasty.md](kofi-paper-dynasty.md)** - Complete setup guide and node configuration
|
||||
- 🧪 **[kofi-testing-guide.md](kofi-testing-guide.md)** - Testing procedures and sample payloads
|
||||
- 🗺️ **[kofi-product-mapping-template.json](kofi-product-mapping-template.json)** - Product configuration template
|
||||
- 📝 **[kofi-implementation-notes.md](kofi-implementation-notes.md)** - Real implementation, gotchas, and lessons learned
|
||||
|
||||
**Features:**
|
||||
- Receives Ko-fi webhooks securely
|
||||
- Multi-method user identification (discord_userid or team abbrev)
|
||||
- Flexible product mapping system
|
||||
- Paper Dynasty API integration
|
||||
- Discord notifications for all outcomes
|
||||
- Comprehensive error handling
|
||||
- Manual review workflow for edge cases
|
||||
|
||||
**Webhook URLs:**
|
||||
- Test: `https://n8n.manticorum.com/webhook-test/kofi-paperdy`
|
||||
- Production: `https://n8n.manticorum.com/webhook/kofi-paperdy`
|
||||
|
||||
---
|
||||
|
||||
## Documentation Structure
|
||||
|
||||
### Quick Start Guide
|
||||
**File:** `QUICK-START.md`
|
||||
**Audience:** First-time setup
|
||||
**Time:** 30 minutes
|
||||
**Contents:**
|
||||
- Prerequisites checklist
|
||||
- Step-by-step setup (8 steps)
|
||||
- Post-setup tasks
|
||||
- Quick troubleshooting
|
||||
|
||||
### Complete Setup Guide
|
||||
**File:** `kofi-paper-dynasty.md`
|
||||
**Audience:** Detailed implementation
|
||||
**Contents:**
|
||||
- Architecture overview
|
||||
- Prerequisites and configuration
|
||||
- Node-by-node workflow setup
|
||||
- Discord notification templates
|
||||
- Troubleshooting guide
|
||||
- Monitoring and maintenance
|
||||
|
||||
### Testing Guide
|
||||
**File:** `kofi-testing-guide.md`
|
||||
**Audience:** QA and validation
|
||||
**Contents:**
|
||||
- 5-phase testing strategy
|
||||
- 8 test scenarios with cURL commands
|
||||
- Validation scripts
|
||||
- Testing checklist
|
||||
- Production monitoring
|
||||
|
||||
### Product Mapping Template
|
||||
**File:** `kofi-product-mapping-template.json`
|
||||
**Audience:** Configuration reference
|
||||
**Contents:**
|
||||
- JSON structure for n8n custom variable
|
||||
- Example product configurations
|
||||
- Pack type reference
|
||||
- Naming conventions
|
||||
- Usage examples
|
||||
|
||||
---
|
||||
|
||||
## Getting Started
|
||||
|
||||
### New to Ko-fi Integration?
|
||||
|
||||
1. **Start here:** [QUICK-START.md](QUICK-START.md)
|
||||
2. **Need details?** [kofi-paper-dynasty.md](kofi-paper-dynasty.md)
|
||||
3. **Ready to test?** [kofi-testing-guide.md](kofi-testing-guide.md)
|
||||
|
||||
### Adding New Products?
|
||||
|
||||
1. Open [kofi-product-mapping-template.json](kofi-product-mapping-template.json)
|
||||
2. Copy an existing product configuration
|
||||
3. Update Ko-fi product code and pack details
|
||||
4. Add to n8n custom variable `KOFI_PRODUCT_MAP`
|
||||
5. Create matching product in Ko-fi shop
|
||||
6. Test with cURL before going live
|
||||
|
||||
### Troubleshooting?
|
||||
|
||||
**Quick Fixes:**
|
||||
- Webhook 404: Check workflow is Active in n8n
|
||||
- Token error: Verify Ko-fi token in n8n credential
|
||||
- Team not found: Check discord_userid or message field
|
||||
- Packs not granted: Review API response in execution log
|
||||
|
||||
**Detailed Help:**
|
||||
- Full troubleshooting: `kofi-paper-dynasty.md` → Troubleshooting section
|
||||
- Test scenarios: `kofi-testing-guide.md` → Common Issues
|
||||
- Quick reference: `QUICK-START.md` → Troubleshooting
|
||||
|
||||
---
|
||||
|
||||
## Workflow Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Ko-fi Shop Order │
|
||||
└──────────────────┬──────────────────────────────────┘
|
||||
│ POST webhook
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ n8n Webhook: /webhook/kofi-paperdy │
|
||||
│ - Parse form data │
|
||||
│ - Validate Ko-fi token │
|
||||
│ - Filter shop orders only │
|
||||
└──────────────────┬──────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Identify Paper Dynasty Team │
|
||||
│ • discord_userid → gmid lookup │
|
||||
│ • OR message field → team abbrev │
|
||||
└──────────────────┬──────────────────────────────────┘
|
||||
│
|
||||
┌─────────┴─────────┐
|
||||
│ Team Found? │
|
||||
└─────────┬─────────┘
|
||||
Yes ↓ ↓ No
|
||||
│ └──→ Manual Review Discord Notification
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Map Ko-fi Products → Paper Dynasty Packs │
|
||||
│ • Load KOFI_PRODUCT_MAP variable │
|
||||
│ • Map shop_items to pack requests │
|
||||
└──────────────────┬──────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Call Paper Dynasty API: POST /packs │
|
||||
│ • Grant packs to team │
|
||||
│ • Auto-retry on failure (3x) │
|
||||
└──────────────────┬──────────────────────────────────┘
|
||||
│
|
||||
┌─────────┴─────────┐
|
||||
│ API Success? │
|
||||
└─────────┬─────────┘
|
||||
Yes ↓ ↓ No
|
||||
│ └──→ Error Discord Notification
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Success Discord Notification │
|
||||
│ • Customer name and team │
|
||||
│ • Packs granted │
|
||||
│ • Transaction ID │
|
||||
└──────────────────┬──────────────────────────────────┘
|
||||
↓
|
||||
Return 200 OK to Ko-fi
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### System Requirements
|
||||
|
||||
- n8n instance running at `https://n8n.manticorum.com`
|
||||
- PostgreSQL database for execution history
|
||||
- HTTPS access via Nginx Proxy Manager
|
||||
- WebSocket support enabled
|
||||
|
||||
### Required Credentials
|
||||
|
||||
1. **Ko-fi Verification Token**
|
||||
- Source: Ko-fi dashboard → Settings → API → Webhooks
|
||||
- Format: UUID (e.g., `8c2a3835-4544-4f27-a53a-adfd1fcc5664`)
|
||||
- Security: Validates webhook authenticity
|
||||
|
||||
2. **Paper Dynasty API Key**
|
||||
- Source: Paper Dynasty admin
|
||||
- Format: Bearer token
|
||||
- Permissions: Read teams, write packs
|
||||
|
||||
3. **Discord Webhook URL**
|
||||
- Source: Discord channel → Integrations → Webhooks
|
||||
- Format: `https://discord.com/api/webhooks/...`
|
||||
- Purpose: Success/error/manual review notifications
|
||||
|
||||
### Required Custom Variables
|
||||
|
||||
1. **KOFI_PRODUCT_MAP**
|
||||
- Type: JSON object
|
||||
- Location: n8n → Settings → Variables
|
||||
- Purpose: Map Ko-fi product codes to PD packs
|
||||
- Template: `kofi-product-mapping-template.json`
|
||||
|
||||
---
|
||||
|
||||
## Monitoring & Maintenance
|
||||
|
||||
### Daily Monitoring
|
||||
|
||||
- ✅ Check Discord for notifications
|
||||
- ✅ Review failed executions in n8n
|
||||
- ✅ Verify packs granted correctly
|
||||
|
||||
### Weekly Tasks
|
||||
|
||||
- 📊 Review n8n execution statistics
|
||||
- 🔍 Check for unknown product codes
|
||||
- 📝 Update product mapping if needed
|
||||
- 🧪 Test new products before launch
|
||||
|
||||
### Monthly Tasks
|
||||
|
||||
- 💾 Backup n8n database
|
||||
- 🔐 Rotate Ko-fi verification token
|
||||
- 🔑 Rotate Paper Dynasty API key
|
||||
- 📖 Update documentation with lessons learned
|
||||
|
||||
### Quarterly Tasks
|
||||
|
||||
- 🔧 Optimize workflow performance
|
||||
- 📈 Review transaction analytics
|
||||
- 🛡️ Security audit (credentials, access logs)
|
||||
- 🧪 Test disaster recovery procedure
|
||||
|
||||
---
|
||||
|
||||
## Support & Resources
|
||||
|
||||
### Internal Documentation
|
||||
|
||||
- **n8n Infrastructure:** `/productivity/n8n/CONTEXT.md`
|
||||
- **n8n Troubleshooting:** `/productivity/n8n/troubleshooting.md`
|
||||
- **Paper Dynasty API:** `/home/cal/.claude/skills/paper-dynasty/SKILL.md`
|
||||
|
||||
### External Resources
|
||||
|
||||
- **n8n Documentation:** https://docs.n8n.io/
|
||||
- **Ko-fi Webhook API:** https://help.ko-fi.com/hc/en-us/articles/360004162298
|
||||
- **n8n Community:** https://community.n8n.io/
|
||||
- **n8n Webhook Templates:** https://n8n.io/workflows/?categories=Webhooks
|
||||
|
||||
### Getting Help
|
||||
|
||||
1. **Check execution logs:** n8n → Executions → Filter by workflow
|
||||
2. **Review Discord notifications:** Look for error details and transaction IDs
|
||||
3. **Test with cURL:** Isolate webhook vs API vs n8n issues
|
||||
4. **Consult troubleshooting guides:** Each doc has troubleshooting section
|
||||
5. **Manual intervention:** Use Discord notifications to process failed orders
|
||||
|
||||
---
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Webhook Security
|
||||
|
||||
✅ **Token Validation:** All webhooks validated against Ko-fi verification token
|
||||
✅ **HTTPS Only:** All communication encrypted via NPM SSL
|
||||
✅ **Rate Limiting:** Configure in NPM for webhook endpoints
|
||||
✅ **Path Obfuscation:** Use non-obvious webhook paths
|
||||
|
||||
### Credential Management
|
||||
|
||||
✅ **Encrypted Storage:** n8n encrypts credentials with N8N_ENCRYPTION_KEY
|
||||
✅ **No Hardcoding:** Never hardcode secrets in workflows
|
||||
✅ **Regular Rotation:** Quarterly rotation schedule for tokens
|
||||
✅ **Backup Security:** Store encryption key in password manager
|
||||
|
||||
### Data Privacy
|
||||
|
||||
✅ **PII Handling:** Minimize customer data in logs and notifications
|
||||
✅ **Retention Policy:** Auto-delete old executions (30 days)
|
||||
✅ **Sanitized Logging:** Remove sensitive fields from error logs
|
||||
✅ **GDPR Compliance:** Data deletion mechanism available
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
|
||||
### Adding New Workflows
|
||||
|
||||
1. Create workflow documentation (follow `kofi-paper-dynasty.md` structure)
|
||||
2. Add testing guide with sample payloads
|
||||
3. Update this README with workflow description
|
||||
4. Create quick-start guide if complex
|
||||
5. Update `/productivity/n8n/CONTEXT.md` with workflow details
|
||||
|
||||
### Documentation Standards
|
||||
|
||||
- **Markdown format** for all docs
|
||||
- **Code blocks** with language syntax highlighting
|
||||
- **cURL examples** for all webhook tests
|
||||
- **Screenshots** where helpful (workflow diagrams)
|
||||
- **Version history** in Change Log section
|
||||
|
||||
### Testing Requirements
|
||||
|
||||
- ✅ Test with simulated webhooks (cURL)
|
||||
- ✅ Test with real data in `/webhook-test/` path
|
||||
- ✅ Validate all error paths
|
||||
- ✅ Verify Discord notifications
|
||||
- ✅ Test edge cases and invalid data
|
||||
|
||||
---
|
||||
|
||||
## Change Log
|
||||
|
||||
### 2025-11-13 - Ko-fi Integration
|
||||
- Added Ko-fi → Paper Dynasty workflow
|
||||
- Created comprehensive documentation suite
|
||||
- Implemented multi-method user identification
|
||||
- Added Discord notification system
|
||||
- Created testing guide with 8 scenarios
|
||||
- Documented product mapping system
|
||||
|
||||
---
|
||||
|
||||
## License & Attribution
|
||||
|
||||
**n8n:** Fair-code licensed (n8n GmbH)
|
||||
**Workflows:** Created by Cal Corum for Paper Dynasty
|
||||
**Documentation:** MIT License
|
||||
|
||||
---
|
||||
|
||||
**Questions?** Consult the [QUICK-START.md](QUICK-START.md) guide or review workflow-specific documentation.
|
||||
462
productivity/n8n/workflows/kofi-implementation-notes.md
Normal file
462
productivity/n8n/workflows/kofi-implementation-notes.md
Normal file
@ -0,0 +1,462 @@
|
||||
# Ko-fi → Paper Dynasty Implementation Notes
|
||||
|
||||
**Date:** 2025-11-13
|
||||
**Status:** Working in pddev, ready for production
|
||||
**n8n Version:** Community (self-hosted)
|
||||
|
||||
## What We Actually Built
|
||||
|
||||
This document captures the real implementation vs. the original plan, including all the gotchas we encountered.
|
||||
|
||||
---
|
||||
|
||||
## Key Differences from Original Plan
|
||||
|
||||
### 1. No Custom Variables (Enterprise Feature)
|
||||
**Original Plan:** Use n8n custom variable `KOFI_PRODUCT_MAP` for product mapping
|
||||
**Actual Implementation:** Hardcoded product mapping in "Map Products" Code node
|
||||
|
||||
**Why:** Custom variables are enterprise-only in n8n
|
||||
|
||||
**Product Mapping (Hardcoded in Code Node):**
|
||||
```javascript
|
||||
const PRODUCT_MAP = {
|
||||
'61de350207': { // Ko-fi product ID from URL
|
||||
name: 'Premium Pack',
|
||||
pack_type_id: 3,
|
||||
packs_per_quantity: 1
|
||||
},
|
||||
'2bdb7a4916': {
|
||||
name: 'Standard Pack',
|
||||
pack_type_id: 1,
|
||||
packs_per_quantity: 1
|
||||
},
|
||||
'3d7a4935aa': {
|
||||
name: 'In-Game Currency',
|
||||
type: 'currency',
|
||||
needs_manual_review: true // Not implemented yet
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### 2. Team Lookup Split into 3 Nodes
|
||||
**Original Plan:** Single "Lookup PD Team" Code node with `$http.request()`
|
||||
**Actual Implementation:** 3 separate nodes
|
||||
|
||||
**Why:** `fetch()` and `$http` are not available in n8n Code nodes
|
||||
|
||||
**The 3 Nodes:**
|
||||
1. **Extract Team Abbrev** (Code) - Regex extraction only
|
||||
2. **Lookup Team API** (HTTP Request) - API call to PD
|
||||
3. **Process Team Result** (Code) - Parse API response
|
||||
|
||||
### 3. Ko-fi Product Codes
|
||||
**Original Plan:** Use Ko-fi's "direct_link_code" custom field
|
||||
**Actual Reality:** Ko-fi doesn't have custom product codes
|
||||
|
||||
**Solution:** Using Ko-fi's internal product IDs from URLs:
|
||||
- `https://ko-fi.com/s/61de350207` → ID is `61de350207`
|
||||
- These IDs appear in webhook's `shop_items[].direct_link_code`
|
||||
|
||||
### 4. Webhook Domain Configuration
|
||||
**Issue:** n8n showed `https://n8n.yourdomain.com` by default
|
||||
**Solution:** Environment variables were set correctly, just needed n8n restart:
|
||||
```bash
|
||||
ssh root@10.10.0.210 "cd /opt/n8n && docker compose down && docker compose up -d"
|
||||
```
|
||||
|
||||
### 5. API Request Body Formatting
|
||||
**Issue:** Paper Dynasty API rejected stringified JSON
|
||||
**Solution:** Use `{{ $json.pack_requests }}` without `JSON.stringify()` or `=` prefix
|
||||
|
||||
**Wrong:** `={{ JSON.stringify({"packs": $json.pack_requests}) }}`
|
||||
**Right:** `{{ $json.pack_requests }}`
|
||||
|
||||
### 6. Discord Notifications
|
||||
**Issue:** Empty body caused "Cannot send an empty message"
|
||||
**Solution:** Always include `content` field even when using embeds
|
||||
|
||||
**Minimum working Discord message:**
|
||||
```json
|
||||
{
|
||||
"content": "Message text here",
|
||||
"embeds": [...]
|
||||
}
|
||||
```
|
||||
|
||||
### 7. Added Donation Support
|
||||
**Extension:** Added Switch node to route by transaction type
|
||||
**Routes:**
|
||||
- Output 0: Shop Order → Grant Packs flow
|
||||
- Output 1: Donation → Thank you notification
|
||||
- Output 2: Subscription → (Future implementation)
|
||||
- Fallback: Unknown types → Return 200 OK
|
||||
|
||||
---
|
||||
|
||||
## Final Working Architecture
|
||||
|
||||
```
|
||||
Ko-fi Webhook POST
|
||||
↓
|
||||
[Webhook: Ko-fi Webhook]
|
||||
Path: /kofi-pd-purchase
|
||||
↓
|
||||
[Code: Parse & Validate]
|
||||
- Parse form data JSON
|
||||
- Validate Ko-fi token (hardcoded)
|
||||
↓
|
||||
[Switch: Route by Type]
|
||||
Value: {{ $json.type }}
|
||||
├─ Output 0: "Shop Order"
|
||||
│ ↓
|
||||
│ [Code: Extract Team Abbrev]
|
||||
│ - Regex: /\b([A-Z]{2,4})\b/
|
||||
│ ↓
|
||||
│ [HTTP: Lookup Team API]
|
||||
│ - URL: pddev.manticorum.com/api/v2/teams?abbrev=...
|
||||
│ - Auth: Paper Dynasty API credential
|
||||
│ ↓
|
||||
│ [Code: Process Team Result]
|
||||
│ - Extract team ID and abbrev
|
||||
│ ↓
|
||||
│ [IF: Team Found?]
|
||||
│ ├─ True → [Code: Map Products]
|
||||
│ │ - Hardcoded product map
|
||||
│ │ - Create pack_requests array
|
||||
│ │ ↓
|
||||
│ │ [HTTP: Grant Packs]
|
||||
│ │ - POST /packs with array
|
||||
│ │ ↓
|
||||
│ │ [IF: API Success?]
|
||||
│ │ ├─ True → [HTTP: Discord Success]
|
||||
│ │ └─ False → [HTTP: Discord Error]
|
||||
│ │
|
||||
│ └─ False → [HTTP: Discord Manual Review]
|
||||
│
|
||||
├─ Output 1: "Donation"
|
||||
│ ↓
|
||||
│ [HTTP: Discord Thank You]
|
||||
│ - Notification for manual thanks
|
||||
│
|
||||
└─ Fallback: Other types
|
||||
↓
|
||||
[Return 200 OK]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Current Configuration
|
||||
|
||||
### Environment
|
||||
- **Testing:** pddev.manticorum.com (Paper Dynasty Dev)
|
||||
- **Production:** pd.manticorum.com (not yet deployed)
|
||||
|
||||
### Credentials Created
|
||||
1. **Paper Dynasty API** (Header Auth)
|
||||
- Header: `Authorization`
|
||||
- Value: `Bearer Tp3aO3jhYve5NJF1IqOmJTmk`
|
||||
|
||||
2. **Discord Webhook** (not used as credential)
|
||||
- URL hardcoded in HTTP Request nodes
|
||||
- Webhook: `1438578498108133488/MGx1zyAa1ewzVy3RqCsH50ZJUiVvT5J5Vl2jExGIkIsu6v0x8A9J3-ruRftClPEq91ej`
|
||||
|
||||
### Ko-fi Settings
|
||||
- **Verification Token:** `44d1f957-ac15-497e-8306-4dc667de55d1`
|
||||
- **Webhook URL (test):** `https://n8n.manticorum.com/webhook-test/kofi-pd-purchase`
|
||||
- **Webhook URL (prod):** `https://n8n.manticorum.com/webhook/kofi-pd-purchase`
|
||||
|
||||
### Products Configured
|
||||
| Ko-fi ID | Name | PD Pack Type | Quantity |
|
||||
|----------|------|--------------|----------|
|
||||
| 61de350207 | Premium Pack | 3 | 1 per qty |
|
||||
| 2bdb7a4916 | Standard Pack | 1 | 1 per qty |
|
||||
| 3d7a4935aa | In-Game Currency | N/A | Manual review |
|
||||
|
||||
---
|
||||
|
||||
## Testing Results
|
||||
|
||||
### Successful Test (2025-11-13)
|
||||
- ✅ Webhook received and parsed
|
||||
- ✅ Token validated
|
||||
- ✅ Team "SKB" found via message extraction
|
||||
- ✅ Product mapped (2bdb7a4916 → 5 standard packs)
|
||||
- ✅ API call succeeded (201 Created)
|
||||
- ✅ 5 packs granted to team SKB in pddev
|
||||
- ✅ Discord success notification sent
|
||||
|
||||
### Test Command Used
|
||||
```bash
|
||||
curl -X POST 'https://n8n.manticorum.com/webhook-test/kofi-pd-purchase' \
|
||||
-H 'Content-Type: application/x-www-form-urlencoded' \
|
||||
--data-urlencode 'data={
|
||||
"verification_token": "44d1f957-ac15-497e-8306-4dc667de55d1",
|
||||
"message_id": "test-001",
|
||||
"timestamp": "2025-11-13T12:00:00Z",
|
||||
"type": "Shop Order",
|
||||
"from_name": "Test Customer",
|
||||
"message": "SKB",
|
||||
"amount": "5.00",
|
||||
"email": "test@example.com",
|
||||
"currency": "USD",
|
||||
"kofi_transaction_id": "test-001",
|
||||
"shop_items": [
|
||||
{
|
||||
"direct_link_code": "2bdb7a4916",
|
||||
"variation_name": "Standard Pack",
|
||||
"quantity": 5
|
||||
}
|
||||
],
|
||||
"discord_userid": null
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Known Limitations
|
||||
|
||||
### 1. No Discord User ID Support Yet
|
||||
**Status:** Code is in place but untested
|
||||
**Why:** Test payload used `discord_userid: null`
|
||||
**Todo:** Test with real Discord-linked Ko-fi account
|
||||
|
||||
### 2. Currency Product Not Implemented
|
||||
**Status:** Routed to manual review
|
||||
**Reason:** Paper Dynasty doesn't have currency system yet
|
||||
**Todo:** Implement currency granting when PD adds support
|
||||
|
||||
### 3. Subscription Type Not Handled
|
||||
**Status:** Returns 200 OK but does nothing
|
||||
**Reason:** Decided to implement shop orders and donations first
|
||||
**Todo:** Add subscription tier handling
|
||||
|
||||
### 4. Single Discord Channel
|
||||
**Status:** All notifications go to "cals-hidey-hole"
|
||||
**Todo:** Consider separate channels for success/errors/reviews
|
||||
|
||||
### 5. Hardcoded Configuration
|
||||
**Status:** All settings hardcoded in Code nodes
|
||||
**Why:** Community edition limitations
|
||||
**Impact:** Must edit code nodes to change products, tokens, or URLs
|
||||
**Mitigation:** Well-documented code with clear comments
|
||||
|
||||
---
|
||||
|
||||
## Production Deployment Checklist
|
||||
|
||||
Before going live with production:
|
||||
|
||||
- [ ] **Update all API URLs from pddev to prod:**
|
||||
- [ ] "Lookup Team API" node URL
|
||||
- [ ] "Grant Packs" node URL
|
||||
|
||||
- [ ] **Update Ko-fi webhook URL:**
|
||||
- [ ] Change from `/webhook-test/` to `/webhook/`
|
||||
- [ ] Configure in Ko-fi dashboard
|
||||
|
||||
- [ ] **Activate workflow:**
|
||||
- [ ] Toggle "Active" switch in n8n
|
||||
|
||||
- [ ] **Test with real Ko-fi purchase:**
|
||||
- [ ] Create $0.01 test product
|
||||
- [ ] Make test purchase
|
||||
- [ ] Verify packs granted in production
|
||||
- [ ] Verify Discord notification
|
||||
|
||||
- [ ] **Monitor first 10 transactions:**
|
||||
- [ ] Watch Discord for notifications
|
||||
- [ ] Check n8n execution logs
|
||||
- [ ] Verify pack distribution accuracy
|
||||
|
||||
- [ ] **Document any issues encountered**
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting Guide
|
||||
|
||||
### Webhook Not Triggering
|
||||
**Symptoms:** n8n shows "Waiting for trigger event" but nothing happens
|
||||
**Solutions:**
|
||||
1. Verify workflow is Active (green toggle)
|
||||
2. Check webhook path matches Ko-fi configuration
|
||||
3. Test with cURL to isolate issue
|
||||
4. Check n8n logs: `ssh root@10.10.0.210 "cd /opt/n8n && docker compose logs -f n8n"`
|
||||
|
||||
### Token Validation Failing
|
||||
**Symptoms:** Workflow stops at "Parse & Validate" with error
|
||||
**Solutions:**
|
||||
1. Verify token in Code node matches Ko-fi dashboard exactly
|
||||
2. Check for extra spaces or quotes
|
||||
3. Re-copy token from Ko-fi
|
||||
|
||||
### Team Not Found
|
||||
**Symptoms:** Workflow takes "Team Found? False" path
|
||||
**Solutions:**
|
||||
1. Check message field has valid team abbrev (2-4 uppercase letters)
|
||||
2. Test API directly: `curl -H "Authorization: Bearer TOKEN" "URL/teams?abbrev=SKB"`
|
||||
3. Verify team exists in target environment (pddev vs prod)
|
||||
|
||||
### API Call Failing
|
||||
**Symptoms:** "API Success?" takes False path
|
||||
**Solutions:**
|
||||
1. Check HTTP Request body format (no JSON.stringify, no = prefix)
|
||||
2. Verify `pack_requests` is an array in "Map Products" output
|
||||
3. Check API credentials are correct
|
||||
4. Review full error in node execution data
|
||||
|
||||
### Discord Notification Not Sending
|
||||
**Symptoms:** No Discord message received
|
||||
**Solutions:**
|
||||
1. Verify webhook URL is correct
|
||||
2. Test webhook directly with cURL
|
||||
3. Check body has `content` field (required even with embeds)
|
||||
4. Verify Discord channel hasn't deleted the webhook
|
||||
|
||||
### Unknown Product Code
|
||||
**Symptoms:** `unknown_products` array has items, takes manual review path
|
||||
**Solutions:**
|
||||
1. Check Ko-fi product ID matches `PRODUCT_MAP` keys
|
||||
2. Get product ID from Ko-fi product URL: `/s/{ID}`
|
||||
3. Add new product to PRODUCT_MAP in "Map Products" code
|
||||
|
||||
---
|
||||
|
||||
## Maintenance Tasks
|
||||
|
||||
### Adding New Products
|
||||
1. Get Ko-fi product ID from product URL
|
||||
2. Edit "Map Products" Code node
|
||||
3. Add entry to `PRODUCT_MAP`:
|
||||
```javascript
|
||||
'new-product-id': {
|
||||
name: 'Product Name',
|
||||
pack_type_id: 1, // 1=Standard, 3=Premium, 8=Team Choice
|
||||
packs_per_quantity: 1
|
||||
}
|
||||
```
|
||||
4. Save and test
|
||||
|
||||
### Changing API Endpoints
|
||||
1. Edit "Lookup Team API" HTTP Request URL
|
||||
2. Edit "Grant Packs" HTTP Request URL
|
||||
3. Update "Parse & Validate" API_TOKEN if needed
|
||||
4. Test thoroughly before activating
|
||||
|
||||
### Updating Discord Webhooks
|
||||
1. Create new webhook in Discord
|
||||
2. Update all Discord HTTP Request node URLs:
|
||||
- Discord Success
|
||||
- Discord Error
|
||||
- Discord Manual Review
|
||||
- Discord Thank You
|
||||
3. Test each notification type
|
||||
|
||||
### Rotating Credentials
|
||||
**Ko-fi Token:**
|
||||
1. Get new token from Ko-fi dashboard
|
||||
2. Update in "Parse & Validate" Code node
|
||||
3. Test with cURL
|
||||
|
||||
**Paper Dynasty API Key:**
|
||||
1. Generate new key in PD admin
|
||||
2. Update "Paper Dynasty API" credential in n8n
|
||||
3. Test API calls
|
||||
|
||||
---
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### Priority 1 (Next Session)
|
||||
- [ ] Test Discord user ID → gmid lookup
|
||||
- [ ] Add Subscription handling
|
||||
- [ ] Implement currency product logic
|
||||
- [ ] Test with real Ko-fi purchases
|
||||
|
||||
### Priority 2 (Nice to Have)
|
||||
- [ ] Separate Discord channels for different notification types
|
||||
- [ ] Email notifications for manual reviews
|
||||
- [ ] Transaction history logging (database or file)
|
||||
- [ ] Analytics dashboard for pack distributions
|
||||
|
||||
### Priority 3 (Optimization)
|
||||
- [ ] Retry logic for failed transactions
|
||||
- [ ] Batch processing for multiple purchases
|
||||
- [ ] Scheduled reconciliation job
|
||||
- [ ] Admin UI for product mapping
|
||||
|
||||
---
|
||||
|
||||
## Lessons Learned
|
||||
|
||||
### 1. n8n Community vs Enterprise
|
||||
Community edition is powerful but limited:
|
||||
- No custom variables (use hardcoded values in Code nodes)
|
||||
- Code nodes can't make HTTP requests (use HTTP Request nodes)
|
||||
- Works great for our use case with workarounds
|
||||
|
||||
### 2. Ko-fi Webhook Structure
|
||||
- Form-urlencoded with JSON in `data` field
|
||||
- Product codes are internal Ko-fi IDs, not custom
|
||||
- `shop_items` can have multiple items in single order
|
||||
- Always validate `verification_token`
|
||||
|
||||
### 3. Paper Dynasty API Integration
|
||||
- Straightforward REST API
|
||||
- Expects JSON arrays, not stringified
|
||||
- Good error messages
|
||||
- Dev environment perfect for testing
|
||||
|
||||
### 4. Discord Webhook Best Practices
|
||||
- Always include `content` field
|
||||
- Embeds are great for rich notifications
|
||||
- @here mentions work for urgent alerts
|
||||
- Test webhooks fail silently if deleted
|
||||
|
||||
### 5. Workflow Development Process
|
||||
- Start simple, add complexity gradually
|
||||
- Test each node individually
|
||||
- Use debug fields (like `debug_info`) during development
|
||||
- Document as you go
|
||||
|
||||
---
|
||||
|
||||
## Resources
|
||||
|
||||
**Documentation Created:**
|
||||
- `/productivity/n8n/workflows/kofi-paper-dynasty.md` - Original detailed guide
|
||||
- `/productivity/n8n/workflows/QUICK-START.md` - 30-minute setup guide
|
||||
- `/productivity/n8n/workflows/kofi-testing-guide.md` - Testing procedures
|
||||
- `/productivity/n8n/workflows/kofi-product-mapping-template.json` - Product config
|
||||
- `/productivity/n8n/workflows/kofi-implementation-notes.md` - This file
|
||||
|
||||
**External References:**
|
||||
- Ko-fi Webhook Docs: https://help.ko-fi.com/hc/en-us/articles/360004162298
|
||||
- n8n Documentation: https://docs.n8n.io/
|
||||
- Paper Dynasty API: See `/home/cal/.claude/skills/paper-dynasty/SKILL.md`
|
||||
|
||||
**Test Files:**
|
||||
- Ko-fi payload structure: `/home/cal/Desktop/kofi-integration.md`
|
||||
|
||||
---
|
||||
|
||||
## Change Log
|
||||
|
||||
### 2025-11-13 - Initial Implementation
|
||||
- Created complete Ko-fi → Paper Dynasty workflow
|
||||
- Working in pddev environment
|
||||
- Shop orders fully functional
|
||||
- Donation notifications implemented
|
||||
- Team identification via message extraction tested
|
||||
- Discord notifications for all paths
|
||||
- Ready for production deployment
|
||||
|
||||
---
|
||||
|
||||
**Next Session Goals:**
|
||||
1. Test Discord user ID lookup
|
||||
2. Deploy to production
|
||||
3. Make first real Ko-fi purchase
|
||||
4. Monitor and iterate
|
||||
|
||||
**Status:** ✅ Ready for production deployment
|
||||
350
productivity/n8n/workflows/kofi-paper-dynasty-workflow.json
Normal file
350
productivity/n8n/workflows/kofi-paper-dynasty-workflow.json
Normal file
@ -0,0 +1,350 @@
|
||||
{
|
||||
"name": "Ko-fi to Paper Dynasty",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"httpMethod": "POST",
|
||||
"path": "kofi-paperdy",
|
||||
"responseMode": "lastNode",
|
||||
"options": {}
|
||||
},
|
||||
"id": "webhook-trigger",
|
||||
"name": "Ko-fi Webhook",
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 1,
|
||||
"position": [250, 300],
|
||||
"webhookId": "kofi-paperdy"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Parse Ko-fi form data\nconst formData = $input.item.json.body;\nconst kofiData = JSON.parse(formData.data);\n\n// Ko-fi verification token\nconst KOFI_TOKEN = '44d1f957-ac15-497e-8306-4dc667de55d1';\n\n// Validate token\nif (kofiData.verification_token !== KOFI_TOKEN) {\n throw new Error('Invalid Ko-fi verification token');\n}\n\n// Add metadata\nreturn [{\n json: {\n ...kofiData,\n _parsed_at: new Date().toISOString(),\n _execution_id: $execution.id\n }\n}];"
|
||||
},
|
||||
"id": "parse-and-validate",
|
||||
"name": "Parse & Validate",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [450, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"string": [
|
||||
{
|
||||
"value1": "={{ $json.type }}",
|
||||
"operation": "equals",
|
||||
"value2": "Shop Order"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "filter-shop-orders",
|
||||
"name": "Shop Order?",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 1,
|
||||
"position": [650, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Lookup Paper Dynasty team\nconst PD_API_BASE = 'https://pd.manticorum.com/api/v2';\nconst API_TOKEN = 'Bearer Tp3aO3jhYve5NJF1IqOmJTmk';\n\nlet teamId = null;\nlet teamAbbrev = null;\nlet identificationMethod = null;\n\n// Method 1: Discord User ID -> gmid lookup\nif ($json.discord_userid) {\n try {\n const response = await $http.request({\n method: 'GET',\n url: `${PD_API_BASE}/teams?gmid=${$json.discord_userid}`,\n headers: {\n 'Authorization': API_TOKEN,\n 'Content-Type': 'application/json'\n }\n });\n\n if (response.body && response.body.length > 0) {\n teamId = response.body[0].id;\n teamAbbrev = response.body[0].abbrev;\n identificationMethod = 'discord_userid';\n }\n } catch (error) {\n console.log('Discord lookup failed:', error.message);\n }\n}\n\n// Method 2: Extract team abbrev from message\nif (!teamId && $json.message) {\n const abbrevMatch = $json.message.match(/\\b([A-Z]{2,4})\\b/);\n \n if (abbrevMatch) {\n const extractedAbbrev = abbrevMatch[1];\n \n try {\n const response = await $http.request({\n method: 'GET',\n url: `${PD_API_BASE}/teams?abbrev=${extractedAbbrev}`,\n headers: {\n 'Authorization': API_TOKEN,\n 'Content-Type': 'application/json'\n }\n });\n\n if (response.body && response.body.length > 0) {\n teamId = response.body[0].id;\n teamAbbrev = response.body[0].abbrev;\n identificationMethod = 'message_abbrev';\n }\n } catch (error) {\n console.log('Team abbrev lookup failed:', error.message);\n }\n }\n}\n\nreturn [{\n json: {\n ...$json,\n pd_team_id: teamId,\n pd_team_abbrev: teamAbbrev,\n identification_method: identificationMethod,\n needs_manual_review: !teamId\n }\n}];"
|
||||
},
|
||||
"id": "lookup-team",
|
||||
"name": "Lookup PD Team",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [850, 200]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"boolean": [
|
||||
{
|
||||
"value1": "={{ $json.needs_manual_review }}",
|
||||
"value2": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "check-team-found",
|
||||
"name": "Team Found?",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 1,
|
||||
"position": [1050, 200]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Product mapping\nconst PRODUCT_MAP = {\n '61de350207': {\n name: 'Premium Pack',\n pack_type_id: 3,\n packs_per_quantity: 1\n },\n '2bdb7a4916': {\n name: 'Standard Pack',\n pack_type_id: 1,\n packs_per_quantity: 1\n },\n '3d7a4935aa': {\n name: 'In-Game Currency',\n type: 'currency',\n needs_manual_review: true\n }\n};\n\nconst shopItems = $json.shop_items;\nconst packRequests = [];\nlet unknownProducts = [];\nlet needsReview = [];\n\nfor (const item of shopItems) {\n const productConfig = PRODUCT_MAP[item.direct_link_code];\n \n if (!productConfig) {\n unknownProducts.push({\n code: item.direct_link_code,\n variation: item.variation_name,\n quantity: item.quantity\n });\n continue;\n }\n \n // Handle currency products\n if (productConfig.type === 'currency') {\n needsReview.push({\n product: productConfig.name,\n quantity: item.quantity,\n reason: 'Currency system not implemented yet'\n });\n continue;\n }\n \n // Calculate total packs\n const totalPacks = item.quantity * productConfig.packs_per_quantity;\n \n // Create pack requests\n for (let i = 0; i < totalPacks; i++) {\n packRequests.push({\n team_id: $json.pd_team_id,\n pack_type_id: productConfig.pack_type_id,\n pack_cardset_id: null\n });\n }\n}\n\nreturn [{\n json: {\n ...$json,\n pack_requests: packRequests,\n unknown_products: unknownProducts,\n needs_review_items: needsReview,\n has_unknown_products: unknownProducts.length > 0,\n has_review_items: needsReview.length > 0,\n total_packs: packRequests.length\n }\n}];"
|
||||
},
|
||||
"id": "map-products",
|
||||
"name": "Map Products",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [1250, 100]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "https://pd.manticorum.com/api/v2/packs",
|
||||
"authentication": "predefinedCredentialType",
|
||||
"nodeCredentialType": "httpHeaderAuth",
|
||||
"sendBody": true,
|
||||
"bodyParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "packs",
|
||||
"value": "={{ JSON.stringify($json.pack_requests) }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"response": {
|
||||
"response": {
|
||||
"fullResponse": true
|
||||
}
|
||||
},
|
||||
"retry": {
|
||||
"retry": {
|
||||
"maxRetries": 3,
|
||||
"retryInterval": 2000
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": "grant-packs",
|
||||
"name": "Grant Packs",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4,
|
||||
"position": [1450, 100],
|
||||
"credentials": {
|
||||
"httpHeaderAuth": {
|
||||
"id": "1",
|
||||
"name": "Paper Dynasty API"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"number": [
|
||||
{
|
||||
"value1": "={{ $json.statusCode }}",
|
||||
"operation": "between",
|
||||
"value2": 200,
|
||||
"value3": 299
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "check-api-success",
|
||||
"name": "API Success?",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 1,
|
||||
"position": [1650, 100]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "https://discord.com/api/webhooks/1438578498108133488/MGx1zyAa1ewzVy3RqCsH50ZJUiVvT5J5Vl2jExGIkIsu6v0x8A9J3-ruRftClPEq91ej",
|
||||
"sendBody": true,
|
||||
"contentType": "json",
|
||||
"body": "={{ JSON.stringify({\n content: '✅ Ko-fi Order Processed',\n embeds: [{\n title: 'Paper Dynasty Packs Granted',\n color: 3066993,\n fields: [\n {\n name: 'Customer',\n value: $('Parse & Validate').item.json.from_name,\n inline: true\n },\n {\n name: 'Team',\n value: $('Lookup PD Team').item.json.pd_team_abbrev,\n inline: true\n },\n {\n name: 'Amount',\n value: '$' + $('Parse & Validate').item.json.amount + ' ' + $('Parse & Validate').item.json.currency,\n inline: true\n },\n {\n name: 'Packs Granted',\n value: $('Map Products').item.json.total_packs + ' packs',\n inline: false\n },\n {\n name: 'Transaction ID',\n value: '`' + $('Parse & Validate').item.json.kofi_transaction_id + '`',\n inline: false\n }\n ],\n timestamp: new Date().toISOString()\n }]\n}) }}"
|
||||
},
|
||||
"id": "discord-success",
|
||||
"name": "Discord Success",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4,
|
||||
"position": [1850, 0]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "https://discord.com/api/webhooks/1438578498108133488/MGx1zyAa1ewzVy3RqCsH50ZJUiVvT5J5Vl2jExGIkIsu6v0x8A9J3-ruRftClPEq91ej",
|
||||
"sendBody": true,
|
||||
"contentType": "json",
|
||||
"body": "={{ JSON.stringify({\n content: '@here ⚠️ Ko-fi Order Processing Error',\n embeds: [{\n title: 'Paper Dynasty API Error',\n color: 15158332,\n fields: [\n {\n name: 'Customer',\n value: $('Parse & Validate').item.json.from_name + ' (' + $('Parse & Validate').item.json.email + ')',\n inline: false\n },\n {\n name: 'Amount',\n value: '$' + $('Parse & Validate').item.json.amount,\n inline: true\n },\n {\n name: 'Team ID',\n value: String($('Lookup PD Team').item.json.pd_team_id || 'Unknown'),\n inline: true\n },\n {\n name: 'API Status',\n value: String($json.statusCode || 'Request Failed'),\n inline: true\n },\n {\n name: 'Transaction ID',\n value: '`' + $('Parse & Validate').item.json.kofi_transaction_id + '`',\n inline: false\n },\n {\n name: 'Action Required',\n value: 'Manual pack distribution needed. Use transaction ID to process manually.',\n inline: false\n }\n ],\n timestamp: new Date().toISOString()\n }]\n}) }}"
|
||||
},
|
||||
"id": "discord-error",
|
||||
"name": "Discord Error",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4,
|
||||
"position": [1850, 200]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "https://discord.com/api/webhooks/1438578498108133488/MGx1zyAa1ewzVy3RqCsH50ZJUiVvT5J5Vl2jExGIkIsu6v0x8A9J3-ruRftClPEq91ej",
|
||||
"sendBody": true,
|
||||
"contentType": "json",
|
||||
"body": "={{ JSON.stringify({\n content: '@here 🔍 Ko-fi Order Needs Manual Review',\n embeds: [{\n title: 'Unable to Identify Paper Dynasty Team',\n color: 16776960,\n fields: [\n {\n name: 'Customer',\n value: $json.from_name + ' (' + $json.email + ')',\n inline: false\n },\n {\n name: 'Amount',\n value: '$' + $json.amount + ' ' + $json.currency,\n inline: true\n },\n {\n name: 'Discord User ID',\n value: $json.discord_userid || 'Not provided',\n inline: true\n },\n {\n name: 'Message',\n value: $json.message || 'No message',\n inline: false\n },\n {\n name: 'Products',\n value: $json.shop_items.map(i => i.quantity + 'x ' + i.variation_name + ' (' + i.direct_link_code + ')').join('\\n'),\n inline: false\n },\n {\n name: 'Transaction ID',\n value: '`' + $json.kofi_transaction_id + '`',\n inline: false\n },\n {\n name: 'Action Required',\n value: '1. Identify user\\'s Paper Dynasty team\\n2. Manually grant packs using PD API\\n3. Reply when resolved',\n inline: false\n }\n ],\n timestamp: new Date().toISOString()\n }]\n}) }}"
|
||||
},
|
||||
"id": "discord-manual-review",
|
||||
"name": "Discord Manual Review",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4,
|
||||
"position": [1250, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"respondWith": "json",
|
||||
"responseBody": "={{ JSON.stringify({\n status: 'success',\n message: 'Order received and processed',\n execution_id: $execution.id\n}) }}"
|
||||
},
|
||||
"id": "respond-webhook",
|
||||
"name": "Return 200 OK",
|
||||
"type": "n8n-nodes-base.respondToWebhook",
|
||||
"typeVersion": 1,
|
||||
"position": [2050, 150]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Ko-fi Webhook": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Parse & Validate",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Parse & Validate": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Shop Order?",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Shop Order?": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Lookup PD Team",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Return 200 OK",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Lookup PD Team": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Team Found?",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Team Found?": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Map Products",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Discord Manual Review",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Map Products": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Grant Packs",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Grant Packs": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "API Success?",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"API Success?": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Discord Success",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Discord Error",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Discord Success": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Return 200 OK",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Discord Error": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Return 200 OK",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Discord Manual Review": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Return 200 OK",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"pinData": {},
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"staticData": null,
|
||||
"tags": [],
|
||||
"triggerCount": 0,
|
||||
"updatedAt": "2025-11-13T00:00:00.000Z",
|
||||
"versionId": "1"
|
||||
}
|
||||
860
productivity/n8n/workflows/kofi-paper-dynasty.md
Normal file
860
productivity/n8n/workflows/kofi-paper-dynasty.md
Normal file
@ -0,0 +1,860 @@
|
||||
# Ko-fi → Paper Dynasty Integration
|
||||
|
||||
Automated workflow that processes Ko-fi shop orders and grants Paper Dynasty packs to users.
|
||||
|
||||
## Overview
|
||||
|
||||
When a user purchases packs on Ko-fi, this workflow:
|
||||
1. Receives the webhook from Ko-fi
|
||||
2. Identifies the Paper Dynasty team (via discord_userid or team abbrev)
|
||||
3. Maps Ko-fi products to Paper Dynasty pack types
|
||||
4. Grants packs via Paper Dynasty API
|
||||
5. Sends Discord notifications on success/failure
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ Ko-fi Shop Order Flow │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
|
||||
Ko-fi Shop Order
|
||||
↓
|
||||
Ko-fi Webhook POST
|
||||
↓
|
||||
https://n8n.manticorum.com/webhook/kofi-paperdy
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ n8n Workflow: Ko-fi to Paper Dynasty │
|
||||
│ │
|
||||
│ 1. Parse Ko-fi payload │
|
||||
│ 2. Validate verification token │
|
||||
│ 3. Identify Paper Dynasty team: │
|
||||
│ • If discord_userid → Lookup by gmid │
|
||||
│ • Else extract team abbrev from message │
|
||||
│ 4. Map Ko-fi products → PD packs │
|
||||
│ 5. Grant packs via PD API │
|
||||
│ 6. Send Discord notification │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
Paper Dynasty Database Updated
|
||||
+
|
||||
Discord Notification Sent
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### 1. Ko-fi Configuration
|
||||
|
||||
**Get Verification Token:**
|
||||
1. Login to Ko-fi: https://ko-fi.com/
|
||||
2. Navigate to: Settings → API → Webhooks → Advanced
|
||||
3. Copy your **Verification Token** (UUID format)
|
||||
4. Save securely for n8n credentials setup
|
||||
|
||||
**Configure Webhook URL:**
|
||||
1. Ko-fi → Settings → API → Webhooks
|
||||
2. Set Webhook URL: `https://n8n.manticorum.com/webhook/kofi-paperdy`
|
||||
3. (Initially use `/webhook-test/` for testing)
|
||||
4. Enable webhook
|
||||
5. Test using Ko-fi's webhook tester
|
||||
|
||||
### 2. Paper Dynasty API
|
||||
|
||||
**API Key:**
|
||||
- Production API: `https://pd.manticorum.com/api/v2/`
|
||||
- Required header: `Authorization: Bearer YOUR_API_KEY`
|
||||
- Store API key securely for n8n credentials
|
||||
|
||||
**Required Endpoints:**
|
||||
- `GET /teams?gmid={discord_userid}` - Lookup team by Discord user ID
|
||||
- `GET /teams?abbrev={team_abbrev}` - Lookup team by abbreviation
|
||||
- `POST /packs` - Grant packs to team
|
||||
|
||||
### 3. Discord Notifications
|
||||
|
||||
**Webhook URL:**
|
||||
1. Discord channel → Settings → Integrations → Webhooks
|
||||
2. Create webhook for Ko-fi notifications
|
||||
3. Copy webhook URL
|
||||
4. Save for n8n credentials setup
|
||||
|
||||
## n8n Credentials Setup
|
||||
|
||||
Navigate to n8n → Settings → Credentials and create:
|
||||
|
||||
### 1. Ko-fi Verification Token
|
||||
|
||||
- **Type:** Generic Credential
|
||||
- **Name:** `Ko-fi Verification Token`
|
||||
- **Field:** `verification_token`
|
||||
- **Value:** `[your-kofi-verification-token-uuid]`
|
||||
|
||||
### 2. Paper Dynasty API Key
|
||||
|
||||
- **Type:** Header Auth
|
||||
- **Name:** `Paper Dynasty API`
|
||||
- **Header Name:** `Authorization`
|
||||
- **Value:** `Bearer [your-pd-api-key]`
|
||||
|
||||
### 3. Discord Webhook
|
||||
|
||||
- **Type:** Discord Webhook (or HTTP Header Auth)
|
||||
- **Name:** `Discord Ko-fi Notifications`
|
||||
- **Webhook URL:** `https://discord.com/api/webhooks/...`
|
||||
|
||||
## Product Mapping Configuration
|
||||
|
||||
Create n8n custom variable for product mapping.
|
||||
|
||||
**Navigate to:** n8n → Settings → Variables
|
||||
|
||||
**Variable Name:** `KOFI_PRODUCT_MAP`
|
||||
|
||||
**Value (JSON):**
|
||||
```json
|
||||
{
|
||||
"pack-standard-5": {
|
||||
"name": "5-Pack Standard",
|
||||
"pack_type_id": 1,
|
||||
"quantity": 5,
|
||||
"pack_cardset_id": null
|
||||
},
|
||||
"pack-premium-10": {
|
||||
"name": "10-Pack Premium",
|
||||
"pack_type_id": 3,
|
||||
"quantity": 10,
|
||||
"pack_cardset_id": null
|
||||
},
|
||||
"pack-team-choice": {
|
||||
"name": "Team Choice Pack",
|
||||
"pack_type_id": 8,
|
||||
"quantity": 1,
|
||||
"pack_cardset_id": 27
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Product Code Convention:**
|
||||
- Use Ko-fi's `direct_link_code` field as key
|
||||
- Set this in Ko-fi product settings
|
||||
- Format: `pack-{tier}-{quantity}` or `pack-{special-name}`
|
||||
- Examples: `pack-standard-5`, `pack-premium-10`, `pack-legendary-single`
|
||||
|
||||
## Workflow Node Configuration
|
||||
|
||||
### Node 1: Webhook Trigger
|
||||
|
||||
**Node Type:** Webhook
|
||||
**Name:** Ko-fi Webhook Trigger
|
||||
|
||||
**Settings:**
|
||||
- **HTTP Method:** POST
|
||||
- **Path:** `kofi-paperdy`
|
||||
- **Authentication:** None (we validate token manually)
|
||||
- **Response Mode:** When Last Node Finishes
|
||||
- **Response Code:** 200
|
||||
|
||||
**Creates URL:** `https://n8n.manticorum.com/webhook/kofi-paperdy`
|
||||
|
||||
---
|
||||
|
||||
### Node 2: Parse Ko-fi Data
|
||||
|
||||
**Node Type:** Code
|
||||
**Name:** Parse Ko-fi Form Data
|
||||
**Mode:** Run Once for All Items
|
||||
|
||||
**Code:**
|
||||
```javascript
|
||||
// Ko-fi sends application/x-www-form-urlencoded
|
||||
// The 'data' field contains JSON string
|
||||
const formData = $input.item.json.body;
|
||||
|
||||
// Parse JSON from data field
|
||||
const kofiData = JSON.parse(formData.data);
|
||||
|
||||
// Add metadata
|
||||
return [{
|
||||
json: {
|
||||
...kofiData,
|
||||
_parsed_at: new Date().toISOString(),
|
||||
_execution_id: $execution.id
|
||||
}
|
||||
}];
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Node 3: Validate Token
|
||||
|
||||
**Node Type:** IF
|
||||
**Name:** Validate Ko-fi Token
|
||||
|
||||
**Condition:**
|
||||
- **Type:** String
|
||||
- **Value 1:** `{{ $json.verification_token }}`
|
||||
- **Operation:** Equal
|
||||
- **Value 2:** `{{ $credentials.kofiVerificationToken.verification_token }}`
|
||||
|
||||
**Routing:**
|
||||
- **True:** Continue to Node 4
|
||||
- **False:** Connect to "Stop and Error" node
|
||||
|
||||
**False Path - Stop and Error Node:**
|
||||
- **Error Message:** `Invalid Ko-fi verification token`
|
||||
|
||||
---
|
||||
|
||||
### Node 4: Check Transaction Type
|
||||
|
||||
**Node Type:** IF
|
||||
**Name:** Filter Shop Orders
|
||||
|
||||
**Condition:**
|
||||
- **Type:** String
|
||||
- **Value 1:** `{{ $json.type }}`
|
||||
- **Operation:** Equal
|
||||
- **Value 2:** `Shop Order`
|
||||
|
||||
**Routing:**
|
||||
- **True:** Continue to Node 5
|
||||
- **False:** Connect to "Respond to Webhook" with message "Not a shop order"
|
||||
|
||||
---
|
||||
|
||||
### Node 5: Identify Paper Dynasty Team
|
||||
|
||||
**Node Type:** Code
|
||||
**Name:** Lookup Paper Dynasty Team
|
||||
**Mode:** Run Once for All Items
|
||||
|
||||
**Code:**
|
||||
```javascript
|
||||
// Multi-method team identification:
|
||||
// 1. discord_userid → lookup by gmid
|
||||
// 2. message field → extract team abbrev
|
||||
|
||||
const PD_API_BASE = 'https://pd.manticorum.com/api/v2';
|
||||
const API_TOKEN = '{{ $credentials.paperDynastyApi.headerAuth.value }}';
|
||||
|
||||
let teamId = null;
|
||||
let teamAbbrev = null;
|
||||
let identificationMethod = null;
|
||||
|
||||
// Method 1: Discord User ID → gmid lookup
|
||||
if ($json.discord_userid) {
|
||||
try {
|
||||
const response = await $http.request({
|
||||
method: 'GET',
|
||||
url: `${PD_API_BASE}/teams?gmid=${$json.discord_userid}`,
|
||||
headers: {
|
||||
'Authorization': API_TOKEN,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
if (response.body && response.body.length > 0) {
|
||||
teamId = response.body[0].id;
|
||||
teamAbbrev = response.body[0].abbrev;
|
||||
identificationMethod = 'discord_userid';
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Discord user ID lookup failed:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Method 2: Extract team abbrev from message field
|
||||
if (!teamId && $json.message) {
|
||||
// Look for team abbreviations (typically 3 uppercase letters)
|
||||
// Match patterns like: "CAR", "SKB", "PPD", etc.
|
||||
const abbrevMatch = $json.message.match(/\b([A-Z]{2,4})\b/);
|
||||
|
||||
if (abbrevMatch) {
|
||||
const extractedAbbrev = abbrevMatch[1];
|
||||
|
||||
try {
|
||||
const response = await $http.request({
|
||||
method: 'GET',
|
||||
url: `${PD_API_BASE}/teams?abbrev=${extractedAbbrev}`,
|
||||
headers: {
|
||||
'Authorization': API_TOKEN,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
if (response.body && response.body.length > 0) {
|
||||
teamId = response.body[0].id;
|
||||
teamAbbrev = response.body[0].abbrev;
|
||||
identificationMethod = 'message_abbrev';
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Team abbrev lookup failed:', error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return result
|
||||
return [{
|
||||
json: {
|
||||
...$json,
|
||||
pd_team_id: teamId,
|
||||
pd_team_abbrev: teamAbbrev,
|
||||
identification_method: identificationMethod,
|
||||
needs_manual_review: !teamId
|
||||
}
|
||||
}];
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Node 6: Route by Identification Status
|
||||
|
||||
**Node Type:** IF
|
||||
**Name:** Check Team Identified
|
||||
|
||||
**Condition:**
|
||||
- **Type:** Boolean
|
||||
- **Value 1:** `{{ $json.needs_manual_review }}`
|
||||
- **Operation:** Equal
|
||||
- **Value 2:** `false`
|
||||
|
||||
**Routing:**
|
||||
- **True:** Continue to Node 7 (process order)
|
||||
- **False:** Connect to "Manual Review" Discord notification
|
||||
|
||||
**False Path - Discord Notification:**
|
||||
- **Message:** Manual review needed (see Discord node config below)
|
||||
|
||||
---
|
||||
|
||||
### Node 7: Map Ko-fi Products
|
||||
|
||||
**Node Type:** Code
|
||||
**Name:** Map Products to PD Packs
|
||||
**Mode:** Run Once for All Items
|
||||
|
||||
**Code:**
|
||||
```javascript
|
||||
// Load product mapping
|
||||
const productMap = JSON.parse($vars.KOFI_PRODUCT_MAP);
|
||||
const shopItems = $json.shop_items;
|
||||
|
||||
// Process each shop item
|
||||
const packRequests = [];
|
||||
let unknownProducts = [];
|
||||
|
||||
for (const item of shopItems) {
|
||||
const productConfig = productMap[item.direct_link_code];
|
||||
|
||||
if (!productConfig) {
|
||||
// Unknown product code
|
||||
unknownProducts.push({
|
||||
code: item.direct_link_code,
|
||||
variation: item.variation_name,
|
||||
quantity: item.quantity
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create pack request for each quantity
|
||||
for (let i = 0; i < item.quantity; i++) {
|
||||
packRequests.push({
|
||||
team_id: $json.pd_team_id,
|
||||
pack_type_id: productConfig.pack_type_id,
|
||||
pack_cardset_id: productConfig.pack_cardset_id
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return [{
|
||||
json: {
|
||||
...$json,
|
||||
pack_requests: packRequests,
|
||||
unknown_products: unknownProducts,
|
||||
has_unknown_products: unknownProducts.length > 0,
|
||||
total_packs: packRequests.length
|
||||
}
|
||||
}];
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Node 8: Grant Packs via API
|
||||
|
||||
**Node Type:** HTTP Request
|
||||
**Name:** Paper Dynasty Grant Packs
|
||||
|
||||
**Settings:**
|
||||
- **Method:** POST
|
||||
- **URL:** `https://pd.manticorum.com/api/v2/packs`
|
||||
- **Authentication:** Use Credential → Select "Paper Dynasty API"
|
||||
- **Body Content Type:** JSON
|
||||
- **Continue on Fail:** ✅ Enabled
|
||||
- **Retry on Fail:** ✅ Enabled
|
||||
- **Max Tries:** 3
|
||||
- **Wait Between Tries:** 2000ms
|
||||
- **Timeout:** 10000ms
|
||||
|
||||
**Body (JSON):**
|
||||
```json
|
||||
{
|
||||
"packs": {{ JSON.stringify($json.pack_requests) }}
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"packs": [
|
||||
{"team_id": 69, "pack_type_id": 1, "pack_cardset_id": null},
|
||||
{"team_id": 69, "pack_type_id": 1, "pack_cardset_id": null},
|
||||
{"team_id": 69, "pack_type_id": 3, "pack_cardset_id": null}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Node 9: Check API Response
|
||||
|
||||
**Node Type:** IF
|
||||
**Name:** API Success Check
|
||||
|
||||
**Condition:**
|
||||
- **Type:** Number
|
||||
- **Value 1:** `{{ $json.statusCode }}`
|
||||
- **Operation:** Between
|
||||
- **Value 2:** `200`
|
||||
- **Value 3:** `299`
|
||||
|
||||
**Routing:**
|
||||
- **True:** Success Discord notification
|
||||
- **False:** Error Discord notification
|
||||
|
||||
---
|
||||
|
||||
### Node 10a: Success Notification
|
||||
|
||||
**Node Type:** Discord
|
||||
**Name:** Success Notification
|
||||
|
||||
**Credential:** Discord Ko-fi Notifications
|
||||
|
||||
**Message (JSON):**
|
||||
```json
|
||||
{
|
||||
"content": "✅ Ko-fi Order Processed",
|
||||
"embeds": [{
|
||||
"title": "Paper Dynasty Packs Granted",
|
||||
"color": 3066993,
|
||||
"fields": [
|
||||
{
|
||||
"name": "Customer",
|
||||
"value": "{{ $('Parse Ko-fi Form Data').item.json.from_name }}",
|
||||
"inline": true
|
||||
},
|
||||
{
|
||||
"name": "Team",
|
||||
"value": "{{ $('Lookup Paper Dynasty Team').item.json.pd_team_abbrev }}",
|
||||
"inline": true
|
||||
},
|
||||
{
|
||||
"name": "Amount",
|
||||
"value": "${{ $('Parse Ko-fi Form Data').item.json.amount }} {{ $('Parse Ko-fi Form Data').item.json.currency }}",
|
||||
"inline": true
|
||||
},
|
||||
{
|
||||
"name": "Packs Granted",
|
||||
"value": "{{ $('Map Products to PD Packs').item.json.total_packs }} packs",
|
||||
"inline": false
|
||||
},
|
||||
{
|
||||
"name": "Products",
|
||||
"value": "{{ $('Parse Ko-fi Form Data').item.json.shop_items.map(i => `${i.quantity}x ${i.variation_name}`).join('\\n') }}",
|
||||
"inline": false
|
||||
},
|
||||
{
|
||||
"name": "Transaction ID",
|
||||
"value": "`{{ $('Parse Ko-fi Form Data').item.json.kofi_transaction_id }}`",
|
||||
"inline": false
|
||||
}
|
||||
],
|
||||
"timestamp": "{{ new Date().toISOString() }}"
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Node 10b: Error Notification
|
||||
|
||||
**Node Type:** Discord
|
||||
**Name:** Error Notification
|
||||
|
||||
**Credential:** Discord Ko-fi Notifications
|
||||
|
||||
**Message (JSON):**
|
||||
```json
|
||||
{
|
||||
"content": "@here ⚠️ Ko-fi Order Processing Error",
|
||||
"embeds": [{
|
||||
"title": "Paper Dynasty API Error",
|
||||
"color": 15158332,
|
||||
"fields": [
|
||||
{
|
||||
"name": "Customer",
|
||||
"value": "{{ $('Parse Ko-fi Form Data').item.json.from_name }} ({{ $('Parse Ko-fi Form Data').item.json.email }})",
|
||||
"inline": false
|
||||
},
|
||||
{
|
||||
"name": "Amount",
|
||||
"value": "${{ $('Parse Ko-fi Form Data').item.json.amount }} {{ $('Parse Ko-fi Form Data').item.json.currency }}",
|
||||
"inline": true
|
||||
},
|
||||
{
|
||||
"name": "Team ID",
|
||||
"value": "{{ $('Lookup Paper Dynasty Team').item.json.pd_team_id || 'Unknown' }}",
|
||||
"inline": true
|
||||
},
|
||||
{
|
||||
"name": "Products",
|
||||
"value": "{{ $('Parse Ko-fi Form Data').item.json.shop_items.map(i => `${i.quantity}x ${i.variation_name}`).join('\\n') }}",
|
||||
"inline": false
|
||||
},
|
||||
{
|
||||
"name": "API Status",
|
||||
"value": "{{ $('Paper Dynasty Grant Packs').item.json.statusCode || 'Request Failed' }}",
|
||||
"inline": true
|
||||
},
|
||||
{
|
||||
"name": "Error Message",
|
||||
"value": "```{{ $('Paper Dynasty Grant Packs').item.json.body?.error || 'Unknown error' }}```",
|
||||
"inline": false
|
||||
},
|
||||
{
|
||||
"name": "Transaction ID",
|
||||
"value": "`{{ $('Parse Ko-fi Form Data').item.json.kofi_transaction_id }}`",
|
||||
"inline": false
|
||||
},
|
||||
{
|
||||
"name": "Action Required",
|
||||
"value": "Manual pack distribution needed. Use transaction ID to process manually.",
|
||||
"inline": false
|
||||
}
|
||||
],
|
||||
"timestamp": "{{ new Date().toISOString() }}"
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Node 10c: Manual Review Notification
|
||||
|
||||
**Node Type:** Discord
|
||||
**Name:** Manual Review Needed
|
||||
|
||||
**Credential:** Discord Ko-fi Notifications
|
||||
|
||||
**Message (JSON):**
|
||||
```json
|
||||
{
|
||||
"content": "@here 🔍 Ko-fi Order Needs Manual Review",
|
||||
"embeds": [{
|
||||
"title": "Unable to Identify Paper Dynasty Team",
|
||||
"color": 16776960,
|
||||
"fields": [
|
||||
{
|
||||
"name": "Customer",
|
||||
"value": "{{ $json.from_name }} ({{ $json.email }})",
|
||||
"inline": false
|
||||
},
|
||||
{
|
||||
"name": "Amount",
|
||||
"value": "${{ $json.amount }} {{ $json.currency }}",
|
||||
"inline": true
|
||||
},
|
||||
{
|
||||
"name": "Discord User ID",
|
||||
"value": "{{ $json.discord_userid || 'Not provided' }}",
|
||||
"inline": true
|
||||
},
|
||||
{
|
||||
"name": "Message",
|
||||
"value": "{{ $json.message || 'No message' }}",
|
||||
"inline": false
|
||||
},
|
||||
{
|
||||
"name": "Products",
|
||||
"value": "{{ $json.shop_items.map(i => `${i.quantity}x ${i.variation_name} (${i.direct_link_code})`).join('\\n') }}",
|
||||
"inline": false
|
||||
},
|
||||
{
|
||||
"name": "Transaction ID",
|
||||
"value": "`{{ $json.kofi_transaction_id }}`",
|
||||
"inline": false
|
||||
},
|
||||
{
|
||||
"name": "Action Required",
|
||||
"value": "1. Identify user's Paper Dynasty team\n2. Manually grant packs using Paper Dynasty API\n3. Reply to this message when resolved",
|
||||
"inline": false
|
||||
}
|
||||
],
|
||||
"timestamp": "{{ new Date().toISOString() }}"
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Node 11: Respond to Webhook
|
||||
|
||||
**Node Type:** Respond to Webhook
|
||||
**Name:** Return 200 OK
|
||||
|
||||
**Settings:**
|
||||
- **Response Code:** 200
|
||||
- **Response Body:** JSON
|
||||
|
||||
**Body:**
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"message": "Order received and processed",
|
||||
"execution_id": "{{ $execution.id }}"
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** This node must execute after all processing to ensure Ko-fi receives the 200 status code.
|
||||
|
||||
## Workflow Visual Structure
|
||||
|
||||
```
|
||||
[Webhook Trigger]
|
||||
↓
|
||||
[Parse Ko-fi Data]
|
||||
↓
|
||||
[Validate Token] → (false) → [Stop and Error]
|
||||
↓ (true)
|
||||
[Filter Shop Orders] → (false) → [Respond: Not shop order]
|
||||
↓ (true)
|
||||
[Lookup PD Team]
|
||||
↓
|
||||
[Check Team Identified]
|
||||
├─ (false) → [Discord: Manual Review] → [Respond to Webhook]
|
||||
↓ (true)
|
||||
[Map Products to PD Packs]
|
||||
↓
|
||||
[PD API: Grant Packs]
|
||||
↓
|
||||
[API Success Check]
|
||||
├─ (true) → [Discord: Success]
|
||||
└─ (false) → [Discord: Error]
|
||||
↓
|
||||
[Respond to Webhook: 200 OK]
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
### Phase 1: Development Testing
|
||||
|
||||
**Use Test Webhook Path:**
|
||||
- Test URL: `https://n8n.manticorum.com/webhook-test/kofi-paperdy`
|
||||
- This won't trigger production workflows
|
||||
- View test executions in n8n UI
|
||||
|
||||
**Test with cURL:**
|
||||
```bash
|
||||
curl -X POST 'https://n8n.manticorum.com/webhook-test/kofi-paperdy' \
|
||||
-H 'Content-Type: application/x-www-form-urlencoded' \
|
||||
--data-urlencode 'data={
|
||||
"verification_token": "YOUR_TOKEN_HERE",
|
||||
"message_id": "test-001",
|
||||
"timestamp": "2025-11-13T12:00:00Z",
|
||||
"type": "Shop Order",
|
||||
"is_public": true,
|
||||
"from_name": "Test Customer",
|
||||
"message": "Team: SKB",
|
||||
"amount": "10.00",
|
||||
"url": "https://ko-fi.com/test",
|
||||
"email": "[email protected]",
|
||||
"currency": "USD",
|
||||
"kofi_transaction_id": "test-txn-001",
|
||||
"shop_items": [
|
||||
{
|
||||
"direct_link_code": "pack-standard-5",
|
||||
"variation_name": "5-Pack Standard",
|
||||
"quantity": 1
|
||||
}
|
||||
],
|
||||
"discord_userid": "012345678901234567"
|
||||
}'
|
||||
```
|
||||
|
||||
### Phase 2: Ko-fi Test Webhook
|
||||
|
||||
**Configure Ko-fi:**
|
||||
1. Ko-fi → API → Webhooks
|
||||
2. Set URL: `https://n8n.manticorum.com/webhook-test/kofi-paperdy`
|
||||
3. Use Ko-fi's "Test Webhook" button
|
||||
4. Verify n8n receives and processes correctly
|
||||
|
||||
### Phase 3: Test Purchases
|
||||
|
||||
**Create Test Products:**
|
||||
1. Ko-fi → Shop → Add Product
|
||||
2. Set product code (e.g., `pack-test-1`)
|
||||
3. Price: $0.01 (minimum)
|
||||
4. Make test purchase
|
||||
5. Verify workflow processes correctly
|
||||
6. Check Discord for notification
|
||||
7. Verify packs granted in Paper Dynasty
|
||||
|
||||
### Phase 4: Production Deployment
|
||||
|
||||
**Switch to Production:**
|
||||
1. Update webhook path in workflow from `/webhook-test/` to `/webhook/`
|
||||
2. Update Ko-fi webhook URL to production path
|
||||
3. Activate workflow in n8n
|
||||
4. Monitor first 10 real transactions closely
|
||||
5. Verify all Discord notifications working
|
||||
6. Check Paper Dynasty pack grants are correct
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Webhook Not Triggering
|
||||
|
||||
**Check:**
|
||||
- Workflow is Active (toggle in n8n)
|
||||
- Webhook path matches Ko-fi configuration
|
||||
- n8n is accessible from internet
|
||||
- NPM proxy host is running
|
||||
|
||||
**Test:**
|
||||
```bash
|
||||
curl -I https://n8n.manticorum.com/webhook/kofi-paperdy
|
||||
# Should return: HTTP/1.1 200 OK or 404 (workflow not active)
|
||||
```
|
||||
|
||||
### Invalid Token Error
|
||||
|
||||
**Check:**
|
||||
- Token in n8n credentials matches Ko-fi dashboard
|
||||
- No extra whitespace in token
|
||||
- Credential name referenced correctly in IF node
|
||||
|
||||
**Fix:**
|
||||
- Re-copy token from Ko-fi
|
||||
- Update n8n credential
|
||||
- Test workflow again
|
||||
|
||||
### Team Not Found
|
||||
|
||||
**Causes:**
|
||||
- Discord user ID not linked to Paper Dynasty account
|
||||
- Team abbrev not in message field
|
||||
- Typo in team abbreviation
|
||||
|
||||
**Resolution:**
|
||||
- Manual review Discord notification sent
|
||||
- Admin manually grants packs
|
||||
- Update user's PD profile with correct gmid
|
||||
|
||||
### API Call Failing
|
||||
|
||||
**Check:**
|
||||
- API key is valid
|
||||
- Paper Dynasty API is online
|
||||
- Request payload format is correct
|
||||
- Team ID exists in database
|
||||
|
||||
**Debug:**
|
||||
- View full API response in n8n execution log
|
||||
- Test API call manually with curl
|
||||
- Check Paper Dynasty API logs
|
||||
|
||||
### Packs Not Granted
|
||||
|
||||
**Verify:**
|
||||
- API returned 200 status
|
||||
- pack_type_id is valid
|
||||
- team_id exists
|
||||
- Correct number of packs in request
|
||||
|
||||
**Check in Paper Dynasty:**
|
||||
```bash
|
||||
# Using Paper Dynasty API
|
||||
curl -H "Authorization: Bearer YOUR_KEY" \
|
||||
"https://pd.manticorum.com/api/v2/packs?team_id=69&opened=false&limit=20"
|
||||
```
|
||||
|
||||
## Monitoring
|
||||
|
||||
### n8n Execution Logs
|
||||
|
||||
**View Executions:**
|
||||
1. n8n → Executions tab
|
||||
2. Filter by workflow: "Ko-fi to Paper Dynasty"
|
||||
3. Review successful and failed executions
|
||||
4. Click execution to see node-by-node data flow
|
||||
|
||||
### Discord Notifications
|
||||
|
||||
**Monitor Channel:**
|
||||
- All successful orders → success notification
|
||||
- All errors → error notification with @here mention
|
||||
- Manual reviews → notification with customer details
|
||||
|
||||
### Paper Dynasty Verification
|
||||
|
||||
**Verify Packs Granted:**
|
||||
```python
|
||||
from api_client import PaperDynastyAPI
|
||||
|
||||
api = PaperDynastyAPI(environment='prod')
|
||||
|
||||
# Check recent packs
|
||||
recent_packs = api.list_packs(opened=False, new_to_old=True, limit=50)
|
||||
|
||||
# Filter for Ko-fi source (if transaction_id stored)
|
||||
kofi_packs = [p for p in recent_packs if 'kofi' in str(p.get('metadata', {}))]
|
||||
```
|
||||
|
||||
## Maintenance
|
||||
|
||||
### Weekly Tasks
|
||||
|
||||
- [ ] Review failed executions in n8n
|
||||
- [ ] Check for unknown product codes
|
||||
- [ ] Verify all manual review items resolved
|
||||
- [ ] Monitor API response times
|
||||
|
||||
### Monthly Tasks
|
||||
|
||||
- [ ] Update product mapping for new Ko-fi products
|
||||
- [ ] Review and optimize workflow performance
|
||||
- [ ] Backup n8n database
|
||||
- [ ] Test disaster recovery procedure
|
||||
|
||||
### Quarterly Tasks
|
||||
|
||||
- [ ] Rotate Ko-fi verification token
|
||||
- [ ] Rotate Paper Dynasty API key
|
||||
- [ ] Review workflow for optimization opportunities
|
||||
- [ ] Update documentation with lessons learned
|
||||
|
||||
## References
|
||||
|
||||
- **Ko-fi Webhook Docs:** https://help.ko-fi.com/hc/en-us/articles/360004162298
|
||||
- **Paper Dynasty API:** `/home/cal/.claude/skills/paper-dynasty/SKILL.md`
|
||||
- **n8n Documentation:** https://docs.n8n.io/
|
||||
- **n8n Webhook Node:** https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.webhook/
|
||||
|
||||
## Change Log
|
||||
|
||||
### 2025-11-13 - Initial Design
|
||||
- Created workflow specification
|
||||
- Defined multi-method team identification
|
||||
- Configured product mapping structure
|
||||
- Designed error handling and manual review process
|
||||
178
productivity/n8n/workflows/kofi-product-mapping-template.json
Normal file
178
productivity/n8n/workflows/kofi-product-mapping-template.json
Normal file
@ -0,0 +1,178 @@
|
||||
{
|
||||
"_comment": "Ko-fi Product Mapping Template for Paper Dynasty",
|
||||
"_instructions": "1. Copy this template to n8n → Settings → Variables → Create Variable 'KOFI_PRODUCT_MAP'",
|
||||
"_instructions_2": "2. Update product codes to match your Ko-fi shop 'direct_link_code' values",
|
||||
"_instructions_3": "3. Set pack_type_id according to Paper Dynasty pack types (see pack_types reference below)",
|
||||
"_instructions_4": "4. Use pack_cardset_id for cardset-specific packs (or null for current/default cardset)",
|
||||
|
||||
"_pack_types_reference": {
|
||||
"1": "Standard",
|
||||
"2": "Starter",
|
||||
"3": "Premium",
|
||||
"4": "Check-In Player",
|
||||
"5": "MVP",
|
||||
"6": "All Star",
|
||||
"7": "Mario",
|
||||
"8": "Team Choice (1 card, choice of 4 from selected MLB club)",
|
||||
"9": "Promo Choice"
|
||||
},
|
||||
|
||||
"_naming_conventions": {
|
||||
"format": "pack-{tier}-{quantity}",
|
||||
"examples": [
|
||||
"pack-standard-5 (5 standard packs)",
|
||||
"pack-premium-10 (10 premium packs)",
|
||||
"pack-team-choice (1 team choice pack)",
|
||||
"pack-mvp-bundle (special bundle)",
|
||||
"sub-monthly-5packs (monthly subscription with 5 packs)"
|
||||
],
|
||||
"best_practices": [
|
||||
"Use lowercase with hyphens",
|
||||
"Be descriptive but concise",
|
||||
"Include quantity in name if variable",
|
||||
"Use consistent prefixes (pack-, sub-, card-)"
|
||||
]
|
||||
},
|
||||
|
||||
"pack-standard-5": {
|
||||
"name": "5-Pack Standard Bundle",
|
||||
"description": "Five standard packs",
|
||||
"pack_type_id": 1,
|
||||
"quantity": 5,
|
||||
"pack_cardset_id": null,
|
||||
"price_usd": 5.00
|
||||
},
|
||||
|
||||
"pack-standard-10": {
|
||||
"name": "10-Pack Standard Bundle",
|
||||
"description": "Ten standard packs - Best Value!",
|
||||
"pack_type_id": 1,
|
||||
"quantity": 10,
|
||||
"pack_cardset_id": null,
|
||||
"price_usd": 9.00
|
||||
},
|
||||
|
||||
"pack-premium-5": {
|
||||
"name": "5-Pack Premium Bundle",
|
||||
"description": "Five premium packs with better odds",
|
||||
"pack_type_id": 3,
|
||||
"quantity": 5,
|
||||
"pack_cardset_id": null,
|
||||
"price_usd": 10.00
|
||||
},
|
||||
|
||||
"pack-premium-10": {
|
||||
"name": "10-Pack Premium Bundle",
|
||||
"description": "Ten premium packs - Maximum Value!",
|
||||
"pack_type_id": 3,
|
||||
"quantity": 10,
|
||||
"pack_cardset_id": null,
|
||||
"price_usd": 18.00
|
||||
},
|
||||
|
||||
"pack-team-choice": {
|
||||
"name": "Team Choice Pack",
|
||||
"description": "One card, choice of 4 from your favorite MLB team",
|
||||
"pack_type_id": 8,
|
||||
"quantity": 1,
|
||||
"pack_cardset_id": 27,
|
||||
"price_usd": 3.00
|
||||
},
|
||||
|
||||
"pack-mvp-special": {
|
||||
"name": "MVP Special Pack",
|
||||
"description": "Special MVP rarity pack",
|
||||
"pack_type_id": 5,
|
||||
"quantity": 1,
|
||||
"pack_cardset_id": null,
|
||||
"price_usd": 15.00
|
||||
},
|
||||
|
||||
"pack-allstar-bundle": {
|
||||
"name": "All-Star 3-Pack Bundle",
|
||||
"description": "Three All-Star rarity packs",
|
||||
"pack_type_id": 6,
|
||||
"quantity": 3,
|
||||
"pack_cardset_id": null,
|
||||
"price_usd": 12.00
|
||||
},
|
||||
|
||||
"sub-monthly-standard": {
|
||||
"name": "Monthly Subscription - Standard",
|
||||
"description": "Monthly subscription with 5 standard packs",
|
||||
"pack_type_id": 1,
|
||||
"quantity": 5,
|
||||
"pack_cardset_id": null,
|
||||
"price_usd": 5.00,
|
||||
"subscription": true,
|
||||
"subscription_period": "monthly"
|
||||
},
|
||||
|
||||
"sub-monthly-premium": {
|
||||
"name": "Monthly Subscription - Premium",
|
||||
"description": "Monthly subscription with 5 premium packs",
|
||||
"pack_type_id": 3,
|
||||
"quantity": 5,
|
||||
"pack_cardset_id": null,
|
||||
"price_usd": 10.00,
|
||||
"subscription": true,
|
||||
"subscription_period": "monthly"
|
||||
},
|
||||
|
||||
"_example_seasonal_products": {
|
||||
"pack-2005-throwback": {
|
||||
"name": "2005 Throwback Pack",
|
||||
"description": "Limited edition 2005 season packs",
|
||||
"pack_type_id": 1,
|
||||
"quantity": 3,
|
||||
"pack_cardset_id": 27,
|
||||
"price_usd": 7.00,
|
||||
"limited_edition": true
|
||||
},
|
||||
|
||||
"pack-holiday-2025": {
|
||||
"name": "Holiday Special 2025",
|
||||
"description": "Holiday bundle with 10 mixed packs",
|
||||
"pack_type_id": 3,
|
||||
"quantity": 10,
|
||||
"pack_cardset_id": null,
|
||||
"price_usd": 20.00,
|
||||
"limited_edition": true,
|
||||
"available_until": "2025-12-31"
|
||||
}
|
||||
},
|
||||
|
||||
"_usage_examples": {
|
||||
"simple_product": {
|
||||
"code": "pack-simple",
|
||||
"config": {
|
||||
"pack_type_id": 1,
|
||||
"quantity": 5,
|
||||
"pack_cardset_id": null
|
||||
}
|
||||
},
|
||||
|
||||
"cardset_specific": {
|
||||
"code": "pack-2005-special",
|
||||
"config": {
|
||||
"pack_type_id": 1,
|
||||
"quantity": 3,
|
||||
"pack_cardset_id": 27
|
||||
}
|
||||
},
|
||||
|
||||
"with_metadata": {
|
||||
"code": "pack-promo-code",
|
||||
"config": {
|
||||
"pack_type_id": 1,
|
||||
"quantity": 5,
|
||||
"pack_cardset_id": null,
|
||||
"promo_code": "WELCOME2025",
|
||||
"tracking": {
|
||||
"source": "email_campaign",
|
||||
"campaign_id": "welcome-2025-q1"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
667
productivity/n8n/workflows/kofi-testing-guide.md
Normal file
667
productivity/n8n/workflows/kofi-testing-guide.md
Normal file
@ -0,0 +1,667 @@
|
||||
# Ko-fi → Paper Dynasty Testing Guide
|
||||
|
||||
Comprehensive testing guide for the Ko-fi integration workflow.
|
||||
|
||||
## Testing Phases
|
||||
|
||||
### Phase 1: Local Development Testing
|
||||
|
||||
Test n8n workflow logic without involving Ko-fi.
|
||||
|
||||
**Objective:** Validate workflow nodes process data correctly
|
||||
|
||||
**Method:** Manual execution in n8n UI
|
||||
|
||||
**Steps:**
|
||||
1. Open workflow in n8n
|
||||
2. Click "Execute Workflow" button
|
||||
3. Workflow will wait at Webhook node
|
||||
4. Use cURL or Postman to send test payload
|
||||
5. Review execution results node-by-node
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: Webhook Testing with cURL
|
||||
|
||||
Test webhook endpoint with simulated Ko-fi payloads.
|
||||
|
||||
**Objective:** Verify webhook receives and parses Ko-fi data
|
||||
|
||||
**Test URL:** `https://n8n.manticorum.com/webhook-test/kofi-paperdy`
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: Ko-fi Test Webhook
|
||||
|
||||
Use Ko-fi's built-in webhook tester.
|
||||
|
||||
**Objective:** Validate integration with Ko-fi's actual webhook system
|
||||
|
||||
**Steps:**
|
||||
1. Ko-fi → Settings → API → Webhooks
|
||||
2. Set webhook URL to test path
|
||||
3. Click "Test Webhook" button
|
||||
4. Review n8n execution logs
|
||||
|
||||
---
|
||||
|
||||
### Phase 4: Test Purchases
|
||||
|
||||
Make real purchases with test products.
|
||||
|
||||
**Objective:** End-to-end validation with real Ko-fi data
|
||||
|
||||
**Steps:**
|
||||
1. Create test products in Ko-fi shop ($0.01 minimum)
|
||||
2. Make test purchases
|
||||
3. Verify packs granted in Paper Dynasty
|
||||
4. Check Discord notifications
|
||||
|
||||
---
|
||||
|
||||
### Phase 5: Production Deployment
|
||||
|
||||
Switch to production webhook path and monitor.
|
||||
|
||||
**Objective:** Deploy to production with confidence
|
||||
|
||||
**Steps:**
|
||||
1. Switch webhook path from `/webhook-test/` to `/webhook/`
|
||||
2. Update Ko-fi webhook URL
|
||||
3. Monitor first 10 transactions manually
|
||||
4. Verify all flows working correctly
|
||||
|
||||
---
|
||||
|
||||
## Test Payloads
|
||||
|
||||
### Test 1: Basic Order with Discord User ID
|
||||
|
||||
**Scenario:** User with linked Discord account purchases 5 standard packs
|
||||
|
||||
**cURL Command:**
|
||||
```bash
|
||||
curl -X POST 'https://n8n.manticorum.com/webhook-test/kofi-paperdy' \
|
||||
-H 'Content-Type: application/x-www-form-urlencoded' \
|
||||
--data-urlencode 'data={
|
||||
"verification_token": "YOUR_KOFI_TOKEN_HERE",
|
||||
"message_id": "test-001-discord",
|
||||
"timestamp": "2025-11-13T12:00:00Z",
|
||||
"type": "Shop Order",
|
||||
"is_public": true,
|
||||
"from_name": "Test Customer Discord",
|
||||
"message": "Thanks for the packs!",
|
||||
"amount": "5.00",
|
||||
"url": "https://ko-fi.com/Home/CoffeeShop?txid=test-001",
|
||||
"email": "[email protected]",
|
||||
"currency": "USD",
|
||||
"is_subscription_payment": false,
|
||||
"is_first_subscription_payment": false,
|
||||
"kofi_transaction_id": "test-001-discord-userid",
|
||||
"shop_items": [
|
||||
{
|
||||
"direct_link_code": "pack-standard-5",
|
||||
"variation_name": "5-Pack Standard",
|
||||
"quantity": 1
|
||||
}
|
||||
],
|
||||
"tier_name": null,
|
||||
"shipping": null,
|
||||
"discord_username": "TestUser#1234",
|
||||
"discord_userid": "012345678901234567"
|
||||
}'
|
||||
```
|
||||
|
||||
**Expected Result:**
|
||||
- ✅ Token validated
|
||||
- ✅ Team found via discord_userid → gmid lookup
|
||||
- ✅ 5 standard packs granted to team
|
||||
- ✅ Success Discord notification sent
|
||||
|
||||
---
|
||||
|
||||
### Test 2: Order with Team Abbrev in Message
|
||||
|
||||
**Scenario:** User without Discord link includes team abbreviation in message
|
||||
|
||||
**cURL Command:**
|
||||
```bash
|
||||
curl -X POST 'https://n8n.manticorum.com/webhook-test/kofi-paperdy' \
|
||||
-H 'Content-Type: application/x-www-form-urlencoded' \
|
||||
--data-urlencode 'data={
|
||||
"verification_token": "YOUR_KOFI_TOKEN_HERE",
|
||||
"message_id": "test-002-abbrev",
|
||||
"timestamp": "2025-11-13T12:05:00Z",
|
||||
"type": "Shop Order",
|
||||
"is_public": true,
|
||||
"from_name": "Test Customer Abbrev",
|
||||
"message": "Team: SKB - Thanks!",
|
||||
"amount": "10.00",
|
||||
"url": "https://ko-fi.com/Home/CoffeeShop?txid=test-002",
|
||||
"email": "[email protected]",
|
||||
"currency": "USD",
|
||||
"is_subscription_payment": false,
|
||||
"is_first_subscription_payment": false,
|
||||
"kofi_transaction_id": "test-002-team-abbrev",
|
||||
"shop_items": [
|
||||
{
|
||||
"direct_link_code": "pack-premium-10",
|
||||
"variation_name": "10-Pack Premium",
|
||||
"quantity": 1
|
||||
}
|
||||
],
|
||||
"tier_name": null,
|
||||
"shipping": null,
|
||||
"discord_username": null,
|
||||
"discord_userid": null
|
||||
}'
|
||||
```
|
||||
|
||||
**Expected Result:**
|
||||
- ✅ Token validated
|
||||
- ✅ Team "SKB" extracted from message
|
||||
- ✅ Team found via abbrev lookup
|
||||
- ✅ 10 premium packs granted to team
|
||||
- ✅ Success Discord notification sent
|
||||
|
||||
---
|
||||
|
||||
### Test 3: Multiple Items in Single Order
|
||||
|
||||
**Scenario:** User purchases multiple product types in one transaction
|
||||
|
||||
**cURL Command:**
|
||||
```bash
|
||||
curl -X POST 'https://n8n.manticorum.com/webhook-test/kofi-paperdy' \
|
||||
-H 'Content-Type: application/x-www-form-urlencoded' \
|
||||
--data-urlencode 'data={
|
||||
"verification_token": "YOUR_KOFI_TOKEN_HERE",
|
||||
"message_id": "test-003-multi",
|
||||
"timestamp": "2025-11-13T12:10:00Z",
|
||||
"type": "Shop Order",
|
||||
"is_public": true,
|
||||
"from_name": "Test Customer Multi",
|
||||
"message": "CAR",
|
||||
"amount": "25.00",
|
||||
"url": "https://ko-fi.com/Home/CoffeeShop?txid=test-003",
|
||||
"email": "[email protected]",
|
||||
"currency": "USD",
|
||||
"is_subscription_payment": false,
|
||||
"is_first_subscription_payment": false,
|
||||
"kofi_transaction_id": "test-003-multiple-items",
|
||||
"shop_items": [
|
||||
{
|
||||
"direct_link_code": "pack-standard-5",
|
||||
"variation_name": "5-Pack Standard",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"direct_link_code": "pack-premium-5",
|
||||
"variation_name": "5-Pack Premium",
|
||||
"quantity": 1
|
||||
},
|
||||
{
|
||||
"direct_link_code": "pack-team-choice",
|
||||
"variation_name": "Team Choice Pack",
|
||||
"quantity": 1
|
||||
}
|
||||
],
|
||||
"tier_name": null,
|
||||
"shipping": null,
|
||||
"discord_username": null,
|
||||
"discord_userid": null
|
||||
}'
|
||||
```
|
||||
|
||||
**Expected Result:**
|
||||
- ✅ Token validated
|
||||
- ✅ Team "CAR" found via abbrev
|
||||
- ✅ 15 total packs granted (10 standard + 5 premium + 1 team choice)
|
||||
- ✅ Success Discord notification with itemized list
|
||||
|
||||
---
|
||||
|
||||
### Test 4: Team Not Found (Manual Review)
|
||||
|
||||
**Scenario:** User provides neither Discord ID nor valid team abbrev
|
||||
|
||||
**cURL Command:**
|
||||
```bash
|
||||
curl -X POST 'https://n8n.manticorum.com/webhook-test/kofi-paperdy' \
|
||||
-H 'Content-Type: application/x-www-form-urlencoded' \
|
||||
--data-urlencode 'data={
|
||||
"verification_token": "YOUR_KOFI_TOKEN_HERE",
|
||||
"message_id": "test-004-unknown",
|
||||
"timestamp": "2025-11-13T12:15:00Z",
|
||||
"type": "Shop Order",
|
||||
"is_public": true,
|
||||
"from_name": "Unknown User",
|
||||
"message": "I love Paper Dynasty!",
|
||||
"amount": "5.00",
|
||||
"url": "https://ko-fi.com/Home/CoffeeShop?txid=test-004",
|
||||
"email": "[email protected]",
|
||||
"currency": "USD",
|
||||
"is_subscription_payment": false,
|
||||
"is_first_subscription_payment": false,
|
||||
"kofi_transaction_id": "test-004-manual-review",
|
||||
"shop_items": [
|
||||
{
|
||||
"direct_link_code": "pack-standard-5",
|
||||
"variation_name": "5-Pack Standard",
|
||||
"quantity": 1
|
||||
}
|
||||
],
|
||||
"tier_name": null,
|
||||
"shipping": null,
|
||||
"discord_username": null,
|
||||
"discord_userid": null
|
||||
}'
|
||||
```
|
||||
|
||||
**Expected Result:**
|
||||
- ✅ Token validated
|
||||
- ❌ No team identified
|
||||
- ⚠️ Manual review Discord notification sent with @here mention
|
||||
- ⚠️ Admin grants packs manually after identifying user
|
||||
|
||||
---
|
||||
|
||||
### Test 5: Unknown Product Code
|
||||
|
||||
**Scenario:** Ko-fi product code not in mapping
|
||||
|
||||
**cURL Command:**
|
||||
```bash
|
||||
curl -X POST 'https://n8n.manticorum.com/webhook-test/kofi-paperdy' \
|
||||
-H 'Content-Type: application/x-www-form-urlencoded' \
|
||||
--data-urlencode 'data={
|
||||
"verification_token": "YOUR_KOFI_TOKEN_HERE",
|
||||
"message_id": "test-005-unknown-product",
|
||||
"timestamp": "2025-11-13T12:20:00Z",
|
||||
"type": "Shop Order",
|
||||
"is_public": true,
|
||||
"from_name": "Test Unknown Product",
|
||||
"message": "SKB",
|
||||
"amount": "99.99",
|
||||
"url": "https://ko-fi.com/Home/CoffeeShop?txid=test-005",
|
||||
"email": "[email protected]",
|
||||
"currency": "USD",
|
||||
"is_subscription_payment": false,
|
||||
"is_first_subscription_payment": false,
|
||||
"kofi_transaction_id": "test-005-unknown-product",
|
||||
"shop_items": [
|
||||
{
|
||||
"direct_link_code": "pack-does-not-exist",
|
||||
"variation_name": "Mystery Pack",
|
||||
"quantity": 1
|
||||
}
|
||||
],
|
||||
"tier_name": null,
|
||||
"shipping": null,
|
||||
"discord_username": null,
|
||||
"discord_userid": null
|
||||
}'
|
||||
```
|
||||
|
||||
**Expected Result:**
|
||||
- ✅ Token validated
|
||||
- ✅ Team "SKB" found
|
||||
- ❌ Product code "pack-does-not-exist" not in mapping
|
||||
- ⚠️ Manual review Discord notification
|
||||
- ⚠️ Admin adds product to mapping or processes manually
|
||||
|
||||
---
|
||||
|
||||
### Test 6: Invalid Verification Token
|
||||
|
||||
**Scenario:** Test security - invalid Ko-fi token
|
||||
|
||||
**cURL Command:**
|
||||
```bash
|
||||
curl -X POST 'https://n8n.manticorum.com/webhook-test/kofi-paperdy' \
|
||||
-H 'Content-Type: application/x-www-form-urlencoded' \
|
||||
--data-urlencode 'data={
|
||||
"verification_token": "INVALID-TOKEN-12345",
|
||||
"message_id": "test-006-invalid",
|
||||
"timestamp": "2025-11-13T12:25:00Z",
|
||||
"type": "Shop Order",
|
||||
"is_public": true,
|
||||
"from_name": "Hacker Attempt",
|
||||
"message": "SKB",
|
||||
"amount": "1000.00",
|
||||
"url": "https://ko-fi.com/Home/CoffeeShop?txid=test-006",
|
||||
"email": "[email protected]",
|
||||
"currency": "USD",
|
||||
"kofi_transaction_id": "test-006-security",
|
||||
"shop_items": [
|
||||
{
|
||||
"direct_link_code": "pack-standard-5",
|
||||
"variation_name": "5-Pack Standard",
|
||||
"quantity": 100
|
||||
}
|
||||
],
|
||||
"discord_userid": null
|
||||
}'
|
||||
```
|
||||
|
||||
**Expected Result:**
|
||||
- ❌ Token validation fails
|
||||
- ⛔ Workflow stops at validation node
|
||||
- ⛔ NO packs granted
|
||||
- ⛔ NO Discord notification (security - don't reveal valid tokens)
|
||||
|
||||
---
|
||||
|
||||
### Test 7: Non-Shop Order Type
|
||||
|
||||
**Scenario:** Ko-fi sends donation (not shop order)
|
||||
|
||||
**cURL Command:**
|
||||
```bash
|
||||
curl -X POST 'https://n8n.manticorum.com/webhook-test/kofi-paperdy' \
|
||||
-H 'Content-Type: application/x-www-form-urlencoded' \
|
||||
--data-urlencode 'data={
|
||||
"verification_token": "YOUR_KOFI_TOKEN_HERE",
|
||||
"message_id": "test-007-donation",
|
||||
"timestamp": "2025-11-13T12:30:00Z",
|
||||
"type": "Donation",
|
||||
"is_public": true,
|
||||
"from_name": "Generous Donor",
|
||||
"message": "Keep up the great work!",
|
||||
"amount": "10.00",
|
||||
"url": "https://ko-fi.com/Home/CoffeeShop?txid=test-007",
|
||||
"email": "[email protected]",
|
||||
"currency": "USD",
|
||||
"is_subscription_payment": false,
|
||||
"kofi_transaction_id": "test-007-donation",
|
||||
"shop_items": null,
|
||||
"discord_userid": null
|
||||
}'
|
||||
```
|
||||
|
||||
**Expected Result:**
|
||||
- ✅ Token validated
|
||||
- ℹ️ Type is "Donation" not "Shop Order"
|
||||
- ⏭️ Workflow skips processing (no packs to grant)
|
||||
- ✅ Returns 200 OK to Ko-fi
|
||||
|
||||
---
|
||||
|
||||
### Test 8: Monthly Subscription Payment
|
||||
|
||||
**Scenario:** Recurring subscription payment
|
||||
|
||||
**cURL Command:**
|
||||
```bash
|
||||
curl -X POST 'https://n8n.manticorum.com/webhook-test/kofi-paperdy' \
|
||||
-H 'Content-Type: application/x-www-form-urlencoded' \
|
||||
--data-urlencode 'data={
|
||||
"verification_token": "YOUR_KOFI_TOKEN_HERE",
|
||||
"message_id": "test-008-subscription",
|
||||
"timestamp": "2025-11-13T12:35:00Z",
|
||||
"type": "Subscription",
|
||||
"is_public": true,
|
||||
"from_name": "Monthly Subscriber",
|
||||
"message": "PPD",
|
||||
"amount": "5.00",
|
||||
"url": "https://ko-fi.com/Home/CoffeeShop?txid=test-008",
|
||||
"email": "[email protected]",
|
||||
"currency": "USD",
|
||||
"is_subscription_payment": true,
|
||||
"is_first_subscription_payment": false,
|
||||
"kofi_transaction_id": "test-008-subscription",
|
||||
"shop_items": [
|
||||
{
|
||||
"direct_link_code": "sub-monthly-standard",
|
||||
"variation_name": "Monthly Standard Subscription",
|
||||
"quantity": 1
|
||||
}
|
||||
],
|
||||
"tier_name": "Standard Member",
|
||||
"discord_username": "Subscriber#5678",
|
||||
"discord_userid": "987654321098765432"
|
||||
}'
|
||||
```
|
||||
|
||||
**Expected Result:**
|
||||
- ✅ Token validated
|
||||
- ✅ Subscription payment processed as shop order
|
||||
- ✅ 5 standard packs granted
|
||||
- ✅ Success notification includes subscription tier
|
||||
|
||||
---
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
### Pre-Deployment Testing
|
||||
|
||||
- [ ] **Test 1:** Basic order with Discord user ID ✅
|
||||
- [ ] **Test 2:** Order with team abbrev in message ✅
|
||||
- [ ] **Test 3:** Multiple items in single order ✅
|
||||
- [ ] **Test 4:** Team not found (manual review) ⚠️
|
||||
- [ ] **Test 5:** Unknown product code ⚠️
|
||||
- [ ] **Test 6:** Invalid verification token ❌
|
||||
- [ ] **Test 7:** Non-shop order type (donation) ⏭️
|
||||
- [ ] **Test 8:** Monthly subscription payment ✅
|
||||
|
||||
### Integration Testing
|
||||
|
||||
- [ ] Ko-fi webhook test button works
|
||||
- [ ] Discord notifications received in correct channel
|
||||
- [ ] Discord @here mentions work for errors
|
||||
- [ ] Paper Dynasty API grants packs correctly
|
||||
- [ ] API retry logic works (simulate timeout)
|
||||
- [ ] Multiple webhooks handled concurrently
|
||||
|
||||
### Security Testing
|
||||
|
||||
- [ ] Invalid tokens rejected
|
||||
- [ ] No sensitive data in Discord notifications
|
||||
- [ ] Webhook accessible only via HTTPS
|
||||
- [ ] n8n basic auth required for UI access
|
||||
- [ ] Credentials encrypted in n8n database
|
||||
|
||||
### Performance Testing
|
||||
|
||||
- [ ] Webhook responds within 5 seconds
|
||||
- [ ] Paper Dynasty API calls timeout correctly
|
||||
- [ ] Large orders (20+ items) process successfully
|
||||
- [ ] Concurrent webhooks don't cause issues
|
||||
|
||||
### Edge Cases
|
||||
|
||||
- [ ] Empty shop_items array
|
||||
- [ ] Null discord_userid and message fields
|
||||
- [ ] Special characters in team abbrev
|
||||
- [ ] Very large quantity (100+ packs)
|
||||
- [ ] Product with quantity: 0
|
||||
- [ ] Duplicate message_id (Ko-fi retry)
|
||||
|
||||
### Monitoring & Alerts
|
||||
|
||||
- [ ] Success notifications readable
|
||||
- [ ] Error notifications actionable
|
||||
- [ ] Manual review requests clear
|
||||
- [ ] n8n execution logs captured
|
||||
- [ ] Failed executions visible in n8n UI
|
||||
|
||||
---
|
||||
|
||||
## Validation Scripts
|
||||
|
||||
### Verify Ko-fi Webhook Format
|
||||
|
||||
**Script:** `validate-kofi-payload.js`
|
||||
|
||||
```javascript
|
||||
// Run in n8n Code node or standalone Node.js
|
||||
|
||||
function validateKofiPayload(payload) {
|
||||
const required = [
|
||||
'verification_token',
|
||||
'message_id',
|
||||
'timestamp',
|
||||
'type',
|
||||
'from_name',
|
||||
'amount',
|
||||
'email',
|
||||
'currency',
|
||||
'kofi_transaction_id'
|
||||
];
|
||||
|
||||
const missing = required.filter(field => !payload[field]);
|
||||
|
||||
if (missing.length > 0) {
|
||||
throw new Error(`Missing required fields: ${missing.join(', ')}`);
|
||||
}
|
||||
|
||||
if (payload.type === 'Shop Order' && !payload.shop_items) {
|
||||
throw new Error('Shop Order missing shop_items array');
|
||||
}
|
||||
|
||||
console.log('✅ Payload valid');
|
||||
return true;
|
||||
}
|
||||
|
||||
// Test
|
||||
const testPayload = JSON.parse($json.body.data);
|
||||
validateKofiPayload(testPayload);
|
||||
```
|
||||
|
||||
### Verify Paper Dynasty Pack Grant
|
||||
|
||||
**Script:** `verify-pack-grant.py`
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""Verify packs were granted in Paper Dynasty"""
|
||||
|
||||
from api_client import PaperDynastyAPI
|
||||
from datetime import datetime, timezone
|
||||
|
||||
api = PaperDynastyAPI(environment='prod', verbose=True)
|
||||
|
||||
# Get packs created in last hour
|
||||
recent_packs = api.list_packs(opened=False, new_to_old=True, limit=100)
|
||||
|
||||
# Filter by timestamp
|
||||
one_hour_ago = datetime.now(timezone.utc).timestamp() - 3600
|
||||
|
||||
for pack in recent_packs:
|
||||
pack_time = pack.get('created_at', 0) / 1000
|
||||
if pack_time > one_hour_ago:
|
||||
team = api.get_team(team_id=pack['team'])
|
||||
print(f"✅ Pack {pack['id']} granted to {team['abbrev']} at {datetime.fromtimestamp(pack_time)}")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Issues During Testing
|
||||
|
||||
### Issue: Webhook 404 Not Found
|
||||
|
||||
**Cause:** Workflow not active or wrong webhook path
|
||||
|
||||
**Fix:**
|
||||
```bash
|
||||
# Check workflow is active in n8n UI
|
||||
# Verify webhook path: n8n → Workflow → Webhook node → Path
|
||||
|
||||
# Test webhook accessibility
|
||||
curl -I https://n8n.manticorum.com/webhook-test/kofi-paperdy
|
||||
# Should return: 200 OK or 400 Bad Request (not 404)
|
||||
```
|
||||
|
||||
### Issue: Token Validation Failing
|
||||
|
||||
**Cause:** Token mismatch or credential not configured
|
||||
|
||||
**Fix:**
|
||||
1. Copy token exactly from Ko-fi dashboard
|
||||
2. Check n8n credential name matches IF node reference
|
||||
3. No extra spaces or quotes in token value
|
||||
4. Re-create credential if needed
|
||||
|
||||
### Issue: Team Not Found
|
||||
|
||||
**Cause:** Discord user ID or team abbrev doesn't exist in PD
|
||||
|
||||
**Fix:**
|
||||
```python
|
||||
# Check if discord_userid exists in PD
|
||||
api.get('teams', params={'gmid': '012345678901234567'})
|
||||
|
||||
# Check if team abbrev exists
|
||||
api.get('teams', params={'abbrev': 'SKB'})
|
||||
|
||||
# If not found, update user's PD profile or use manual review
|
||||
```
|
||||
|
||||
### Issue: Packs Not Granted
|
||||
|
||||
**Cause:** API request succeeded but packs not visible
|
||||
|
||||
**Fix:**
|
||||
1. Check API response body in n8n execution log
|
||||
2. Verify team_id is correct
|
||||
3. Check pack_type_id is valid
|
||||
4. Query PD database directly:
|
||||
```python
|
||||
api.list_packs(team_id=69, opened=False, limit=20)
|
||||
```
|
||||
|
||||
### Issue: Discord Notification Not Sent
|
||||
|
||||
**Cause:** Discord webhook URL invalid or permissions issue
|
||||
|
||||
**Fix:**
|
||||
1. Test Discord webhook directly:
|
||||
```bash
|
||||
curl -X POST 'DISCORD_WEBHOOK_URL' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"content": "Test notification"}'
|
||||
```
|
||||
2. Regenerate webhook in Discord if needed
|
||||
3. Update n8n credential with new URL
|
||||
|
||||
---
|
||||
|
||||
## Production Monitoring
|
||||
|
||||
### Daily Checks
|
||||
|
||||
- [ ] Review n8n execution logs (filter by workflow)
|
||||
- [ ] Check for failed executions
|
||||
- [ ] Review manual review Discord notifications
|
||||
- [ ] Verify all orders processed successfully
|
||||
|
||||
### Weekly Checks
|
||||
|
||||
- [ ] Review unknown product codes
|
||||
- [ ] Update product mapping for new Ko-fi products
|
||||
- [ ] Check Paper Dynasty pack distribution accuracy
|
||||
- [ ] Review API response times
|
||||
|
||||
### Monthly Checks
|
||||
|
||||
- [ ] Test disaster recovery procedure
|
||||
- [ ] Backup n8n database
|
||||
- [ ] Rotate Ko-fi verification token
|
||||
- [ ] Update documentation with lessons learned
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- **Main Workflow Documentation:** `kofi-paper-dynasty.md`
|
||||
- **Product Mapping Template:** `kofi-product-mapping-template.json`
|
||||
- **Ko-fi Webhook Docs:** https://help.ko-fi.com/hc/en-us/articles/360004162298
|
||||
- **Paper Dynasty API:** `/home/cal/.claude/skills/paper-dynasty/SKILL.md`
|
||||
|
||||
## Change Log
|
||||
|
||||
### 2025-11-13 - Initial Version
|
||||
- Created comprehensive testing guide
|
||||
- Defined 8 test scenarios with cURL commands
|
||||
- Added validation scripts and troubleshooting
|
||||
- Documented testing checklist and monitoring procedures
|
||||
438
productivity/troubleshooting.md
Normal file
438
productivity/troubleshooting.md
Normal file
@ -0,0 +1,438 @@
|
||||
# Productivity Tools Troubleshooting Guide
|
||||
|
||||
## Task Manager Issues
|
||||
|
||||
### Problem: Commands not found
|
||||
**Symptoms**: `task: command not found` or `task-dashboard: command not found`
|
||||
|
||||
**Root Cause**: PATH not configured or shell not reloaded
|
||||
|
||||
### Solution: PATH Configuration
|
||||
1. **Verify PATH addition** in `~/.bashrc`:
|
||||
```bash
|
||||
grep "task-manager" ~/.bashrc
|
||||
# Should show: export PATH="$HOME/.claude/tools/task-manager:$PATH"
|
||||
```
|
||||
|
||||
2. **Reload shell configuration**:
|
||||
```bash
|
||||
source ~/.bashrc
|
||||
# Or open new terminal window/tab
|
||||
```
|
||||
|
||||
3. **Test commands**:
|
||||
```bash
|
||||
which task
|
||||
which task-dashboard
|
||||
# Both should show: /home/cal/.claude/tools/task-manager/[command]
|
||||
```
|
||||
|
||||
### Temporary Workaround
|
||||
Use full paths until shell reloaded:
|
||||
```bash
|
||||
~/.claude/tools/task-manager/task
|
||||
~/.claude/tools/task-manager/task-dashboard
|
||||
```
|
||||
|
||||
## Dashboard Display Issues
|
||||
|
||||
### Problem: Dashboard not updating after CLI changes
|
||||
**Symptoms**: Run `task dump "something"` but dashboard doesn't show new task
|
||||
|
||||
**Root Cause**: File system delay or dashboard not running
|
||||
|
||||
### Solution: Verify Dashboard Operation
|
||||
1. **Check dashboard is running**:
|
||||
```bash
|
||||
ps aux | grep dashboard.py
|
||||
```
|
||||
|
||||
2. **Dashboard auto-refreshes every 1 second** - wait briefly
|
||||
|
||||
3. **If stuck, restart dashboard**:
|
||||
- Ctrl+C to stop
|
||||
- Restart: `task-dashboard`
|
||||
|
||||
4. **Verify data file exists**:
|
||||
```bash
|
||||
ls -la ~/.claude/tools/task-manager/tasks.json
|
||||
cat ~/.claude/tools/task-manager/tasks.json
|
||||
```
|
||||
|
||||
### Problem: Dashboard rendering issues (broken characters, layout problems)
|
||||
**Symptoms**: Terminal shows garbled text, boxes not aligned
|
||||
|
||||
**Cause**: Terminal too small or doesn't support Unicode
|
||||
|
||||
### Solution: Terminal Requirements
|
||||
1. **Minimum terminal size**: 80x24 characters
|
||||
2. **Resize terminal window** to be larger
|
||||
3. **Ensure Unicode support**:
|
||||
```bash
|
||||
echo $LANG
|
||||
# Should show UTF-8 (e.g., en_US.UTF-8)
|
||||
```
|
||||
|
||||
4. **Test rich library**:
|
||||
```bash
|
||||
python3 -c "from rich.console import Console; Console().print('[green]✓[/green] Unicode works')"
|
||||
```
|
||||
|
||||
## Task Management Issues
|
||||
|
||||
### Problem: High priority tasks not loading first
|
||||
**Symptoms**: Low/medium priority task becomes current before high priority task
|
||||
|
||||
**Cause**: High priority task already loaded earlier or wrong priority flag used
|
||||
|
||||
### Solution: Priority System Understanding
|
||||
**Priority Order** (automatic):
|
||||
1. High priority tasks (🔴) - `--high` flag
|
||||
2. Medium priority tasks (🟡) - `--medium` flag or default
|
||||
3. Low priority tasks (🟢) - `--low` flag
|
||||
|
||||
**Check task priorities**:
|
||||
```bash
|
||||
task
|
||||
# Shows all tasks with priority emojis
|
||||
```
|
||||
|
||||
**Fix task priority** (workaround):
|
||||
```bash
|
||||
# Currently can't change priority - delete and re-add:
|
||||
task next # Skip past wrong-priority task
|
||||
task dump "correct description" --high # Re-add with right priority
|
||||
```
|
||||
|
||||
### Problem: Task completed but still shows as current
|
||||
**Symptoms**: After `task done`, same task still in current focus
|
||||
|
||||
**Cause**: Command failed or data file locked
|
||||
|
||||
### Solution: Verify Completion
|
||||
1. **Check task status**:
|
||||
```bash
|
||||
task
|
||||
# Should show different current task or empty
|
||||
```
|
||||
|
||||
2. **Check data file**:
|
||||
```bash
|
||||
cat ~/.claude/tools/task-manager/tasks.json | python3 -m json.tool
|
||||
# Look for tasks with status: "completed"
|
||||
```
|
||||
|
||||
3. **Force move to next**:
|
||||
```bash
|
||||
task next # Skip to next task manually
|
||||
```
|
||||
|
||||
### Problem: Snoozed task not waking up
|
||||
**Symptoms**: Task snoozed hours ago but not appearing
|
||||
|
||||
**Cause**: Wake-up time not reached or system checks only on command execution
|
||||
|
||||
### Solution: Snooze Behavior
|
||||
**How snooze works**:
|
||||
- Task marked with future wake-up timestamp
|
||||
- Next task auto-loaded immediately
|
||||
- Snoozed task returns to queue when `snoozed_until` time passes
|
||||
- **Only checked when commands run** (not automatic background wake)
|
||||
|
||||
**Trigger wake-up check**:
|
||||
```bash
|
||||
task done # Complete current task
|
||||
# System checks snoozed tasks and loads if ready
|
||||
```
|
||||
|
||||
**Check snoozed tasks manually**:
|
||||
```bash
|
||||
cat ~/.claude/tools/task-manager/tasks.json | grep -A 5 '"status": "snoozed"'
|
||||
```
|
||||
|
||||
## Data and Persistence Issues
|
||||
|
||||
### Problem: Tasks disappearing or data loss
|
||||
**Symptoms**: Tasks added but gone after restart
|
||||
|
||||
**Cause**: File write permissions or corrupted JSON
|
||||
|
||||
### Solution: Data File Verification
|
||||
1. **Check file permissions**:
|
||||
```bash
|
||||
ls -la ~/.claude/tools/task-manager/tasks.json
|
||||
# Should be writable by user
|
||||
```
|
||||
|
||||
2. **Fix permissions**:
|
||||
```bash
|
||||
chmod 644 ~/.claude/tools/task-manager/tasks.json
|
||||
```
|
||||
|
||||
3. **Validate JSON format**:
|
||||
```bash
|
||||
python3 -m json.tool ~/.claude/tools/task-manager/tasks.json
|
||||
# Should show formatted JSON without errors
|
||||
```
|
||||
|
||||
4. **Check for corruption**:
|
||||
```bash
|
||||
# Backup current file
|
||||
cp ~/.claude/tools/task-manager/tasks.json ~/.claude/tools/task-manager/tasks.json.backup
|
||||
|
||||
# Try to load with Python
|
||||
python3 -c "import json; json.load(open('$HOME/.claude/tools/task-manager/tasks.json'))"
|
||||
```
|
||||
|
||||
### Problem: Data file doesn't exist
|
||||
**Symptoms**: `task` command shows errors about missing file
|
||||
|
||||
**Cause**: First run or file accidentally deleted
|
||||
|
||||
### Solution: Initialize Data File
|
||||
**Data file auto-creates on first use**:
|
||||
```bash
|
||||
task dump "First task"
|
||||
# Creates tasks.json automatically
|
||||
```
|
||||
|
||||
**Manual initialization** (if needed):
|
||||
```bash
|
||||
cat > ~/.claude/tools/task-manager/tasks.json << 'EOF'
|
||||
{
|
||||
"tasks": [],
|
||||
"last_updated": "2025-01-01T00:00:00"
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
## Python Dependency Issues
|
||||
|
||||
### Problem: rich library not found
|
||||
**Symptoms**: `ModuleNotFoundError: No module named 'rich'`
|
||||
|
||||
**Cause**: rich library not installed
|
||||
|
||||
### Solution: Install Dependencies
|
||||
```bash
|
||||
pip3 install --user rich
|
||||
```
|
||||
|
||||
### Problem: Python version incompatibility
|
||||
**Symptoms**: Syntax errors or import failures
|
||||
|
||||
**Cause**: Python version too old (requires 3.7+)
|
||||
|
||||
### Solution: Check Python Version
|
||||
```bash
|
||||
python3 --version
|
||||
# Should be 3.7 or higher
|
||||
```
|
||||
|
||||
If too old, use system Python 3 or install newer version
|
||||
|
||||
## Workflow and Usage Issues
|
||||
|
||||
### Problem: Forgetting to check dashboard
|
||||
**Symptoms**: Dashboard running but never looking at it
|
||||
|
||||
**Cause**: Not visible enough, muscle memory not formed
|
||||
|
||||
### Solution: Visibility Improvements
|
||||
1. **Use tmux/screen persistent split**:
|
||||
```bash
|
||||
tmux split-window -h task-dashboard
|
||||
# Always visible in split pane
|
||||
```
|
||||
|
||||
2. **Dedicated terminal on second monitor** - always visible
|
||||
|
||||
3. **Position terminal window** to always be on top/visible
|
||||
|
||||
4. **Build muscle memory**:
|
||||
- Use `task` command frequently to check status
|
||||
- Set reminders to check dashboard first 2 weeks
|
||||
- Terminal placement habit formation
|
||||
|
||||
### Problem: Still context switching instead of brain dumping
|
||||
**Symptoms**: Remembering something and immediately switching tasks
|
||||
|
||||
**Cause**: Old habit pattern, not yet muscle memory
|
||||
|
||||
### Solution: Habit Building
|
||||
**Interrupt pattern**:
|
||||
1. Notice you're about to switch → STOP
|
||||
2. Type: `task dump "[the thing]"`
|
||||
3. See it appear in brain dump
|
||||
4. Continue current work
|
||||
|
||||
**Muscle memory building**:
|
||||
- Put note on monitor: "DUMP DON'T SWITCH"
|
||||
- First week: Expect to fail, keep practicing
|
||||
- Second week: Start catching yourself
|
||||
- Third week: Becomes automatic
|
||||
|
||||
**Track progress**:
|
||||
```bash
|
||||
task
|
||||
# Check completed today - celebrate wins!
|
||||
```
|
||||
|
||||
## Performance Issues
|
||||
|
||||
### Problem: Dashboard slow or laggy
|
||||
**Symptoms**: Dashboard updates slowly or terminal sluggish
|
||||
|
||||
**Cause**: Too many tasks in JSON file or terminal performance
|
||||
|
||||
### Solution: Data Cleanup
|
||||
1. **Archive completed tasks**:
|
||||
```bash
|
||||
# Manual cleanup (create archive script if needed)
|
||||
# Edit tasks.json to remove old completed tasks
|
||||
python3 -c "
|
||||
import json
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
with open('$HOME/.claude/tools/task-manager/tasks.json', 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
# Keep only last 7 days of completed tasks
|
||||
week_ago = (datetime.now() - timedelta(days=7)).isoformat()
|
||||
data['tasks'] = [
|
||||
t for t in data['tasks']
|
||||
if t['status'] != 'completed' or t.get('completed_at', '') > week_ago
|
||||
]
|
||||
|
||||
with open('$HOME/.claude/tools/task-manager/tasks.json', 'w') as f:
|
||||
json.dump(data, f, indent=2)
|
||||
"
|
||||
```
|
||||
|
||||
2. **Check terminal performance**:
|
||||
- Try different terminal emulator
|
||||
- Reduce dashboard refresh rate (edit dashboard.py)
|
||||
|
||||
### Problem: Commands slow to execute
|
||||
**Symptoms**: `task dump` takes several seconds
|
||||
|
||||
**Cause**: Large JSON file or slow file I/O
|
||||
|
||||
### Solution: Optimize Data Storage
|
||||
- Keep tasks.json under 1000 tasks
|
||||
- Archive old completed tasks monthly
|
||||
- Consider SSD storage if on HDD
|
||||
|
||||
## Integration Issues
|
||||
|
||||
### Problem: Dashboard not visible in tmux/screen
|
||||
**Symptoms**: Dashboard starts but can't see in multiplexer
|
||||
|
||||
**Cause**: Rich library rendering issues in some multiplexers
|
||||
|
||||
### Solution: Terminal Compatibility
|
||||
1. **Set TERM variable**:
|
||||
```bash
|
||||
export TERM=xterm-256color
|
||||
task-dashboard
|
||||
```
|
||||
|
||||
2. **Try alternate terminal**:
|
||||
```bash
|
||||
# Instead of tmux, try dedicated terminal window
|
||||
```
|
||||
|
||||
3. **Check tmux/screen version** - update if old
|
||||
|
||||
## Emergency Recovery
|
||||
|
||||
### Complete Reset
|
||||
```bash
|
||||
# Stop dashboard
|
||||
pkill -f dashboard.py
|
||||
|
||||
# Backup current data
|
||||
mv ~/.claude/tools/task-manager/tasks.json \
|
||||
~/.claude/tools/task-manager/tasks.json.backup
|
||||
|
||||
# Reinitialize
|
||||
task dump "Test task"
|
||||
|
||||
# Verify working
|
||||
task
|
||||
|
||||
# Restart dashboard
|
||||
task-dashboard
|
||||
```
|
||||
|
||||
### Restore from Backup
|
||||
```bash
|
||||
# If you have backup file
|
||||
cp ~/.claude/tools/task-manager/tasks.json.backup \
|
||||
~/.claude/tools/task-manager/tasks.json
|
||||
|
||||
# Verify restore
|
||||
task
|
||||
```
|
||||
|
||||
### Reinstall System
|
||||
```bash
|
||||
# Scripts already installed at:
|
||||
# ~/.claude/tools/task-manager/
|
||||
|
||||
# Just reload PATH
|
||||
source ~/.bashrc
|
||||
|
||||
# Initialize fresh data
|
||||
task dump "Fresh start"
|
||||
```
|
||||
|
||||
## Common Error Patterns
|
||||
|
||||
### "No module named 'task_manager'"
|
||||
**Cause**: Running Python scripts directly from wrong directory
|
||||
**Solution**: Use `task` and `task-dashboard` commands, not direct Python execution
|
||||
|
||||
### "Permission denied"
|
||||
**Cause**: Scripts not executable
|
||||
**Solution**:
|
||||
```bash
|
||||
chmod +x ~/.claude/tools/task-manager/task*
|
||||
```
|
||||
|
||||
### Dashboard exits immediately
|
||||
**Cause**: Python exception or Ctrl+C pressed
|
||||
**Solution**: Run with error output:
|
||||
```bash
|
||||
python3 ~/.claude/tools/task-manager/dashboard.py
|
||||
# See any error messages
|
||||
```
|
||||
|
||||
## Prevention Best Practices
|
||||
|
||||
1. **Keep dashboard running** in persistent pane
|
||||
2. **Use `task dump` immediately** when thoughts appear
|
||||
3. **Trust the system** - don't manually organize JSON file
|
||||
4. **Regular status checks** - `task` command throughout day
|
||||
5. **Weekly review** - clean up stale tasks
|
||||
6. **Back up data** - tasks.json to cloud storage
|
||||
7. **Build muscle memory** - takes 2-3 weeks of consistent use
|
||||
|
||||
## Debug Mode
|
||||
|
||||
### Enable Detailed Logging
|
||||
Add to Python scripts if troubleshooting needed:
|
||||
|
||||
```python
|
||||
# In task_manager.py or cli.py
|
||||
import logging
|
||||
logging.basicConfig(level=logging.DEBUG,
|
||||
filename='/tmp/task-manager-debug.log')
|
||||
```
|
||||
|
||||
### Check Logs
|
||||
```bash
|
||||
tail -f /tmp/task-manager-debug.log
|
||||
```
|
||||
|
||||
This troubleshooting guide addresses common issues with the ADHD task management system and provides recovery procedures for all known failure modes.
|
||||
Loading…
Reference in New Issue
Block a user