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

871
EXPLORATION_REPORT.md Normal file
View File

@@ -0,0 +1,871 @@
# MemeStudio Server Project - Thorough Exploration Report
## 1. PROJECT OVERVIEW
**Project Name:** Meme Studio (Homophone Pun Game Operation Platform)
**Framework:** Next.js 14 (App Router)
**Type:** Backend/Admin Dashboard for a wordplay game
**Location:** `/Users/richard/Documents/code/xieyingeng/MemeStudio`
### Tech Stack
- **Framework**: Next.js 14 App Router
- **Runtime**: Node.js (Standalone output)
- **Language**: TypeScript
- **ORM**: Prisma v6.5.0
- **Database**: MySQL
- **Authentication**: Better Auth v1.2.7 (email/password with Prisma adapter)
- **Frontend UI**: shadcn/ui + Tailwind CSS
- **State Management**: TanStack Query v5.69.0
- **Drag & Drop**: @dnd-kit/sortable
- **File Upload**: Tencent COS (Cloud Object Storage)
- **Password Hashing**: bcryptjs v3.0.2
- **Process Manager**: PM2 (ecosystem.config.js)
---
## 2. PROJECT STRUCTURE
```
MemeStudio/
├── app/ # Next.js App Router
│ ├── (auth)/ # Auth routes (no sidebar)
│ │ ├── layout.tsx
│ │ └── login/page.tsx
│ ├── (dashboard)/ # Protected routes (with sidebar)
│ │ ├── layout.tsx
│ │ ├── levels/page.tsx # Game levels management
│ │ ├── users/page.tsx # Admin user management
│ │ └── wx-users/page.tsx # WeChat mini program users
│ ├── api/ # API routes
│ │ ├── auth/[...all]/ # Better Auth endpoints
│ │ ├── levels/ # Level CRUD
│ │ ├── users/ # User CRUD
│ │ ├── wx-users/ # WeChat user endpoints
│ │ ├── cos/temp-key/ # Tencent COS credentials
│ │ └── v1/wechat-game/ # (empty, future API)
│ ├── layout.tsx # Root layout
│ ├── page.tsx # Root page (redirects to /dashboard)
│ ├── providers.tsx # Client providers (Query, etc)
│ └── globals.css
├── lib/ # Core utilities
│ ├── auth.ts # Better Auth config
│ ├── auth-client.ts # Client-side auth hooks
│ ├── prisma.ts # Prisma client singleton
│ ├── api.ts # API fetch wrapper with basePath
│ ├── cos.ts # Tencent COS utilities
│ └── utils.ts # UI utilities (cn())
├── components/ # React components
│ ├── ui/ # shadcn/ui components (button, card, dialog, input, etc)
│ ├── layout/ # Header, sidebar
│ ├── levels/ # Level-related components
│ ├── users/ # User dialog
│ └── wx-users/ # WeChat user detail dialog
├── types/index.ts # TypeScript interfaces
├── middleware.ts # Next.js middleware (session check)
├── prisma/
│ ├── schema.prisma # Prisma schema
│ └── seed.ts # Admin user seeding
├── public/ # Static assets
├── next.config.js # Next.js config (basePath, COS remotePatterns)
├── tsconfig.json
├── tailwind.config.ts
├── package.json
├── ecosystem.config.js # PM2 config
└── deploy.sh # Deployment script
```
---
## 3. AUTHENTICATION & MIDDLEWARE
### Middleware Pattern (`middleware.ts`)
```typescript
// Non-blocking: API routes, static files, login page
// Blocking: All dashboard pages require session cookie
// Cookie detection:
- 'better-auth.session_token' (HTTP)
- '__Secure-better-auth.session_token' (HTTPS)
// Redirect pattern:
- Unauthenticated users /login?callbackUrl=/original/path
- Note: basePath (/studio) is NOT included in pathname within middleware
```
### Auth Implementation (`lib/auth.ts`)
```typescript
betterAuth({
database: prismaAdapter(prisma),
emailAndPassword: { enabled: true },
basePath: '/api/auth', // CRITICAL: NO basePath prefix here
secret: process.env.BETTER_AUTH_SECRET,
session: {
expiresIn: 7 days,
updateAge: 1 day,
},
})
```
### Key Auth Files
- `lib/auth.ts` - Server-side Better Auth configuration
- `lib/auth-client.ts` - Client-side hooks: `useSession`, `signIn`, `signOut`
- `middleware.ts` - Edge Runtime middleware (cannot use Prisma, only cookie check)
- `app/api/auth/[...all]/route.ts` - Better Auth endpoints
---
## 4. DATABASE SCHEMA (Prisma)
### Models
#### 1. **User** (Admin/staff 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[]
}
```
#### 2. **Session** (Better Auth)
```prisma
model Session {
id String @id
expiresAt DateTime
token String @unique # Critical for session validation
ipAddress String?
userAgent String?
userId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
```
#### 3. **Account** (Provider credentials)
```prisma
model Account {
id String @id @default(uuid())
accountId String
providerId String # 'credential' for email/password
userId String
password String? # Only for 'credential' provider
accessToken String?
refreshToken String?
# ... expires, scope, tokens
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
```
#### 4. **Verification** (Email verification tokens)
```prisma
model Verification {
id String @id @default(uuid())
identifier String
value String
expiresAt DateTime
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
```
#### 5. **Level** (Game levels)
```prisma
model Level {
id String @id @default(uuid())
imageUrl String # COS URL
answer String # Correct answer (Chinese pun text)
hint1 String?
hint2 String?
hint3 String?
sortOrder Int @default(0) # Display order
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
userProgress WxUserLevelProgress[]
}
```
#### 6. **WxUser** (WeChat mini program players)
```prisma
model WxUser {
id String @id @default(uuid())
openid String @unique # WeChat OpenID
sessionKey String? # WeChat session key
nickname String?
avatarUrl String?
points Int @default(10)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
levelProgress WxUserLevelProgress[]
}
```
#### 7. **WxUserLevelProgress** (Game progress tracking)
```prisma
model WxUserLevelProgress {
id String @id @default(uuid())
userId String
levelId String
completedAt DateTime @default(now()) # When level was completed
user WxUser @relation(fields: [userId], references: [id], onDelete: Cascade)
level Level @relation(fields: [levelId], references: [id])
}
```
### Database Configuration
- **Provider**: MySQL
- **Binary Targets**: `["native", "rhel-openssl-3.0.x"]` (macOS dev → Linux server compatibility)
- **Environment Variable**: `DATABASE_URL=mysql://...`
---
## 5. API ROUTES & ENDPOINTS
### 5.1 Authentication Endpoints
**Route:** `/api/auth/[...all]`
**Handler:** Better Auth (`toNextJsHandler(auth)`)
**Methods:** GET, POST
**Auth Required:** No (this is the auth endpoint itself)
Common endpoints:
- `POST /api/auth/sign-in/email` - Email sign in
- `POST /api/auth/sign-out` - Sign out
- `POST /api/auth/sign-up/email` - Create new account (might be disabled)
---
### 5.2 Levels API
**Route:** `/api/levels`
**Auth Required:** Yes (session check)
#### GET - Fetch all levels
```typescript
GET /api/levels
Response: Level[]
Sorting: By sortOrder (ASC)
```
#### POST - Create level
```typescript
POST /api/levels
Body: {
imageUrl: string (required),
answer: string (required),
hint1?: string,
hint2?: string,
hint3?: string
}
Response: Level (with auto-generated UUID, sortOrder = max+1)
```
#### PUT - Update level
```typescript
PUT /api/levels
Body: {
id: string (required),
imageUrl?: string,
answer?: string,
hint1?: string,
hint2?: string,
hint3?: string
}
Response: Updated Level
```
#### DELETE - Delete level
```typescript
DELETE /api/levels?id=<levelId>
Response: { success: true }
```
---
### 5.3 Levels Reorder API
**Route:** `/api/levels/reorder`
**Auth Required:** Yes
#### PUT - Batch update sort order
```typescript
PUT /api/levels/reorder
Body: {
orders: Array<{ id: string, sortOrder: number }>
}
Response: { success: true }
Implementation: Prisma $transaction (atomic operation)
```
**Use Case:** Drag & drop reordering in UI
---
### 5.4 Users API (Admin)
**Route:** `/api/users`
**Auth Required:** Yes
**Purpose:** Manage admin/staff accounts
#### GET - Fetch all users
```typescript
GET /api/users
Response: Array<{
id, email, emailVerified, name, image, createdAt, updatedAt
}>
Sorting: By createdAt DESC
```
#### POST - Create new admin user
```typescript
POST /api/users
Body: {
email: string (required),
password: string (required),
name?: string
}
Response: User object
- Auto-generates UUID for user ID
- Hashes password with better-auth/crypto
- Creates User + Account records in transaction
- Error: "该邮箱已被注册" if email exists
```
#### PUT - Update user
```typescript
PUT /api/users
Body: {
id: string (required),
email?: string,
password?: string,
name?: string
}
Response: Updated User
- Can update email (checks uniqueness vs other users)
- Can update password (hashes with better-auth)
- Transaction: Updates User + Account records
```
#### DELETE - Delete user
```typescript
DELETE /api/users?id=<userId>
Response: { success: true }
- Prevents self-deletion: "不能删除自己的账户"
- Cascading delete via Prisma relations
```
**Password Hashing:** Always uses `hashPassword` from `better-auth/crypto` for consistency
---
### 5.5 WeChat Users API
**Route:** `/api/wx-users`
**Auth Required:** Yes
**Purpose:** Manage WeChat mini program player accounts
#### GET - List WeChat users (paginated + search)
```typescript
GET /api/wx-users?search=<text>&page=<1>&limit=<20>
Response: {
users: Array<WxUser>,
meta: { total, page, limit, totalPages }
}
Filtering: Search by nickname OR openid (contains search)
Sorting: By createdAt DESC
Dynamic Rendering: export const dynamic = 'force-dynamic'
```
---
### 5.6 WeChat User Detail
**Route:** `/api/wx-users/[id]`
**Auth Required:** Yes
**Method:** GET
```typescript
GET /api/wx-users/<userId>
Response: WxUser with nested levelProgress array
Include:
- All WxUser fields
- levelProgress: Array of {
id,
userId,
levelId,
completedAt,
level: { id, answer }
}
Sorting: levelProgress by completedAt DESC
```
---
### 5.7 WeChat User Level Progress
**Route:** `/api/wx-users/level-progress`
**Auth Required:** Yes
**Method:** DELETE
```typescript
DELETE /api/wx-users/level-progress
Body: {
ids: Array<string> # Array of progress record IDs
}
Response: { deleted: number }
Validation: ids must be non-empty array of strings
```
**Use Case:** Batch delete player progress (e.g., reset levels)
---
### 5.8 Tencent COS Credentials
**Route:** `/api/cos/temp-key`
**Auth Required:** Yes
**Method:** GET
```typescript
GET /api/cos/temp-key
Response: {
credentials: {
tmpSecretId: string,
tmpSecretKey: string,
sessionToken: string
},
startTime: number,
expiredTime: number,
bucket: string,
region: string
}
```
**Implementation Details:**
- Uses `qcloud-cos-sts` library
- Policy allows: `cos:PutObject`, `cos:PostObject`
- Limited to: `mini_game/images/*` directory
- Duration: 30 minutes (1800 seconds)
- Returns bucket config for frontend upload
---
### 5.9 Empty/Stub Routes
- `/api/v1/wechat-game/` - Directory structure exists but no handlers
---
## 6. LIB UTILITIES
### 6.1 `lib/auth.ts` - Better Auth Configuration
```typescript
export const auth = betterAuth({
database: prismaAdapter(prisma),
emailAndPassword: { enabled: true },
basePath: '/api/auth', // CRITICAL GOTCHA
trustedOrigins: [process.env.BETTER_AUTH_URL],
secret: process.env.BETTER_AUTH_SECRET,
session: { expiresIn: 7 days, updateAge: 1 day }
})
export type Auth = typeof auth
```
**Critical Gotchas:**
1. `basePath` must be `/api/auth` (NOT `/studio/api/auth`)
2. `BETTER_AUTH_URL` must be origin-only (NOT include path)
3. Better Auth's `withPath()` silently ignores basePath if URL has path component
---
### 6.2 `lib/auth-client.ts` - Client Auth Hooks
```typescript
const basePath = process.env.NEXT_PUBLIC_BASE_PATH || '/studio'
const appUrl = process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3001'
export const authClient = createAuthClient({
baseURL: `${appUrl}${basePath}/api/auth`
})
export const { signIn, signOut, useSession } = authClient
```
**Usage:**
- `useSession()` - Hook to get current session
- `signIn(email, password)` - Email/password login
- `signOut()` - Logout
---
### 6.3 `lib/prisma.ts` - Prisma Singleton
```typescript
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined
}
export const prisma = globalForPrisma.prisma ?? new PrismaClient()
if (process.env.NODE_ENV !== 'production')
globalForPrisma.prisma = prisma
```
**Purpose:** Prevent multiple Prisma client instances in dev mode
---
### 6.4 `lib/api.ts` - API Fetch Helper
```typescript
const basePath = process.env.NEXT_PUBLIC_BASE_PATH || ''
export function apiFetch(input: string, init?: RequestInit): Promise<Response> {
const url = input.startsWith('/') ? `${basePath}${input}` : input
return fetch(url, init)
}
```
**Purpose:** Wrapper that auto-prepends basePath to relative URLs (client-side)
---
### 6.5 `lib/cos.ts` - Tencent COS Utilities
```typescript
interface TempKeyResult {
credentials: {
tmpSecretId: string
tmpSecretKey: string
sessionToken: string
}
startTime: number
expiredTime: number
}
export function getBucketName(): string
// Returns "bucket-appid" format
export async function getTempKey(): Promise<TempKeyResult>
// STS.getCredential with 30-min duration
// Policy: PUT/POST to mini_game/images/*
export function getBucketConfig()
// Returns { bucket, region }
```
**Environment Variables:**
- `COS_SECRET_ID`
- `COS_SECRET_KEY`
- `COS_BUCKET`
- `COS_REGION` (default: ap-guangzhou)
- `COS_APPID`
---
### 6.6 `lib/utils.ts` - UI Utilities
```typescript
export function cn(...inputs: ClassValue[])
// Combines clsx + tailwind-merge for class deduplication
```
---
## 7. ENVIRONMENT VARIABLES
**Required Variables:**
```
DATABASE_URL=mysql://user:pass@host:port/dbname
BETTER_AUTH_SECRET=<32+ chars, openssl rand -base64 32>
BETTER_AUTH_URL=http://localhost:3001 # Origin only, NO path
NEXT_PUBLIC_APP_URL=http://localhost:3001
NEXT_PUBLIC_BASE_PATH=/studio
ADMIN_EMAIL=admin@example.com
ADMIN_PASSWORD=password123
COS_SECRET_ID=<tencent cloud secret>
COS_SECRET_KEY=<tencent cloud secret>
COS_BUCKET=bucket-name
COS_REGION=ap-guangzhou
COS_APPID=<tencent app id>
```
**Production Considerations:**
- `.env.production` exists alongside `.env`
- `BETTER_AUTH_SECRET` should be 32+ characters
- HTTPS deployment adds `__Secure-` cookie prefix
- Database should use SSL connection
---
## 8. DEPLOYMENT ARCHITECTURE
### Standalone Output
```javascript
// next.config.js
module.exports = {
output: 'standalone', # Self-contained binary
basePath: '/studio', # Reverse proxy path
images: {
remotePatterns: [{
protocol: 'https',
hostname: '*.myqcloud.com' # Tencent COS pattern
}]
}
}
```
### Deployment Flow (deploy.sh)
1. Build locally: `next build` (creates .next/standalone)
2. rsync files to server (excluding node_modules)
3. SSH to server: `npm install --production`
4. Generate Prisma: `npx prisma generate`
5. PM2 restart: `pm2 restart ecosystem.config.js`
### Server Details
- **Host:** root@119.91.211.52
- **Path:** /root/apps/meme-studio
- **Process Manager:** PM2
- **Config:** ecosystem.config.js
### PM2 Configuration
```javascript
// ecosystem.config.js (typical setup)
module.exports = {
apps: [{
name: 'meme-studio',
script: './.next/standalone/server.js',
instances: 1,
exec_mode: 'cluster'
}]
}
```
---
## 9. CRITICAL GOTCHAS & IMPORTANT NOTES
### 9.1 basePath Handling
-`next.config.js` sets `basePath: '/studio'`
- ✅ All pages/API routes served under `/studio/...`
-**NEVER** include `/studio` in `BETTER_AUTH_URL` or hardcoded paths
- ⚠️ `request.nextUrl.pathname` in middleware EXCLUDES basePath (shows `/levels` not `/studio/levels`)
- ⚠️ Next.js strips basePath from `request.url` in route handlers
### 9.2 Authentication Patterns
- ✅ Session validation: `await auth.api.getSession({ headers: request.headers })`
- ✅ Session check in middleware uses cookies (Edge Runtime compatible)
- ⚠️ Password hashing: ALWAYS use `hashPassword` from `better-auth/crypto`
- ⚠️ Session token field has unique constraint in database
### 9.3 Database & ORM
- ✅ Prisma adapter with MySQL provider
- ✅ Binary targets: `["native", "rhel-openssl-3.0.x"]` for cross-platform deployment
- ⚠️ Middleware cannot use Prisma (Edge Runtime incompatible)
- ⚠️ Transaction usage for consistency: `prisma.$transaction([...])`
### 9.4 File Upload (COS)
- ✅ Frontend gets temporary credentials via `/api/cos/temp-key`
- ✅ Browser uploads directly to COS (S3-compatible)
- ✅ Limited to `mini_game/images/*` directory by policy
- ⚠️ Policy duration: 30 minutes (1800 seconds)
### 9.5 Git & Development
- ⚠️ **Commit messages must be written in Chinese**
- ✅ ESLint configured: `npm run lint`
- ✅ Dev server runs on port 3001: `npm run dev`
- ✅ Database seeding: `npm run db:seed` (creates/updates admin user)
---
## 10. SHARE/INVITE LOGIC
**Current Status:****NO existing share or invite functionality**
No database models, API endpoints, or UI components for:
- Sharing levels with other users
- Generating shareable links/tokens
- Inviting users to teams or projects
- Publishing/unpublishing levels
- Level access control
**All levels are:**
- Managed by admin users
- Accessible to all authenticated admins
- Automatically available in the WeChat mini program
---
## 11. KEY PATTERNS USED
### 11.1 API Route Pattern
All protected API routes follow:
```typescript
import { auth } from '@/lib/auth'
export async function GET(request: NextRequest) {
const session = await auth.api.getSession({ headers: request.headers })
if (!session) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
// ... handler logic
}
```
### 11.2 Error Handling
- 400: Bad request (validation errors)
- 401: Unauthorized (no session)
- 404: Not found (resource doesn't exist)
- 500: Server error (logged to console)
### 11.3 Response Format
- Success: `NextResponse.json(data)`
- Error: `NextResponse.json({ error: string }, { status: number })`
- Batch operations: `{ success: true }` or `{ deleted: count }`
### 11.4 Transaction Pattern
```typescript
await prisma.$transaction(async (tx) => {
const user = await tx.user.create({ data: {...} })
await tx.account.create({ data: {...} })
return user
})
```
### 11.5 Pagination Pattern
```typescript
const skip = (page - 1) * limit
const [data, total] = await Promise.all([
prisma.model.findMany({ skip, take: limit }),
prisma.model.count()
])
return { data, meta: { total, page, limit, totalPages: Math.ceil(total/limit) } }
```
---
## 12. FILE STRUCTURE SUMMARY
| File | Purpose |
|------|---------|
| `middleware.ts` | Session validation, redirect to login |
| `next.config.js` | basePath, COS image patterns, standalone output |
| `lib/auth.ts` | Better Auth server configuration |
| `lib/auth-client.ts` | Client-side auth hooks |
| `lib/prisma.ts` | Prisma singleton |
| `lib/api.ts` | Fetch wrapper with basePath |
| `lib/cos.ts` | Tencent COS STS credentials |
| `prisma/schema.prisma` | Database schema (7 models) |
| `prisma/seed.ts` | Admin user seeding |
| `app/api/auth/[...all]/route.ts` | Better Auth endpoints |
| `app/api/levels/route.ts` | Level CRUD (GET, POST, PUT, DELETE) |
| `app/api/levels/reorder/route.ts` | Batch reorder (PUT) |
| `app/api/users/route.ts` | Admin user CRUD (GET, POST, PUT, DELETE) |
| `app/api/wx-users/route.ts` | List WeChat users (GET, paginated) |
| `app/api/wx-users/[id]/route.ts` | Get user + progress (GET) |
| `app/api/wx-users/level-progress/route.ts` | Delete progress (DELETE) |
| `app/api/cos/temp-key/route.ts` | Get COS credentials (GET) |
| `app/(dashboard)/levels/page.tsx` | Levels management UI |
| `app/(dashboard)/users/page.tsx` | Admin users management UI |
| `app/(dashboard)/wx-users/page.tsx` | WeChat users viewer UI |
| `app/(auth)/login/page.tsx` | Login page |
| `types/index.ts` | TypeScript interfaces (Level, User, WxUser, etc) |
---
## 13. COMPONENT STRUCTURE
**UI Components (shadcn/ui):**
- `button.tsx` - Button component
- `card.tsx` - Card container
- `dialog.tsx` - Modal dialog
- `input.tsx` - Text input
- `label.tsx` - Form label
- `textarea.tsx` - Textarea input
- `spinner.tsx` - Loading spinner
**Layout Components:**
- `header.tsx` - Top navigation header
- `sidebar.tsx` - Left sidebar with navigation
**Feature Components:**
- `levels/level-dialog.tsx` - Create/edit level modal
- `levels/level-list.tsx` - Drag-and-drop sortable list
- `levels/level-card.tsx` - Individual level card
- `levels/image-uploader.tsx` - COS image upload
- `users/user-dialog.tsx` - Create/edit admin user modal
- `wx-users/wx-user-detail-dialog.tsx` - View WeChat user progress
---
## 14. DATA FLOW EXAMPLES
### Example 1: Create Level
1. Admin clicks "Add Level" button
2. UI opens `level-dialog.tsx` modal
3. Admin fills form (imageUrl, answer, hints)
4. `image-uploader.tsx` gets temp COS credentials from `/api/cos/temp-key`
5. Browser uploads image directly to COS
6. Admin submits form
7. API calls `POST /api/levels` with imageUrl, answer, hints
8. Backend validates session, creates Level record
9. sortOrder auto-calculated (max+1)
10. Level appears in list (sorted by sortOrder)
### Example 2: Reorder Levels
1. Admin drags level up/down in `level-list.tsx`
2. @dnd-kit/sortable updates UI state
3. Admin confirms or auto-save triggers
4. API calls `PUT /api/levels/reorder` with reordered IDs
5. Backend updates sortOrder for all levels in transaction
6. List re-renders in new order
### Example 3: View WeChat Player
1. Admin opens "WeChat Users" dashboard page
2. `wx-users/page.tsx` calls `GET /api/wx-users?page=1&limit=20`
3. Backend queries paginated WxUser list
4. Admin clicks on player name
5. Opens `wx-user-detail-dialog.tsx`
6. Component calls `GET /api/wx-users/<id>`
7. Backend returns WxUser + nested levelProgress array
8. Dialog displays player info and completed levels
---
## 15. SECURITY CONSIDERATIONS
### Implemented
- ✅ Session-based authentication (Better Auth)
- ✅ Password hashing (bcryptjs via Better Auth)
- ✅ CSRF protection (Better Auth built-in)
- ✅ Middleware session validation
- ✅ COS temporary credentials (limited scope, 30-min expiry)
- ✅ SQL injection protection (Prisma ORM)
- ✅ Email uniqueness constraints
### Not Implemented
- ❌ Role-based access control (all authenticated users have admin access)
- ❌ Audit logging
- ❌ Rate limiting
- ❌ Input validation/sanitization (rely on Prisma types)
- ❌ API key authentication (only cookie-based)
- ❌ CORS configuration (not needed for same-origin)
---
## SUMMARY
MemeStudio is a **well-structured Next.js admin dashboard** for managing a WeChat mini program game. It has:
- **Clean Auth**: Better Auth with Prisma adapter, session-based
- **Full CRUD**: Levels, admin users, WeChat player tracking
- **Cloud Integration**: Tencent COS for image uploads
- **Scalable ORM**: Prisma with MySQL
- **No Sharing Logic**: All data is globally accessible to admins
**For Adding Share/Invite Feature:**
- Add `SharedLevel` or `LevelAccess` model (permissions)
- Add `InviteToken` model (temporary join links)
- Add `/api/invites` endpoints (generate, accept, list)
- Add role/permission fields to User model
- Add UI for managing shares/invites