Commit b0a016d5 authored by Sihan Chen's avatar Sihan Chen
Browse files

secondth commit

parents
node_modules
.next
.git
.gitignore
README.md
Dockerfile
.dockerignore
.env*
prisma/dev.db*
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env*.local
.env
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
# database
prisma/dev.db*
\ No newline at end of file
# 使用官方 Node.js 运行时作为基础镜像
FROM node:18-alpine AS base
# 安装依赖项
FROM base AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
# 复制包文件
COPY package.json package-lock.json* ./
RUN npm ci
# 构建应用
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# 生成 Prisma 客户端
RUN npx prisma generate
# 构建 Next.js 应用
RUN npm run build
# 生产镜像,复制所有文件并运行 Next.js
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
# 复制构建的应用
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/prisma ./prisma
COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma
USER nextjs
EXPOSE 3000
ENV PORT 3000
ENV HOSTNAME "0.0.0.0"
CMD ["node", "server.js"]
# 快速启动指南
## 🚀 三步启动聊天室
### 方法一:自动设置(推荐)
**Windows 用户:**
```bash
setup.bat
```
**Linux/Mac 用户:**
```bash
chmod +x setup.sh
./setup.sh
```
### 方法二:手动设置
1. **安装依赖**
```bash
npm install
```
2. **初始化数据库**
```bash
npx prisma generate
npx prisma migrate dev --name init
npx prisma db seed
```
3. **启动服务器**
```bash
npm run dev
```
### 方法三:Docker 部署
```bash
docker-compose up -d
```
## 🌐 访问应用
打开浏览器访问:[http://localhost:3000](http://localhost:3000)
## 👤 演示账号
- **用户名**: `admin` **密码**: `123456`
- **用户名**: `demo` **密码**: `123456`
## 🎯 使用说明
1. **注册/登录**:首次使用请注册账号,或使用演示账号登录
2. **设置昵称**:登录后需要设置聊天昵称
3. **加入房间**:在左侧房间列表中选择房间
4. **发送消息**:在底部输入框输入消息并发送
5. **创建房间**:点击"+ 添加房间"按钮创建新房间
6. **删除房间**:右键点击房间可删除(仅创建者可删除)
## 🔧 开发命令
- `npm run dev` - 启动开发服务器
- `npm run build` - 构建生产版本
- `npm run start` - 启动生产服务器
- `npm run lint` - 代码检查
- `npx prisma studio` - 打开数据库管理界面
## 📱 功能特性
✅ 用户注册和登录
✅ 自定义聊天昵称
✅ 创建和删除聊天房间
✅ 实时消息更新
✅ 响应式设计
✅ Docker 容器化部署
## 🐛 常见问题
**Q: 数据库文件在哪里?**
A: SQLite 数据库文件位于 `prisma/dev.db`
**Q: 如何重置数据库?**
A: 删除 `prisma/dev.db` 文件,然后重新运行 `npx prisma migrate dev``npx prisma db seed`
**Q: 如何更改数据库类型?**
A: 修改 `prisma/schema.prisma` 中的 `datasource` 配置
**Q: 端口被占用怎么办?**
A: 修改 `package.json` 中的 dev 脚本,添加 `-p 端口号` 参数
# 聊天室 Pro
一个基于 Next.js + TypeScript + Prisma 的全栈聊天应用。
## 功能特性
- 🔐 **用户认证**:注册、登录、JWT 身份验证
- 💬 **实时聊天**:支持多房间聊天,消息实时更新
- 🏠 **房间管理**:创建、删除聊天房间
- 👤 **自定义昵称**:用户可设置聊天昵称
- 📱 **响应式设计**:支持桌面和移动端
- 🐳 **Docker 部署**:完整的容器化解决方案
## 技术栈
### 前端
- **Next.js 14**:React 全栈框架,使用 App Router
- **TypeScript**:类型安全的 JavaScript
- **SWR**:数据获取和缓存库
- **CSS Modules**:样式管理
### 后端
- **Next.js API Routes**:后端 API 实现
- **Prisma ORM**:数据库操作和管理
- **SQLite**:轻量级数据库(可换成其他数据库)
- **JWT**:用户身份验证
- **bcryptjs**:密码加密
### 部署
- **Docker**:容器化部署
- **Docker Compose**:多服务编排
## 快速开始
### 本地开发
1. **克隆项目**
```bash
git clone <repository-url>
cd chatroom_pro
```
2. **安装依赖**
```bash
npm install
```
3. **设置环境变量**
```bash
cp .env.example .env.local
```
编辑 `.env.local` 文件,设置必要的环境变量:
```
DATABASE_URL="file:./dev.db"
JWT_SECRET="your-super-secret-jwt-key"
NEXTAUTH_SECRET="your-nextauth-secret"
NEXTAUTH_URL="http://localhost:3000"
```
4. **初始化数据库**
```bash
npx prisma generate
npx prisma migrate dev
npx prisma db seed
```
5. **启动开发服务器**
```bash
npm run dev
```
6. **访问应用**
打开浏览器访问 [http://localhost:3000](http://localhost:3000)
### Docker 部署
1. **使用 Docker Compose 部署**
```bash
docker-compose up -d
```
2. **访问应用**
打开浏览器访问 [http://localhost:3000](http://localhost:3000)
## 演示账号
系统默认创建了以下演示账号:
- **用户名**: `admin` **密码**: `123456`
- **用户名**: `demo` **密码**: `123456`
## 项目结构
```
chatroom_pro/
├── src/
│ ├── app/ # Next.js App Router 页面
│ │ ├── api/ # API 路由
│ │ │ ├── auth/ # 认证相关 API
│ │ │ ├── room/ # 房间相关 API
│ │ │ └── message/ # 消息相关 API
│ │ ├── globals.css # 全局样式
│ │ ├── layout.tsx # 根布局
│ │ └── page.tsx # 主页面
│ ├── components/ # React 组件
│ │ └── ChatRoom.tsx # 聊天室主组件
│ ├── contexts/ # React Context
│ │ └── AuthContext.tsx # 认证上下文
│ ├── lib/ # 工具库
│ │ ├── api.ts # API 客户端
│ │ ├── auth.ts # 认证工具
│ │ └── prisma.ts # Prisma 客户端
│ └── types/ # TypeScript 类型定义
│ └── api.ts # API 类型
├── prisma/ # 数据库相关
│ ├── migrations/ # 数据库迁移文件
│ ├── schema.prisma # 数据库模式
│ └── seed.ts # 种子数据
├── docker-compose.yml # Docker Compose 配置
├── Dockerfile # Docker 镜像配置
└── README.md # 项目文档
```
## API 接口文档
### 认证相关
#### 用户注册
- **URL**: `/api/auth/register`
- **方法**: `POST`
- **参数**:
```typescript
{
username: string;
password: string;
}
```
#### 用户登录
- **URL**: `/api/auth/login`
- **方法**: `POST`
- **参数**:
```typescript
{
username: string;
password: string;
}
```
### 房间管理
#### 创建房间
- **URL**: `/api/room/add`
- **方法**: `POST`
- **参数**:
```typescript
{
user: string;
roomName: string;
}
```
#### 获取房间列表
- **URL**: `/api/room/list`
- **方法**: `GET`
#### 删除房间
- **URL**: `/api/room/delete`
- **方法**: `POST`
- **参数**:
```typescript
{
user: string;
roomId: number;
}
```
### 消息管理
#### 发送消息
- **URL**: `/api/message/add`
- **方法**: `POST`
- **参数**:
```typescript
{
roomId: number;
content: string;
sender: string;
}
```
#### 获取房间消息
- **URL**: `/api/room/message/list`
- **方法**: `GET`
- **参数**: `?roomId=number`
#### 获取新消息
- **URL**: `/api/room/message/getUpdate`
- **方法**: `GET`
- **参数**: `?roomId=number&sinceMessageId=number`
## 开发说明
### 数据库模式
项目使用 Prisma ORM 管理数据库,包含以下模型:
- **User**: 用户表,存储用户名和加密密码
- **Room**: 房间表,存储房间信息和创建者
- **Message**: 消息表,存储聊天消息
### 认证机制
使用 JWT (JSON Web Token) 进行用户认证:
1. 用户登录后获得 JWT token
2. 前端将 token 存储在 localStorage
3. 每次 API 请求在 Authorization header 中携带 token
4. 后端验证 token 有效性
### 实时更新
目前使用轮询机制实现消息的实时更新:
- 每秒检查一次新消息
- 使用 SWR 进行数据缓存和更新
- 可以考虑升级到 WebSocket 实现真正的实时通信
## 部署指南
### 环境要求
- Node.js 18+
- Docker(可选)
- Git
### 生产环境配置
1. **设置环境变量**
```bash
DATABASE_URL="your-production-database-url"
JWT_SECRET="your-production-jwt-secret"
NEXTAUTH_SECRET="your-production-nextauth-secret"
NEXTAUTH_URL="your-production-domain"
```
2. **数据库迁移**
```bash
npx prisma migrate deploy
```
3. **构建应用**
```bash
npm run build
```
4. **启动服务**
```bash
npm start
```
### Docker 部署
使用提供的 `docker-compose.yml` 文件一键部署:
```bash
docker-compose up -d
```
## 贡献指南
1. Fork 项目
2. 创建功能分支 (`git checkout -b feature/AmazingFeature`)
3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
4. 推送到分支 (`git push origin feature/AmazingFeature`)
5. 开启 Pull Request
## 许可证
本项目采用 MIT 许可证 - 查看 [LICENSE](LICENSE) 文件了解详情。
## 联系方式
如有问题或建议,请提交 Issue 或联系项目维护者。
version: '3.8'
services:
# Next.js 应用
app:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=file:/app/data/dev.db
- JWT_SECRET=your-super-secret-jwt-key-for-production
- NEXTAUTH_SECRET=your-nextauth-secret-for-production
- NEXTAUTH_URL=http://localhost:3000
volumes:
- app_data:/app/data
depends_on:
- db_setup
restart: unless-stopped
# 数据库初始化服务
db_setup:
build: .
command: sh -c "npx prisma migrate deploy && npx prisma db seed"
environment:
- DATABASE_URL=file:/app/data/dev.db
volumes:
- app_data:/app/data
restart: "no"
volumes:
app_data:
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
appDir: true,
},
output: 'standalone',
}
module.exports = nextConfig
This diff is collapsed.
{
"name": "chatroom-pro",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"db:migrate": "prisma migrate dev",
"db:generate": "prisma generate",
"db:seed": "tsx prisma/seed.ts"
},
"prisma": {
"seed": "tsx prisma/seed.ts"
},
"dependencies": {
"next": "14.0.0",
"react": "^18",
"react-dom": "^18",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"typescript": "^5",
"swr": "^2.2.4",
"@prisma/client": "^5.6.0",
"prisma": "^5.6.0",
"bcryptjs": "^2.4.3",
"@types/bcryptjs": "^2.4.6",
"jsonwebtoken": "^9.0.2",
"@types/jsonwebtoken": "^9.0.5",
"zod": "^3.22.4",
"tsx": "^4.6.0"
},
"devDependencies": {
"eslint": "^8",
"eslint-config-next": "14.0.0"
}
}
\ No newline at end of file
-- CreateTable
CREATE TABLE "users" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"username" TEXT NOT NULL,
"password" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- CreateTable
CREATE TABLE "rooms" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"creatorId" INTEGER NOT NULL,
CONSTRAINT "rooms_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateTable
CREATE TABLE "messages" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"content" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"roomId" INTEGER NOT NULL,
"senderId" INTEGER NOT NULL,
CONSTRAINT "messages_roomId_fkey" FOREIGN KEY ("roomId") REFERENCES "rooms" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT "messages_senderId_fkey" FOREIGN KEY ("senderId") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateIndex
CREATE UNIQUE INDEX "users_username_key" ON "users"("username");
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
username String @unique
password String
createdAt DateTime @default(now())
// 用户创建的房间
createdRooms Room[]
// 用户发送的消息
messages Message[]
@@map("users")
}
model Room {
id Int @id @default(autoincrement())
name String
createdAt DateTime @default(now())
// 房间创建者
creatorId Int
creator User @relation(fields: [creatorId], references: [id], onDelete: Cascade)
// 房间内的消息
messages Message[]
@@map("rooms")
}
model Message {
id Int @id @default(autoincrement())
content String
createdAt DateTime @default(now())
// 消息所属房间
roomId Int
room Room @relation(fields: [roomId], references: [id], onDelete: Cascade)
// 消息发送者
senderId Int
sender User @relation(fields: [senderId], references: [id], onDelete: Cascade)
@@map("messages")
}
\ No newline at end of file
import { PrismaClient } from '@prisma/client'
import bcrypt from 'bcryptjs'
const prisma = new PrismaClient()
async function main() {
console.log('开始初始化数据库...')
// 创建演示用户
const hashedPassword = await bcrypt.hash('123456', 12)
const user1 = await prisma.user.upsert({
where: { username: 'admin' },
update: {},
create: {
username: 'admin',
password: hashedPassword,
},
})
const user2 = await prisma.user.upsert({
where: { username: 'demo' },
update: {},
create: {
username: 'demo',
password: hashedPassword,
},
})
// 创建演示房间
const room1 = await prisma.room.upsert({
where: { id: 1 },
update: {},
create: {
name: '欢迎大厅',
creatorId: user1.id,
},
})
const room2 = await prisma.room.upsert({
where: { id: 2 },
update: {},
create: {
name: '技术讨论',
creatorId: user1.id,
},
})
// 创建演示消息
await prisma.message.upsert({
where: { id: 1 },
update: {},
create: {
content: '欢迎来到聊天室!',
roomId: room1.id,
senderId: user1.id,
},
})
await prisma.message.upsert({
where: { id: 2 },
update: {},
create: {
content: '大家好,这里是技术讨论区',
roomId: room2.id,
senderId: user1.id,
},
})
console.log('数据库初始化完成!')
console.log('演示账号:')
console.log('用户名: admin, 密码: 123456')
console.log('用户名: demo, 密码: 123456')
}
main()
.then(async () => {
await prisma.$disconnect()
})
.catch(async (e) => {
console.error(e)
await prisma.$disconnect()
process.exit(1)
})
@echo off
echo 🚀 开始设置聊天室 Pro 项目...
:: 检查 Node.js 是否安装
where node >nul 2>nul
if %errorlevel% neq 0 (
echoNode.js 未安装,请先安装 Node.js 18+
pause
exit /b 1
)
:: 检查 npm 是否安装
where npm >nul 2>nul
if %errorlevel% neq 0 (
echonpm 未安装,请先安装 npm
pause
exit /b 1
)
echo 📦 安装依赖...
call npm install
echo 🗃️ 生成 Prisma 客户端...
call npx prisma generate
echo 🗄️ 运行数据库迁移...
call npx prisma migrate dev --name init
echo 🌱 填充种子数据...
call npx prisma db seed
echo.
echo ✅ 设置完成!
echo.
echo 🎉 您现在可以启动开发服务器:
echo npm run dev
echo.
echo 📱 然后在浏览器中访问:
echo http://localhost:3000
echo.
echo 👤 演示账号:
echo 用户名: admin, 密码: 123456
echo 用户名: demo, 密码: 123456
echo.
pause
#!/bin/bash
echo "🚀 开始设置聊天室 Pro 项目..."
# 检查 Node.js 是否安装
if ! command -v node &> /dev/null; then
echo "❌ Node.js 未安装,请先安装 Node.js 18+)"
exit 1
fi
# 检查 npm 是否安装
if ! command -v npm &> /dev/null; then
echo "❌ npm 未安装,请先安装 npm"
exit 1
fi
echo "📦 安装依赖..."
npm install
echo "🗃️ 生成 Prisma 客户端..."
npx prisma generate
echo "🗄️ 运行数据库迁移..."
npx prisma migrate dev --name init
echo "🌱 填充种子数据..."
npx prisma db seed
echo "✅ 设置完成!"
echo ""
echo "🎉 您现在可以启动开发服务器:"
echo " npm run dev"
echo ""
echo "📱 然后在浏览器中访问:"
echo " http://localhost:3000"
echo ""
echo "👤 演示账号:"
echo " 用户名: admin, 密码: 123456"
echo " 用户名: demo, 密码: 123456"
import { NextRequest, NextResponse } from 'next/server'
import { loginUser, generateToken } from '@/lib/auth'
import { LoginRequest, ApiResponse } from '@/types/api'
export async function POST(request: NextRequest) {
try {
const body: LoginRequest = await request.json()
const { username, password } = body
if (!username || !password) {
return NextResponse.json({
message: '用户名和密码不能为空',
code: 1,
data: null
} as ApiResponse, { status: 400 })
}
const user = await loginUser(username, password)
const token = generateToken(user)
return NextResponse.json({
message: '登录成功',
code: 0,
data: {
user,
token
}
} as ApiResponse)
} catch (error) {
return NextResponse.json({
message: error instanceof Error ? error.message : '登录失败',
code: 1,
data: null
} as ApiResponse, { status: 401 })
}
}
import { NextRequest, NextResponse } from 'next/server'
import { registerUser, generateToken } from '@/lib/auth'
import { RegisterRequest, ApiResponse } from '@/types/api'
export async function POST(request: NextRequest) {
try {
const body: RegisterRequest = await request.json()
const { username, password } = body
if (!username || !password) {
return NextResponse.json({
message: '用户名和密码不能为空',
code: 1,
data: null
} as ApiResponse, { status: 400 })
}
if (username.length < 2 || password.length < 6) {
return NextResponse.json({
message: '用户名至少2个字符,密码至少6个字符',
code: 1,
data: null
} as ApiResponse, { status: 400 })
}
const user = await registerUser(username, password)
const token = generateToken(user)
return NextResponse.json({
message: '注册成功',
code: 0,
data: {
user,
token
}
} as ApiResponse)
} catch (error) {
return NextResponse.json({
message: error instanceof Error ? error.message : '注册失败',
code: 1,
data: null
} as ApiResponse, { status: 400 })
}
}
import { NextRequest, NextResponse } from 'next/server'
import { getUserFromRequest } from '@/lib/auth'
import { prisma } from '@/lib/prisma'
import { MessageAddArgs, ApiResponse } from '@/types/api'
export async function POST(request: NextRequest) {
try {
// 验证用户身份
const user = await getUserFromRequest(request)
if (!user) {
return NextResponse.json({
message: '未登录或登录已过期',
code: 1,
data: null
} as ApiResponse, { status: 401 })
}
const body: MessageAddArgs = await request.json()
const { roomId, content } = body
if (!roomId || !content || content.trim().length === 0) {
return NextResponse.json({
message: '房间 ID 和消息内容不能为空',
code: 1,
data: null
} as ApiResponse, { status: 400 })
}
// 检查房间是否存在
const room = await prisma.room.findUnique({
where: { id: roomId }
})
if (!room) {
return NextResponse.json({
message: '房间不存在',
code: 1,
data: null
} as ApiResponse, { status: 404 })
}
// 创建消息
await prisma.message.create({
data: {
content: content.trim(),
roomId,
senderId: user.id
}
})
return NextResponse.json({
message: '消息发送成功',
code: 0,
data: null
} as ApiResponse)
} catch (error) {
console.error('发送消息失败:', error)
return NextResponse.json({
message: '发送消息失败',
code: 1,
data: null
} as ApiResponse, { status: 500 })
}
}
import { NextRequest, NextResponse } from 'next/server'
import { getUserFromRequest } from '@/lib/auth'
import { prisma } from '@/lib/prisma'
import { RoomAddArgs, ApiResponse, RoomAddRes } from '@/types/api'
export async function POST(request: NextRequest) {
try {
// 验证用户身份
const user = await getUserFromRequest(request)
if (!user) {
return NextResponse.json({
message: '未登录或登录已过期',
code: 1,
data: null
} as ApiResponse, { status: 401 })
}
const body: RoomAddArgs = await request.json()
const { roomName } = body
if (!roomName || roomName.trim().length === 0) {
return NextResponse.json({
message: '房间名称不能为空',
code: 1,
data: null
} as ApiResponse, { status: 400 })
}
// 创建房间
const room = await prisma.room.create({
data: {
name: roomName.trim(),
creatorId: user.id
}
})
return NextResponse.json({
message: '房间创建成功',
code: 0,
data: {
roomId: room.id
} as RoomAddRes
} as ApiResponse)
} catch (error) {
console.error('创建房间失败:', error)
return NextResponse.json({
message: '创建房间失败',
code: 1,
data: null
} as ApiResponse, { status: 500 })
}
}
import { NextRequest, NextResponse } from 'next/server'
import { getUserFromRequest } from '@/lib/auth'
import { prisma } from '@/lib/prisma'
import { RoomDeleteArgs, ApiResponse } from '@/types/api'
export async function POST(request: NextRequest) {
try {
// 验证用户身份
const user = await getUserFromRequest(request)
if (!user) {
return NextResponse.json({
message: '未登录或登录已过期',
code: 1,
data: null
} as ApiResponse, { status: 401 })
}
const body: RoomDeleteArgs = await request.json()
const { roomId } = body
if (!roomId) {
return NextResponse.json({
message: '房间 ID 不能为空',
code: 1,
data: null
} as ApiResponse, { status: 400 })
}
// 检查房间是否存在以及用户是否为创建者
const room = await prisma.room.findUnique({
where: { id: roomId }
})
if (!room) {
return NextResponse.json({
message: '房间不存在',
code: 1,
data: null
} as ApiResponse, { status: 404 })
}
if (room.creatorId !== user.id) {
return NextResponse.json({
message: '只有房间创建者可以删除房间',
code: 1,
data: null
} as ApiResponse, { status: 403 })
}
// 删除房间(会级联删除相关消息)
await prisma.room.delete({
where: { id: roomId }
})
return NextResponse.json({
message: '房间删除成功',
code: 0,
data: null
} as ApiResponse)
} catch (error) {
console.error('删除房间失败:', error)
return NextResponse.json({
message: '删除房间失败',
code: 1,
data: null
} as ApiResponse, { status: 500 })
}
}
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
import { ApiResponse, RoomListRes, RoomPreviewInfo } from '@/types/api'
export async function GET(request: NextRequest) {
try {
// 获取所有房间及其最新消息
const rooms = await prisma.room.findMany({
include: {
messages: {
orderBy: {
createdAt: 'desc'
},
take: 1,
include: {
sender: {
select: {
username: true
}
}
}
},
creator: {
select: {
username: true
}
}
},
orderBy: {
createdAt: 'desc'
}
})
const roomPreviews: RoomPreviewInfo[] = rooms.map(room => ({
roomId: room.id,
roomName: room.name,
lastMessage: room.messages.length > 0 ? {
messageId: room.messages[0].id,
roomId: room.messages[0].roomId,
sender: room.messages[0].sender.username,
content: room.messages[0].content,
time: room.messages[0].createdAt.getTime()
} : null
}))
return NextResponse.json({
message: '获取房间列表成功',
code: 0,
data: {
rooms: roomPreviews
} as RoomListRes
} as ApiResponse)
} catch (error) {
console.error('获取房间列表失败:', error)
return NextResponse.json({
message: '获取房间列表失败',
code: 1,
data: null
} as ApiResponse, { status: 500 })
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment