| 
					
				 | 
			
			
				@@ -1,9 +1,101 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-import { Hono } from 'hono' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import { Hono } from "hono" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import { Client, isFullPage } from "@notionhq/client" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import { z } from "zod" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import { zValidator } from "@hono/zod-validator" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-const app = new Hono() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+type Bindings = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  NOTION_KEY: string 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  NOTION_DB_ID: string 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-app.get('/', (c) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  return c.text('Hello Hono!') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const xRateQuerySchema = z.object({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  from: z 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    .string() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    .min(3, "Currency code must at length 3.") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    .max(3, "Currency code must at length 3."), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  to: z 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    .string() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    .min(3, "Currency code must at length 3.") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    .max(3, "Currency code must at length 3."), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  amount: z.string(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 }) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const app = new Hono<{ Bindings: Bindings }>() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+app.get( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  "/convert/:from/:to/:amount", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  zValidator("param", xRateQuerySchema, (result, c) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (!result.success) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      return c.json({ error: result.error.errors }, 400) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  }), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  async c => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const notionKey = c.env.NOTION_KEY 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const notionDatabaseId = c.env.NOTION_DB_ID 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (!notionDatabaseId || !notionKey) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      return c.json({ error: "Missing environment variables." }, 500) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const from = c.req.param("from") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const to = c.req.param("to") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const amount = parseFloat(c.req.param("amount")) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      const client = new Client({ auth: notionKey, fetch: fetch.bind(globalThis) }) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      const query = await client.databases.query({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        database_id: notionDatabaseId, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        page_size: 2, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        filter: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          or: [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+              type: "title", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+              property: "Code", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+              title: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                equals: from, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+              }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+              type: "title", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+              property: "Code", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+              title: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                equals: to, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+              }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          ], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      }) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      const pages = query.results.filter(page => isFullPage(page)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      let ratesMap: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        [key: string]: number 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } = {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      for (const page of pages) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let currencyCodeProp = page.properties["Code"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let rateProp = page.properties["Rate"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (currencyCodeProp!.type !== "title") { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          continue 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (rateProp!.type !== "number") { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          continue 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ratesMap[currencyCodeProp.title[0]!.plain_text] = rateProp.number! 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      return c.json({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        rates: ratesMap, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        amount: amount, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        target: amount * ratesMap[to] / ratesMap[from] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      }, 200, { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        "Content-Type": "application/json; charset=utf-8", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        "Cache-Control": "s-maxage=300, stale-while-revalidate=60", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        "Access-Control-Allow-Origin": "*", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      }) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } catch (error: any) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      console.log(error) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      return c.json({ error: error.message }, 500) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 export default app 
			 |