ソースを参照

feat: Provider

kotoyuuko 1 日 前
コミット
35f7e1489c
4 ファイル変更124 行追加49 行削除
  1. 23 49
      src/index.ts
  2. 10 0
      src/model.ts
  3. 28 0
      src/provider/boc.ts
  4. 63 0
      src/provider/notion.ts

+ 23 - 49
src/index.ts

@@ -1,7 +1,8 @@
 import { Hono } from "hono"
-import { Client, isFullPage } from "@notionhq/client"
 import { z } from "zod"
 import { zValidator } from "@hono/zod-validator"
+import { NotionProvider } from "./provider/notion"
+import { BocProvider } from "./provider/boc"
 
 type Bindings = {
   NOTION_KEY: string
@@ -42,56 +43,22 @@ app.get(
     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 notion = new NotionProvider(notionKey, notionDatabaseId)
+      const ratesMap = await notion.queryRates([from, to])
+      return c.json(
+        {
+          rates: ratesMap,
+          exchange_rate: ratesMap[to] / ratesMap[from],
+          amount: amount,
+          target: (amount * ratesMap[to]) / ratesMap[from],
         },
-      })
-      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
+        200,
+        {
+          "Content-Type": "application/json; charset=utf-8",
+          "Cache-Control": "s-maxage=300, stale-while-revalidate=60",
+          "Access-Control-Allow-Origin": "*",
         }
-        ratesMap[currencyCodeProp.title[0]!.plain_text] = rateProp.number!
-      }
-
-      return c.json({
-        rates: ratesMap,
-        exchange_rate: ratesMap[to] / ratesMap[from],
-        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)
@@ -99,4 +66,11 @@ app.get(
   }
 )
 
+app.get("/boc", async c => {
+  const boc = new BocProvider()
+  return c.json({
+    resp: await boc.queryRates(),
+  })
+})
+
 export default app

+ 10 - 0
src/model.ts

@@ -0,0 +1,10 @@
+export interface Rate {
+  [key: string]: number
+}
+
+export interface ConvertResponse {
+  rates: Rate
+  exchange_rate: number
+  amount: number
+  target: number
+}

+ 28 - 0
src/provider/boc.ts

@@ -0,0 +1,28 @@
+export class BocProvider {
+  public async queryRates() {
+    const url = new URL("https://proxy.azurlogic.dev/https://www.boc.cn/sourcedb/whpj/index.html")
+    let headers = new Headers()
+    headers.set("User-Agent", 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36')
+    headers.set("Accept", 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9')
+    headers.set("Accept-Language", 'en-US,en;q=0.9')
+    headers.set("Accept-Encoding", 'gzip, deflate, br')
+    headers.set("Connection", 'keep-alive')
+    headers.set("Upgrade-Insecure-Requests", '1')
+    headers.set("Sec-Fetch-Dest", 'document')
+    headers.set("Sec-Fetch-Mode", 'navigate')
+    headers.set("Sec-Fetch-Site", 'none')
+    headers.set("Sec-Fetch-User", '?1')
+    headers.set("Host", url.host)
+    headers.set("Origin", url.origin)
+    headers.set("Referer", url.href)
+    const request = new Request(url.toString(), {
+      method: "GET",
+      headers,
+      body: null,
+      redirect: 'manual',
+    })
+    const resp = await fetch(request)
+    console.log(resp)
+    return resp.body
+  }
+}

+ 63 - 0
src/provider/notion.ts

@@ -0,0 +1,63 @@
+import { Client, isFullPage } from "@notionhq/client"
+import {
+  PageObjectResponse,
+  QueryDatabaseResponse,
+} from "@notionhq/client/build/src/api-endpoints"
+import { Rate } from "../model"
+
+export class NotionProvider {
+  #NOTION_KEY: string
+  #DATABASE_ID: string
+
+  public constructor(notionKey: string, databaseId: string) {
+    this.#NOTION_KEY = notionKey
+    this.#DATABASE_ID = databaseId
+  }
+
+  public async queryRates(currencies: Array<string>): Promise<Rate> {
+    const client = new Client({
+      auth: this.#NOTION_KEY,
+      fetch: fetch.bind(globalThis),
+    })
+
+    let query: QueryDatabaseResponse
+    let startCursor: string | null = null
+    let pages: Array<PageObjectResponse> = []
+    do {
+      query = await client.databases.query({
+        database_id: this.#DATABASE_ID,
+        page_size: 100,
+        start_cursor: startCursor ?? undefined,
+        filter: {
+          or: currencies.map(currency => ({
+            type: "title",
+            property: "Code",
+            title: {
+              equals: currency,
+            },
+          })),
+        },
+      })
+
+      const queryResults = query.results.filter(page => isFullPage(page))
+      startCursor = query.next_cursor
+
+      pages = [...pages, ...queryResults]
+    } while (query.next_cursor !== null)
+
+    let ratesMap: Rate = {}
+    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 ratesMap
+  }
+}