Astroを使ったサイト制作時のメモ書き
WordPress検討
- userへのエンドポイントアクセス制御(functions.php)
- SiteGuard WP Pluginインストール(ログインurl / 画像認証)
- データベースアップグレード中
- 設定ファイル系をgit管理に変更
CloudFlare Pagesで、Astroプロジェクトのurlにリダイレクトする
CloudFlare Pagesのリダイレクト設定
- ファイル
_redirects
- 書き方
/test.html /posts/22 301
/2024/07/25/当クリニックの感染症対策について/ /posts/9 301
/2024/07/25/当クリニックの%E3%80%80感染症対策について/ /posts/9 301
日本語URLの特殊文字はエンコードする
- 全角スペース:
%E3%80%80
- 半角スペース:
%20
Astroへの配置
/public/
配下に上記のファイルを置くとgithubからのデプロイに対応可能
使用するサービスの設定
- WordPressをサーバーに公開&インストール
- Githubアカウント作成
- CloudFlareアカウント作成
- microCMSアカウント作成
- Googleカレンダー作成
GoogleカレンダーAPIキーの取得
-
Googleカレンダーの準備をする
- 設定と共通(一般公開して誰でも利用できるようにする)
- カレンダーのIDをメモ(カレンダーの統合カレンダー ID)
- GoogleカレンダーのAPIキーを取得する
-
Google Cloud プラットフォームにアクセス
- プロジェクトも必要
- Google Calendar APIを選択して、有効にする
- 認証情報を作成からAPIキーを作成する
-
Google Cloud プラットフォームにアクセス
- APIキーに制限をつける
- webサイトを選択、URLを指定し、APIをCalenderAPIのみにする
- webサイトを選択、URLを指定し、APIをCalenderAPIのみにする
参考サイト
AstroをInit(必要なパッケージをインストール)
プロジェクト用ディレクトリを作成するので実行するディレクトリ階層に注意!
npm create astro@latest -- --template minimal
cd [プロジェクト用ディレクトリ名]
npm run dev
パッケージインストール(当時のメモ)
lint系の設定ファイルも追加した(詳細割愛)
"dependencies": {
"@astrojs/check": "^0.5.10",
"@astrojs/cloudflare": "^11.1.0",
"@astrojs/tailwind": "^5.1.0",
"@astrojs/vue": "^4.4.0",
"@headlessui/tailwindcss": "^0.2.1",
"@headlessui/vue": "^1.7.22",
"@heroicons/vue": "^2.1.5",
"astro": "^4.15.10",
"autoprefixer": "^10.4.20",
"microcms-js-sdk": "^3.1.1",
"postcss": "^8.4.41",
"tailwindcss": "^3.4.10",
"typescript": "^5.4.5",
"vue": "^3.4.27"
},
"devDependencies": {
"@eslint/js": "^9.11.1",
"@fullcalendar/core": "^6.1.14",
"@fullcalendar/daygrid": "^6.1.14",
"@fullcalendar/google-calendar": "^6.1.14",
"@typescript-eslint/eslint-plugin": "^8.8.0",
"@typescript-eslint/parser": "^8.8.0",
"add": "^2.0.6",
"eslint": "^9.11.1",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.1",
"globals": "^15.10.0",
"prettier": "^3.3.3",
"prettier-plugin-astro": "^0.14.1",
"prettier-plugin-organize-imports": "^4.1.0",
"typescript-eslint": "^8.8.0"
}
Tailwind設定
- Init and create a tailwind.config.mjs
npx tailwindcss init
- add this basic configuration to your tailwind.config.mjs file:
/** @type {import('tailwindcss').Config} */
export default {
+ content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
theme: {
extend: {},
},
plugins: [],
};
レイアウトとメニュー作成
Layout
レイアウトは、ページテンプレートのような再利用可能なUI構造を作成するために使用されるAstroコンポーネントです。
HeadlessUI (for Vue)
import tailwind from '@astrojs/tailwind'
import vue from '@astrojs/vue'
import { defineConfig } from 'astro/config'
// https://astro.build/config
export default defineConfig({
integrations: [
tailwind(),
vue({
script: 'module',
devtools: true,
}),
],
})
CloudFlarePagesを作ってデプロイ
以下をプロジェクトに設置
Actionのworkflowsファイル
.github/workflows/main.yml
name: 🚀 Deploy on push
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
name: Deploy
steps:
- name: 🚚 Get latest code
uses: actions/checkout@v4
- name: Use Node.js 20
uses: actions/setup-node@v4
with:
node-version: 20
cache: yarn
- name: Install
shell: bash
run: yarn install
- name: Build
shell: bash
run: |
yarn run build
- name: Publish
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CF_ACCOUNT_ID }}
command: pages publish --project-name=${{ secrets.CF_PROJECT_NAME }} ./dist
Astro設定ファイル
astro.config.mjs
import cloudflare from '@astrojs/cloudflare'
// 省略
export default defineConfig({
output: 'server',
adapter: cloudflare(),
integrations: [
tailwind(),
vue({
devtools: true,
}),
],
vite: {
define: {
'import.meta.env.RUNTIME': JSON.stringify('cloudflare'),
},
ssr: {
external: ['node:fs', 'node:path'],
},
},
})
Githubでの対応
以下をActionsSecretに登録する
- CF_API_TOKEN: 作る
- CF_ACCOUNT_ID: アカウントID
- CF_PROJECT_NAME: プロジェクトネーム
CloudFlareでの対応
Pagesの作成ページでGithubリポジトリimportする
microCMSとの連携
- APIを作る
-
.env.local
にmicroCMSのドメインとAPIキーを書く - importロジックを書く
src/library/microcms.ts
//SDK利用準備
import { createClient, type MicroCMSQueries } from 'microcms-js-sdk'
//型定義
export type News = {
id: string
createdAt: string
updatedAt: string
publishedAt: string
revisedAt: string
date: string
title: string
content: string
link: string
}
export type NewsResponse = {
totalCount: number
offset: number
limit: number
contents: News[]
}
//APIの呼び出し
export const getNews = async (
clientDomain: any,
clientKey: any,
queries?: MicroCMSQueries,
) => {
if ((clientDomain as string) && (clientKey as string)) {
const client = createClient({
serviceDomain: clientDomain as string,
apiKey: clientKey as string,
})
return (await client.get<NewsResponse>({ endpoint: 'news', queries }))
.contents
} else {
return []
}
}
- pageでimport後表示する
---
import { getNews } from '../library/microcms'
const contents = (await getNews(
import.meta.env.VITE_MICROCMS_DOMAIN,
import.meta.env.VITE_MICROCMS_APIKEY,
{ fields: ['id', 'title'] },
)) as {
id: string
title: string
}[]
---
<ul class="space-y-2">
{
contents.map((content) => (
<li class="text-gray-600">{content.title}</li>
))
}
</ul>
環境変数
ローカル
.env.localに追加する
CloudFlarePages
シークレットにもAPI KEYとDOMAINを追加する
参考
Googleカレンダーを追加
環境変数は、microCMSと同様に設定
FullCalendarのサンプルVueコンポーネント
<script setup>
import { onMounted } from 'vue';
import { Calendar } from '@fullcalendar/core';
import dayGridPlugin from '@fullcalendar/daygrid';
import googleCalendarPlugin from '@fullcalendar/google-calendar';
const props = defineProps({
apiKey: {
type: String,
required: true
},
apiId: {
type: String,
required: true
}
});
onMounted(() => {
const calendarEl = document.getElementById('calendar');
const calendar = new Calendar(calendarEl, {
plugins: [googleCalendarPlugin, dayGridPlugin],
headerToolbar: {
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek',
},
googleCalendarApiKey: props.apiKey,
events: {
googleCalendarId: props.apiId,
},
});
calendar.render();
});
</script>
<template>
<div id="calendar"></div>
</template>
ページへの配置
---
import FullCalendar from '../components/FullCalendar.vue'
const googleCalendarId = (import.meta.env.GOOGLE_CALENDAR_ID as string) ?? ''
const googleCalendarApiKey =
(import.meta.env.GOOGLE_CALENDAR_API_KEY as string) ?? ''
---
<FullCalendar
v-if="googleCalendarApiKey && googleCalendarId"
client:only="vue"
apiId={googleCalendarId}
apiKey={googleCalendarApiKey}
/>
WordPressリダイレクト設定
プラグイン:Redirect Non-Admin Users
プラグイン作成後、有効化し、トップページを固定ページに変更する
<?php
/*
Plugin Name: Redirect Non-Admin Users
Description: 未ログインまたは管理者以外のユーザーを指定したURLへリダイレクトします。
Version: 1.0
Author: ***
*/
function redirect_non_admin_users() {
if ( ! is_user_logged_in() || ! current_user_can( 'administrator' ) ) {
if ( is_singular( 'post' ) || is_page() ) {
wp_redirect( 'https://takada-kodomo.com/404' );
exit;
}
}
}
add_action( 'template_redirect', 'redirect_non_admin_users' );
REST API の 'users' エンドポイントへのアクセスを無効化
子テーマを作り、functions.phpに以下を設定する
functions.php
<?php
// REST API の 'users' エンドポイントへのアクセスを無効化
add_filter( 'rest_endpoints', 'disable_users_endpoint' );
function disable_users_endpoint( $endpoints ) {
if ( isset( $endpoints['/wp/v2/users'] ) ) {
unset( $endpoints['/wp/v2/users'] );
}
if ( isset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] ) ) {
unset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] );
}
return $endpoints;
}
WordPressパーマリンク
post idに変更
AstroでWPRestAPIをFetch
src/library/wordpress.ts
export async function getWPPosts(wpApiUrl: string): Promise<any[]> {
try {
const postsRes = await fetch(`${wpApiUrl}/?rest_route=/wp/v2/posts`)
// レスポンスのステータスを確認
if (!postsRes.ok) {
throw new Error(`HTTP error! status: ${postsRes.status}`)
}
// JSONに変換
const postsData = await postsRes.json()
return postsData // 必要に応じて戻り値として返す
} catch (error) {
console.error('Error fetching posts:', error)
return []
}
}
importサンプル
---
import { getWPPosts } from '../library/wordpress'
const wpApiUrl = import.meta.env.WP_API_URL ?? ''
const posts = (await getWPPosts(wpApiUrl)) as any[]
---
<ul class="space-y-2">
{
posts.map((post: any) => (
<li>
<a
href={`/posts/${post.id}`}
class="text-blue-600 hover:underline"
>
<h3
class="text-xl font-semibold"
set:html={post.title.rendered}
/>
</a>
</li>
))
}
</ul>
CloudFlarePagesのリダイレクト
public/_redirects
に以下を追加して確認
/test.html /posts/1 301
Cloud Flare Pagesのメール認証
previewデプロイ時にメール認証を入れる(productionは仮ページにする)
(登録メールアカウントでの認証が入る)
Google Calendar APIへ、 preview urlを追加
test.ymlを追加
name: 🚀 Deploy on push (preview)
on:
push:
branches:
- *** # プレビューブランチ名を入れる、プレビューブランチへのpushでトリガー
jobs:
deploy:
runs-on: ubuntu-latest
name: Deploy (preview)
steps:
- name: 🚚 Get latest code
uses: actions/checkout@v4
- name: Use Node.js 20
uses: actions/setup-node@v4
with:
node-version: 20
cache: yarn
- name: Install
shell: bash
run: yarn install
- name: Build
shell: bash
run: |
yarn run build
- name: Publish preview
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CF_ACCOUNT_ID }}
command: pages publish --project-name=${{ secrets.CF_PROJECT_NAME }} ./dist --branch=preview # プレビューデプロイ用の設定
認証系
env.d.ts
/// <reference path="../.astro/types.d.ts" />
/// <reference types="astro/client" />
interface WorkerRuntime {
runtime: {
waitUntil: (promise: Promise<any>) => void
env: Env
cf: CFRequest['cf']
caches: typeof caches
}
}
declare namespace App {
interface Locals extends WorkerRuntime {}
}
認証時
.env.local
VITE_MICROCMS_DOMAIN=
VITE_MICROCMS_APIKEY=
GOOGLE_CALENDAR_ID=
GOOGLE_CALENDAR_API_KEY=
WP_API_URL=
RUNTIME_ENV=local
Astro内
const runtime = Astro.locals.runtime
const cloudFlareRuntime = import.meta.env.RUNTIME_ENV !== 'local'
const wpApiUrl =
(cloudFlareRuntime
? runtime.env.WP_API_URL
: (import.meta.env.WP_API_URL as string)) ?? ''
Github
WordPressでTailwindcssを使う
先にスタイルを作って、WordPressを合わせた方がやりやすそうなので、後に回す
FigmaのスタイルをTailwindに設定する
tailwind.config.mjs
export default {
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
theme: {
extend: {
colors: {
// カラー設定
yellow: {
500: '#fff200',
},
},
fontSize: {
// フォントサイズを追加
xxs: ['0.625rem', { lineHeight: '0.75rem' }],
},
fontWeight: {
normal: '400',
bold: '700',
},
},
},
// StyleGuideに使う背景色のクラス名を指定
safelist: [
'bg-yellow-500',...
],
plugins: [],
}
styleguide.astro
カラーとタイポグラフィー用のVueコンポーネントを作って作成
font
Layout.astroに游ゴシックの設定をいれた
SmartHRのデザインシステムを参考にした
<style>
@tailwind base;
@font-face {
font-family: AdjustedYuGothic;
font-weight: 400;
src: local('Yu Gothic Medium');
}
@font-face {
font-family: AdjustedYuGothic;
font-weight: 700;
src: local('Yu Gothic Bold');
}
@layer base {
html {
font-family:
AdjustedYuGothic,
Yu Gothic,
YuGothic,
sans-serif;
}
}
</style>
StyleGuideページをbuild時はリダイレクトさせる
public/_redirects
/styleguide / 301
メニュー・ヘッダー・フッターの追加
- IconはSVGファイルにした
- ヘッダーメニューとフッターメニューのリストは一旦共有
- メニューはheadlessUIのVueなので、一旦vueで
- 内容精査はしていくが、スタイルは大枠できた
- Astroでもpropsが使えることがわかった。emitも使えるか調べていく
カレンダー実装
GoogleカレンダーAPIを使う
- 入力用スプレッドシート内のイベント名を入力して、月々の更新を行う
- テストレビュー用に翌月をPreview表示できるようにしたい(未)
カレンダーの切替
- HeadlessUI Switch
- eventで取得できる内容が異なるので、更新時注意する(コメントを入れておく)
FullCalendar
テーブル表示用
TableCalendar
APIのみ使用
Modal
- EventとConsultationに分けた
- HeadlessUI
翌々月レビュー
子育て応援(途中)
サーバーを変更(さくらインターネットに変更)
リポジトリを変更してスタート
セキュリティーは一旦全部envにできたので、必要に応じて対応
Fullカレンダーを使う場合とAPIから値を取得して表示する場合で
型定義などが異なるので注意する
- ConsultationとEventを分けた
- Fullカレンダー・Tableカレンダー・トップページでそれぞれ設定がことなる
- リファクタリングもできそうだが、今回は一旦このまま進める
- Modal
- Tableカレンダー・トップページは同じ構造で作成
- スクロールリンク
- ざっくり動くようにした
- 細かい調整は、お知らせ作成時に対応する
WordPressのexportはすべてをexport→importで不要な投稿を削除した
REST APIにカテゴリ追加
cheerioを使って本文にクラスをつけた
indexページは、以下に分けてページング実装した
wordpress.tsに別途functionsを追加して作成
- /staffblog/index.astro
- /staffblog/[page].astro
/**
* 子テーマでのファイルの読み込み
*/
add_action('wp_enqueue_scripts', function() {
wp_enqueue_style('child_style', get_stylesheet_directory_uri() .'/style.css', [], date("ymdHis", filemtime( get_stylesheet_directory() .'/assets/output.css')));
wp_enqueue_style('my-child_style', get_stylesheet_directory_uri() .'/assets/output.css', array('child_style'), date("ymdHis", filemtime( get_stylesheet_directory() .'/assets/output.css')));
});
WordPressのTailwind設定
wp-bakで作業
- TailwindをStandAloneで動作させる
- tailwind.config.jsをコピー
- index.htmlにHTMLをコピーし、StandAloneで作成したTailwindCSSを適用して、WordPressのクラスや- HTML構造に合わせたスタイルをinput.cssに作成
- VS CodeのGo Liveで表示させる
- Start a watcher
./tailwindcss -i input.css -o output.css --watch
- Open index.html
- Edit input.css
- See the result in output.css
- Copy and paste the result to wordpress
- See the result in wordpress
- Repeat from 3
- Done and do minify
./tailwindcss -i input.css -o output.css --minify
- Copy and paste the minified css to wordpress
お知らせ
- microCMS
- news
- emergency
- 1つ目を表示・空にすると非表示
- スクロールリンクを調整
- CSS+JS:handleClickメソッドを作成
- offsetはJSが効いている状態・時間あれば調整する
- 11/14再度修正した
:root {
--header-height: 58px; /* ヘッダーの実際の高さに合わせて調整してください */
}
html {
scroll-padding-top: var(--header-height);
scroll-behavior: smooth;
}
WordPressのクライアントスクリプト対応
・アクセス制限(URL)
/**
* REST APIを特定のサイトにのみ許可
*/
function restrict_rest_api_access($result) {
// WordPressの管理画面からのアクセスは許可
if (is_admin()) {
return $result;
}
// ローカル環境からのアクセスは許可
if (isset($_SERVER['REMOTE_ADDR']) && in_array($_SERVER['REMOTE_ADDR'], ['127.0.0.1', '::1'])) {
return $result;
}
// リクエスト元のURLを取得
$request_origin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : '';
// User-Agentをチェック(Astroからのリクエストを許可)
$user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
if (strpos($user_agent, 'Node.js') !== false || strpos($user_agent, 'node-fetch') !== false) {
return $result;
}
// 許可するURL
$allowed_urls = [
'', // ここに追加
get_site_url() // WPURL
];
// Originがある場合のみチェック
if (!empty($request_origin) && !in_array($request_origin, $allowed_urls)) {
error_log('Forbidden access. Origin: ' . $request_origin);
return new WP_Error('forbidden_rest_api',
__('REST API access is forbidden from this origin.', 'text-domain'),
array('status' => 403)
);
}
return $result;
}
// フィルターの追加
add_filter('rest_authentication_errors', 'restrict_rest_api_access');
- 動的ルーティングを実現するためにクエリパラムURLに変更
- Astroではページが1ページになるため
- リストページは20ページ分を作成して、リストがない場合はリダイレクトさせた
microCMSのクライアントスクリプト対応
- getのみのAPIキーを作成した
- IP制御などセキュリティーを上げるためには費用が必要