|
@@ -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
|