feat: 支持批量上传关卡

This commit is contained in:
richarjiang
2026-05-01 08:44:56 +08:00
parent f3f27def2b
commit 66a9ee2950
27 changed files with 5262 additions and 515 deletions

944
PROJECT_ANALYSIS.md Normal file
View File

@@ -0,0 +1,944 @@
# MemeStudio Server Project - Comprehensive Analysis
## 1. PROJECT OVERVIEW
**Project Name**: Meme Studio (谐音梗小游戏运营平台)
**Description**: A homophone pun game operation platform built with Next.js 14 for level configuration management
**Current Version**: 0.1.0
**Key Purpose**: Management platform for game levels, users, and mini-program users
---
## 2. FRAMEWORK & TECH STACK
### Core Framework
- **Next.js**: v14.2.28 (App Router)
- **Runtime**: Node.js (Standalone output mode)
- **Language**: TypeScript 5.8.2
- **Build Output**: `output: 'standalone'` (for server deployment)
### Authentication & Database
- **Auth System**: Better Auth v1.2.7
- Method: Email/Password with Prisma adapter
- Session Duration: 7 days
- Session Update Age: 1 day
- Database Provider: MySQL via Prisma
- **ORM**: Prisma v6.5.0
- **Database**: MySQL
- **Adapter**: Better Auth Prisma adapter
### UI & Styling
- **UI Components**: shadcn/ui (Radix UI based)
- **CSS Framework**: Tailwind CSS v3.4.17
- **Form Handling**: react-hook-form v7.54.2 + Zod validation
- **State Management**: TanStack React Query v5.69.0
- **Drag & Drop**: @dnd-kit (core v6.3.1 + sortable v10.0.0)
- **Icons**: lucide-react v0.483.0
### File Storage & CDN
- **Cloud Storage**: Tencent Cloud COS (Object Storage Service)
- **SDK**:
- qcloud-cos-sts v3.1.1 (For temporary credentials)
- cos-nodejs-sdk-v5 v2.14.0 (Server-side)
- cos-js-sdk-v5 v1.10.1 (Client-side)
### Utilities
- **UUID Generation**: uuid v11.1.0
- **Password Hashing**: bcryptjs v3.0.2 + better-auth/crypto
- **Form Validation**: Zod v3.24.2
---
## 3. PROJECT STRUCTURE
```
MemeStudio/
├── app/ # Next.js App Router
│ ├── (auth)/ # Route group: No sidebar (login page)
│ │ └── login/
│ │ └── page.tsx
│ ├── (dashboard)/ # Route group: With sidebar (protected)
│ │ ├── layout.tsx
│ │ ├── levels/
│ │ │ └── page.tsx
│ │ ├── users/
│ │ │ └── page.tsx
│ │ └── wx-users/
│ │ └── page.tsx
│ ├── api/ # API Routes
│ │ ├── auth/[...all]/
│ │ │ └── route.ts # Better Auth endpoints
│ │ ├── levels/
│ │ │ ├── route.ts # CRUD endpoints
│ │ │ └── reorder/
│ │ │ └── route.ts # Batch update sort order
│ │ ├── cos/
│ │ │ └── temp-key/
│ │ │ └── route.ts # Tencent COS credentials
│ │ ├── users/
│ │ │ └── route.ts # User management CRUD
│ │ └── wx-users/
│ │ ├── route.ts # Mini-program user list
│ │ ├── [id]/
│ │ │ └── route.ts # Single user details
│ │ └── level-progress/
│ │ └── route.ts # Batch delete progress
│ ├── layout.tsx # Root layout
│ ├── page.tsx # Home page (redirects to /levels)
│ └── providers.tsx # Client providers
├── components/ # React Components
│ ├── layout/
│ │ ├── header.tsx # Header with session info
│ │ └── sidebar.tsx # Navigation sidebar
│ ├── levels/
│ │ ├── level-list.tsx # Drag-and-drop list
│ │ ├── level-card.tsx # Individual level card
│ │ ├── level-dialog.tsx # Create/edit dialog
│ │ └── image-uploader.tsx # COS image upload
│ ├── users/
│ │ └── user-dialog.tsx # User create/edit dialog
│ ├── wx-users/
│ │ └── wx-user-detail-dialog.tsx # Mini-program user details
│ └── ui/ # shadcn/ui components
│ ├── button.tsx
│ ├── input.tsx
│ ├── label.tsx
│ ├── dialog.tsx
│ ├── card.tsx
│ ├── textarea.tsx
│ └── spinner.tsx
├── lib/ # Utilities & Helpers
│ ├── auth.ts # Better Auth configuration
│ ├── auth-client.ts # Client-side auth hooks
│ ├── prisma.ts # Prisma singleton
│ ├── cos.ts # Tencent COS utilities
│ ├── api.ts # apiFetch helper with basePath
│ └── utils.ts # General utilities (cn for Tailwind)
├── types/ # TypeScript interfaces
│ └── index.ts # All data models
├── prisma/
│ ├── schema.prisma # Database schema
│ └── seed.ts # Seed script for admin user
├── public/ # Static assets
├── middleware.ts # Session validation middleware
├── next.config.js # Next.js configuration
├── tsconfig.json # TypeScript config
├── tailwind.config.ts # Tailwind CSS config
├── package.json # Dependencies
├── deploy.sh # Deployment script
└── ecosystem.config.js # PM2 configuration
```
---
## 4. DATABASE SCHEMA (Prisma)
### Models Overview
#### 1. **Level** (Game Levels)
```prisma
model Level {
id String @id @default(uuid())
imageUrl String @map("image_url")
answer String
hint1 String?
hint2 String?
hint3 String?
sortOrder Int @default(0) @map("sort_order")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
userProgress WxUserLevelProgress[] # Relationship to progress
}
```
- Stores game level information
- Each level has one image, one answer, and up to 3 hints
- `sortOrder` is used for drag-and-drop reordering
- Related to WxUserLevelProgress (one-to-many)
#### 2. **User** (Admin/Portal Users)
```prisma
model User {
id String @id @default(uuid())
email String @unique
emailVerified Boolean @default(false)
name String?
image String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
sessions Session[]
accounts Account[]
}
```
- Better Auth user model
- Email-password authentication
- Can have multiple sessions and accounts
#### 3. **Session** (Auth Sessions)
```prisma
model Session {
id String @id
expiresAt DateTime
token String @unique
ipAddress String?
userAgent String?
userId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
```
- Better Auth session model
- Stores session tokens with IP and user agent info
- 7-day expiration
#### 4. **Account** (Auth Accounts)
```prisma
model Account {
id String @id @default(uuid())
accountId String
providerId String
userId String
accessToken String?
refreshToken String?
idToken String?
accessTokenExpires DateTime?
refreshTokenExpires DateTime?
scope String?
password String? # Hash for email/password provider
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
```
- Better Auth account model
- Supports multiple auth providers
- Stores hashed passwords for credential provider
#### 5. **Verification** (Email Verification)
```prisma
model Verification {
id String @id @default(uuid())
identifier String
value String
expiresAt DateTime
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
```
- Better Auth verification model for email verification tokens
#### 6. **WxUser** (Mini-Program Users)
```prisma
model WxUser {
id String @id @default(uuid())
openid String @unique
sessionKey String?
nickname String?
avatarUrl String?
points Int @default(10)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
levelProgress WxUserLevelProgress[]
}
```
- Users from WeChat mini-program
- OpenID uniquely identifies users (WeChat standard)
- Stores user points/score
- Related to level progress
#### 7. **WxUserLevelProgress** (User Progress Tracking)
```prisma
model WxUserLevelProgress {
id String @id @default(uuid())
userId String
levelId String
completedAt DateTime @default(now())
user WxUser @relation(fields: [userId], references: [id], onDelete: Cascade)
level Level @relation(fields: [levelId], references: [id])
}
```
- Tracks which levels each WeChat user has completed
- Stores completion timestamp
- Composite relationship between WxUser and Level
### Database Configuration
- **Provider**: MySQL
- **Connection String**: Defined via `DATABASE_URL` env var
- **Targets**: `["native", "rhel-openssl-3.0.x"]` for cross-platform compatibility (macOS dev → Linux server)
---
## 5. API ROUTES - COMPREHENSIVE DOCUMENTATION
### Authentication Routes
#### `POST /api/auth/[...all]`
- **Handler**: Better Auth routes
- **Auth**: Yes (Better Auth handles internally)
- **Purpose**: All authentication endpoints
- **Endpoints Provided**:
- `POST /api/auth/sign-up` - User registration
- `POST /api/auth/sign-in` - User login
- `POST /api/auth/sign-out` - User logout
- `GET /api/auth/session` - Get current session
- etc. (all Better Auth endpoints)
### Levels Management
#### `GET /api/levels`
- **Auth**: Required (401 if unauthorized)
- **Purpose**: Fetch all game levels
- **Query Params**: None
- **Returns**: Array of Level objects, ordered by sortOrder ASC
- **Response**:
```json
[
{
"id": "uuid",
"imageUrl": "https://...",
"answer": "答案",
"hint1": "提示1",
"hint2": null,
"hint3": null,
"sortOrder": 0,
"createdAt": "2026-04-05T...",
"updatedAt": "2026-04-05T..."
}
]
```
#### `POST /api/levels`
- **Auth**: Required
- **Purpose**: Create a new game level
- **Request Body**:
```json
{
"imageUrl": "string (required)",
"answer": "string (required)",
"hint1": "string (optional)",
"hint2": "string (optional)",
"hint3": "string (optional)"
}
```
- **Logic**:
- Gets max sortOrder and sets new level to max+1
- Uses UUID for ID
- Validates required fields (imageUrl, answer)
- **Returns**: Created Level object (201)
#### `PUT /api/levels`
- **Auth**: Required
- **Purpose**: Update an existing level
- **Request Body**:
```json
{
"id": "string (required)",
"imageUrl": "string",
"answer": "string",
"hint1": "string",
"hint2": "string",
"hint3": "string"
}
```
- **Logic**: Direct update via Prisma
- **Returns**: Updated Level object
#### `DELETE /api/levels`
- **Auth**: Required
- **Purpose**: Delete a level
- **Query Params**: `id` (string, required)
- **Returns**: `{ "success": true }`
#### `PUT /api/levels/reorder`
- **Auth**: Required
- **Purpose**: Batch update sort order for drag-and-drop
- **Request Body**:
```json
{
"orders": [
{ "id": "level-id", "sortOrder": 0 },
{ "id": "level-id", "sortOrder": 1 }
]
}
```
- **Logic**: Uses Prisma transaction to update all in parallel
- **Returns**: `{ "success": true }`
### Users Management (Admin/Portal Users)
#### `GET /api/users`
- **Auth**: Required
- **Purpose**: Fetch all portal users
- **Query Params**: None
- **Returns**: Array of User objects (ordered by createdAt DESC)
- **Excludes**: Password hash is not returned
#### `POST /api/users`
- **Auth**: Required
- **Purpose**: Create a new portal user
- **Request Body**:
```json
{
"email": "string (required)",
"password": "string (required)",
"name": "string (optional)"
}
```
- **Logic**:
- Checks if email already exists (error: "该邮箱已被注册")
- Hashes password using `better-auth/crypto`
- Creates User + Account in transaction
- Account has providerId="credential"
- **Returns**: Created User object (201)
#### `PUT /api/users`
- **Auth**: Required
- **Purpose**: Update a portal user
- **Request Body**:
```json
{
"id": "string (required)",
"email": "string (optional)",
"password": "string (optional)",
"name": "string (optional)"
}
```
- **Logic**:
- Validates email not taken by another user
- Updates password if provided (hashing in transaction)
- Uses transaction for consistency
- **Returns**: Updated User object
#### `DELETE /api/users`
- **Auth**: Required
- **Purpose**: Delete a portal user
- **Query Params**: `id` (string, required)
- **Logic**:
- Prevents deleting yourself (checks session.user.id)
- Error: "不能删除自己的账户"
- **Returns**: `{ "success": true }`
### Mini-Program Users
#### `GET /api/wx-users`
- **Auth**: Required
- **Purpose**: Get WeChat mini-program users with pagination and search
- **Query Params**:
- `search` - Search in nickname or openid (optional)
- `page` - Page number (default: 1)
- `limit` - Results per page (default: 20)
- **Logic**:
- Search: OR condition on nickname.contains and openid.contains
- Returns paginated results with metadata
- **Returns**:
```json
{
"users": [
{
"id": "uuid",
"openid": "string",
"nickname": "string",
"avatarUrl": "string",
"points": 10,
"createdAt": "2026-04-05T...",
"updatedAt": "2026-04-05T..."
}
],
"meta": {
"total": 100,
"page": 1,
"limit": 20,
"totalPages": 5
}
}
```
- **Performance**: Uses `select` to exclude sessionKey
#### `GET /api/wx-users/[id]`
- **Auth**: Required
- **Purpose**: Get single WeChat user with level progress history
- **Params**: `id` - User UUID
- **Returns**:
```json
{
"id": "uuid",
"openid": "string",
"nickname": "string",
"avatarUrl": "string",
"points": 10,
"createdAt": "2026-04-05T...",
"updatedAt": "2026-04-05T...",
"levelProgress": [
{
"id": "uuid",
"userId": "uuid",
"levelId": "uuid",
"completedAt": "2026-04-05T...",
"level": {
"id": "uuid",
"answer": "答案"
}
}
]
}
```
- **Logic**: Includes levelProgress ordered by completedAt DESC
#### `DELETE /api/wx-users/level-progress`
- **Auth**: Required
- **Purpose**: Batch delete level progress records
- **Request Body**:
```json
{
"ids": ["progress-id-1", "progress-id-2"]
}
```
- **Logic**:
- Validates ids is non-empty array of strings
- Uses deleteMany with IN clause
- **Returns**: `{ "deleted": 5 }`
### COS (Tencent Cloud Object Storage)
#### `GET /api/cos/temp-key`
- **Auth**: Required
- **Purpose**: Get temporary credentials for frontend image upload
- **Query Params**: None
- **Logic**:
- Uses Tencent COS STS service
- Generates temp credentials valid for 30 minutes (1800 seconds)
- Restricts upload to `mini_game/images/*` prefix
- **Returns**:
```json
{
"credentials": {
"tmpSecretId": "string",
"tmpSecretKey": "string",
"sessionToken": "string"
},
"startTime": 1712345678,
"expiredTime": 1712347478,
"bucket": "lookai-1308511832",
"region": "ap-guangzhou"
}
```
### Authentication Patterns
- **Session Check Pattern**:
```typescript
const session = await auth.api.getSession({ headers: request.headers })
if (!session) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
```
- **All routes use the same pattern** (except Better Auth routes)
- **No role/permission system yet** - all authenticated users have full access
---
## 6. MIDDLEWARE & AUTHENTICATION
### File: `middleware.ts`
**Location**: Root level
**Purpose**: Session validation for protected pages
**Features**:
- Checks for session cookies on protected pages
- Handles both HTTP and HTTPS cookie naming:
- `better-auth.session_token` (HTTP)
- `__Secure-better-auth.session_token` (HTTPS with Secure prefix)
- Redirects to login if no session
- Allows public access to:
- `/login` page
- `/api/*` routes (API auth handled separately)
- Static assets (`/_next`, `/favicon`, files with extensions)
- **Important**: Middleware runs at Edge Runtime, so **cannot use Prisma** - only cookie check
- Preserves callbackUrl for redirect after login
**basePath Gotcha**:
- `request.nextUrl.pathname` does NOT include basePath
- Path comparison is done without `/studio` prefix
- Redirect URLs must manually prepend basePath
### Better Auth Configuration
**File**: `lib/auth.ts`
```typescript
export const auth = betterAuth({
database: prismaAdapter(prisma, { provider: 'mysql' }),
emailAndPassword: { enabled: true },
basePath: '/api/auth',
trustedOrigins: [process.env.BETTER_AUTH_URL],
secret: process.env.BETTER_AUTH_SECRET,
session: {
expiresIn: 60 * 60 * 24 * 7, // 7 days
updateAge: 60 * 60 * 24, // Update token every 1 day of activity
},
})
```
**Key Points**:
- Uses Prisma adapter for MySQL
- basePath: `/api/auth` (NOT `/studio/api/auth`)
- BETTER_AUTH_URL must be origin only (NO path component)
- Secret must be 32+ characters
- Sessions auto-refresh if 1+ day of activity
### Client Auth Hooks
**File**: `lib/auth-client.ts`
```typescript
export const { signIn, signOut, useSession } = authClient
```
Provides:
- `useSession()` hook - Get current session data
- `signIn()` - Login function
- `signOut()` - Logout function
- Automatically uses basePath + appUrl to construct auth endpoint URL
---
## 7. LIBRARY UTILITIES
### `lib/api.ts` - API Fetch Helper
```typescript
export function apiFetch(input: string, init?: RequestInit): Promise<Response>
```
- Prepends `basePath` to API routes
- Example: `apiFetch('/api/levels')` → `/studio/api/levels`
- Used throughout all client components
### `lib/prisma.ts` - Prisma Singleton
```typescript
const prisma = globalForPrisma.prisma ?? new PrismaClient()
```
- Prevents multiple Prisma instances in development (hot reload)
- Exports single prisma instance for all API routes
### `lib/cos.ts` - Tencent COS Utilities
**Functions**:
- `getTempKey()` - Get temporary STS credentials
- Duration: 30 minutes
- Policy: Upload only to `mini_game/images/*`
- `getBucketName()` - Format bucket name (appends APPID if needed)
- `getBucketConfig()` - Get bucket and region for SDK
### `lib/utils.ts` - General Utilities
```typescript
export function cn(...inputs: ClassValue[])
```
- Tailwind CSS utility merger (clsx + tailwind-merge)
- Used throughout components for className management
---
## 8. UI COMPONENTS & PATTERNS
### Component Structure
All components use React patterns:
- **Client Components**: `'use client'` directive
- **Controlled Forms**: State-driven form handling
- **TanStack Query**: For server state management
- **Dialog-based Operations**: Create/Edit in modals
### Key Components
#### Pages
1. **Levels Page** (`app/(dashboard)/levels/page.tsx`)
- List all levels with drag-and-drop reordering
- Add/Edit/Delete levels
- Uses TanStack Query mutations
2. **Users Page** (`app/(dashboard)/users/page.tsx`)
- Table view of portal users
- Add/Edit/Delete users
- Delete prevention (can't delete self)
3. **WeChat Users Page** (`app/(dashboard)/wx-users/page.tsx`)
- Search and pagination
- Click to view user details + progress history
- Batch delete progress records
#### UI Components
- **Button**: shadcn/ui with variants (default, outline, ghost)
- **Input/Textarea**: Form inputs
- **Dialog**: Modal dialogs with header, content, footer
- **Spinner**: Loading indicator
- **Card**: Container component
### Image Upload Flow
1. User clicks upload in level dialog
2. Frontend calls `/api/cos/temp-key` to get credentials
3. Frontend uses Tencent COS SDK to upload directly to COS
4. COS returns image URL
5. URL stored in database
### Form Submission Pattern
```typescript
// TanStack Query mutation
const mutation = useMutation({
mutationFn: async (data) => { /* API call */ },
onSuccess: () => { queryClient.invalidateQueries(...) }
})
// On form submit
await mutation.mutateAsync(data)
// Automatic refetch on success
```
---
## 9. ENVIRONMENT VARIABLES
### Required Variables
```
# Database
DATABASE_URL=mysql://user:password@host:port/database
# Better Auth
BETTER_AUTH_SECRET=32+ character random string
BETTER_AUTH_URL=https://domain.com (origin only, NO path)
# Public URLs (Client-side)
NEXT_PUBLIC_APP_URL=https://domain.com (same as BETTER_AUTH_URL)
NEXT_PUBLIC_BASE_PATH=/studio
# Admin Account (Seed script)
ADMIN_EMAIL=admin@example.com
ADMIN_PASSWORD=secure_password
# Tencent COS
COS_SECRET_ID=AKID...
COS_SECRET_KEY=...
COS_BUCKET=lookai-1308511832
COS_REGION=ap-guangzhou
COS_APPID=1308511832
```
### Important Notes
- **BETTER_AUTH_URL**: Must NOT contain path (e.g., ❌ `https://domain.com/studio`, ✅ `https://domain.com`)
- **NEXT_PUBLIC_BASE_PATH**: Used client-side for apiFetch
- **COS_BUCKET**: Can include or exclude APPID (utility handles both)
---
## 10. DEPLOYMENT & CONFIGURATION
### Next.js Configuration (`next.config.js`)
```javascript
{
output: 'standalone', // Single binary output
basePath: '/studio', // App served at /studio
images: {
remotePatterns: [
{ protocol: 'https', hostname: '*.myqcloud.com' } // Allow COS images
]
}
}
```
### Deployment Setup
**Server**: `root@119.91.211.52` at `/root/apps/meme-studio`
**Process Manager**: PM2 (`ecosystem.config.js`)
**Deploy Script** (`./deploy.sh`):
1. Build locally: `next build`
2. rsync files (excludes node_modules, .next)
3. SSH to server, run:
- `npm install --production`
- `npx prisma generate`
- Restart PM2 process
**Key Gotchas**:
- Prisma is devDependency but needs to be generated on server
- `output: 'standalone'` bundles Next.js runtime
- Deployment requires `DATABASE_URL` set on server
- Cross-platform binary target: `rhel-openssl-3.0.x` for Linux server
---
## 11. EXISTING PATTERNS & CONVENTIONS
### API Route Pattern
```typescript
// 1. Import required modules
import { auth } from '@/lib/auth'
import { prisma } from '@/lib/prisma'
// 2. Check session
const session = await auth.api.getSession({ headers: request.headers })
if (!session) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
// 3. Parse request
const body = await request.json()
const { searchParams } = new URL(request.url)
// 4. Validate input
if (!required_field) return NextResponse.json({ error: 'msg' }, { status: 400 })
// 5. Database operation
const result = await prisma.model.operation()
// 6. Return response
return NextResponse.json(result, { status: 201 })
```
### Mutation Pattern (Client)
```typescript
const mutation = useMutation({
mutationFn: async (data) => {
const res = await apiFetch('/api/endpoint', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
})
if (!res.ok) {
const error = await res.json()
throw new Error(error.error || 'Failed')
}
return res.json()
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['levels'] })
},
})
mutation.mutate(data)
```
### Type Definitions Pattern
```typescript
// Database model
export interface Level {
id: string
imageUrl: string
// ...
createdAt: Date
updatedAt: Date
}
// Form data (typically a subset)
export interface LevelFormData {
imageUrl: string
answer: string
hint1?: string
// ... (no id, dates for new records)
}
```
---
## 12. KEY GOTCHAS & IMPORTANT NOTES
### 1. basePath Handling
- ✅ `request.nextUrl.pathname` excludes basePath (returns `/levels`, not `/studio/levels`)
- ❌ Never manually prepend `/studio` in API routes
- ✅ Client-side: use `apiFetch()` which prepends basePath automatically
- ❌ `router.push()` auto-prepends basePath, don't add manually
### 2. Better Auth Configuration
- **CRITICAL**: `BETTER_AUTH_URL` must be origin only, NO path component
- Better Auth's `withPath()` silently ignores `basePath` if URL has path
- Cookie naming: HTTPS adds `__Secure-` prefix
- Middleware must check both cookie names
### 3. Session Validation
- ✅ Middleware: Cookie check only (no Prisma in Edge Runtime)
- ✅ API Routes: Full session validation with `auth.api.getSession()`
- Middleware doesn't verify token validity, only presence
### 4. Database Relationships
- **No explicit join tables** for many-to-many (not needed here)
- One-to-many: WxUser → WxUserLevelProgress → Level
- Cascade delete: Sessions/Accounts deleted when User deleted
### 5. Image Upload Flow
- Frontend gets temp credentials from `/api/cos/temp-key`
- Frontend uploads directly to COS (not through backend)
- Backend only stores URL
- URLs expire or become invalid if credentials expire
### 6. Authentication Flow
- Login → Session created → Cookie set
- Page navigation → Middleware checks cookie
- Redirects to login if missing
- All API calls check session in route handler
### 7. Search Patterns
- WeChat users: `OR` condition on nickname OR openid contains
- Case-sensitive by default (MySQL depends on collation)
- Pagination: `skip = (page - 1) * limit`
---
## 13. ABSENCE OF SHARE/INVITE LOGIC
**Current Status**: ❌ NO sharing/invite functionality exists
**What's Missing**:
- No share link/token model in schema
- No invite API endpoint
- No permissions/role system
- All authenticated users have full admin access to:
- Level management
- User management
- WeChat user data
**Considerations for Implementation**:
- Determine if shares/invites are for:
- Sharing levels with mini-program users?
- Inviting new admins to platform?
- Sharing user progress data?
- Would need new database model
- Permission checks in API routes
- Share link generation and validation
- Expiration logic
---
## 14. RECOMMENDATIONS FOR EXPLORATION
### For Share/Invite Feature (if needed):
1. **Schema Design**:
- ShareToken model with expiry, permissions
- Or UserRole model for permission levels
2. **API Endpoints Needed**:
- `POST /api/shares` - Generate share link
- `GET /api/shares/[token]` - Validate share token
- `DELETE /api/shares/[id]` - Revoke share
3. **Permission System**:
- Add role field to User model
- Permission checks in middleware or API routes
4. **Frontend Needed**:
- Share dialog component
- Permission management UI
---
## SUMMARY TABLE
| Category | Item | Details |
|----------|------|---------|
| **Framework** | Next.js | 14.2.28, App Router, Standalone output |
| **Auth** | Better Auth | Email/password, MySQL Prisma adapter |
| **Database** | MySQL | Prisma ORM, 7 models |
| **Storage** | Tencent COS | STS temp credentials, 30 min validity |
| **UI** | shadcn/ui | Tailwind, React Query, @dnd-kit |
| **Deployment** | PM2 | Server at 119.91.211.52, basePath=/studio |
| **Auth Routes** | 5 endpoints | Levels, Users, WxUsers, COS, Auth |
| **Protected Routes** | YES | Middleware + per-route checks |
| **Sharing** | ❌ NONE | Not implemented |
| **Permissions** | ❌ FLAT | All authenticated users = full admin |
| **Search** | Implemented | WeChat users only (nickname, openid) |