get_gotify_extras() previously only ever surfaced the *resolved* click_url,
never the action_code that produced it -- so the frontend had no way to
know which action_code a notification carried, and got nothing at all for
a code that hasn't been configured in NotificationLink yet. Now
action_code rides along under client::notification.action_code whenever
it's set, independent of whether it resolved to a click_url. Verified live
against a running Gotify container, both resolved and unresolved.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Found by an end-to-end run against a live Gotify container:
- action_code / NotificationLink.action_code were SlugField, which rejects
dots. Every documented action_code (billboard.approved, escrow.timeout,
...) is dotted, so any real request using the catalog got a 400 before
this fix -- only click_url ever worked. Switched both to CharField with
a validator that keeps slug's charset but allows ".".
- send_push_notification set push_message.state instead of .stats after a
successful send, so PushMessage.stats stayed stuck at INIT forever
regardless of delivery outcome. Fixed the attribute name and scoped the
save with update_fields.
Verified live: POST with a dotted action_code now resolves through
NotificationLink and lands in Gotify with the correct click.url, and the
PushMessage row flips to DONE.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds `seed_notification_links`, which upserts one NotificationLink row per
action_code documented in docs/frontend-notification-click-actions.md (12
billboard + 15 escrow codes), each active with a sample url_template
(https://app.gooyal.ir/ads/{object_id} / .../escrow/{object_id}). Lets
producer notifications resolve a click_url end to end today; the real
routes still need confirming with whoever owns the frontend, and can be
edited any time via the Django admin. Run with --force to overwrite rows
that already exist; existing rows are otherwise left untouched so manual
admin edits aren't clobbered by a rerun.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
`code` was ambiguous next to Python's own use of "code"; `action_code` names
its actual role (a click-action lookup key). click_object_id_value pairs it
consistently with action_code. Renamed on both PushMessage and
NotificationLink via a data-preserving RenameField migration, threaded
through the serializer, admin, bulk-push path, and docs.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Reflects the switch from hard-coded FRONTEND_BASE_URL/utils.deep_links to
admin-managed NotificationLink rows: implementation.md documents the
resolution logic and the new migration, frontend-notification-click-actions.md
replaces the "confirm your URL scheme" ask with the actual code catalog
(now that the scheme itself is an admin panel concern, not something the
frontend team needs to weigh in on for us to ship), and
notification-click-actions.md gets a pointer at the top so it reads as the
historical first pass it now is, not the current mechanism.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The click_url a producer sends had to be built in that producer's own
Python code (utils/deep_links.py in advertising), so changing the URL
scheme meant a code deploy. Adds NotificationLink: an admin-editable
(code, url_template, is_active) row per notification type, with
url_template supporting a {object_id} placeholder.
PushMessage gains `code` and `click_object_id` — a producer can send either
a raw click_url (unchanged, still wins if both are given) or a code + the
referenced object's id, and resolve_click_url() looks up the template at
send time. An unset or not-yet-configured/inactive code resolves to no
click action rather than an error, consistent with "not every notification
is clickable." Wired through both the single-push API (serializer) and the
bulk Excel path (two new optional columns).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Three docs for three audiences: service-overview.md for anyone integrating
with or operating the service, implementation.md for engineers maintaining
this codebase, and frontend-notification-click-actions.md as a self-contained
handoff for the client team to start building tap-to-navigate against — it
flags the click_url URL format as an unconfirmed placeholder needing their
input, and catalogs every notification type currently sending one.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Not every notification is clickable, so click_url is optional end to end
(null/blank on the model, required=False on the serializer). When set, it's
merged into Gotify's own client::notification.click.url extras convention
without disturbing any other extras a producer already sends, so official
Gotify clients can open it on tap. Wired through both message-creation
paths: the single-push REST API and the bulk Excel upload. Also fixes a
pre-existing NameError in the bulk path (json.loads(extras) referenced an
undefined name instead of extras_str).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>