From 244524e53fc3cf472350d75bf4cbd298557b0056 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 6 Sep 2025 23:33:20 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20Algolia=E3=82=B5=E3=83=BC=E3=83=81?= =?UTF-8?q?=E3=82=92=E7=B5=B1=E5=90=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ヘッダーに検索窓を追加し、Algoliaを利用してブログ記事の全文検索を可能にします。 GitHub Actionsのワークフローを更新し、デプロイ前に自動でAlgoliaのインデックスが更新されるようにしました。 Algoliaの認証情報は環境変数とGitHub Secrets経由で設定する必要があります。 --- .github/workflows/deploy.yml | 9 +++++ components/Header.tsx | 22 +++++++++++- deno.json | 3 +- islands/Search.tsx | 59 +++++++++++++++++++++++++++++++++ scripts/update_algolia_index.ts | 35 +++++++++++++++++++ 5 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 islands/Search.tsx create mode 100644 scripts/update_algolia_index.ts diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 6becddf..7c97032 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -23,10 +23,19 @@ jobs: with: deno-version: v1.x + - name: Update Algolia Index + if: github.ref == 'refs/heads/main' + run: deno run -A scripts/update_algolia_index.ts + env: + ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }} + ALGOLIA_ADMIN_KEY: ${{ secrets.ALGOLIA_ADMIN_KEY }} + ALGOLIA_INDEX_NAME: ${{ secrets.ALGOLIA_INDEX_NAME }} + - name: Build step run: "deno task build" - name: Upload to Deno Deploy + if: github.ref == 'refs/heads/main' uses: denoland/deployctl@v1 with: project: "9renpoto" diff --git a/components/Header.tsx b/components/Header.tsx index d9c177a..3227eb0 100644 --- a/components/Header.tsx +++ b/components/Header.tsx @@ -1,8 +1,19 @@ import IconActivity from "https://deno.land/x/tabler_icons_tsx@0.0.5/tsx/activity.tsx"; import Campfire from "https://deno.land/x/tabler_icons_tsx@0.0.5/tsx/campfire.tsx"; import IconRss from "https://deno.land/x/tabler_icons_tsx@0.0.5/tsx/rss.tsx"; +import Search from "@/islands/Search.tsx"; -export function Header({ title }: { title: string }) { +interface HeaderProps { + title: string; + algolia?: { + appId: string; + searchKey: string; + indexName: string; + }; +} + +export function Header(props: HeaderProps) { + const { title, algolia } = props; const menus = [ { name: , href: "/rss.xml" }, { name: , href: "https://9renpoto.github.io/upptime" }, @@ -27,6 +38,15 @@ export function Header({ title }: { title: string }) { ))} + {algolia && ( +
  • + +
  • + )} ); diff --git a/deno.json b/deno.json index 8e8870c..16ccae7 100644 --- a/deno.json +++ b/deno.json @@ -27,7 +27,8 @@ "@std/testing": "jsr:@std/testing@^0.225.3", "preact": "https://esm.sh/preact@10.22.0", "preact/": "https://esm.sh/preact@10.22.0/", - "tailwindcss": "npm:tailwindcss@^3.4.6" + "tailwindcss": "npm:tailwindcss@^3.4.6", + "algoliasearch": "https://esm.sh/algoliasearch@4.20.0" }, "lint": { "rules": { diff --git a/islands/Search.tsx b/islands/Search.tsx new file mode 100644 index 0000000..a1e4543 --- /dev/null +++ b/islands/Search.tsx @@ -0,0 +1,59 @@ +import { useState, useEffect, useRef } from "preact/hooks"; +import algoliasearch from "algoliasearch/lite"; + +interface SearchProps { + appId: string; + searchKey: string; + indexName: string; +} + +interface Hit { + objectID: string; + title: string; + snippet: string; +} + +export default function Search(props: SearchProps) { + const { appId, searchKey, indexName } = props; + const [query, setQuery] = useState(""); + const [hits, setHits] = useState([]); + const searchInput = useRef(null); + + const searchClient = algoliasearch(appId, searchKey); + const index = searchClient.initIndex(indexName); + + useEffect(() => { + if (query.length > 0) { + index.search(query).then(({ hits }) => { + setHits(hits); + }); + } else { + setHits([]); + } + }, [query]); + + return ( +
    + setQuery(e.currentTarget.value)} + placeholder="Search..." + class="border rounded-lg px-2 py-1" + /> + {hits.length > 0 && ( + + )} +
    + ); +} diff --git a/scripts/update_algolia_index.ts b/scripts/update_algolia_index.ts new file mode 100644 index 0000000..2cca031 --- /dev/null +++ b/scripts/update_algolia_index.ts @@ -0,0 +1,35 @@ +import "https://deno.land/std@0.192.0/dotenv/load.ts"; +import algoliasearch from "algoliasearch"; +import { getPosts } from "@/utils/posts.ts"; + +const appId = Deno.env.get("ALGOLIA_APP_ID"); +const adminKey = Deno.env.get("ALGOLIA_ADMIN_KEY"); +const indexName = Deno.env.get("ALGOLIA_INDEX_NAME"); + +if (!appId || !adminKey || !indexName) { + console.error( + "ALGOLIA_APP_ID, ALGOLIA_ADMIN_KEY, and ALGOLIA_INDEX_NAME must be set", + ); + Deno.exit(1); +} + +const client = algoliasearch(appId, adminKey); +const index = client.initIndex(indexName); + +const posts = await getPosts(); + +const records = posts.map((post) => ({ + objectID: post.slug, + title: post.title, + snippet: post.snippet, + content: post.content, + publishedAt: post.publishedAt.toISOString(), +})); + +try { + await index.saveObjects(records); + console.log("Algolia index updated successfully!"); +} catch (error) { + console.error("Failed to update Algolia index:", error); + Deno.exit(1); +}