70 lines
2.3 KiB
Python
70 lines
2.3 KiB
Python
"""Ads blueprint: click tracking redirect, sponsor directory.
|
|
Ad creative upload lives in the admin blueprint (Phase 6).
|
|
"""
|
|
from flask import (Blueprint, redirect, abort, render_template,
|
|
request, current_app)
|
|
from flask_login import current_user
|
|
from flask_babel import get_locale
|
|
|
|
from app.extensions import db
|
|
from app.models.ads import Ad
|
|
from app.services.ads import (get_ad, record_impression, record_click,
|
|
active_sponsors)
|
|
|
|
ads_bp = Blueprint("ads", __name__)
|
|
|
|
|
|
# ── Click tracking redirect ────────────────────────────────────────────────
|
|
|
|
@ads_bp.route("/ads/<int:ad_id>/click")
|
|
def click(ad_id):
|
|
ad = db.session.get(Ad, ad_id)
|
|
if ad is None or not ad.is_running:
|
|
abort(404)
|
|
try:
|
|
record_click(ad_id)
|
|
except Exception as e:
|
|
current_app.logger.error("Click record error: %s", e)
|
|
return redirect(ad.target_url)
|
|
|
|
|
|
# ── Sponsor directory ──────────────────────────────────────────────────────
|
|
|
|
@ads_bp.route("/sponsors")
|
|
def sponsor_directory():
|
|
sponsors = active_sponsors(tier="directory")
|
|
return render_template("sponsors/directory.html", sponsors=sponsors)
|
|
|
|
|
|
# ── Context processor: inject ads into every template ─────────────────────
|
|
|
|
def inject_ads():
|
|
"""Called by the app factory context processor.
|
|
Returns ad slots for use in templates.
|
|
Ads suppressed for subscribers (ad_free plan limit).
|
|
"""
|
|
show_ads = True
|
|
if current_user.is_authenticated:
|
|
plan = current_user.tier
|
|
if plan and plan.limit("ad_free"):
|
|
show_ads = False
|
|
|
|
if not show_ads:
|
|
return {"ads": {}, "show_ads": False}
|
|
|
|
lang = str(get_locale()) if get_locale() else None
|
|
state = request.args.get("state") or None
|
|
|
|
ads = {}
|
|
for slot in Ad.SLOTS:
|
|
ad = get_ad(slot, lang=lang, state=state)
|
|
if ad:
|
|
ads[slot] = ad
|
|
# record impression (fire-and-forget; errors suppressed)
|
|
try:
|
|
record_impression(ad.id)
|
|
except Exception:
|
|
pass
|
|
|
|
return {"ads": ads, "show_ads": True}
|