Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/nomikai-ai-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: nomikai-ai CI

on:
push:
branches: [main, "cursor/**"]
paths:
- "nomikai-ai/**"
pull_request:
paths:
- "nomikai-ai/**"

defaults:
run:
working-directory: nomikai-ai

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: npm
cache-dependency-path: nomikai-ai/package-lock.json
- run: npm ci
- run: npm run lint
- run: npm run build
31 changes: 31 additions & 0 deletions .github/workflows/nomikai-ai-deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Deploy nomikai-ai to Render

on:
push:
branches: [main]
paths:
- "nomikai-ai/**"
- ".github/workflows/nomikai-ai-deploy.yml"

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Install Render CLI
run: |
curl -L https://github.com/render-oss/cli/releases/download/v2.21.0/cli_2.21.0_linux_amd64.zip -o render.zip
unzip render.zip
sudo mv cli_v2.21.0 /usr/local/bin/render

- name: Trigger deploy
env:
RENDER_API_KEY: ${{ secrets.RENDER_API_KEY }}
SERVICE_ID: ${{ secrets.NOMAIKAI_AI_RENDER_SERVICE_ID }}
CI: true
run: |
if [ -z "$RENDER_API_KEY" ] || [ -z "$SERVICE_ID" ]; then
echo "Render secrets not configured — skipping deploy"
echo "Set RENDER_API_KEY and NOMAIKAI_AI_RENDER_SERVICE_ID in repo Secrets"
exit 0
fi
render deploys create "$SERVICE_ID" --output json --confirm
32 changes: 32 additions & 0 deletions business-ops/briefs/nomikai-ai-brief.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# 飲み会盛り上げAI — プロジェクトブリーフ

| 項目 | 記入 |
|------|------|
| プロジェクト名 | nomikai-ai |
| 一言説明 | 幹事がリンクを共有するだけで、全員の予定・中間地点・店・盛り上げプランがスマホで見られる飲み会調整アプリ |

## 目的

- 飲み会の幹事が LINE 等で URL を共有し、参加者がログインなしで予定・最寄駅を入力
- 全員の中間地点・店舗候補・盛り上げプランを自動生成
- スマホから閲覧・コピーできる

## MVP機能

1. イベント作成 & 参加者入力(共有リンク方式)
2. 中間地点 & 店舗候補(HeartRails API + ルールベース)
3. 盛り上げ AI プラン(ルールベース、コピー UI)

## 技術選定

| 項目 | 選択 |
|------|------|
| フレームワーク | Next.js 16 + TS + Tailwind |
| DB | SQLite (better-sqlite3) |
| 参考プロジェクト | coupon-board, optimal-match |

## スコープ外

- ログイン・認証
- LLM / Google Maps API(Phase 2)
- プッシュ通知
29 changes: 29 additions & 0 deletions nomikai-ai/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# dependencies
node_modules
npm-debug.log*

# build
.next
out

# git
.git
.gitignore

# env (inject at runtime)
.env*
!.env.example

# data (mount as volume)
data

# docs / dev
README.md
*.md
.claude
AGENTS.md

# docker
Dockerfile
docker-compose.yml
.dockerignore
9 changes: 9 additions & 0 deletions nomikai-ai/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
node_modules/
.next/
dist/
build/
.env
.env.local
*.log
.DS_Store
data/
5 changes: 5 additions & 0 deletions nomikai-ai/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<!-- BEGIN:nextjs-agent-rules -->
# This is NOT the Next.js you know

This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code.
<!-- END:nextjs-agent-rules -->
84 changes: 84 additions & 0 deletions nomikai-ai/BASIC_DESIGN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# 飲み会盛り上げAI 基本設計 v1.2

> 要件: [REQUIREMENTS.md](./REQUIREMENTS.md)

## システム構成

```mermaid
flowchart TB
subgraph Client["クライアント(スマホブラウザ)"]
UI[Next.js Pages]
end
subgraph Server["サーバー"]
API[API Routes]
Lib[geo / venue / boost / push / oauth]
DB[(SQLite)]
end
subgraph External["外部"]
HR[HeartRails Express API]
Google[Google OAuth / Places]
LLM[OpenAI / Anthropic]
end
UI --> API
API --> Lib
API --> DB
Lib --> HR
Lib --> Google
Lib --> LLM
```

## 画面一覧

