mirror of
https://github.com/koala73/worldmonitor.git
synced 2026-04-25 17:14:57 +02:00
* feat(auth): user-facing API key management (create / list / revoke) Adds full-stack API key management so authenticated users can create, list, and revoke their own API keys from the Settings UI. Backend: - Convex `userApiKeys` table with SHA-256 key hash storage - Mutations: createApiKey, listApiKeys, revokeApiKey - Internal query validateKeyByHash + touchKeyLastUsed for gateway - HTTP endpoints: /api/api-keys (CRUD) + /api/internal-validate-api-key - Gateway middleware validates user-owned keys via Convex + Redis cache Frontend: - New "API Keys" tab in UnifiedSettings (visible when signed in) - Create form with copy-on-creation banner (key shown once) - List with prefix display, timestamps, and revoke action - Client-side key generation + hashing (plaintext never sent to DB) Closes #3116 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(api-keys): address PR review — cache invalidation, prefix validation, revoked-key guard - Invalidate Redis cache on key revocation so gateway rejects revoked keys immediately instead of waiting for 5-min TTL expiry (P1) - Enforce `wm_` prefix format with regex instead of loose length check (P2) - Skip `touchKeyLastUsed` for revoked keys to preserve clean audit trail (P2) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(api-keys): address consolidated PR review (P0–P3) P0: gate createApiKey on pro entitlement (tier >= 1); isCallerPremium now verifies key-owner tier instead of treating existence as premium. P1: wire wm_ user keys into the domain gateway auth path with async Convex-backed validation; user keys go through entitlement checks (only admin keys bypass). Lower cache TTL 300s → 60s and await revocation cache-bust instead of fire-and-forget. P2: remove dead HTTP create/list/revoke path from convex/http.ts; switch to cachedFetchJson (stampede protection, env-prefixed keys, standard NEG_SENTINEL); add tenancy check on cache-invalidation endpoint via new /api/internal-get-key-owner route; add 22 Convex tests covering tier gate, per-user limit, duplicate hash, ownership revoke guard, getKeyOwner, and touchKeyLastUsed debounce. P3: tighten keyPrefix regex to exactly 5 hex chars; debounce touchKeyLastUsed (5 min); surface PRO_REQUIRED in UI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(api-keys): gate on apiAccess (not tier), wire wm_ keys through edge routes, harden error paths - Gate API key creation/validation on features.apiAccess instead of tier >= 1. Pro (tier 1, apiAccess=false) can no longer mint keys — only API_STARTER+. - Wire wm_ user keys through standalone edge routes (shipping/route-intelligence, shipping/webhooks) that were short-circuiting on validateApiKey before async Convex validation could run. - Restore fail-soft behavior in validateUserApiKey: transient Convex/network errors degrade to unauthorized instead of bubbling a 500. - Fail-closed on cache invalidation endpoint: ownership check errors now return 503 instead of silently proceeding (surfaces Convex outages in logs). - Tests updated: positive paths use api_starter (apiAccess=true), new test locks Pro-without-API-access rejection. 23 tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(webhooks): remove wm_ user key fallback from shipping webhooks Webhook ownership is keyed to SHA-256(apiKey) via callerFingerprint(), not to the user. With user-owned keys (up to 5 per user), this causes cross-key blindness (webhooks invisible when calling with a different key) and revoke-orphaning (revoking the creating key makes the webhook permanently unmanageable). User keys remain supported on the read-only route-intelligence endpoint. Webhook ownership migration to userId will follow in a separate PR. --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Elie Habib <elie.habib@gmail.com>
176 lines
6.0 KiB
TypeScript
176 lines
6.0 KiB
TypeScript
/* eslint-disable */
|
|
/**
|
|
* Generated `api` utility.
|
|
*
|
|
* THIS CODE IS AUTOMATICALLY GENERATED.
|
|
*
|
|
* To regenerate, run `npx convex dev`.
|
|
* @module
|
|
*/
|
|
|
|
import type * as alertRules from "../alertRules.js";
|
|
import type * as apiKeys from "../apiKeys.js";
|
|
import type * as config_productCatalog from "../config/productCatalog.js";
|
|
import type * as constants from "../constants.js";
|
|
import type * as contactMessages from "../contactMessages.js";
|
|
import type * as crons from "../crons.js";
|
|
import type * as emailSuppressions from "../emailSuppressions.js";
|
|
import type * as entitlements from "../entitlements.js";
|
|
import type * as http from "../http.js";
|
|
import type * as lib_auth from "../lib/auth.js";
|
|
import type * as lib_dodo from "../lib/dodo.js";
|
|
import type * as lib_entitlements from "../lib/entitlements.js";
|
|
import type * as lib_env from "../lib/env.js";
|
|
import type * as lib_identitySigning from "../lib/identitySigning.js";
|
|
import type * as notificationChannels from "../notificationChannels.js";
|
|
import type * as payments_billing from "../payments/billing.js";
|
|
import type * as payments_cacheActions from "../payments/cacheActions.js";
|
|
import type * as payments_checkout from "../payments/checkout.js";
|
|
import type * as payments_seedProductPlans from "../payments/seedProductPlans.js";
|
|
import type * as payments_subscriptionEmails from "../payments/subscriptionEmails.js";
|
|
import type * as payments_subscriptionHelpers from "../payments/subscriptionHelpers.js";
|
|
import type * as payments_webhookHandlers from "../payments/webhookHandlers.js";
|
|
import type * as payments_webhookMutations from "../payments/webhookMutations.js";
|
|
import type * as registerInterest from "../registerInterest.js";
|
|
import type * as resendWebhookHandler from "../resendWebhookHandler.js";
|
|
import type * as telegramPairingTokens from "../telegramPairingTokens.js";
|
|
import type * as userPreferences from "../userPreferences.js";
|
|
|
|
import type {
|
|
ApiFromModules,
|
|
FilterApi,
|
|
FunctionReference,
|
|
} from "convex/server";
|
|
|
|
declare const fullApi: ApiFromModules<{
|
|
alertRules: typeof alertRules;
|
|
apiKeys: typeof apiKeys;
|
|
"config/productCatalog": typeof config_productCatalog;
|
|
constants: typeof constants;
|
|
contactMessages: typeof contactMessages;
|
|
crons: typeof crons;
|
|
emailSuppressions: typeof emailSuppressions;
|
|
entitlements: typeof entitlements;
|
|
http: typeof http;
|
|
"lib/auth": typeof lib_auth;
|
|
"lib/dodo": typeof lib_dodo;
|
|
"lib/entitlements": typeof lib_entitlements;
|
|
"lib/env": typeof lib_env;
|
|
"lib/identitySigning": typeof lib_identitySigning;
|
|
notificationChannels: typeof notificationChannels;
|
|
"payments/billing": typeof payments_billing;
|
|
"payments/cacheActions": typeof payments_cacheActions;
|
|
"payments/checkout": typeof payments_checkout;
|
|
"payments/seedProductPlans": typeof payments_seedProductPlans;
|
|
"payments/subscriptionEmails": typeof payments_subscriptionEmails;
|
|
"payments/subscriptionHelpers": typeof payments_subscriptionHelpers;
|
|
"payments/webhookHandlers": typeof payments_webhookHandlers;
|
|
"payments/webhookMutations": typeof payments_webhookMutations;
|
|
registerInterest: typeof registerInterest;
|
|
resendWebhookHandler: typeof resendWebhookHandler;
|
|
telegramPairingTokens: typeof telegramPairingTokens;
|
|
userPreferences: typeof userPreferences;
|
|
}>;
|
|
|
|
/**
|
|
* A utility for referencing Convex functions in your app's public API.
|
|
*
|
|
* Usage:
|
|
* ```js
|
|
* const myFunctionReference = api.myModule.myFunction;
|
|
* ```
|
|
*/
|
|
export declare const api: FilterApi<
|
|
typeof fullApi,
|
|
FunctionReference<any, "public">
|
|
>;
|
|
|
|
/**
|
|
* A utility for referencing Convex functions in your app's internal API.
|
|
*
|
|
* Usage:
|
|
* ```js
|
|
* const myFunctionReference = internal.myModule.myFunction;
|
|
* ```
|
|
*/
|
|
export declare const internal: FilterApi<
|
|
typeof fullApi,
|
|
FunctionReference<any, "internal">
|
|
>;
|
|
|
|
export declare const components: {
|
|
dodopayments: {
|
|
lib: {
|
|
checkout: FunctionReference<
|
|
"action",
|
|
"internal",
|
|
{
|
|
apiKey: string;
|
|
environment: "test_mode" | "live_mode";
|
|
payload: {
|
|
allowed_payment_method_types?: Array<string>;
|
|
billing_address?: {
|
|
city?: string;
|
|
country: string;
|
|
state?: string;
|
|
street?: string;
|
|
zipcode?: string;
|
|
};
|
|
billing_currency?: string;
|
|
confirm?: boolean;
|
|
customer?:
|
|
| { email: string; name?: string; phone_number?: string }
|
|
| { customer_id: string };
|
|
customization?: {
|
|
force_language?: string;
|
|
show_on_demand_tag?: boolean;
|
|
show_order_details?: boolean;
|
|
theme?: string;
|
|
};
|
|
discount_code?: string;
|
|
feature_flags?: {
|
|
allow_currency_selection?: boolean;
|
|
allow_discount_code?: boolean;
|
|
allow_phone_number_collection?: boolean;
|
|
allow_tax_id?: boolean;
|
|
always_create_new_customer?: boolean;
|
|
};
|
|
force_3ds?: boolean;
|
|
metadata?: Record<string, string>;
|
|
product_cart: Array<{
|
|
addons?: Array<{ addon_id: string; quantity: number }>;
|
|
amount?: number;
|
|
product_id: string;
|
|
quantity: number;
|
|
}>;
|
|
return_url?: string;
|
|
show_saved_payment_methods?: boolean;
|
|
subscription_data?: {
|
|
on_demand?: {
|
|
adaptive_currency_fees_inclusive?: boolean;
|
|
mandate_only: boolean;
|
|
product_currency?: string;
|
|
product_description?: string;
|
|
product_price?: number;
|
|
};
|
|
trial_period_days?: number;
|
|
};
|
|
};
|
|
},
|
|
{ checkout_url: string }
|
|
>;
|
|
customerPortal: FunctionReference<
|
|
"action",
|
|
"internal",
|
|
{
|
|
apiKey: string;
|
|
dodoCustomerId: string;
|
|
environment: "test_mode" | "live_mode";
|
|
send_email?: boolean;
|
|
},
|
|
{ portal_url: string }
|
|
>;
|
|
};
|
|
};
|
|
};
|