feat(affiliate): migration 0026 — affiliate_products + affiliate_clicks tables
Adds affiliate product catalog and click tracking tables. UNIQUE(slug, language) mirrors articles schema for multi-language support. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,65 @@
|
|||||||
|
"""Migration 0026: Affiliate product catalog + click tracking tables.
|
||||||
|
|
||||||
|
affiliate_products: admin-managed product catalog for editorial affiliate cards.
|
||||||
|
- slug+language uniqueness mirrors articles (same slug can exist in DE + EN
|
||||||
|
with different affiliate URLs, copy, and pros/cons).
|
||||||
|
- retailer: display name (Amazon, Padel Nuestro, etc.) — stored in full URL
|
||||||
|
with tracking params already baked into affiliate_url.
|
||||||
|
- cta_label: per-product override; empty → use i18n default "Zum Angebot".
|
||||||
|
- status: draft/active/archived — only active products are baked into articles.
|
||||||
|
|
||||||
|
affiliate_clicks: one row per /go/<slug> redirect hit.
|
||||||
|
- ip_hash: SHA256(ip + YYYY-MM-DD + SECRET_KEY[:16]), daily rotation for GDPR.
|
||||||
|
- article_slug: best-effort extraction from Referer header.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def up(conn) -> None:
|
||||||
|
conn.execute("""
|
||||||
|
CREATE TABLE affiliate_products (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
slug TEXT NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
brand TEXT NOT NULL DEFAULT '',
|
||||||
|
category TEXT NOT NULL DEFAULT 'accessory',
|
||||||
|
retailer TEXT NOT NULL DEFAULT '',
|
||||||
|
affiliate_url TEXT NOT NULL,
|
||||||
|
image_url TEXT NOT NULL DEFAULT '',
|
||||||
|
price_cents INTEGER,
|
||||||
|
currency TEXT NOT NULL DEFAULT 'EUR',
|
||||||
|
rating REAL,
|
||||||
|
pros TEXT NOT NULL DEFAULT '[]',
|
||||||
|
cons TEXT NOT NULL DEFAULT '[]',
|
||||||
|
description TEXT NOT NULL DEFAULT '',
|
||||||
|
cta_label TEXT NOT NULL DEFAULT '',
|
||||||
|
status TEXT NOT NULL DEFAULT 'draft',
|
||||||
|
language TEXT NOT NULL DEFAULT 'de',
|
||||||
|
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||||
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||||
|
updated_at TEXT,
|
||||||
|
UNIQUE(slug, language)
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
conn.execute("""
|
||||||
|
CREATE TABLE affiliate_clicks (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
product_id INTEGER NOT NULL REFERENCES affiliate_products(id),
|
||||||
|
article_slug TEXT,
|
||||||
|
referrer TEXT,
|
||||||
|
ip_hash TEXT NOT NULL,
|
||||||
|
clicked_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
# Queries: products by category+status, clicks by product and time
|
||||||
|
conn.execute(
|
||||||
|
"CREATE INDEX idx_affiliate_products_category_status"
|
||||||
|
" ON affiliate_products(category, status)"
|
||||||
|
)
|
||||||
|
conn.execute(
|
||||||
|
"CREATE INDEX idx_affiliate_clicks_product_id"
|
||||||
|
" ON affiliate_clicks(product_id)"
|
||||||
|
)
|
||||||
|
conn.execute(
|
||||||
|
"CREATE INDEX idx_affiliate_clicks_clicked_at"
|
||||||
|
" ON affiliate_clicks(clicked_at)"
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user