| パス | 説明 |
|------|------|
| `/` | トップ・サービス説明 |
| `/create` | イベント作成フォーム(想定参加人数) |
| `/e/[slug]` | イベント詳細・プラン表示・幹事操作 |
| `/e/[slug]/join` | 参加者入力フォーム |
| `/e/[slug]/edit` | 参加者の回答編集(participant_token) |
| `/login` | 幹事ログイン(メール / Google / LINE / Apple) |
| `/signup` | 幹事新規登録 |
| `/my` | マイページ(ログイン幹事のイベント一覧) |
| `/terms` | 利用規約 |
| `/privacy` | プライバシーポリシー |

## i18n 方針

- locale: `ja` / `en`
- `?lang=en` を付与すると middleware が `lang` cookie を保存
- サーバーコンポーネントは cookie から locale を解決
- 主要UIで `withLang()` を用いてリンクに言語を維持

## データモデル

- **users / sessions**: 幹事アカウント(任意ログイン、google_id / line_id / apple_id 対応)
- **events**: イベント本体(slug, edit_token, organizer_user_id, expires_at, expected_participant_count, all_answered_notified_at)
- **participants**: 参加者(名前, 最寄駅, participant_token)
- **plans**: 生成プラン(中間駅, 店候補 JSON, 盛り上げ JSON)
- **push_subscriptions**: 幹事向け(UNIQUE event_id + endpoint)
- **participant_push_subscriptions**: 参加者向け(UNIQUE participant_id + endpoint)

## API

| Method | Path | 用途 |
|--------|------|------|
| POST | `/api/events` | イベント作成 |
| GET | `/api/events/[slug]` | イベント取得 |
| POST | `/api/events/[slug]/join` | 参加者追加 |
| POST | `/api/events/[slug]/plan` | プラン生成(edit_token 必須) |
| POST | `/api/events/[slug]/clone` | イベント複製 |
| PUT | `/api/events/[slug]/participants/[id]` | 参加者更新(participant_token) |
| DELETE | `/api/events/[slug]/participants/[id]` | 参加者削除 |
| DELETE | `/api/events/[slug]` | イベント削除(edit_token) |
| GET/POST | `/api/push/subscribe` | Web Push 購読 |
| GET | `/api/auth/google` | Google OAuth 開始 |
| GET | `/api/auth/google/callback` | Google OAuth コールバック |
| GET | `/api/auth/line` | LINE OAuth 開始 |
| GET | `/api/auth/line/callback` | LINE OAuth コールバック |
| GET | `/api/auth/apple` | Apple Sign In 開始 |
| GET | `/api/auth/apple/callback` | Apple Sign In コールバック |
| GET | `/api/og/[slug]` | OGP 画像生成 |
| POST | `/api/auth/signup` | 幹事登録 |
| POST | `/api/auth/login` | ログイン |
| GET | `/api/auth/me` | セッション確認 |
| GET | `/api/health` | ヘルスチェック |
82 changes: 82 additions & 0 deletions nomikai-ai/DEPLOY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# 本番デプロイ手順 — 飲み会盛り上げAI

## 前提

