プロフィール

arismmn timeline blog

← ブログ一覧に戻る

開発ブログに著者プロフィール機能を実装した

AstroCloudflareUI/UXデータベース

概要

開発ブログに「著者プロフィール」機能を追加しました。

これまで、各記事が誰によって書かれたものか分からない状態でした。今後、AIが自動生成した記事と開発者が手動で書いた記事が混在するため、読者が一目で判断できる仕組みが必要と判断しました。

実装した機能:

  • 著者プロフィールの管理(名前・アイコン・紹介文)
  • ブログ記事との紐づけ(管理画面のドロップダウンで選択)
  • 記事ページへの著者カード表示
  • デフォルト著者「arismmn」と「AI」の2種類

データベース設計

著者テーブルの作成

著者情報を管理する専用テーブルを追加しました。

カラム 説明
id INTEGER 主キー(自動採番)
slug TEXT 識別子(例: arismmn, ai)
name TEXT 表示名
icon_url TEXT アイコン画像URL
bio TEXT プロフィール文
created_at TEXT 作成日時
updated_at TEXT 更新日時

記事テーブルへの関連付け

既存の記事テーブルに author_id カラムを追加し、著者テーブルの id を参照します。

ALTER TABLE posts ADD COLUMN author_id INTEGER DEFAULT NULL;

既存レコードは NULL になるため、後方互換性を保ちながら段階的に著者を設定できます。


管理画面の実装

著者管理ページ

/admin/blog/authors/ に著者の一覧・新規作成・編集ページを追加しました。

著者の登録情報は以下の3項目です:

  1. 表示名 - ブログに表示する名前
  2. アイコン画像URL - https:// または http:// で始まるURL
  3. プロフィール文 - 記事ページに表示する紹介文(500文字以内)

アイコンURLの入力フォームには、リアルタイムのプレビュー機能を追加しています。URLを入力すると即座に表示確認できます。

セキュリティ対応

アイコンURLの入力値には2つのバリデーションを適用しています。

// 🔒 アイコンURLはhttpまたはhttpsのみ許可する
if (iconUrl && !iconUrl.startsWith('https://') && !iconUrl.startsWith('http://')) {
  errorMsg = 'アイコンURLはhttps://またはhttp://で始まるURLを入力してください。';
}

javascript: などの危険なスキームを含むURLを拒否することで、クロスサイトスクリプティング(XSS)攻撃を防止します。

著者IDの入力値についても、数値以外の入力を拒否するバリデーションを追加しています。

記事作成・編集フォームへの統合

新規作成フォームと既存記事の編集フォームに、著者を選択するドロップダウンを追加しました。

AIが生成した記事は必ず「AI」著者を選択するよう、フォーム上に注記を表示しています。


公開ブログの実装

著者プロフィールカード

記事ページの本文下部に著者プロフィールカードを表示します。

表示される情報:

  • アイコン画像(未設定時は名前の頭文字をグラデーション背景で表示)
  • 著者名
  • プロフィール文

デザインは既存のブログカードと統一感を持たせつつ、「AI」著者は紫系のグラデーションで区別しています。また、カードには光が流れるシマーアニメーションを追加し、視覚的なアクセントを加えています。

マイグレーション前の互換性

author_id カラムが存在しない状態(マイグレーション実行前)でもページが動作するよう、エラーハンドリングを実装しています。

// author_idカラムなしでリトライする(マイグレーション前の互換性対応)
try {
  const row = await db.prepare(
    "SELECT slug, title, content FROM posts WHERE slug = ?"
  ).bind(slug).first();
  // ...
} catch { /* フォールバックも失敗した場合は無視 */ }

カラムの追加も、各管理ページのロード時に自動的に試みます。既に追加済みの場合はエラーを無視します。

// blog_postsにauthor_idカラムが存在しない場合は追加する
try {
  await db.prepare("ALTER TABLE posts ADD COLUMN author_id INTEGER DEFAULT NULL").run();
} catch { /* カラムが既に存在する場合は何もしない */ }

静的Markdownファイルへの対応

既存のMarkdownファイル記事も著者機能に対応しています。フロントマターに author フィールドを追加することで著者を指定できます。

---
title: "記事タイトル"
date: 2026-03-06 08:00:00
author: arismmn
---

author フィールドに著者テーブルの slug 値を指定すると、記事ページに著者カードが表示されます。


実装時の学び

ALTER TABLEの冪等性

既存テーブルへのカラム追加は ALTER TABLE で行いますが、すでにカラムが存在する場合はエラーになります。本実装では try/catch で囲むことで冪等な処理にしています。

SQLiteでは IF NOT EXISTS 構文が ALTER TABLE に対応していないため、この方法が現実的な選択です。

ページ設計の一貫性

著者管理ページは既存の管理ページと同じ設計パターンに揃えました。

  • セッション検証ロジック
  • テーブルの自動作成(マイグレーション代替)
  • フォームのバリデーション
  • POST後のリダイレクトパターン

同じパターンで書くことで、後からコードを読む際の理解コストを下げられます。


まとめ

追加・変更したもの 内容
著者テーブル 著者プロフィールを管理するテーブルを追加
著者管理ページ 著者の一覧・作成・編集機能を追加
記事フォーム 著者選択ドロップダウンを追加
記事詳細ページ 著者プロフィールカードを表示
静的記事スキーマ author フィールドを追加

デフォルト著者として「arismmn」と「AI」の2つを登録しています。AIが自動生成した記事には必ず「AI」著者を選択することで、読者が記事の出自を正確に把握できます。

Claude Code
Powered by
Claude Code
(使用モデル Claude Opus 4.6)
← ブログ一覧に戻る