mirror of
https://github.com/Such-Software/smirk-backend-core.git
synced 2026-10-17 07:21:32 +02:00
feat(tips): targeted tips, off until an operator asks for one
A targeted tip is addressed to a Smirk handle rather than to whoever holds a share URL. The v3 port shipped public-only and the schema said so: there was no recipient column anywhere, so create_social_tip rejected is_public=false outright and the two inbox endpoints returned an empty list unconditionally. This is the missing half. The migration adds recipient_user_id plus two CHECKs that keep each kind of tip well formed: a targeted tip must name its recipient, and must carry the ciphertext, because with no URL fragment that ciphertext is the only way to claim it. Without the second one a draft could be created that looks healthy and is permanently unclaimable. Both are satisfied by every existing row, which is public, so nothing is backfilled or rewritten. is_public is derived from the recipient in the INSERT rather than passed alongside it. Two independent sources for "who may claim this" is how a tip ends up addressed to one person and claimable by anyone. Claiming was already guarded by is_public = TRUE, so no targeted tip could be claimed at all; that becomes "public, or the caller is the recipient". The claimable query repeats exactly the conditions the claim call enforces, so the inbox cannot show a Claim button the backend then refuses. It is off by default and separately from FEATURE_TIPS, because it is a distinct surface: an inbox, and a claim authorised by identity rather than by holding a secret. It also needs the migration to have run, so an instance must opt in rather than acquire it by upgrading. The capability is advertised only when tipping is also on, since a targeted tip is still a tip, and recipient resolution checks the flag before anything else: a backend that cannot serve this must refuse, never quietly fall back to creating a public tip from a targeted request, which would hand a tip meant for one person to anyone with the URL. The config fixture moves to Config::test_default so a test of what a flag does can live next to the flag.
This commit is contained in:
+9
-2
@@ -756,7 +756,8 @@
|
||||
"tags": [
|
||||
"tips"
|
||||
],
|
||||
"summary": "Tips CLAIMABLE by the caller. As with `received`, a public-only instance has\nno targeted-claimable inbox, so this is always empty (served 200, not 404).",
|
||||
"summary": "Tips CLAIMABLE by the caller right now.",
|
||||
"description": "A strict subset of `received`, filtered by exactly the conditions the claim\ncall itself enforces, so the inbox never offers a Claim the backend will then\nrefuse.",
|
||||
"operationId": "get_claimable_social_tips",
|
||||
"responses": {
|
||||
"200": {
|
||||
@@ -788,7 +789,8 @@
|
||||
"tags": [
|
||||
"tips"
|
||||
],
|
||||
"summary": "Tips RECEIVED by the caller. Public-only instances have no targeted-recipient\ninbox (public tips are claimed via share URL, never delivered to a user), so\nthis is always empty — served (200) rather than 404 so the client's inbox\npoll doesn't error on a targeted-only endpoint this instance doesn't serve.",
|
||||
"summary": "Tips RECEIVED by the caller, newest first.",
|
||||
"description": "Public tips never appear here: they are claimed by holding a share URL and\nhave no addressee to deliver to. An instance with targeted tips off has\nnothing to list and answers 200 with an empty list, so the client's inbox\npoll does not error on a backend that simply does not offer the feature.",
|
||||
"operationId": "get_received_social_tips",
|
||||
"responses": {
|
||||
"200": {
|
||||
@@ -3324,6 +3326,7 @@
|
||||
"premium_relay",
|
||||
"feed",
|
||||
"tips",
|
||||
"targeted_tips",
|
||||
"xmr_subaddr_provisioning",
|
||||
"utxo_multi_address"
|
||||
],
|
||||
@@ -3360,6 +3363,10 @@
|
||||
"type": "boolean",
|
||||
"description": "Fiat price feed."
|
||||
},
|
||||
"targeted_tips": {
|
||||
"type": "boolean",
|
||||
"description": "Tips addressed to a Smirk handle rather than to a share URL. A client\nmust not offer the targeted option unless this is true, because the\ncreate call refuses it and the send would fail after the user picked a\nrecipient and an amount."
|
||||
},
|
||||
"tips": {
|
||||
"type": "boolean",
|
||||
"description": "Tipping (parked)."
|
||||
|
||||
Reference in New Issue
Block a user