Conversation
added 2 commits
September 27, 2026 14:20
Add a public donation flow: anyone can contribute a provider account and receive New API credit for it, without a console key. Backend - internal/control/donations.go: Donations service. It resolves the target site from system settings, imports the contributed credential through the existing account import path, and only then credits the contributor via New API POST /api/user/manage. - internal/console/donations.go: GET /api/donations advertises the accepted credential formats from the provider registry; POST submits a contribution. - Registered on the public router, deliberately outside withConsoleKey: a contributor has no console key and the reward goes to their own numeric New API user id. - System settings gain donation_base_url and donation_token. The token is stored as a secret and reported only as donation_configured, never echoed. Ordering and failure handling - Import first, credit second. A contribution that fails validation never reaches the credit call, so an unusable credential is never rewarded. - A credit failure after a successful import keeps the account and returns credited=false with credit_error, rather than rolling back a valid contribution or silently dropping the reward. Frontend - New public /donations page (HeroUI) outside RequireAuth, with per-format fields: a JSON credential, or the Qoder auth blob plus machine id. - api/donations.ts client, zh/en strings, and the rebuilt embedded assets. Tests cover the quota conversion, format-to-provider mapping, the credit request shape (Authorization + New-Api-User), site business errors, the no-credit-before-import ordering, and that the stored token is never exposed.
Contributions now use each provider's own browser authorization instead of
asking the contributor to hand over a raw credential. Command Code has no
browser login (it uses a PAT), so the pasted-credential path is kept for it.
Backend
- Donations.StartSession creates the contributed account disabled, opens the
provider login through the existing LoginSessionProvider path, and records a
pending session. Nothing is credited here.
- Donations.PollSession reports progress and, once the provider login completes,
enables the account and issues the reward exactly once. A repeat poll returns
the stored outcome rather than crediting again.
- Donations.CancelSession abandons a pending round and removes its placeholder
account; a settled round refuses cancellation.
- Pending sessions expire after 15 minutes and are swept with their placeholder
accounts, so an abandoned round leaves nothing behind. A settled session
survives the TTL so a finished reward stays reportable.
- Routes: POST /api/donations (start), GET|DELETE /api/donations/sessions/{id},
POST /api/donations/credential (legacy). All remain public.
The two invariants from the original flow still hold, and are now asserted for
the web path too: no reward before the account is authorized, and a credit
failure after a successful authorization keeps the (now enabled) account while
reporting credited=false with credit_error.
Frontend
- The page leads with "Authorize in browser": it starts the round, opens the
auth URL, and polls to completion, with a cancel action and a reopen link.
- Credential fields render only for formats that have no web login.
- GET /api/donations reports web_auth per format so the page never hardcodes it.
Tests cover the no-credit-before-authorization invariant, single-credit on
completion, keeping the account when the credit fails, placeholder cleanup on
login failure and cancel, refusing providers without a web login, and TTL
sweeping of pending but not settled sessions.
Gaozx1
force-pushed
the
feat/donations
branch
from
September 27, 2026 06:20
4d16b0c to
3f3e424
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this adds
A public donation flow: anyone can contribute a provider account (WorkBuddy, Qoder, Trae, Devin, Command Code) and receive New API credit for it, without a console key.
Reachable at
/donations, backed by:GET /api/donationsPOST /api/donationsGET /api/donations/sessions/{id}DELETE /api/donations/sessions/{id}POST /api/donations/credentialWeb authorization
Contributions use each provider's own browser login, so a contributor never hands over a raw credential.
POST /api/donationscreates the account, opens the provider login through the existingLoginSessionProviderpath, and returns the URL to visit. Polling it drives the round to completion.Command Code has no browser login (it authenticates with a PAT), so it keeps the pasted-credential path.
GET /api/donationsreportsweb_authper format and the page renders credential fields only for those formats — the frontend never hardcodes which provider supports what.Why it is public
A contributor has no console key, and the reward goes to their own numeric New API user id. The route is therefore registered outside
withConsoleKey, deliberately — the credential itself is the abuse control, because the account must authorize before anything is paid out.The invariants that matter
Four properties are pinned by tests:
credited:falsepluscredit_error, and the now-enabled account stays in the pool. Rolling back a valid contribution, or silently dropping the reward, is worse for both sides; an operator can credit manually without asking for a re-login.Credit call
New API validates
POST /api/user/managewith the user AccessToken inAuthorizationplus the matching numeric id inNew-Api-User. The shape is asserted in a test:{"id": 400, "action": "add_quota", "mode": "add", "value": 500000}1 USD = 500000 quota, which is New API's default
QuotaPerUnit.Configuration
The site is set under System settings (
donation_base_url,donation_token) and read per request, so it can be changed without a restart. The token is stored as a secret and is reported only asdonation_configured; it is never echoed back. A test asserts that.Validation
Verified live end-to-end against a New API 1.2.0 site:
pendingwith quota unchanged at 0 — no premature credit.credited:false+credit_errorwith the account retained.Note on the console UI
/donationsis public, so it is intentionally not in the authenticated nav. Link to it from wherever you advertise the program.