Next.js (App Router) で構築したポートフォリオサイトです。
トップページで Terminal Mode / UI Mode を切り替え、プロフィール・ブログ・Product・SNS を閲覧できます。
- ヘッダートグル:
Terminal Mode / UI Mode - 日本語UI
- ブログ連携: Zenn 投稿一覧
- Product連携: Zenn の
productタグ記事のみ表示 - 予約ページ:
meet / 対面選択、対面時は場所必須バリデーション - 予約 API: Google Calendar / Meet / Resend 対応(
mockモードあり) - レスポンシブ対応(mobile / tablet / desktop)
pnpm install
pnpm dev.env.local の例:
NEXT_PUBLIC_ZENN_USERNAME=dokkiitech
NEXT_PUBLIC_GITHUB_USERNAME=dokkiitech
GH_TOKEN=
# GH_TOKEN は GitHub API 認証用(public_repo 読み取りで可)
# private含める場合は Fine-grained PAT で Metadata: Read-only + 対象リポジトリ権限
# Zennフィード取得失敗時のフォールバック(JSON配列文字列)
# ZENN_FALLBACK_ARTICLES_JSON=[{"title":"...","link":"...","pubDate":"...","description":"...","tags":["product"]}]
ZENN_FALLBACK_ARTICLES_JSON=
# 予約バックエンド切替
# mock: モック応答
# gcp: Google Calendar + Meet + 招待 + Resend
BOOKING_BACKEND_MODE=mock
# GCP連携時に必須
GOOGLE_CALENDAR_CALENDAR_ID=
GOOGLE_SERVICE_ACCOUNT_EMAIL=
GOOGLE_PRIVATE_KEY=
GOOGLE_DELEGATED_USER_EMAIL=
# 任意(未指定時は既定値)
BOOKING_TIMEZONE=Asia/Tokyo
BOOKING_TIMEZONE_OFFSET=+09:00
BOOKING_SLOT_MINUTES=60
BOOKING_SLOT_START_HOUR=10
BOOKING_SLOT_END_HOUR=24
# Resend(予約完了メール通知)
RESEND_API_KEY=
RESEND_FROM=booking@your-domain.com
# Discord webhook(コンシェルジュ通知)
DISCORD_CONCIERGE_WEBHOOK_URL=
BOOKING_ADMIN_BASE_URL=https://your-domain.com
# 予約者専用ページ(Supabase)
SUPABASE_URL=
SUPABASE_SERVICE_ROLE_KEY=
BOOKING_PORTAL_BASE_URL=https://your-domain.com
# Vercel Cron(MTGデイリーサマリー)
CRON_SECRET=CRON_SECRET/api/cron/mtg-summaryの認証用Authorization: Bearer <CRON_SECRET>またはx-cron-secretを受け付けます
DISCORD_CONCIERGE_WEBHOOK_URL- Discord の投稿先 webhook URL
SUPABASE_URLSUPABASE_SERVICE_ROLE_KEYbooking_portalテーブルの当日・翌日分取得に使用
Supabase に接続できない場合でも、Discord には DBアクセス状況 セクション付きで失敗通知を送る実装です。
- 1st:
https://zenn.dev/{username}/feed(RSS) - 2nd:
rss2json経由のフォールバック - 3rd:
ZENN_FALLBACK_ARTICLES_JSON
/blog は全記事、/products は product タグのみ表示します。
- Endpoint:
GET /api/bookings?date=YYYY-MM-DD(空き時間照会)POST /api/bookings(予約確定)POST /api/bookings/manage/{id}/verify(管理ページ認証)POST /api/bookings/manage/{id}/password(初回パスワード設定)PATCH /api/bookings/manage/{id}(予約変更)DELETE /api/bookings/manage/{id}(予約キャンセル)
- Request:
{
"name": "山田 太郎",
"email": "taro@example.com",
"company": "株式会社サンプル",
"bookingType": "meet",
"date": "2026-03-15",
"timeSlot": "14:00",
"agenda": "新規プロダクト相談",
"location": "渋谷"
}- 仕様:
name/email/agendaは必須companyは任意bookingTypeはmeetまたは対面bookingType=対面の場合はlocation必須BOOKING_BACKEND_MODE=mockの場合はモック応答BOOKING_BACKEND_MODE=gcpの場合:- Google Calendar FreeBusy で空き照会
- 予約時に Calendar Event 作成 + ユーザーへ招待送信
meetの場合は Meet URL 自動発行対面の場合はlocationをイベント場所に設定- Resend 設定時は予約完了メールを送信
- Discord webhook 設定時は、コンシェルジュ名義のリッチ通知を Discord に送信
- attendees招待を有効にするには、Google Workspace の Domain-Wide Delegation +
GOOGLE_DELEGATED_USER_EMAILが必要
- Supabase設定済みの場合は、予約後に
managePortal.url(予約者専用ページURL)を返却 - Resendメール本文に
managePortal.urlとパスワードを同梱 - 予約者ページでの変更/キャンセルは、
gcpモード時に Google Calendar イベントにも同期
create table if not exists booking_portal (
id uuid primary key,
booking_id text not null,
name text not null,
email text not null,
company text,
booking_type text not null,
date text not null,
time_slot text not null,
agenda text not null,
location text,
status text not null default 'active',
calendar_event_id text,
calendar_event_url text,
meet_url text,
manage_token_hash text not null,
manage_password_hash text,
expires_at timestamptz not null,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now()
);- GitHub に push
- Vercel で
dokkiitech/portfolioを Import - Build Settings
- Framework Preset:
Next.js - Install Command:
pnpm install(または lockfile 準拠) - Build Command:
pnpm build
- Framework Preset:
- Environment Variables に上記
.env.localと同じ値を設定 - Deploy
- エンドポイント:
/api/cron/mtg-summary - 実行時刻: 毎日 09:00 JST
- Vercel cron の schedule:
0 0 * * *
Vercel Cron の cron 式は UTC 基準です。
09:00 JST は UTC では 00:00 のため、0 0 * * * を設定します。
vercel.json の例:
{
"crons": [
{
"path": "/api/cron/mtg-summary",
"schedule": "0 0 * * *"
}
]
}Discord には以下 3 セクションを日本語で投稿します。
今日のMTG明日のMTGDBアクセス状況
当日・翌日に有効な予約が 0 件の場合は各セクションで なし を明示します。
Supabase の env 不足やクエリ失敗時も、DBアクセス状況 に Supabase 未照会 または Supabase 取得失敗 を明記します。
管理画面 /appointment/admin には、同じサマリーを即時送信する手動実行ボタンもあります。
pnpm lint
pnpm build