Two-layer structure: Sources (raw notes) + Wiki (compile output) Four domains: Dev (40), Venture (3), Homelab (23), Reference (0) Includes CLAUDE.md spec, index pages at all levels, compile log Co-Authored-By: Lovebug <lovebug@herbylab.dev>
5.7 KiB
| created | path | project | status | tags | type | updated | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2026-03-16 | Sources/Dev | instagram-automation-session-notes | active |
|
session-notes | 2026-03-16 |
Session Notes — MySQL Schema, Error Checking & The Great Lockout of 2026
✅ Accomplished This Session
Error Checking — Instagram Reply Workflow
All four error checks are now wired to Notify Travis → Google Chat:
- ✅ Subscribe check notification
- ✅ Hash verification failure notification (with ⛔ alert level)
- ✅ Empty table lookup notification (includes Reel ID, commenter, comment text)
- ✅ Keyword not matched notification
Key learnings:
- n8n "Always Output Data" needed on MySQL node so IF node can evaluate empty results
- When "Always Output Data" is on, check
$('node').first().json.idexists instead oflength > 0(length returns 1 for empty item) - Google Chat HTTP Request node must use "Using Fields Below" mode, not raw JSON — literal newlines from expressions break JSON
- Build message strings as single
={{ }}expression using+concatenation to keep\nas escaped characters $now.toFormat('MMM dd, HH:mm:ss')works in both test and live contexts (unlike$execution.startedAt)- Timestamp and title passed from calling workflow, Notify Travis just passes them through
MySQL Schema — New Tables Created
pbs_recipes:
CREATE TABLE IF NOT EXISTS pbs_recipes (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
post_id BIGINT UNSIGNED NOT NULL,
title VARCHAR(500) NOT NULL,
url VARCHAR(500) NOT NULL,
keyword VARCHAR(100) NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY (post_id)
);
instagram_posts:
CREATE TABLE IF NOT EXISTS instagram_posts (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
reel_id VARCHAR(50) NOT NULL,
post_id BIGINT UNSIGNED NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY (reel_id),
FOREIGN KEY (post_id) REFERENCES pbs_recipes(post_id)
);
Key decisions:
post_idis nullable ininstagram_posts— NULL means unmatched reel, not an errorreel_idstored as VARCHAR not BIGINT (Instagram ID integer overflow issue)- Normalized schema — keyword and URL live in
pbs_recipesonly, JOIN at query time - Staying on shared MySQL instance — at current scale (2k/month, 10-20 replies) separate instance is overkill
- phpMyAdmin added back to Docker Compose for direct MySQL access
WordPress Publish Webhook — Proof of Concept Complete
- WPCode Lite snippet fires on post publish (admin only)
- Payload: post_id, title, url, tags, categories
- HMAC signature verification via X-PBS-Signature header
- Raw body passed via X-PBS-Body header (base64 encoded) for exact signature verification
- n8n Code node verifies signature before processing
- Ready to wire into
pbs_recipestable insert workflow
Architecture Decision — Reply Workflow Refactor
Current two-table lookup + merge is being replaced with:
- Single
instagram_postslookup only - URL stored directly in
instagram_posts(populated at reel insert time frompbs_recipes) - Simpler, faster, fewer failure points
🔧 The Great WordPress Lockout of 2026
What happened: Wordfence locked out staging WP admin. Unlock email never arrived.
What we tried (none of it worked):
- WP-CLI wf:unblock-ip command (not a registered command)
- Clearing wp_options Wordfence entries in MySQL
- Disabling Wordfence plugin
- Restarting WordPress container
- Deleting wflogs directory
Root cause discovered: Wordfence WAF runs as a PHP auto_prepend_file — completely independent of the plugin being active. Found in .htaccess and wordfence-waf.php in WordPress root.
Actual fix: Wrong email address on the admin account. Changed it, logged right in. 🤦
Real lesson learned: Read the error message carefully before spending an hour troubleshooting! 😄
Lockout duration if we hadn't figured it out: 1 MONTH
📋 Runbook Addition — WordPress Lockout Recovery
# Get into WordPress container
docker exec -it wordpress bash
cd /var/www/html
# List all users
wp --allow-root user list
# Reset password by username
wp --allow-root user update admin --user_pass=newpassword
# Reset password by email
wp --allow-root user update user@email.com --user_pass=newpassword
# If WAF is blocking login page independently of plugin:
# 1. Remove Wordfence WAF block from .htaccess
# 2. Rename wordfence-waf.php to wordfence-waf.php.disabled
# 3. docker restart wordpress
🔜 Next Steps
- Wire WordPress publish webhook → n8n workflow → insert into
pbs_recipes - Build one-time migration workflow — n8n datatables → MySQL
- Finish reply workflow refactor — remove Merge node, single
instagram_postslookup - Fix production WordPress admin email (same issue exists there!)
- Deploy all changes to production once staging is verified
📝 Notes
- All development on staging (staging.plantbasedsoutherner.com)
- n8n datatables to be retired once MySQL migration complete
- Foreign key constraint requires
pbs_recipesrecord to exist beforeinstagram_postsinsert - Use
WHERE post_id IS NULLquery to find unmatched reels for Content Hub dashboard
Last Updated: March 16, 2026 Maintained by: Travis Project: Plant Based Southerner — Instagram Automation