Create pbs-membership-loe1-recipe-saving.md via n8n
This commit is contained in:
parent
f8127eea9a
commit
912eaff153
287
PBS/Tech/Projects/pbs-membership-loe1-recipe-saving.md
Normal file
287
PBS/Tech/Projects/pbs-membership-loe1-recipe-saving.md
Normal file
@ -0,0 +1,287 @@
|
||||
---
|
||||
project: pbs-membership-loe1-recipe-saving
|
||||
type: project-plan
|
||||
status: active
|
||||
tags:
|
||||
- pbs
|
||||
- flask
|
||||
- docker
|
||||
- mysql
|
||||
- membership
|
||||
- automation
|
||||
created: 2026-04-06
|
||||
updated: 2026-04-06
|
||||
path: PBS/Tech/Projects/
|
||||
---
|
||||
|
||||
|
||||
# PBS Membership Platform — LOE 1: Recipe Saving
|
||||
|
||||
## Vision
|
||||
|
||||
The PBS Membership Platform is a long-term initiative to transform
|
||||
plantbasedsoutherner.com from a content site into a full lifestyle
|
||||
platform. The end-state vision includes personalized recipe
|
||||
recommendations, a whole food diversity tracker, meal planning, a companion
|
||||
app, and a custom LLM trained on plant-based cooking knowledge.
|
||||
|
||||
Recipe Saving is LOE 1 — the foundational project that establishes the
|
||||
membership infrastructure all other LOEs build on.
|
||||
|
||||
---
|
||||
|
||||
## LOE 1 Phases Overview
|
||||
|
||||
- **Phase 0** — Ultimate Member setup (registration, login, user roles,
|
||||
basic profile page)
|
||||
- **Phase 1** — Backend foundation (auth middleware, database, API
|
||||
endpoints) ← THIS DOC
|
||||
- **Phase 2** — Save button on WordPress recipe pages (JavaScript widget)
|
||||
- **Phase 3** — Saved recipes page in pbs-hub (frontend UI)
|
||||
- **Phase 4** — Collections (named groups, organize saved recipes)
|
||||
- **Phase 5** — Polish (mobile optimization, loading states, error handling)
|
||||
|
||||
---
|
||||
|
||||
## All 4 Lines of Effort
|
||||
|
||||
| LOE | Project | Status |
|
||||
|-----|---------|--------|
|
||||
| 1 | Recipe Saving | Active — Phase 1 |
|
||||
| 2 | Weekly Meal Plan Viewer | Queued |
|
||||
| 3 | Whole Food Diversity Tracker | Queued |
|
||||
| 4 | User Profiles & Preferences | Queued |
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Backend Foundation
|
||||
|
||||
### Goal
|
||||
|
||||
Build the API layer and database schema that powers recipe saving. No
|
||||
frontend — just a working API testable via curl and verifiable in
|
||||
phpMyAdmin.
|
||||
|
||||
### Estimated Time: 4-6 hours
|
||||
|
||||
---
|
||||
|
||||
### Architecture
|
||||
|
||||
```
|
||||
Sunnie browser
|
||||
→ WordPress cookie (set by Ultimate Member login)
|
||||
→ Request to pbs-hub API
|
||||
→ Flask auth middleware validates cookie against wp_users (read-only)
|
||||
→ API endpoint processes request
|
||||
→ Writes to pbs_hub database
|
||||
→ Returns JSON response
|
||||
```
|
||||
|
||||
**Key principle:** WordPress owns identity. pbs-hub owns membership
|
||||
features. The only bridge is the session cookie and read-only MySQL access.
|
||||
|
||||
---
|
||||
|
||||
### Database Design
|
||||
|
||||
#### New database: pbs_hub
|
||||
|
||||
**user_interactions** — generic table for all post-related user actions
|
||||
(save, like, cooked, planned, rated). Serves LOE 1 now and LOEs 2-4 later.
|
||||
|
||||
```sql
|
||||
CREATE DATABASE pbs_hub;
|
||||
|
||||
CREATE TABLE pbs_hub.user_interactions (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
wp_user_id INT UNSIGNED NOT NULL,
|
||||
wp_post_id BIGINT UNSIGNED NOT NULL,
|
||||
action VARCHAR(50) NOT NULL,
|
||||
metadata JSON NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY unique_action (wp_user_id, wp_post_id, action)
|
||||
);
|
||||
```
|
||||
|
||||
**user_preferences** — key-value store for platform-specific user settings
|
||||
(LOE 4, but created now for completeness).
|
||||
|
||||
```sql
|
||||
CREATE TABLE pbs_hub.user_preferences (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
wp_user_id INT UNSIGNED NOT NULL,
|
||||
pref_key VARCHAR(100) NOT NULL,
|
||||
pref_value JSON NOT NULL,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE
|
||||
CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY unique_pref (wp_user_id, pref_key)
|
||||
);
|
||||
```
|
||||
|
||||
#### MySQL user (scoped permissions)
|
||||
|
||||
```sql
|
||||
CREATE USER 'pbshub_app'@'%' IDENTIFIED BY 'strong_password_here';
|
||||
|
||||
-- Read-only on WordPress (auth + recipe display)
|
||||
GRANT SELECT ON wordpress.wp_users TO 'pbshub_app'@'%';
|
||||
GRANT SELECT ON wordpress.wp_usermeta TO 'pbshub_app'@'%';
|
||||
GRANT SELECT ON wordpress.wp_posts TO 'pbshub_app'@'%';
|
||||
|
||||
-- Full control on pbs_hub
|
||||
GRANT ALL ON pbs_hub.* TO 'pbshub_app'@'%';
|
||||
|
||||
FLUSH PRIVILEGES;
|
||||
```
|
||||
|
||||
**Note:** Also audit existing MySQL users (pbs-api, n8n) and scope their
|
||||
permissions. Stop using root for application access.
|
||||
|
||||
---
|
||||
|
||||
### API Endpoints
|
||||
|
||||
| Method | Path | Description | Auth | Tier |
|
||||
|--------|------|-------------|------|------|
|
||||
| POST | /api/interactions | Create interaction (save, like, etc.) |
|
||||
Required | Free |
|
||||
| DELETE | /api/interactions | Remove interaction | Required | Free |
|
||||
| GET | /api/interactions?action=save | List user's interactions by action
|
||||
type | Required | Free |
|
||||
| GET | /api/interactions/check?post_id=123&action=save | Check if specific
|
||||
interaction exists | Required | Free |
|
||||
|
||||
**Request body (POST):**
|
||||
```json
|
||||
{
|
||||
"post_id": 123,
|
||||
"action": "save",
|
||||
"metadata": null
|
||||
}
|
||||
```
|
||||
|
||||
**Response (GET list):**
|
||||
```json
|
||||
{
|
||||
"interactions": [
|
||||
{
|
||||
"post_id": 123,
|
||||
"action": "save",
|
||||
"post_title": "Southern Black-Eyed Pea Stew",
|
||||
"post_slug": "southern-black-eyed-pea-stew",
|
||||
"saved_at": "2026-04-06T12:00:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** The GET list endpoint joins pbs_hub.user_interactions with
|
||||
wordpress.wp_posts to return recipe title and slug. Query filters on
|
||||
`post_status = 'publish'` and `post_type IN ('post', 'wprm_recipe')` to
|
||||
exclude revisions, drafts, and other post types.
|
||||
|
||||
---
|
||||
|
||||
### Auth Middleware
|
||||
|
||||
Flask `before_request` middleware that:
|
||||
1. Reads `wordpress_logged_in_{hash}` cookie from request
|
||||
2. Extracts user ID and token from cookie value
|
||||
3. Validates HMAC against wp_users password hash + WordPress secret keys
|
||||
4. Sets `g.current_user` (user ID) and `g.user_tier` (role) for the request
|
||||
5. Returns 401 if missing or invalid
|
||||
|
||||
**WordPress secret keys** needed from wp-config.php:
|
||||
- LOGGED_IN_KEY
|
||||
- LOGGED_IN_SALT
|
||||
|
||||
**Security considerations:**
|
||||
- HTTPS enforced everywhere (Traefik + Cloudflare)
|
||||
- Read-only MySQL user for WordPress tables
|
||||
- CSRF protection via Flask-WTF (for future form submissions)
|
||||
- Cookie is httponly and secure (WordPress default)
|
||||
|
||||
---
|
||||
|
||||
### Tier Authorization (Future-Proofed)
|
||||
|
||||
Not needed for Phase 1 (everything is free), but the pattern is established:
|
||||
|
||||
```python
|
||||
TIER_PERMISSIONS = {
|
||||
"free": ["save", "like"],
|
||||
"basic": ["save", "like", "planned", "cooked"],
|
||||
"premium": ["save", "like", "planned", "cooked", "collections",
|
||||
"ai_suggest"],
|
||||
}
|
||||
```
|
||||
|
||||
Adding paid tiers later is a code change, not a schema change.
|
||||
|
||||
---
|
||||
|
||||
### Phase 1 Checklist
|
||||
|
||||
- [ ] Create pbs_hub database on staging MySQL
|
||||
- [ ] Create user_interactions table
|
||||
- [ ] Create user_preferences table
|
||||
- [ ] Create pbshub_app MySQL user with scoped permissions
|
||||
- [ ] Audit and scope existing MySQL users (root cleanup)
|
||||
- [ ] Extract WordPress secret keys for cookie validation
|
||||
- [ ] Build Flask auth middleware (cookie → user ID)
|
||||
- [ ] Build POST /api/interactions endpoint
|
||||
- [ ] Build DELETE /api/interactions endpoint
|
||||
- [ ] Build GET /api/interactions endpoint (with wp_posts join)
|
||||
- [ ] Build GET /api/interactions/check endpoint
|
||||
- [ ] Test all endpoints via curl
|
||||
- [ ] Verify data in phpMyAdmin
|
||||
- [ ] Error handling and logging
|
||||
|
||||
---
|
||||
|
||||
### Key Decisions Made
|
||||
|
||||
| Decision | Choice | Rationale |
|
||||
|----------|--------|-----------|
|
||||
| Backend language | Python / Flask | Travis's strongest language. Python
|
||||
handles PBS scale easily. Revisit Go/Rust only if a specific bottleneck
|
||||
appears. |
|
||||
| Auth approach | WordPress cookie validation | Ultimate Member handles
|
||||
registration/login (Phase 0). pbs-hub validates the cookie for API access.
|
||||
Plugin is treated as disposable scaffolding. |
|
||||
| Database | MySQL (pbs_hub database) | Same server, cross-database
|
||||
read-only joins to WordPress. No data duplication. |
|
||||
| Interaction table | Generic user_interactions | Single table serves
|
||||
saves, likes, cooked, planned, rated across all LOEs. Action type is a
|
||||
column, not a separate table. |
|
||||
| Recipe data source | WordPress wp_posts (read-only join) | Title and slug
|
||||
come from wp_posts directly, filtered by post_status and post_type to
|
||||
exclude revisions. No dependency on pbs_recipes (Instagram automation stays
|
||||
independent). |
|
||||
| Paid tier authorization | Code-level, not schema-level | TIER_PERMISSIONS
|
||||
dictionary gates actions in the API layer. Database doesn't know tiers
|
||||
exist. |
|
||||
| MySQL user | Scoped pbshub_app | Read-only on WordPress tables, full
|
||||
access on pbs_hub. No more root for applications. |
|
||||
|
||||
---
|
||||
|
||||
### Open Questions
|
||||
|
||||
- **WordPress cookie hash suffix:** Need to check wp-config.php for the
|
||||
exact cookie name (wordpress_logged_in_{hash} where hash is derived from
|
||||
site URL)
|
||||
- **WPRM post type:** Confirm whether recipes are stored as 'wprm_recipe'
|
||||
post type or as regular 'post' with recipe metadata
|
||||
- **pbs-hub deployment:** Does this extend the existing pbs-hub container
|
||||
or is Phase 1 a standalone Flask app for testing?
|
||||
|
||||
---
|
||||
|
||||
*Next Step: Create pbs_hub database and tables on staging, then build the
|
||||
auth middleware*
|
||||
|
||||
|
||||
--
|
||||
...sent from Jenny & Travis
|
||||
Loading…
Reference in New Issue
Block a user