import { Container, getContainer } from "@cloudflare/containers"; import { Context, Hono } from "hono"; // Forward declaration for Env interface export class MyContainer extends Container { // Port the container listens on (default: 8080) defaultPort = 8080; // Time before container sleeps due to inactivity (default: 30s) sleepAfter = "2m"; override onError(error: unknown) { console.error("[Container] Error:", error); } // Optional lifecycle hooks override onStart() { console.log("[Container] PAYMENTS container started"); } constructor(ctx: any, env: any) { super(ctx, env); this.ctx.blockConcurrencyWhile(async () => { this.envVars = { // Mail configuration MAIL_SMTP_PASSWORD: env.MAIL_SMTP_PASSWORD, MAIL_SENDER_EMAIL: env.MAIL_SENDER_EMAIL, MAIL_SMTP_SERVER: env.MAIL_SMTP_SERVER, // Stripe API STRIPE_API_KEY: env.STRIPE_API_KEY, // Firebase configuration FIREBASE__TYPE: env.FIREBASE__TYPE, FIREBASE__PROJECT_ID: env.FIREBASE__PROJECT_ID, FIREBASE__PRIVATE_KEY_ID: env.FIREBASE__PRIVATE_KEY_ID, FIREBASE__PRIVATE_KEY: env.FIREBASE__PRIVATE_KEY, FIREBASE__CLIENT_EMAIL: env.FIREBASE__CLIENT_EMAIL, FIREBASE__CLIENT_ID: env.FIREBASE__CLIENT_ID, FIREBASE__AUTH_URI: env.FIREBASE__AUTH_URI, FIREBASE__TOKEN_URI: env.FIREBASE__TOKEN_URI, FIREBASE__AUTH_PROVIDER_X509_CERT_URL: env.FIREBASE__AUTH_PROVIDER_X509_CERT_URL, // MongoDB MONGODB_URL: env.MONGODB_URL, // DigitalOcean Spaces DIGITALOCEAN_ID: env.DIGITALOCEAN_ID, DIGITALOCEAN_SECRET: env.DIGITALOCEAN_SECRET, DIGITALOCEAN_ENDPOINT: env.DIGITALOCEAN_ENDPOINT, DIGITALOCEAN_REGION: env.DIGITALOCEAN_REGION, DIGITALOCEAN_BUCKET: env.DIGITALOCEAN_BUCKET, // Proxy PROXY_URL: env.PROXY_URL, }; }); } } // Helper function to get container with timeout and retry logic async function getContainerWithTimeout( containerNamespace: DurableObjectNamespace, name: string, c: Context ) { const container = getContainer(containerNamespace, name); // Add a timeout wrapper to prevent blockConcurrencyWhile from hanging const timeoutPromise = new Promise((_, reject) => { setTimeout( () => reject(new Error("Container operation timed out")), 240000 ); // 4 minute timeout }); try { // Try to connect to the container with retries let lastError: Error | null = null; for (let attempt = 1; attempt <= 3; attempt++) { try { console.log( `Attempting to connect to container ${name} (attempt ${attempt}/3)` ); await container.startAndWaitForPorts({ ports: [8080], startOptions: { enableInternet: true, ...c.env, }, }); // Race between the container operation and the timeout await Promise.race([container.fetch(c.req.raw), timeoutPromise]); console.log(`Successfully connected to container ${name}`); return container; } catch (error) { lastError = error instanceof Error ? error : new Error(String(error)); console.log( `Attempt ${attempt} failed for container ${name}:`, lastError.message ); if (attempt < 3) { // Wait before retrying await new Promise((resolve) => setTimeout(resolve, 5000)); // 5 second delay } } } throw ( lastError || new Error("Container connection failed after 3 attempts") ); } catch (error) { console.error(`Container ${name} not ready or timed out:`, error); throw error; } } // Create Hono app with proper typing for Cloudflare Workers const app = new Hono<{ Bindings: Env; }>(); // Root route - Service information app.get("/", (c) => { return c.json({ service: "SimpleCoaching Payments API", version: "0.1.0", status: "healthy", // documentation: "https://dev.payment.simplecoachingapp.com/api/v1", // endpoints: { // products: "/api/v1/products", // contracts: "/api/v1/contracts", // invoices: "/api/v1/invoices", // contractCards: "/api/v1/contract-cards", // stripe: "/api/v1/stripe/*" // } }); }); // Wildcard route for all API requests - proxies to Go service container app.all("/api/v1/*", async (c) => { // Use singleton pattern for container instance const container = await getContainerWithTimeout( c.env.MY_CONTAINER, "api", c ); // Forward the request to the Go service container // return await container.fetch(c.req.raw); }); export default app;