絵・小説・VRChat向け3Dアバター/ギミック制作を掲載する、Astro製の静的サイトです。
| 使用箇所 | 使用技術 |
|---|---|
| サイト生成 | Astro v7, TypeScript |
| CSS | UnoCSS, custom CSS |
| CMS | Sveltia CMS + Cloudflare Access proxy |
| 検索 | Pagefind |
| OGP | satori + sharp |
| ホスティング | Cloudflare Pages |
| 広告 | Google AdSense |
| コメント | Cloudflare Pages Functions + D1 |
Node.js 24.18.0 以上を使用してください。リポジトリの固定バージョンは .node-version に記載しています。
npm install
npm run devSveltia CMS の編集対象 branch は main 固定です。CMS 保存は /admin/api/* の Pages Functions proxy が受け、許可済みの画像とコンテンツだけを main の同じ commit に直接保存します。
npm run build
npm run validate:content
npm run test:cms
npm run typecheck:functionsnpm run build は astro build && node scripts/audit-image-alts.mjs && pagefind --site dist を実行し、画像の代替テキストを監査してから検索インデックスを生成します。
- 管理画面:
/admin/index.html - 設定:
public/admin/config.yml - GitHub proxy:
functions/admin/api/github/[[path]].ts - GraphQL proxy:
functions/admin/api/graphql.ts - Access session:
functions/admin/api/session.ts - 認証方式: Cherry 型。編集者は Cloudflare Access で
/admin/に入り、保存は専用 GitHub App の短期 installation token を使う proxy が行います。 - Access application の Allow policy はサイト専用の
hatt-cms-editorsgroup だけを参照します。共有管理者 group やメールドメイン一括許可は使いません。 - ブログ、タグ、著者、モデリング項目、キャンペーン通知、サイト基本設定を編集できます。
- ブログ記事の
公開日は日本時間のYYYY-MM-DDTHH:mmとして扱います。 - 未来日時の記事カードと記事本文は HTML に残しつつ、訪問者のブラウザ時刻で表示を切り替えます。デプロイ後も時刻到達時に表示されます。
Cloudflare Pages のproductionだけに以下のGitHub App設定を置いてください。previewへmain書込鍵を配布してはいけません。
- Variable:
CMS_GITHUB_APP_CLIENT_ID - Variable:
CMS_GITHUB_APP_INSTALLATION_ID - Secret:
CMS_GITHUB_APP_PRIVATE_KEY(PKCS#8 PEM)
Access検証設定は必要なproduction / preview環境に設定できます。
- Optional Variable:
CMS_ACCESS_TEAM_DOMAIN=https://acecore.cloudflareaccess.com - Optional Variable:
CMS_ACCESS_AUD=044fc6624d4c84e5bcf78bc8a0ac1b505c9d2227cb6b1dba4dd6c4e10d4579d4 - Secret または Variable:
CMS_ACCESS_ALLOWED_EMAILS=editor@example.com - Variable:
CMS_ACCESS_HOSTNAMES=hatt.acecore.net,www.hatt.acecore.net,homepage-hatt.pages.dev
CMS_ACCESS_ALLOWED_EMAILS は hatt-cms-editors と同じ完全一致メールだけを production / preview の両方へ設定します。他サイト編集者、共有管理者、メールドメイン一括許可は追加しません。
CMS_ACCESS_TEAM_DOMAIN と CMS_ACCESS_AUD は上記の値を既定値として持ちます。Access application を作り直した場合だけ、新しい値で上書きしてください。
GitHub App は acecore-systems/homepage-hatt だけへインストールし、Repository permissions は Contents: Read and write、Metadata: Read-only にします。proxy は秘密鍵で9分以内のApp JWTを署名し、repositoryと権限を再指定した1時間以内のinstallation tokenを発行します。
GitHub App を新規作成または置換するときは npm run setup:cms-app を実行します。セットアップ画面では homepage-hatt だけを選択してください。補助スクリプトはAppの所有者、権限、対象repositoryが1件だけであることを検証し、秘密鍵をファイルへ保存せず、productionだけへ必要な3 secretを登録します。preview FunctionsはGitHub App設定不足で書込みをfail closedします。
- 本番 CMS の publication branch は
mainです。cms-contentのような恒久的な別本流 branch は使いません。 - CMS の保存は Pages Functions proxy が共有content schema、Markdown、raster mediaを同期検証し、許可済みの画像とコンテンツを
mainの同じcommitへ直接保存します。保存直前に照合した正確なmaincommit SHAからCMS対象を読み、同じ保存の追加・削除を反映したprojected stateで全contentを再検証します。記事のauthor・tagと/uploads/hatt/の画像参照は同じ保存で追加する対象を含めて存在確認し、欠損参照はGitHub送信前に拒否します。 - CMS textはGitHub GraphQL readで本文が省略されない448 KiB以下に限定します。author id、tag slug、記事の実効slug(frontmatter
slug、未指定時はfilename)へ共有route形式制約を適用し、tagと記事はprojected state全体で一意性も確認します。tagのindexは静的一覧routeとの衝突を避けるため予約済みです。 - CMS content collectionは各folder直下のファイルだけを許可し、下位directoryへは保存・削除・readできません。
public/uploads/hatt/**のmediaだけは下位directoryを利用できます。 - PNGは全chunkのCRC、IHDR、連結IDATのzlib展開、scanline長とfilterを確認し、JPEG / GIF / WebP / AVIFはcontainer、marker、宣言length、終端の構造を確認します。各形式のchunk、marker、sub-block、box数には上限を設け、極端な小block列を拒否します。
expectedHeadOidが現在のHEADと一致しない場合は上書きせず、再読み込みを求めます。 - 必須
src/content/site/main.json、author、tagと、コンテンツから参照され得るpublic/uploads/hatt/**はCMSから削除できません。 - 保存後はGitHub連携のCloudflare Pagesがproduction deployを開始します。CIや手動mergeの完了をCMS保存リクエスト内で待ちません。
- Functions、CMS設定、schema、workflow、サイトコードなどの変更は通常のbranch・PR・CIを通します。CMS用GitHub Appはこれらのpathへ書き込めません。
mainのrepository rulesetでは通常のPR・CI要件を維持し、repository限定のAcecore Hatt CMSAppだけをbypass actorに指定します。- Cloudflare Pages の production deploy 元は GitHub 連携の
mainにします。 - 詳細は
docs/cms-write-workflow.mdを参照してください。 - 旧 remote
cms-contentbranch は未反映差分がないことを確認して削除済みです。
src/content/campaigns/*.json を Sveltia CMS の「キャンペーン通知」から編集できます。
種別: トップ告知バナーはサイト上部に表示されます。種別: ページ内キャンペーン通知は選択した表示位置に表示されます。表示する、表示開始日時、表示終了日時で公開期間を制御します。日時は日本時間として扱われ、デプロイ済みのページ上でも訪問者の表示時刻で自動的に切り替わります。
記事ページのコメントは Cloudflare Pages Function + D1 + Turnstile で動きます。
Cloudflare Pages 側で以下を設定してください。
- D1 binding:
COMMENTS_DB - Secret:
TURNSTILE_SECRET_KEY - Secret:
COMMENT_HASH_SALT - Variable:
COMMENT_ALLOWED_HOSTNAMES=hatt.acecore.net,www.hatt.acecore.net,homepage-hatt.pages.dev
D1 schema は migrations/0001_create_blog_comments.sql です。D1 database を作成後、同ファイルを適用してください。
Turnstile の公開 Site Key は CMS の「サイト設定」から turnstileSiteKey に設定します。
/modeling-course/ から無料体験申し込みを受け付け、Cloudflare Email Sendingで通知メールを送信します。
Cloudflare Pages 側で以下を設定してください。
- Email Sending domain: 送信元ドメインをCloudflare Email Serviceにonboard
- Service binding:
COURSE_EMAIL_SERVICE->homepage-hatt-course-email - Secret:
TURNSTILE_SECRET_KEY - Variable:
COURSE_SIGNUP_EMAIL_FROM=Hattのホームページ <noreply@hatt.acecore.net> - Variable:
COURSE_SIGNUP_EMAIL_TO=borubin@outlook.jp
メールの本文には名前、連絡先、相談内容、希望日時が入ります。連絡先がメールアドレスの場合は返信先としても設定します。