13 KiB
13 KiB
LinguaForge 網頁版環境設置指南
🚀 快速開始(15分鐘)
完整指令(複製貼上即可)
# 1. 建立專案
npx create-next-app@latest linguaforge-web --typescript --tailwind --app --src-dir=false --import-alias="@/*"
cd linguaforge-web
# 2. 安裝所有套件
npm install @supabase/supabase-js @supabase/ssr @supabase/auth-helpers-nextjs
npm install zustand @tanstack/react-query @google/generative-ai
npm install next-pwa next-themes
npm install lucide-react date-fns zod
npm install -D @types/node
# 3. 安裝 shadcn/ui
npx shadcn-ui@latest init -y
# 4. 安裝常用元件
npx shadcn-ui@latest add button card dialog form input label textarea toast alert badge skeleton tabs
# 5. 建立環境變數檔
touch .env.local
# 6. 啟動開發伺服器
npm run dev
📋 前置需求檢查
必要工具
- Node.js 18+ (檢查:
node -v) - npm 或 pnpm (檢查:
npm -v) - Git (檢查:
git --version) - VS Code 或其他編輯器
必要帳號
- Supabase - 資料庫
- Google AI Studio - Gemini API
- Vercel - 部署平台
- GitHub - 版本控制
🔧 詳細設置步驟
Step 1: Supabase 設置
1.1 建立專案
- 前往 app.supabase.com
- 點擊「New project」
- 設定:
- Project name:
linguaforge - Database Password: 設定強密碼
- Region:
Southeast Asia (Singapore)(離台灣近)
- Project name:
1.2 建立資料表
在 SQL Editor 執行:
-- 啟用 UUID 擴充
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- 用戶表(使用 Supabase Auth)
CREATE TABLE profiles (
id UUID REFERENCES auth.users(id) ON DELETE CASCADE PRIMARY KEY,
username TEXT UNIQUE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc', NOW()),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc', NOW())
);
-- 詞卡表
CREATE TABLE cards (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL,
word TEXT NOT NULL,
pronunciation TEXT,
definition TEXT NOT NULL,
part_of_speech TEXT,
examples JSONB DEFAULT '[]',
source_sentence TEXT,
difficulty TEXT CHECK (difficulty IN ('beginner', 'intermediate', 'advanced')),
-- SM-2 演算法欄位
next_review_date TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc', NOW()),
easiness_factor DECIMAL(3,2) DEFAULT 2.5,
interval_days INTEGER DEFAULT 0,
repetition_count INTEGER DEFAULT 0,
created_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc', NOW()),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc', NOW()),
INDEX idx_user_cards (user_id),
INDEX idx_next_review (user_id, next_review_date)
);
-- 複習記錄表
CREATE TABLE review_logs (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
card_id UUID REFERENCES cards(id) ON DELETE CASCADE NOT NULL,
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL,
quality INTEGER CHECK (quality >= 0 AND quality <= 5) NOT NULL,
reviewed_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc', NOW()),
time_spent_seconds INTEGER,
INDEX idx_user_reviews (user_id, reviewed_at)
);
-- 啟用 Row Level Security
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
ALTER TABLE cards ENABLE ROW LEVEL SECURITY;
ALTER TABLE review_logs ENABLE ROW LEVEL SECURITY;
-- RLS 政策
CREATE POLICY "Users can view own profile" ON profiles
FOR SELECT USING (auth.uid() = id);
CREATE POLICY "Users can update own profile" ON profiles
FOR UPDATE USING (auth.uid() = id);
CREATE POLICY "Users can create own profile" ON profiles
FOR INSERT WITH CHECK (auth.uid() = id);
CREATE POLICY "Users can view own cards" ON cards
FOR SELECT USING (auth.uid() = user_id);
CREATE POLICY "Users can create own cards" ON cards
FOR INSERT WITH CHECK (auth.uid() = user_id);
CREATE POLICY "Users can update own cards" ON cards
FOR UPDATE USING (auth.uid() = user_id);
CREATE POLICY "Users can delete own cards" ON cards
FOR DELETE USING (auth.uid() = user_id);
CREATE POLICY "Users can view own reviews" ON review_logs
FOR SELECT USING (auth.uid() = user_id);
CREATE POLICY "Users can create own reviews" ON review_logs
FOR INSERT WITH CHECK (auth.uid() = user_id);
-- 觸發器:自動更新 updated_at
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = TIMEZONE('utc', NOW());
RETURN NEW;
END;
$$ language 'plpgsql';
CREATE TRIGGER update_profiles_updated_at BEFORE UPDATE ON profiles
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_cards_updated_at BEFORE UPDATE ON cards
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
1.3 取得 API Keys
- 進入 Project Settings > API
- 複製:
URL(Project URL)anon publickey
Step 2: Gemini API 設置
2.1 取得 API Key
- 前往 Google AI Studio
- 點擊「Get API key」
- 選擇或建立 Google Cloud 專案
- 複製 API Key
2.2 測試 API
curl -X POST "https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contents": [{
"parts": [{
"text": "Hello"
}]
}]
}'
Step 3: 專案配置
3.1 環境變數設置
編輯 .env.local:
# Supabase
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
# Gemini API
GEMINI_API_KEY=your-gemini-api-key
# App
NEXT_PUBLIC_APP_URL=http://localhost:3000
3.2 TypeScript 配置
更新 tsconfig.json:
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
3.3 Tailwind 配置
更新 tailwind.config.ts:
import type { Config } from 'tailwindcss'
const config: Config = {
darkMode: ["class"],
content: [
'./pages/**/*.{ts,tsx}',
'./components/**/*.{ts,tsx}',
'./app/**/*.{ts,tsx}',
],
theme: {
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
},
},
plugins: [require("tailwindcss-animate")],
}
export default config
Step 4: 建立專案結構
# 建立資料夾結構
mkdir -p app/{api,\(auth\),\(dashboard\)}
mkdir -p app/api/{gemini,cards,review}
mkdir -p app/\(auth\)/{login,register}
mkdir -p app/\(dashboard\)/{cards,generate,review,stats}
mkdir -p components/{ui,cards,layout,providers}
mkdir -p lib/{supabase,gemini,algorithms}
mkdir -p hooks
mkdir -p types
mkdir -p public/{icons,images}
Step 5: 初始化核心檔案
5.1 Supabase Client
建立 lib/supabase/client.ts:
import { createBrowserClient } from '@supabase/ssr'
export function createClient() {
return createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
}
5.2 根 Layout
更新 app/layout.tsx:
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import './globals.css'
const inter = Inter({ subsets: ['latin'] })
export const metadata: Metadata = {
title: 'LinguaForge - AI 英語詞彙學習',
description: 'AI 驅動的個人化英語詞彙學習平台',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="zh-TW">
<body className={inter.className}>{children}</body>
</html>
)
}
5.3 首頁
更新 app/page.tsx:
import Link from 'next/link'
import { Button } from '@/components/ui/button'
export default function HomePage() {
return (
<div className="flex min-h-screen flex-col items-center justify-center">
<div className="text-center space-y-6">
<h1 className="text-4xl font-bold">LinguaForge</h1>
<p className="text-xl text-muted-foreground">
AI 驅動的英語詞彙學習平台
</p>
<div className="flex gap-4 justify-center">
<Button asChild>
<Link href="/register">開始學習</Link>
</Button>
<Button variant="outline" asChild>
<Link href="/login">登入</Link>
</Button>
</div>
</div>
</div>
)
}
Step 6: 測試運行
# 啟動開發伺服器
npm run dev
# 開啟瀏覽器
open http://localhost:3000
🚀 部署到 Vercel
6.1 準備部署
# 初始化 Git
git init
git add .
git commit -m "Initial commit"
# 推送到 GitHub
git remote add origin https://github.com/your-username/linguaforge-web.git
git push -u origin main
6.2 Vercel 部署
- 前往 vercel.com
- 點擊「Import Project」
- 選擇 GitHub repo
- 設定環境變數:
NEXT_PUBLIC_SUPABASE_URLNEXT_PUBLIC_SUPABASE_ANON_KEYGEMINI_API_KEY
- 點擊「Deploy」
6.3 自訂網域(可選)
- 在 Vercel Dashboard > Settings > Domains
- 新增自訂網域
- 按照指示設定 DNS
🛠️ VS Code 設置
推薦擴充套件
{
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"bradlc.vscode-tailwindcss",
"prisma.prisma",
"ms-vscode.vscode-typescript-next",
"christian-kohler.path-intellisense",
"aaron-bond.better-comments",
"usernamehw.errorlens"
]
}
工作區設定
.vscode/settings.json:
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true,
"tailwindCSS.experimental.classRegex": [
["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"],
["cx\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"]
]
}
🧪 測試檢查清單
功能測試
- 首頁正常顯示
- Supabase 連線成功
- 可以註冊新用戶
- 可以登入
- Gemini API 可以呼叫
效能測試
# Lighthouse 測試
npm run build
npm run start
# 開啟 Chrome DevTools > Lighthouse
🐛 常見問題
1. Supabase 連線失敗
# 檢查環境變數
echo $NEXT_PUBLIC_SUPABASE_URL
echo $NEXT_PUBLIC_SUPABASE_ANON_KEY
# 確認 .env.local 有被載入
# 重啟開發伺服器
2. Gemini API 錯誤
# 檢查 API Key
curl "https://generativelanguage.googleapis.com/v1beta/models?key=YOUR_KEY"
# 檢查配額
# 前往 Google Cloud Console 查看
3. TypeScript 錯誤
# 重新生成類型
npm run build
# 清除快取
rm -rf .next
npm run dev
4. Vercel 部署失敗
# 本地測試 production build
npm run build
npm run start
# 檢查環境變數
vercel env pull
📝 下一步
環境設置完成後:
- 參考
web-mvp-master-plan.md開始開發 - 參考
web-technical-architecture.md了解技術細節 - 開始 Week 1 的任務
🎉 完成確認
如果以下都完成,你就可以開始開發了:
- ✅ 專案在 http://localhost:3000 運行
- ✅ Supabase 資料表建立完成
- ✅ Gemini API Key 測試成功
- ✅ Git repository 初始化
- ✅ 首次 commit 完成
恭喜!你的開發環境已經準備就緒!🚀
現在可以開始按照 6 週計劃開發你的 MVP 了!