diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 63bd675c..d5b9f346 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,6 +39,9 @@ jobs: - name: TypeScript (web) run: pnpm --filter @nepp-chan/web exec tsc --noEmit + - name: TypeScript (lp) + run: pnpm --filter @nepp-chan/lp exec tsc --noEmit + test: name: Test runs-on: ubuntu-latest diff --git a/.github/workflows/deploy-dev.yml b/.github/workflows/deploy-dev.yml index 78bbfd47..68a977d2 100644 --- a/.github/workflows/deploy-dev.yml +++ b/.github/workflows/deploy-dev.yml @@ -61,7 +61,33 @@ jobs: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} PUBLIC_API_URL: https://dev-api.nepp-chan.ai + PUBLIC_LP_URL: https://dev-lp.nepp-chan.ai PUBLIC_SENTRY_DSN: ${{ secrets.SENTRY_DSN_WEB }} SENTRY_ORG: ${{ secrets.SENTRY_ORG }} SENTRY_PROJECT_WEB: ${{ secrets.SENTRY_PROJECT_WEB }} SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + + deploy-lp: + name: Deploy LP + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + with: + version: 10 + + - uses: actions/setup-node@v4 + with: + node-version: 24 + cache: "pnpm" + + - run: pnpm install --frozen-lockfile + + - name: Deploy LP + run: pnpm lp:deploy:dev + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + PUBLIC_API_URL: https://dev-api.nepp-chan.ai + PUBLIC_WEB_URL: https://dev-web.nepp-chan.ai diff --git a/.github/workflows/deploy-prd.yml b/.github/workflows/deploy-prd.yml index 94fcd671..b440b12f 100644 --- a/.github/workflows/deploy-prd.yml +++ b/.github/workflows/deploy-prd.yml @@ -63,7 +63,34 @@ jobs: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} PUBLIC_API_URL: https://api.nepp-chan.ai + PUBLIC_LP_URL: https://nepp-chan.ai PUBLIC_SENTRY_DSN: ${{ secrets.SENTRY_DSN_WEB }} SENTRY_ORG: ${{ secrets.SENTRY_ORG }} SENTRY_PROJECT_WEB: ${{ secrets.SENTRY_PROJECT_WEB }} SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + + deploy-lp: + name: Deploy LP + runs-on: ubuntu-latest + environment: production + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + with: + version: 10 + + - uses: actions/setup-node@v4 + with: + node-version: 24 + cache: "pnpm" + + - run: pnpm install --frozen-lockfile + + - name: Deploy LP + run: pnpm lp:deploy:prd + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + PUBLIC_API_URL: https://api.nepp-chan.ai + PUBLIC_WEB_URL: https://web.nepp-chan.ai diff --git a/.tagpr b/.tagpr index 740885ad..c5e321a3 100644 --- a/.tagpr +++ b/.tagpr @@ -3,6 +3,6 @@ [tagpr] vPrefix = true releaseBranch = develop -versionFile = package.json,server/package.json,web/package.json +versionFile = package.json,server/package.json,web/package.json,lp/package.json release = true changelog = true diff --git a/CLAUDE.md b/CLAUDE.md index 0f20b9d2..24b656d1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -9,6 +9,7 @@ Cloudflare Workers(API)+ Pages(Web)のモノレポ構成。 # 開発 pnpm server:dev # API 開発サーバー(8787) pnpm web:dev # Web 開発サーバー(5173) +pnpm lp:dev # LP 開発サーバー(5174) # 品質チェック pnpm lint # Biome + astro check + tsc @@ -30,7 +31,8 @@ pnpm knowledge:upload:prd # prd 環境 ```text server/ → API(詳細: server/CLAUDE.md) -web/ → フロントエンド(詳細: web/CLAUDE.md) +web/ → アプリ(チャット・ダッシュボード等)(詳細: web/CLAUDE.md) +lp/ → LP(apex 配信の静的サイト) knowledge/ → RAG 用 Markdown ファイル ``` @@ -128,11 +130,11 @@ wrangler secret put GOOGLE_GENERATIVE_AI_API_KEY ## デプロイ環境 -| 環境 | Web | API | -| ---- | --- | --- | -| ローカル | http://localhost:5173 | http://localhost:8787 | -| dev | https://dev-web.nepp-chan.ai | https://dev-api.nepp-chan.ai | -| prd | https://web.nepp-chan.ai | https://api.nepp-chan.ai | +| 環境 | LP | Web | API | +| ---- | --- | --- | --- | +| ローカル | http://localhost:5174 | http://localhost:5173 | http://localhost:8787 | +| dev | https://dev-lp.nepp-chan.ai | https://dev-web.nepp-chan.ai | https://dev-api.nepp-chan.ai | +| prd | https://nepp-chan.ai | https://web.nepp-chan.ai | https://api.nepp-chan.ai | ## ブランチ diff --git a/biome.json b/biome.json index 4bdbaed2..05cc3ef0 100644 --- a/biome.json +++ b/biome.json @@ -11,6 +11,7 @@ "**", "!**/node_modules", "!**/dist", + "!**/.astro", "!**/.mastra", "!**/.wrangler", "!**/*.d.ts", diff --git a/lp/.env.example b/lp/.env.example new file mode 100644 index 00000000..c1640dd0 --- /dev/null +++ b/lp/.env.example @@ -0,0 +1,2 @@ +PUBLIC_API_URL=http://localhost:8787 +PUBLIC_WEB_URL=http://localhost:5173 diff --git a/lp/.gitignore b/lp/.gitignore new file mode 100644 index 00000000..a547bf36 --- /dev/null +++ b/lp/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/lp/astro.config.ts b/lp/astro.config.ts new file mode 100644 index 00000000..43f082fa --- /dev/null +++ b/lp/astro.config.ts @@ -0,0 +1,17 @@ +import react from "@astrojs/react"; +import tailwindcss from "@tailwindcss/vite"; +import { defineConfig } from "astro/config"; + +export default defineConfig({ + output: "static", + server: { port: 5174 }, + integrations: [react()], + vite: { + plugins: [tailwindcss()], + resolve: { + alias: { + "~": "/src", + }, + }, + }, +}); diff --git a/lp/functions/_middleware.ts b/lp/functions/_middleware.ts new file mode 100644 index 00000000..951ad8fa --- /dev/null +++ b/lp/functions/_middleware.ts @@ -0,0 +1,23 @@ +interface Env { + BASIC_AUTH_USER: string; + BASIC_AUTH_PASSWORD: string; +} + +export const onRequest: PagesFunction = async (context) => { + const { BASIC_AUTH_USER, BASIC_AUTH_PASSWORD } = context.env; + + // Basic 認証 + if (BASIC_AUTH_USER && BASIC_AUTH_PASSWORD) { + const auth = context.request.headers.get("Authorization"); + const expected = `Basic ${btoa(`${BASIC_AUTH_USER}:${BASIC_AUTH_PASSWORD}`)}`; + + if (auth !== expected) { + return new Response("Unauthorized", { + status: 401, + headers: { "WWW-Authenticate": 'Basic realm="Secure Area"' }, + }); + } + } + + return context.next(); +}; diff --git a/lp/package.json b/lp/package.json new file mode 100644 index 00000000..4e658af5 --- /dev/null +++ b/lp/package.json @@ -0,0 +1,37 @@ +{ + "name": "@nepp-chan/lp", + "private": true, + "version": "0.4.3", + "type": "module", + "scripts": { + "dev": "astro dev", + "build": "astro build", + "check": "astro check", + "preview": "astro preview", + "deploy": "pnpm build && wrangler pages deploy --project-name nepp-chan-lp-dev --branch develop", + "deploy:prd": "pnpm build && wrangler pages deploy --project-name nepp-chan-lp-prd --branch main" + }, + "dependencies": { + "@ai-sdk/react": "^2.0.109", + "@astrojs/check": "^0.9.8", + "@astrojs/react": "^5.0.1", + "ai": "^5.0.108", + "astro": "^6.1.3", + "clsx": "^2.1.1", + "lucide-react": "^0.562.0", + "react": "^19.2.4", + "react-dom": "^19.2.4", + "react-markdown": "^10.1.0", + "remark-gfm": "^4.0.1", + "tailwind-merge": "^3.4.0" + }, + "devDependencies": { + "@tailwindcss/vite": "^4.2.2", + "@types/node": "^25.5.0", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "tailwindcss": "^4.1.10", + "typescript": "~5.9.3", + "wrangler": "^4.76.0" + } +} diff --git a/lp/public/bg-winter.png b/lp/public/bg-winter.png new file mode 100644 index 00000000..104b4799 Binary files /dev/null and b/lp/public/bg-winter.png differ diff --git a/lp/public/favicon-chat.svg b/lp/public/favicon-chat.svg new file mode 100644 index 00000000..74c5fb81 --- /dev/null +++ b/lp/public/favicon-chat.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/lp/public/logo-neppu.png b/lp/public/logo-neppu.png new file mode 100644 index 00000000..c22860b6 Binary files /dev/null and b/lp/public/logo-neppu.png differ diff --git a/web/public/lp/icon_line.png b/lp/public/lp/icon_line.png similarity index 100% rename from web/public/lp/icon_line.png rename to lp/public/lp/icon_line.png diff --git a/web/public/lp/neppuchan-fullbody.png b/lp/public/lp/neppuchan-fullbody.png similarity index 100% rename from web/public/lp/neppuchan-fullbody.png rename to lp/public/lp/neppuchan-fullbody.png diff --git a/web/public/lp/otoineppu-illust.png b/lp/public/lp/otoineppu-illust.png similarity index 100% rename from web/public/lp/otoineppu-illust.png rename to lp/public/lp/otoineppu-illust.png diff --git a/lp/public/mascot/expr-content.png b/lp/public/mascot/expr-content.png new file mode 100644 index 00000000..3482fd28 Binary files /dev/null and b/lp/public/mascot/expr-content.png differ diff --git a/lp/public/mascot/expr-laugh.png b/lp/public/mascot/expr-laugh.png new file mode 100644 index 00000000..0d335d90 Binary files /dev/null and b/lp/public/mascot/expr-laugh.png differ diff --git a/lp/public/mascot/expr-pout.png b/lp/public/mascot/expr-pout.png new file mode 100644 index 00000000..b9e9eb1a Binary files /dev/null and b/lp/public/mascot/expr-pout.png differ diff --git a/lp/public/mascot/expr-shy.png b/lp/public/mascot/expr-shy.png new file mode 100644 index 00000000..9ffdbd0b Binary files /dev/null and b/lp/public/mascot/expr-shy.png differ diff --git a/lp/public/mascot/expr-startled.png b/lp/public/mascot/expr-startled.png new file mode 100644 index 00000000..c3e2097c Binary files /dev/null and b/lp/public/mascot/expr-startled.png differ diff --git a/lp/public/mascot/expr-surprise.png b/lp/public/mascot/expr-surprise.png new file mode 100644 index 00000000..b8d6eefd Binary files /dev/null and b/lp/public/mascot/expr-surprise.png differ diff --git a/lp/public/mascot/expr-wave-smile.png b/lp/public/mascot/expr-wave-smile.png new file mode 100644 index 00000000..80250c56 Binary files /dev/null and b/lp/public/mascot/expr-wave-smile.png differ diff --git a/lp/public/mascot/expr-zzz.png b/lp/public/mascot/expr-zzz.png new file mode 100644 index 00000000..44fa7ede Binary files /dev/null and b/lp/public/mascot/expr-zzz.png differ diff --git a/lp/public/mascot/pose-banzai.png b/lp/public/mascot/pose-banzai.png new file mode 100644 index 00000000..5e4f3367 Binary files /dev/null and b/lp/public/mascot/pose-banzai.png differ diff --git a/lp/public/mascot/pose-fullbody-v2.png b/lp/public/mascot/pose-fullbody-v2.png new file mode 100644 index 00000000..3787bed6 Binary files /dev/null and b/lp/public/mascot/pose-fullbody-v2.png differ diff --git a/lp/public/mascot/pose-fullbody-v3.png b/lp/public/mascot/pose-fullbody-v3.png new file mode 100644 index 00000000..1891d06c Binary files /dev/null and b/lp/public/mascot/pose-fullbody-v3.png differ diff --git a/lp/public/mascot/pose-nap-curled.png b/lp/public/mascot/pose-nap-curled.png new file mode 100644 index 00000000..5dcaaf4c Binary files /dev/null and b/lp/public/mascot/pose-nap-curled.png differ diff --git a/lp/public/mascot/pose-point.png b/lp/public/mascot/pose-point.png new file mode 100644 index 00000000..64470872 Binary files /dev/null and b/lp/public/mascot/pose-point.png differ diff --git a/lp/public/mascot/pose-seated.png b/lp/public/mascot/pose-seated.png new file mode 100644 index 00000000..c5e82f62 Binary files /dev/null and b/lp/public/mascot/pose-seated.png differ diff --git a/lp/public/mascot/pose-shy-stand.png b/lp/public/mascot/pose-shy-stand.png new file mode 100644 index 00000000..ccc81754 Binary files /dev/null and b/lp/public/mascot/pose-shy-stand.png differ diff --git a/lp/public/mascot/pose-sleeping.png b/lp/public/mascot/pose-sleeping.png new file mode 100644 index 00000000..2032fae2 Binary files /dev/null and b/lp/public/mascot/pose-sleeping.png differ diff --git a/lp/public/mascot/pose-stretch.png b/lp/public/mascot/pose-stretch.png new file mode 100644 index 00000000..ee7ed12a Binary files /dev/null and b/lp/public/mascot/pose-stretch.png differ diff --git a/lp/public/mascot/pose-wave.png b/lp/public/mascot/pose-wave.png new file mode 100644 index 00000000..a795b0e9 Binary files /dev/null and b/lp/public/mascot/pose-wave.png differ diff --git a/web/src/app/lp/LpPage.tsx b/lp/src/app/lp/LpPage.tsx similarity index 100% rename from web/src/app/lp/LpPage.tsx rename to lp/src/app/lp/LpPage.tsx diff --git a/web/src/app/lp/components/FinalCTA.tsx b/lp/src/app/lp/components/FinalCTA.tsx similarity index 96% rename from web/src/app/lp/components/FinalCTA.tsx rename to lp/src/app/lp/components/FinalCTA.tsx index d22b5fa6..971f0312 100644 --- a/web/src/app/lp/components/FinalCTA.tsx +++ b/lp/src/app/lp/components/FinalCTA.tsx @@ -1,4 +1,5 @@ import { MessageCircleIcon } from "lucide-react"; +import { WEB_URL } from "~/constants/urls"; import { useScrollReveal } from "~/hooks/useScrollReveal"; import { LineIcon } from "./LineIcon"; @@ -22,7 +23,7 @@ export const FinalCTA = () => {