- Render アカウント(https://render.com)
- **課金:** Starter プラン + 1GB ディスクで月額約 $7〜10(要確認)
- SQLite は `/app/data` に永続化(ディスク必須)

## 方法A: Render Blueprint(推奨)

1. Render Dashboard → **New** → **Blueprint**
2. このリポジトリを接続(Root Directory: `nomikai-ai`)
3. `nomikai-ai/render.yaml` を検出してデプロイ
4. 環境変数を設定(下表参照)
5. デプロイ完了後、公開 URL で動作確認

## 方法B: Docker 手動

```bash
cd nomikai-ai
docker build -t nomikai-ai .
docker run -p 3000:3000 -v nomikai-data:/app/data nomikai-ai
```

## 方法C: GitHub Actions(既存シークレット)

リポジトリ Secrets に以下を設定:

| Secret | 内容 |
|--------|------|
| `RENDER_API_KEY` | Render API キー |
| `NOMAIKAI_AI_RENDER_SERVICE_ID` | nomikai-ai サービス ID |

`main` マージで `.github/workflows/nomikai-ai-deploy.yml` が自動デプロイ。

## デプロイ後チェック

```bash
PORT=3000 bash scripts/verify-prod.sh
```

- [ ] `/api/health` が `{"status":"ok"}`
- [ ] トップ → 飲み会作成 → 参加登録 → プラン生成
- [ ] スマホでホーム画面に追加(PWA)
- [ ] データが再起動後も残る(ディスクマウント確認)

## 環境変数(Render Dashboard)

| 変数 | 必須 | 用途 |
|------|------|------|
| `APP_URL` | 推奨 | 公開 URL(プッシュ通知・絶対リンク) |
| `OPENAI_API_KEY` | 任意 | AI 盛り上げ(OpenAI) |
| `ANTHROPIC_API_KEY` | 任意 | AI 盛り上げ(Anthropic) |
| `GOOGLE_MAPS_API_KEY` | 任意 | Places 店舗検索 |
| `NEXT_PUBLIC_GOOGLE_MAPS_API_KEY` | 任意 | 地図 Embed |
| `VAPID_PUBLIC_KEY` | 任意 | Web Push 公開鍵 |
| `VAPID_PRIVATE_KEY` | 任意 | Web Push 秘密鍵 |
| `NEXT_PUBLIC_VAPID_PUBLIC_KEY` | 任意 | クライアント用公開鍵 |
| `VAPID_SUBJECT` | 任意 | `mailto:you@example.com` |
| `GOOGLE_CLIENT_ID` | 任意 | Google OAuth |
| `GOOGLE_CLIENT_SECRET` | 任意 | Google OAuth |

初回セットアップの詳細は [RENDER_SETUP.md](./RENDER_SETUP.md) を参照。

VAPID 生成:

```bash
npx web-push generate-vapid-keys
```

## 注意

- **無料プラン**はディスク非永続のため非推奨(データ消失)
- 本番では HTTPS が Render により自動付与
- API キー未設定時はテンプレートベースで動作

## ローカル本番相当

```bash
npm run build
npm run start
```
49 changes: 49 additions & 0 deletions nomikai-ai/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# syntax=docker/dockerfile:1

FROM node:20-bookworm-slim AS base
WORKDIR /app

FROM base AS deps
RUN apt-get update && apt-get install -y --no-install-recommends \
python3 make g++ \
&& rm -rf /var/lib/apt/lists/*
COPY package.json package-lock.json ./
RUN npm ci

FROM base AS builder
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
RUN npm run build

FROM base AS runner
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
ENV PORT=3000
ENV HOSTNAME=0.0.0.0

RUN apt-get update && apt-get install -y --no-install-recommends \
libsqlite3-0 \
&& rm -rf /var/lib/apt/lists/*

RUN addgroup --system --gid 1001 nodejs \
&& adduser --system --uid 1001 --ingroup nodejs nextjs

RUN mkdir -p /app/data && chown -R nextjs:nodejs /app/data

COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
COPY --from=deps --chown=nextjs:nodejs /app/node_modules/better-sqlite3 ./node_modules/better-sqlite3
COPY --from=deps --chown=nextjs:nodejs /app/node_modules/bindings ./node_modules/bindings
COPY --from=deps --chown=nextjs:nodejs /app/node_modules/file-uri-to-path ./node_modules/file-uri-to-path

USER nextjs
EXPOSE 3000

VOLUME ["/app/data"]

HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
CMD node -e "fetch('http://127.0.0.1:3000/api/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"

CMD ["node", "server.js"]
30 changes: 30 additions & 0 deletions nomikai-ai/IMPLEMENTATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# 飲み会盛り上げAI 実装対応表

**ステータス: Phase 9 完了**

## 機能 → ファイル

| 機能 ID | 機能 | ページ | API | Lib / コンポーネント |
|---------|------|--------|-----|---------------------|
| F21 | Google OAuth | `/login`, `/signup` | `/api/auth/google`, `.../callback` | `oauth.ts`, `GoogleSignInButton.tsx` |
| F28 | LINE ログイン | `/login`, `/signup` | `/api/auth/line`, `.../callback` | `line-oauth.ts` |
| F29 | Apple Sign In | `/login`, `/signup` | `/api/auth/apple`, `.../callback` | `apple-oauth.ts` |
| F30 | 多言語対応 (ja/en) | `/`, `/create`, `/login`, `/signup`, `/my`, `/e/*`, `/terms`, `/privacy` | `middleware.ts` (`lang` cookie) | `i18n.ts`, `constants.ts` (locale helpers), `LanguageSwitcher.tsx` |
| F25 | 全員回答済み通知 | `/create` | `join`, `PUT .../participants/[id]` | `notification-service.ts` |
| F26 | イベント OGP | `/e/[slug]` | `GET /api/og/[slug]` | `e/[slug]/page.tsx` |
| F27 | PWA アイコン | — | — | `public/icons/` |

(Phase 1〜5 の対応表は前バージョンと同様)

## 起動

```bash
cd nomikai-ai
npm install
npm run dev
```

## 本番デプロイ

- [DEPLOY.md](./DEPLOY.md) — デプロイ方法概要
- [RENDER_SETUP.md](./RENDER_SETUP.md) — Render 初回セットアップチェックリスト
Loading
Loading