A lexicon-driven AppView for ATProto. happyview.dev
backfill firehose jetstream atproto appview oauth lexicon
8
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(dashboard): allow API clients to be created without rate limits

Trezy 3cafc69d 55c9a305

+38 -10
+34 -6
web/src/app/dashboard/settings/api-clients/page.tsx
··· 226 226 const [clientUri, setClientUri] = useState(""); 227 227 const [redirectUris, setRedirectUris] = useState<string[]>([""]); 228 228 const [scopes, setScopes] = useState<string[]>([""]); 229 + const [rateLimitEnabled, setRateLimitEnabled] = useState(true); 229 230 const [rateLimitCapacity, setRateLimitCapacity] = useState( 230 231 String(config.default_rate_limit_capacity) 231 232 ); ··· 245 246 setClientUri(""); 246 247 setRedirectUris([""]); 247 248 setScopes([""]); 249 + setRateLimitEnabled(true); 248 250 setRateLimitCapacity(String(config.default_rate_limit_capacity)); 249 251 setRateLimitRefillRate(String(config.default_rate_limit_refill_rate)); 250 252 setError(null); ··· 272 274 setError("Name, Client ID URL, and Client URI are required."); 273 275 return; 274 276 } 275 - if (!rateLimitCapacity || !rateLimitRefillRate) { 277 + if (rateLimitEnabled && (!rateLimitCapacity || !rateLimitRefillRate)) { 276 278 setError("Rate limit capacity and refill rate are required."); 277 279 return; 278 280 } ··· 283 285 client_uri: clientUri.trim(), 284 286 redirect_uris: allUris, 285 287 scopes: allScopes, 286 - rate_limit_capacity: Number(rateLimitCapacity), 287 - rate_limit_refill_rate: Number(rateLimitRefillRate), 288 + rate_limit_capacity: rateLimitEnabled ? Number(rateLimitCapacity) : null, 289 + rate_limit_refill_rate: rateLimitEnabled ? Number(rateLimitRefillRate) : null, 288 290 }); 289 291 setCreated(result); 290 292 } catch (e: unknown) { ··· 432 434 </fieldset> 433 435 <fieldset className="flex flex-col gap-3 rounded-lg border p-4"> 434 436 <legend className="text-sm font-medium px-1">Rate Limiting</legend> 437 + <div className="flex items-center gap-3"> 438 + <Switch 439 + id="rl-enabled" 440 + checked={rateLimitEnabled} 441 + onCheckedChange={setRateLimitEnabled} 442 + /> 443 + <Label htmlFor="rl-enabled" className="cursor-pointer">Enabled</Label> 444 + </div> 435 445 <p className="text-muted-foreground text-xs"> 436 446 Each client gets a token bucket. Requests consume tokens and the bucket 437 447 refills over time. When the bucket is empty, requests are rejected until ··· 446 456 min={1} 447 457 value={rateLimitCapacity} 448 458 onChange={(e) => setRateLimitCapacity(e.target.value)} 459 + disabled={!rateLimitEnabled} 449 460 /> 450 461 <p className="text-muted-foreground text-xs"> 451 462 Maximum number of tokens. This is the burst limit. ··· 460 471 step="any" 461 472 value={rateLimitRefillRate} 462 473 onChange={(e) => setRateLimitRefillRate(e.target.value)} 474 + disabled={!rateLimitEnabled} 463 475 /> 464 476 <p className="text-muted-foreground text-xs"> 465 477 Tokens added per second. ··· 515 527 ); 516 528 const [scopes, setScopes] = useState<string[]>(parseScopes(client.scopes)); 517 529 const [isActive, setIsActive] = useState(client.is_active); 530 + const [rateLimitEnabled, setRateLimitEnabled] = useState( 531 + client.rate_limit_capacity != null && client.rate_limit_refill_rate != null 532 + ); 518 533 const [rateLimitCapacity, setRateLimitCapacity] = useState( 519 534 String(client.rate_limit_capacity ?? config.default_rate_limit_capacity) 520 535 ); ··· 532 547 setRedirectUris(parseRedirectUris(client.redirect_uris)); 533 548 setScopes(parseScopes(client.scopes)); 534 549 setIsActive(client.is_active); 550 + setRateLimitEnabled( 551 + client.rate_limit_capacity != null && client.rate_limit_refill_rate != null 552 + ); 535 553 setRateLimitCapacity( 536 554 String(client.rate_limit_capacity ?? config.default_rate_limit_capacity) 537 555 ); ··· 544 562 545 563 async function handleSave() { 546 564 setError(null); 547 - if (!rateLimitCapacity || !rateLimitRefillRate) { 565 + if (rateLimitEnabled && (!rateLimitCapacity || !rateLimitRefillRate)) { 548 566 setError("Rate limit capacity and refill rate are required."); 549 567 return; 550 568 } ··· 560 578 redirect_uris: allUris, 561 579 scopes: allScopes, 562 580 is_active: isActive, 563 - rate_limit_capacity: Number(rateLimitCapacity), 564 - rate_limit_refill_rate: Number(rateLimitRefillRate), 581 + rate_limit_capacity: rateLimitEnabled ? Number(rateLimitCapacity) : null, 582 + rate_limit_refill_rate: rateLimitEnabled ? Number(rateLimitRefillRate) : null, 565 583 }); 566 584 setOpen(false); 567 585 onSuccess(); ··· 637 655 </fieldset> 638 656 <fieldset className="flex flex-col gap-3 rounded-lg border p-4"> 639 657 <legend className="text-sm font-medium px-1">Rate Limiting</legend> 658 + <div className="flex items-center gap-3"> 659 + <Switch 660 + id="edit-rl-enabled" 661 + checked={rateLimitEnabled} 662 + onCheckedChange={setRateLimitEnabled} 663 + /> 664 + <Label htmlFor="edit-rl-enabled" className="cursor-pointer">Enabled</Label> 665 + </div> 640 666 <p className="text-muted-foreground text-xs"> 641 667 Each client gets a token bucket. Requests consume tokens and the bucket 642 668 refills over time. When the bucket is empty, requests are rejected until ··· 651 677 min={1} 652 678 value={rateLimitCapacity} 653 679 onChange={(e) => setRateLimitCapacity(e.target.value)} 680 + disabled={!rateLimitEnabled} 654 681 /> 655 682 <p className="text-muted-foreground text-xs"> 656 683 Maximum number of tokens. This is the burst limit. ··· 665 692 step="any" 666 693 value={rateLimitRefillRate} 667 694 onChange={(e) => setRateLimitRefillRate(e.target.value)} 695 + disabled={!rateLimitEnabled} 668 696 /> 669 697 <p className="text-muted-foreground text-xs"> 670 698 Tokens added per second.
+4 -4
web/src/lib/api.ts
··· 373 373 client_uri: string 374 374 redirect_uris: string[] 375 375 scopes?: string 376 - rate_limit_capacity: number 377 - rate_limit_refill_rate: number 376 + rate_limit_capacity: number | null 377 + rate_limit_refill_rate: number | null 378 378 } 379 379 ) { 380 380 return apiFetch<CreateApiClientResponse>("/admin/api-clients", { ··· 390 390 client_uri?: string 391 391 redirect_uris?: string[] 392 392 scopes?: string 393 - rate_limit_capacity?: number 394 - rate_limit_refill_rate?: number 393 + rate_limit_capacity?: number | null 394 + rate_limit_refill_rate?: number | null 395 395 is_active?: boolean 396 396 } 397 397 ) {