16}
1718const url = `https://discord.com/api/v10${endpoint}`;
19const headers = {
20"Authorization": `Bot ${token}`,
3435if (!response.ok) {
36console.error(`❌ Discord API error: ${response.status}`);
37throw new Error(`Discord API error: ${response.status}`);
38}
39
iClickWolfHomePage.tsx4 matches
11}
1213// Default item to use if API fails
14const DEFAULT_ITEM: Item = {
15id: 1,
29async function fetchItem() {
30try {
31const response = await fetch("/api/items");
32if (response.ok) {
33const data = await response.json();
34setItem(data);
35} else {
36// If API fails, use default item
37console.warn("Using default item data because API returned an error");
38setItem(DEFAULT_ITEM);
39
iClickWolfREADME.md10 matches
78- **Backend**: `/backend/index.ts` - The main HTTP server that serves the
9frontend and API endpoints
10- `/backend/database/` - Contains SQLite database setup and queries
11- `/backend/routes/` - API routes for checkout, webhook, and items
1213- **Frontend**: `/frontend/index.html` - The main HTML entry point
26The following environment variables are required:
2728- `STRIPE_SECRET` - Stripe API secret key
29- `STRIPE_PUBLIC` - Stripe publishable key (for frontend)
30- `STRIPE_WEBHOOK_SECRET` - Secret for verifying Stripe webhook events
31- `VAL_EMAIL_KEY` - API key for the email sending service
3233## Error Handling
35The application includes robust error handling:
3637- If Stripe is not configured (missing API keys), appropriate error messages are
38shown
39- If the database fails to load items, default values are used as a fallback
40- All API errors are properly logged and user-friendly messages are displayed
41- Form validation prevents submission of invalid data
4248- `iclickwolf_preorders_v1` - Stores customer preorder information
4950## API Endpoints
5152- `POST /api/checkout` - Creates a Stripe checkout session and preorder
53- `POST /api/webhook` - Handles Stripe webhook events
54- `GET /api/items` - Returns the first item (iClicker)
55
iClickWolfPurchasePage.tsx1 match
3435try {
36const response = await fetch("/api/checkout", {
37method: "POST",
38headers: {
iClickWolfindex.ts5 matches
24}
2526// API routes
27app.route("/api/checkout", checkoutRoutes);
28app.route("/api/webhook", webhookRoutes);
29app.route("/api/items", itemsRoutes);
3031// Serve frontend files
32app.get("/frontend/*", (c) => serveFile(c.req.path, import.meta.url));
3334// Handle SPA routes - serve index.html for all non-API, non-asset routes
35const serveIndex = async (c: any) => {
36try {
iClickWolfcheckout.ts2 matches
4const app = new Hono();
56// Initialize Stripe only if the API key is available
7let stripe: any = null;
8const stripeKey = Deno.env.get("STRIPE_SECRET");
12const { Stripe } = await import("npm:stripe");
13stripe = new Stripe(stripeKey, {
14apiVersion: "2025-04-30.basil",
15});
16} catch (error) {
iClickWolfwebhook.ts5 matches
8const app = new Hono();
910// Initialize Stripe only if the API key is available
11let stripe: any = null;
12const stripeKey = Deno.env.get("STRIPE_SECRET");
16const { Stripe } = await import("npm:stripe");
17stripe = new Stripe(stripeKey, {
18apiVersion: "2025-04-30.basil",
19});
20} catch (error) {
31html?: string;
32}) {
33const emailSenderApiUrl = "https://wolf-emailsender.web.val.run/";
34const emailKey = Deno.env.get("VAL_EMAIL_KEY");
3536if (!emailKey) {
37console.error("Email API key not configured");
38throw new Error("Email service is not configured");
39}
4041const response = await fetch(emailSenderApiUrl, {
42method: "POST",
43headers: {
1// discord-rate-limit-service.ts - Advanced rate limiting for Discord API with
2// exponential backoff, circuit breaker, global rate limit handling, and blob persistence
314}
1516// API call tracking for global rate limits
17interface ApiCallRecord {
18timestamp: number;
19}
33private static instance: DiscordRateLimitService;
34private rateLimitBuckets: Map<string, RateLimitInfo> = new Map();
35private apiCallTimes: ApiCallRecord[] = [];
36private globalLock: boolean = false;
37217console.log(`⚡ Circuit breaker open, blocking requests for ${waitTime} more seconds`);
218throw new Error(
219`Circuit breaker open. API requests blocked for ${waitTime} more seconds due to too many failures.`,
220);
221}
263}
264265// Main method to execute API calls with rate limiting
266public async executeWithRateLimit<T>(
267routeKey: string,
289await this.ensureDynamicRateLimitCompliance(routeKey);
290291// Execute the actual API call
292const result = await action();
293401// Check for Discord's global rate limit message
402if (error.message && typeof error.message === "string") {
403if (error.message.includes("You are being blocked from accessing our API temporarily")) {
404// Set a longer global rate limit time (e.g., 10 minutes)
405this.globalRateLimitUntil = Date.now() + 10 * 60 * 1000;
567568// Also track for global rate limiting
569this.trackApiCall();
570}
571572// Track an API call for global rate limiting
573private trackApiCall(): void {
574const now = Date.now();
575this.apiCallTimes.push({ timestamp: now });
576577// Clean up old records
578this.apiCallTimes = this.apiCallTimes.filter(call => now - call.timestamp <= this.RATE_LIMIT_WINDOW_MS);
579}
580651652// Clean up old records
653this.apiCallTimes = this.apiCallTimes.filter(call => now - call.timestamp <= this.RATE_LIMIT_WINDOW_MS);
654655// If we have capacity, return immediately
656if (this.apiCallTimes.length < this.RATE_LIMIT_PER_SECOND) {
657return;
658}
659660// Calculate how long to wait before making another request
661if (this.apiCallTimes.length > 0) {
662// Get the oldest API call in our window
663const oldestCall = this.apiCallTimes[0];
664665// Calculate when that call will expire from our window
luciaMagicLinkStarterindex.ts2 matches
15app.get("/frontend/**/*", c => serveFile(c.req.path, import.meta.url));
1617// Add your API routes here
18// app.get("/api/data", c => c.json({ hello: "world" }));
1920// Unwrap and rethrow Hono errors as the original error
19import type {
20HellLetLooseMapData,
21MapInfo,
22} from "https://esm.town/v/ktodaz/Discord_Bot_Services/map_vote/map-vote-getHellLetLooseMapData.tsx";
2324import type { CurrentConfig } from "https://esm.town/v/ktodaz/Discord_Bot_Services/map_vote/map-vote-getCurrentConfig.tsx";
2526// Discord API endpoints
27const API_BASE = "https://discord.com/api/v10";
2829// Initialize the Discord rate limit service
44};
4546// Enhanced Discord API request function with rate limiting
47async function discordRequest(endpoint: string, options: RequestInit = {}) {
48const token = Deno.env.get("DISCORD_BOT_TOKEN");
51}
5253const url = `${API_BASE}${endpoint}`;
54const headers = {
55"Authorization": `Bot ${token}`,
82}
8384console.error(`❌ Discord API error: ${response.status}`, errorBody);
85throw new Error(`Discord API error: ${response.status} ${JSON.stringify(errorBody)}`);
86}
87106107return rateLimitService.executeWithRateLimit(routeKey, async () => {
108const url = `${API_BASE}/channels/${channelId}/messages/${messageId}/reactions/${emoji}/@me`;
109const token = Deno.env.get("DISCORD_BOT_TOKEN");
110237title: initialEmbedData.InitialEmbedTitle || "Loading . . .",
238description: initialEmbedData.InitialEmbedDescription
239|| "Due to Discord API rate limits this will take a few minutes.",
240fields: [
241{
272273// Process variants for a map and add emoji reactions
274async function processMapVariants(messageId: string, channelId: string, map: MapInfo, variantOptions: any) {
275const mapMetaVariants: string[] = [];
276const mapEnabledVariants: string[] = [];