Commit 496f7668 authored by 何 广一's avatar 何 广一
Browse files

prisma & auth api

parent 35afac8f
...@@ -39,3 +39,5 @@ yarn-error.log* ...@@ -39,3 +39,5 @@ yarn-error.log*
# typescript # typescript
*.tsbuildinfo *.tsbuildinfo
next-env.d.ts next-env.d.ts
/src/generated/prisma
...@@ -3,25 +3,32 @@ ...@@ -3,25 +3,32 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev --turbopack", "dev": "pnpm run redis & next dev --turbopack",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"lint": "next lint" "lint": "next lint",
"redis": "start redis-server --port 6379"
}, },
"dependencies": { "dependencies": {
"@prisma/client": "6.14.0",
"next": "15.4.6",
"rate-limiter-flexible": "^7.2.0",
"react": "19.1.0", "react": "19.1.0",
"react-dom": "19.1.0", "react-dom": "19.1.0",
"next": "15.4.6" "redis": "^5.8.2",
"zod": "^4.1.1"
}, },
"devDependencies": { "devDependencies": {
"typescript": "^5", "@eslint/eslintrc": "^3",
"@tailwindcss/postcss": "^4",
"@types/node": "^20", "@types/node": "^20",
"@types/react": "^19", "@types/react": "^19",
"@types/react-dom": "^19", "@types/react-dom": "^19",
"@tailwindcss/postcss": "^4", "concurrently": "^9.2.0",
"tailwindcss": "^4",
"eslint": "^9", "eslint": "^9",
"eslint-config-next": "15.4.6", "eslint-config-next": "15.4.6",
"@eslint/eslintrc": "^3" "prisma": "6.14.0",
"tailwindcss": "^4",
"typescript": "^5"
} }
} }
This diff is collapsed.
-- CreateTable
CREATE TABLE "User" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" TEXT NOT NULL,
"password" TEXT NOT NULL,
"salt" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL
);
-- CreateTable
CREATE TABLE "Room" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"roomName" TEXT NOT NULL,
"creatorId" INTEGER NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "Room_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateTable
CREATE TABLE "Message" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"content" TEXT NOT NULL,
"roomId" INTEGER NOT NULL,
"senderId" INTEGER NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "Message_roomId_fkey" FOREIGN KEY ("roomId") REFERENCES "Room" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT "Message_senderId_fkey" FOREIGN KEY ("senderId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateIndex
CREATE UNIQUE INDEX "User_name_key" ON "User"("name");
-- CreateIndex
CREATE INDEX "Room_creatorId_idx" ON "Room"("creatorId");
-- CreateIndex
CREATE INDEX "Message_roomId_idx" ON "Message"("roomId");
-- CreateIndex
CREATE INDEX "Message_senderId_idx" ON "Message"("senderId");
/*
Warnings:
- You are about to drop the column `senderId` on the `Message` table. All the data in the column will be lost.
- You are about to drop the column `creatorId` on the `Room` table. All the data in the column will be lost.
- The primary key for the `User` table will be changed. If it partially fails, the table could be left without primary key constraint.
- You are about to drop the column `id` on the `User` table. All the data in the column will be lost.
- Added the required column `senderName` to the `Message` table without a default value. This is not possible if the table is not empty.
- Added the required column `creatorToken` to the `Room` table without a default value. This is not possible if the table is not empty.
- Added the required column `userToken` to the `User` table without a default value. This is not possible if the table is not empty.
*/
-- CreateTable
CREATE TABLE "AuthToken" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"token" TEXT NOT NULL,
"userName" TEXT NOT NULL,
"expiresAt" DATETIME NOT NULL,
"ua" TEXT,
"ip" TEXT,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "AuthToken_userName_fkey" FOREIGN KEY ("userName") REFERENCES "User" ("name") ON DELETE CASCADE ON UPDATE CASCADE
);
-- RedefineTables
PRAGMA defer_foreign_keys=ON;
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_Message" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"content" TEXT NOT NULL,
"roomId" INTEGER NOT NULL,
"senderName" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "Message_roomId_fkey" FOREIGN KEY ("roomId") REFERENCES "Room" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT "Message_senderName_fkey" FOREIGN KEY ("senderName") REFERENCES "User" ("name") ON DELETE CASCADE ON UPDATE CASCADE
);
INSERT INTO "new_Message" ("content", "createdAt", "id", "roomId") SELECT "content", "createdAt", "id", "roomId" FROM "Message";
DROP TABLE "Message";
ALTER TABLE "new_Message" RENAME TO "Message";
CREATE INDEX "Message_roomId_idx" ON "Message"("roomId");
CREATE INDEX "Message_senderName_idx" ON "Message"("senderName");
CREATE TABLE "new_Room" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"roomName" TEXT NOT NULL,
"creatorToken" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "Room_creatorToken_fkey" FOREIGN KEY ("creatorToken") REFERENCES "User" ("userToken") ON DELETE CASCADE ON UPDATE CASCADE
);
INSERT INTO "new_Room" ("createdAt", "id", "roomName", "updatedAt") SELECT "createdAt", "id", "roomName", "updatedAt" FROM "Room";
DROP TABLE "Room";
ALTER TABLE "new_Room" RENAME TO "Room";
CREATE INDEX "Room_creatorToken_idx" ON "Room"("creatorToken");
CREATE TABLE "new_User" (
"name" TEXT NOT NULL PRIMARY KEY,
"password" TEXT NOT NULL,
"salt" TEXT NOT NULL,
"userToken" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL
);
INSERT INTO "new_User" ("createdAt", "name", "password", "salt", "updatedAt") SELECT "createdAt", "name", "password", "salt", "updatedAt" FROM "User";
DROP TABLE "User";
ALTER TABLE "new_User" RENAME TO "User";
CREATE UNIQUE INDEX "User_name_key" ON "User"("name");
CREATE UNIQUE INDEX "User_userToken_key" ON "User"("userToken");
PRAGMA foreign_keys=ON;
PRAGMA defer_foreign_keys=OFF;
-- CreateIndex
CREATE UNIQUE INDEX "AuthToken_token_key" ON "AuthToken"("token");
-- CreateIndex
CREATE INDEX "AuthToken_userName_idx" ON "AuthToken"("userName");
-- CreateIndex
CREATE INDEX "AuthToken_token_idx" ON "AuthToken"("token");
# Please do not edit this file manually
# It should be added in your version-control system (e.g., Git)
provider = "sqlite"
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
generator client {
provider = "prisma-client-js"
output = "../src/generated/prisma"
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
model User {
name String @id @unique
password String
salt String
userToken String @unique
createdRooms Room[] @relation("RoomCreator")
messages Message[]
authTokens AuthToken[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model AuthToken {
id Int @id @default(autoincrement())
token String @unique
userName String
user User @relation(fields: [userName], references: [name], onDelete: Cascade)
expiresAt DateTime // 过期时间
ua String?
ip String?
createdAt DateTime @default(now())
@@index([userName])
@@index([token])
}
model Room {
id Int @id @default(autoincrement())
roomName String
creatorToken String
creator User @relation("RoomCreator", fields: [creatorToken], references: [userToken], onDelete: Cascade)
messages Message[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([creatorToken])
}
model Message {
id Int @id @default(autoincrement())
content String
roomId Int
room Room @relation(fields: [roomId], references: [id], onDelete: Cascade)
senderName String
sender User @relation(fields: [senderName], references: [name], onDelete: Cascade)
createdAt DateTime @default(now())
@@index([roomId])
@@index([senderName])
}
\ No newline at end of file
import { NextRequest, NextResponse } from 'next/server';
import * as Ty from '../../../interface'
import * as Db from '../../room-api';
import * as Auth from '../../login-interface';
/*
- url: /api/auth/autoLogin
- method: POST
- Read & Check Cookie
*/
export async function POST(req: NextRequest): Promise<NextResponse<Ty.BackendResponse<Auth.LoginResult | null>>> {
const cookieData = await Db.VerifyCookie(req.cookies)
if('code' in cookieData) return NextResponse.json(cookieData);
const Args: Auth.AutoLoginArgs = {
cookie: cookieData
};
const loginResult = await Db.AutoLogin(Args)
return NextResponse.json(loginResult)
}
\ No newline at end of file
import { NextRequest, NextResponse } from 'next/server';
import * as Ty from '../../../interface'
import * as Db from '../../room-api';
import * as Auth from '../../login-interface';
/*
- url: /api/auth/login
- method: POST
- Set Cookie
*/
export async function POST(req: NextRequest): Promise<NextResponse<Ty.BackendResponse<Auth.LoginResult | null>>> {
const result = Auth.authArgsSchema.safeParse(await req.json());
if (!result.success) {
return Db.MakeError('无效的请求参数', 114514, 400);
}
const Args: Auth.AuthArgs = result.data;
Args.userIP = req.headers.get('x-forwarded-for') || null;
const loginResult = await Db.Login(Args);
const res = NextResponse.json(loginResult);
if(loginResult.data)
{
res.cookies.set('userToken', loginResult.data.cookie.userToken, { path: '/' });
res.cookies.set('authToken', loginResult.data.cookie.authToken, { path: '/' });
}
return res;
}
\ No newline at end of file
import { NextRequest, NextResponse } from 'next/server';
import * as Ty from '../../../interface'
import * as Db from '../../room-api';
import * as Auth from '../../login-interface';
/*
- url: /api/auth/logout
- method: POST
- Read & Check Cookie
*/
export async function POST(req: NextRequest): Promise<NextResponse<Ty.BackendResponse<null>>> {
const cookieData = await Db.VerifyCookie(req.cookies)
if('code' in cookieData)return NextResponse.json(cookieData);
const logoutResult = await Db.Logout(cookieData);
const res = NextResponse.json(logoutResult);
res.cookies.set('userToken', '', { maxAge: 0, path: '/' });
res.cookies.set('authToken', '', { maxAge: 0, path: '/' });
return res;
}
\ No newline at end of file
import { NextRequest, NextResponse } from 'next/server';
import * as Ty from '../../../interface'
import * as Db from '../../room-api';
import * as Auth from '../../login-interface';
/*
- url: /api/auth/register
- method: POST
*/
export async function POST(req: NextRequest): Promise<NextResponse<Ty.BackendResponse<Auth.RegisterResult | null>>> {
const result = Auth.authArgsSchema.safeParse(await req.json());
if (!result.success) {
return Db.MakeError('无效的请求参数', 114514, 400);
}
const Args: Auth.AuthArgs = result.data;
Args.userIP = req.headers.get('x-forwarded-for') || null;
const registerResult = await Db.Register(Args);
return NextResponse.json(registerResult);
}
\ No newline at end of file
import { NextRequest, NextResponse } from 'next/server';
import * as Ty from '../../../interface'
import * as Db from '../../room-api';
import * as Auth from '../../login-interface';
/*
- url: /api/auth/startSession
- method: GET
*/
export async function GET(req: NextRequest): Promise<NextResponse<Ty.BackendResponse<Auth.Session | null>>> {
const result = await Db.StartAuthSession();
return NextResponse.json(result);
}
\ No newline at end of file
import {z} from 'zod';
export interface Session {
sessionId: string;
}
export interface AuthArgs {
sessionId: string;
username: string;
password: string;
userAgent: string | null;
userIP: string | null;
}
export interface LoginCookieData {
userToken: string;
authToken: string;
}
export interface LoginResult {
user: string;
cookie: LoginCookieData;
}
export interface RegisterResult {
user: string;
}
export interface AutoLoginArgs {
cookie: LoginCookieData;
}
export const cookieSchema = z.object({
userToken: z.string().nullable(),
authToken: z.string().nullable()
});
export const minPasswordLength = 8;
export const maxPasswordLength = 100;
export const authArgsSchema = z.object({
sessionId: z.string(),
username: z.string().min(2).max(100),
password: z.string().min(minPasswordLength).max(maxPasswordLength),
userAgent: z.string().max(200).nullable(),
userIP: z.string().max(100).nullable()
});
export const reloginErrorCode = 1919810;
export const invalidUserErrorCode = 8848;
export const notAuthorizedToDeleteRoomErrorCode = 2;
...@@ -14,6 +14,10 @@ interface MessageAddArgs { ...@@ -14,6 +14,10 @@ interface MessageAddArgs {
- response: null (只要code为0即为成功) - response: null (只要code为0即为成功)
*/ */
export async function POST(req: NextRequest): Promise<NextResponse<Ty.BackendResponse<null>>> { export async function POST(req: NextRequest): Promise<NextResponse<Ty.BackendResponse<null>>> {
const cookieData = await Db.VerifyCookie(req.cookies);
if('code' in cookieData)return NextResponse.json(cookieData);
const { roomId, content, sender } = await req.json() as Ty.MessageAddArgs; const { roomId, content, sender } = await req.json() as Ty.MessageAddArgs;
const response = await Db.AddMessage({ roomId, content, sender }); const response = await Db.AddMessage({ roomId, content, sender });
return NextResponse.json(response); return NextResponse.json(response);
......
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
import { RequestCookies } from 'next/dist/compiled/@edge-runtime/cookies';
import { createHash, randomBytes } from 'crypto';
import * as Ty from '../interface' import * as Ty from '../interface'
import {RoomInfo, State} from './tmp-state' import {prisma, redis} from "@/lib/db";
import * as Auth from './login-interface';
export async function MakeError(message: string, code: number = 1): Promise<NextResponse<Ty.BackendResponse<null>>> { export async function MakeError(message: string, code: number = 1, status: number = 500): Promise<NextResponse<Ty.BackendResponse<null>>> {
return NextResponse.json({ return NextResponse.json({
code, code,
message, message,
data: null data: null
}); }, { status });
} }
export async function GetRoomList(): Promise<Ty.BackendResponse<Ty.RoomListRes>> { const UserExists = async (name: string): Promise<boolean> => {
console.log('获取到的房间列表:', State.fakeRooms as Ty.RoomPreviewInfo[]); const user = await prisma.user.findUnique({
where: { name },
select: { name: true }
});
return user !== null;
};
const toFrontMsg = async (m: any) : Promise<Ty.Message | null> => {
if(!m.senderName.length) return null;
if(!await UserExists(m.senderName)) return null;
return { return {
code: 0, messageId: m.id,
message: '房间获取成功', roomId: m.roomId,
data: { sender: m.senderName,
rooms: State.fakeRooms as Ty.RoomPreviewInfo[] content: m.content,
} time: m.createdAt.getTime(),
}; };
};
const getUserToken = async (name: string) => {
const user = await prisma.user.findUnique({
where: { name },
select: { userToken: true }
});
return user ? user.userToken : null;
};
const getRandomToken = (): string => {
return [...crypto.getRandomValues(new Uint8Array(16))]
.map(b => b.toString(36))
.join('');
};
const getRandomStr = () => (randomBytes(32).toString('hex'));
export async function GetRoomList(): Promise<Ty.BackendResponse<Ty.RoomListRes | null>> {
const rooms = await prisma.room.findMany({
select: {
id: true,
roomName: true,
messages: {
orderBy: { createdAt: 'desc' },
take: 1
},
},
});
const preview: Ty.RoomPreviewInfo[] = await Promise.all(rooms.map(async (r) => {
const lastMessage = r.messages[0] ? await toFrontMsg(r.messages[0]) : null;
return {
roomId: r.id,
roomName: r.roomName,
lastMessage
};
}));
return {
code: 0,
message: '房间获取成功',
data: {
rooms: preview
}
};
} }
export async function AddRoom(data: Ty.RoomAddArgs): Promise<Ty.BackendResponse<Ty.RoomAddResult>> { export async function AddRoom(data: Ty.RoomAddArgs): Promise<Ty.BackendResponse<Ty.RoomAddResult>> {
const newRoom: RoomInfo = {
roomId: State.maxRoomId + 1, const userName = data.user;
roomName: data.roomName, const creatorToken = await getUserToken(userName);
lastMessage: null,
creator: data.user if(!creatorToken)
}; {
State.fakeRooms = [...State.fakeRooms, newRoom]; return {
console.log('当前房间列表:', State.fakeRooms); code: 1,
State.maxRoomId++; message: '用户不存在',
data: {
roomId: -1
}
};
}
const newRoom = await prisma.room.create({
data: {
roomName: data.roomName,
creatorToken
}
});
return { return {
code: 0, code: 0,
message: '房间创建成功', message: '房间创建成功',
data: { data: {
roomId: newRoom.roomId roomId: newRoom.id
} }
}; };
} }
export async function GetMessageList(data : Ty.RoomMessageListArgs): Promise<Ty.BackendResponse<Ty.RoomMessageListRes>> { export async function GetMessageList(data : Ty.RoomMessageListArgs): Promise<Ty.BackendResponse<Ty.RoomMessageListRes>> {
const { roomId } = data; const { roomId } = data;
const messages = State.fakeMsg.filter(msg => msg.roomId === roomId); const messages = await prisma.message.findMany({
where: { roomId },
orderBy: { createdAt: 'asc' }
});
const messageList: Ty.Message[] =
await Promise.all(messages.map(toFrontMsg))
.then(msgs => msgs.filter((m): m is Ty.Message => m !== null));
return { return {
code: 0, code: 0,
message: '消息获取成功', message: '消息获取成功',
data: { data: {
messages messages: messageList
} }
}; };
} }
export async function UpdateMessageList(data: Ty.RoomMessageGetUpdateArgs): Promise<Ty.BackendResponse<Ty.RoomMessageGetUpdateRes>> { export async function UpdateMessageList(data: Ty.RoomMessageGetUpdateArgs): Promise<Ty.BackendResponse<Ty.RoomMessageGetUpdateRes>> {
const { roomId, sinceMessageId } = data; const { roomId, sinceMessageId } = data;
const messages = State.fakeMsg.filter(msg => msg.roomId === roomId && msg.messageId > sinceMessageId); const messages = await prisma.message.findMany({
where: {
roomId,
id: { gt: sinceMessageId }
},
orderBy: { createdAt: 'asc' }
});
const messageList: Ty.Message[] =
await Promise.all(messages.map(toFrontMsg))
.then(msgs => msgs.filter((m): m is Ty.Message => m !== null));
return { return {
code: 0, code: 0,
message: '消息更新成功', message: '消息更新成功',
data: { data: {
messages messages: messageList
} }
}; };
} }
//后续需要补上房间建立者和删除者不同的无权限错误
export async function DeleteRoom(data: Ty.RoomDeleteArgs): Promise<Ty.BackendResponse<null>> { export async function DeleteRoom(data: Ty.RoomDeleteArgs, operatorToken: String ): Promise<Ty.BackendResponse<null>> {
const { user, roomId } = data; const { user, roomId } = data;
State.fakeRooms = State.fakeRooms.filter(room => room.roomId !== roomId);
State.fakeMsg = State.fakeMsg.filter(msg => msg.roomId !== roomId); const room = await prisma.room.findUnique({
where: { id: roomId },
select: { creator: { select: { name: true, userToken: true } } }
});
if (!room || room.creator.name !== user || room.creator.userToken !== operatorToken) {
return {
code: Auth.notAuthorizedToDeleteRoomErrorCode,
message: '无权限',
data: null
};
}
await prisma.room.delete({
where: { id: roomId }
});
return { return {
code: 0, code: 0,
message: '房间删除成功', message: '房间删除成功',
...@@ -78,18 +185,235 @@ export async function DeleteRoom(data: Ty.RoomDeleteArgs): Promise<Ty.BackendRes ...@@ -78,18 +185,235 @@ export async function DeleteRoom(data: Ty.RoomDeleteArgs): Promise<Ty.BackendRes
export async function AddMessage(data: Ty.MessageAddArgs): Promise<Ty.BackendResponse<null>> { export async function AddMessage(data: Ty.MessageAddArgs): Promise<Ty.BackendResponse<null>> {
const { roomId, sender, content } = data; const { roomId, sender, content } = data;
const newMessage: Ty.Message = {
messageId: State.maxMessageId + 1, console.log(`Adding message to room ${roomId} from user ${sender}: ${content}`);
roomId,
sender, if (!await UserExists(sender)) {
content, return {
time: Date.now() code: 1,
}; message: '用户不存在',
State.fakeMsg = [...State.fakeMsg, newMessage]; data: null
State.maxMessageId++; };
}
await prisma.message.create({
data: {
roomId,
senderName: sender,
content
}
});
return { return {
code: 0, code: 0,
message: '消息发送成功', message: '消息发送成功',
data: null data: null
}; };
}
export async function StartAuthSession() : Promise<Ty.BackendResponse<Auth.Session>> {
const sessionId = getRandomToken();
redis.set(`Session:${sessionId}`, "{}");
redis.expire(`Session:${sessionId}`, 60 * 5); // 5分钟内登录/注册有效
return {
code: 0,
message: '获取成功',
data: {
sessionId,
}
};
}
export async function Login(args: Auth.AuthArgs) : Promise<Ty.BackendResponse<Auth.LoginResult | null>> {
const { sessionId, username, password, userAgent, userIP } = args;
const sessionStr = await redis.get(`Session:${sessionId}`);
if(!sessionStr)
{
return {
code: 1,
message: '会话过期,请刷新页面后重试',
data: null
};
}
const user = await prisma.user.findUnique({
where: { name: username },
select: { password: true, salt: true, userToken: true }
});
if(!user || user.password !== createHash('sha256').update(password + user.salt).digest('hex'))
{
return {
code: 3,
message: '用户名或密码错误',
data: null
};
}
const authToken = getRandomToken();
await prisma.authToken.create({
data: {
token: authToken,
userName: username,
expiresAt: new Date(Date.now() + 60 * 60 * 1000 * 24 * 7), // 7 days
ua: userAgent,
ip: userIP
}
});
await redis.del(`Session:${sessionId}`);
return {
code: 0,
message: '登录成功',
data: {
user: username,
cookie: {
userToken: user.userToken,
authToken
}
}
};
}
export async function AutoLogin(args: Auth.AutoLoginArgs) : Promise<Ty.BackendResponse<Auth.LoginResult | null>> {
const { cookie } = args;
const { authToken } = cookie;
const user = await prisma.authToken.findUnique({
where: { token: authToken },
select: { user: true, expiresAt: true }
});
if (!user) {
return {
code: 101,
message: '无效的用户信息',
data: null
};
}
return {
code: 0,
message: '自动登录成功',
data: {
user: user.user.name,
cookie: {
userToken: cookie.userToken,
authToken: cookie.authToken
}
}
};
}
export async function Register(args: Auth.AuthArgs) : Promise<Ty.BackendResponse<Auth.RegisterResult | null>> {
const { sessionId, username, password } = args;
const sessionStr = await redis.get(`Session:${sessionId}`);
if(!sessionStr)
{
return {
code: 1,
message: '会话过期,请刷新页面后重试',
data: null
};
}
const user = await prisma.user.findUnique({
where: { name: username },
select: { name: true }
});
if(user)
{
return {
code: 3,
message: '用户名已存在',
data: null
};
}
const salt = getRandomStr();
const hashedPassword = createHash('sha256').update(password + salt).digest('hex');
await prisma.user.create({
data: {
name: username,
password: hashedPassword,
salt,
userToken: getRandomToken()
}
});
await redis.del(`Session:${sessionId}`);
return {
code: 0,
message: '注册成功',
data: {
user: username,
}
};
}
export async function Logout(args: Auth.LoginCookieData) : Promise<Ty.BackendResponse<null>> {
const { authToken } = args;
await prisma.authToken.delete({
where: { token: authToken }
});
return {
code: 0,
message: '登出成功',
data: null
};
}
export async function VerifyCookie(cookie : RequestCookies) : Promise<Ty.BackendResponse<null> | Auth.LoginCookieData> {
const result = Auth.cookieSchema.safeParse({
userToken: cookie.get('userToken')?.value || null,
authToken: cookie.get('authToken')?.value || null
});
if(!result.success || !result.data.authToken || !result.data.userToken) {
return {
code: 1,
message: '无效的Cookie',
data: null
};
}
const user = await prisma.authToken.findUnique({
where: { token: result.data.authToken },
select: { user: true, expiresAt: true }
});
if (!user || user.user.userToken !== result.data.userToken) {
return {
code: Auth.invalidUserErrorCode,
message: '无效的用户信息',
data: null
};
}
if (user.expiresAt < new Date()) {
await prisma.authToken.delete({
where: { token: result.data.authToken }
});
return {
code: Auth.reloginErrorCode,
message: '登录过期,请重新登录。',
data: null
};
}
const cookieData: Auth.LoginCookieData = {
userToken: result.data.userToken,
authToken: result.data.authToken
};
return cookieData;
} }
\ No newline at end of file
...@@ -14,7 +14,11 @@ interface RoomDeleteArgs { ...@@ -14,7 +14,11 @@ interface RoomDeleteArgs {
*/ */
export async function POST(req: NextRequest): Promise<NextResponse<Ty.BackendResponse<null>>> { export async function POST(req: NextRequest): Promise<NextResponse<Ty.BackendResponse<null>>> {
const cookieData = await Db.VerifyCookie(req.cookies);
if('code' in cookieData) return NextResponse.json(cookieData);
const { user, roomId } = await req.json() as Ty.RoomDeleteArgs; const { user, roomId } = await req.json() as Ty.RoomDeleteArgs;
const response = await Db.DeleteRoom({ user, roomId }); const response = await Db.DeleteRoom({ user, roomId }, cookieData.userToken);
return NextResponse.json(response); return NextResponse.json(response);
} }
\ No newline at end of file
...@@ -11,8 +11,10 @@ interface RoomListRes { ...@@ -11,8 +11,10 @@ interface RoomListRes {
rooms: RoomPreviewInfo[]; rooms: RoomPreviewInfo[];
} }
*/ */
export async function GET(req: NextRequest) : export async function GET(req: NextRequest) : Promise<NextResponse<Ty.BackendResponse<Ty.RoomListRes | null>>> {
Promise<NextResponse<Ty.BackendResponse<Ty.RoomListRes>>> { const cookieData = await Db.VerifyCookie(req.cookies);
if('code' in cookieData) return NextResponse.json(cookieData);
const response = await Db.GetRoomList(); const response = await Db.GetRoomList();
return NextResponse.json(response); return NextResponse.json(response);
} }
\ No newline at end of file
...@@ -16,7 +16,11 @@ interface RoomMessageGetUpdateRes { ...@@ -16,7 +16,11 @@ interface RoomMessageGetUpdateRes {
} }
*/ */
export async function GET(req: NextRequest): Promise<NextResponse<Ty.BackendResponse<Ty.RoomMessageGetUpdateRes>>> { export async function GET(req: NextRequest): Promise<NextResponse<Ty.BackendResponse<Ty.RoomMessageGetUpdateRes | null>>> {
const cookieData = await Db.VerifyCookie(req.cookies);
if('code' in cookieData) return NextResponse.json(cookieData);
const Params = req.nextUrl.searchParams; const Params = req.nextUrl.searchParams;
const result = await Db.UpdateMessageList({ const result = await Db.UpdateMessageList({
roomId: Number(Params.get('roomId')), roomId: Number(Params.get('roomId')),
......
...@@ -14,7 +14,10 @@ interface RoomMessageListRes { ...@@ -14,7 +14,10 @@ interface RoomMessageListRes {
messages: Message[]; messages: Message[];
} }
*/ */
export async function GET(req: NextRequest): Promise<NextResponse<Ty.BackendResponse<Ty.RoomMessageListRes>>> { export async function GET(req: NextRequest): Promise<NextResponse<Ty.BackendResponse<Ty.RoomMessageListRes | null>>> {
const cookieData = await Db.VerifyCookie(req.cookies);
if('code' in cookieData) return NextResponse.json(cookieData);
const Params = req.nextUrl.searchParams; const Params = req.nextUrl.searchParams;
const response = await Db.GetMessageList({ roomId: Number(Params.get('roomId')) }); const response = await Db.GetMessageList({ roomId: Number(Params.get('roomId')) });
return NextResponse.json(response); return NextResponse.json(response);
......
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