Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
何 广一
chatroom
Commits
496f7668
Commit
496f7668
authored
Aug 26, 2025
by
何 广一
Browse files
prisma & auth api
parent
35afac8f
Changes
26
Expand all
Hide whitespace changes
Inline
Side-by-side
chat-room/.gitignore
View file @
496f7668
...
...
@@ -39,3 +39,5 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
/src/generated/prisma
chat-room/package.json
View file @
496f7668
...
...
@@ -3,25 +3,32 @@
"version"
:
"0.1.0"
,
"private"
:
true
,
"scripts"
:
{
"dev"
:
"next dev --turbopack"
,
"dev"
:
"
pnpm run redis &
next dev --turbopack"
,
"build"
:
"next build"
,
"start"
:
"next start"
,
"lint"
:
"next lint"
"lint"
:
"next lint"
,
"redis"
:
"start redis-server --port 6379"
},
"dependencies"
:
{
"@prisma/client"
:
"6.14.0"
,
"next"
:
"15.4.6"
,
"rate-limiter-flexible"
:
"^7.2.0"
,
"react"
:
"19.1.0"
,
"react-dom"
:
"19.1.0"
,
"next"
:
"15.4.6"
"redis"
:
"^5.8.2"
,
"zod"
:
"^4.1.1"
},
"devDependencies"
:
{
"typescript"
:
"^5"
,
"@eslint/eslintrc"
:
"^3"
,
"@tailwindcss/postcss"
:
"^4"
,
"@types/node"
:
"^20"
,
"@types/react"
:
"^19"
,
"@types/react-dom"
:
"^19"
,
"@tailwindcss/postcss"
:
"^4"
,
"tailwindcss"
:
"^4"
,
"concurrently"
:
"^9.2.0"
,
"eslint"
:
"^9"
,
"eslint-config-next"
:
"15.4.6"
,
"@eslint/eslintrc"
:
"^3"
"prisma"
:
"6.14.0"
,
"tailwindcss"
:
"^4"
,
"typescript"
:
"^5"
}
}
chat-room/pnpm-lock.yaml
View file @
496f7668
This diff is collapsed.
Click to expand it.
chat-room/prisma/dev.db
0 → 100644
View file @
496f7668
File added
chat-room/prisma/migrations/20250824063252_init/migration.sql
0 → 100644
View file @
496f7668
-- 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"
);
chat-room/prisma/migrations/20250825155101_update_relation/migration.sql
0 → 100644
View file @
496f7668
/*
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"
);
chat-room/prisma/migrations/migration_lock.toml
0 → 100644
View file @
496f7668
# Please do not edit this file manually
# It should be added in your version-control system (e.g., Git)
provider
=
"sqlite"
chat-room/prisma/schema.prisma
0 → 100644
View file @
496f7668
//
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
chat-room/src/app/api/auth/autoLogin/route.tsx
0 → 100644
View file @
496f7668
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
chat-room/src/app/api/auth/login/route.tsx
0 → 100644
View file @
496f7668
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
chat-room/src/app/api/auth/logout/route.tsx
0 → 100644
View file @
496f7668
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
chat-room/src/app/api/auth/register/route.tsx
0 → 100644
View file @
496f7668
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
chat-room/src/app/api/auth/startSession/route.tsx
0 → 100644
View file @
496f7668
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
chat-room/src/app/api/login-interface.tsx
0 → 100644
View file @
496f7668
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
;
chat-room/src/app/api/message/add/route.tsx
View file @
496f7668
...
...
@@ -14,6 +14,10 @@ interface MessageAddArgs {
- response: null (只要code为0即为成功)
*/
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
response
=
await
Db
.
AddMessage
({
roomId
,
content
,
sender
});
return
NextResponse
.
json
(
response
);
...
...
chat-room/src/app/api/room-api.tsx
View file @
496f7668
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
{
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
({
code
,
message
,
data
:
null
});
},
{
status
});
}
export
async
function
GetRoomList
():
Promise
<
Ty
.
BackendResponse
<
Ty
.
RoomListRes
>>
{
console
.
log
(
'
获取到的房间列表:
'
,
State
.
fakeRooms
as
Ty
.
RoomPreviewInfo
[]);
const
UserExists
=
async
(
name
:
string
):
Promise
<
boolean
>
=>
{
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
{
code
:
0
,
message
:
'
房间获取成功
'
,
data
:
{
rooms
:
State
.
fakeRooms
as
Ty
.
RoomPreviewInfo
[]
}
};
messageId
:
m
.
id
,
roomId
:
m
.
roomId
,
sender
:
m
.
senderName
,
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
>>
{
const
newRoom
:
RoomInfo
=
{
roomId
:
State
.
maxRoomId
+
1
,
roomName
:
data
.
roomName
,
lastMessage
:
null
,
creator
:
data
.
user
};
State
.
fakeRooms
=
[...
State
.
fakeRooms
,
newRoom
];
console
.
log
(
'
当前房间列表:
'
,
State
.
fakeRooms
);
State
.
maxRoomId
++
;
const
userName
=
data
.
user
;
const
creatorToken
=
await
getUserToken
(
userName
);
if
(
!
creatorToken
)
{
return
{
code
:
1
,
message
:
'
用户不存在
'
,
data
:
{
roomId
:
-
1
}
};
}
const
newRoom
=
await
prisma
.
room
.
create
({
data
:
{
roomName
:
data
.
roomName
,
creatorToken
}
});
return
{
code
:
0
,
message
:
'
房间创建成功
'
,
data
:
{
roomId
:
newRoom
.
roomI
d
roomId
:
newRoom
.
i
d
}
};
}
export
async
function
GetMessageList
(
data
:
Ty
.
RoomMessageListArgs
):
Promise
<
Ty
.
BackendResponse
<
Ty
.
RoomMessageListRes
>>
{
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
{
code
:
0
,
message
:
'
消息获取成功
'
,
data
:
{
messages
messages
:
messageList
}
};
}
export
async
function
UpdateMessageList
(
data
:
Ty
.
RoomMessageGetUpdateArgs
):
Promise
<
Ty
.
BackendResponse
<
Ty
.
RoomMessageGetUpdateRes
>>
{
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
{
code
:
0
,
message
:
'
消息更新成功
'
,
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
;
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
{
code
:
0
,
message
:
'
房间删除成功
'
,
...
...
@@ -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
>>
{
const
{
roomId
,
sender
,
content
}
=
data
;
const
newMessage
:
Ty
.
Message
=
{
messageId
:
State
.
maxMessageId
+
1
,
roomId
,
sender
,
content
,
time
:
Date
.
now
()
};
State
.
fakeMsg
=
[...
State
.
fakeMsg
,
newMessage
];
State
.
maxMessageId
++
;
console
.
log
(
`Adding message to room
${
roomId
}
from user
${
sender
}
:
${
content
}
`
);
if
(
!
await
UserExists
(
sender
))
{
return
{
code
:
1
,
message
:
'
用户不存在
'
,
data
:
null
};
}
await
prisma
.
message
.
create
({
data
:
{
roomId
,
senderName
:
sender
,
content
}
});
return
{
code
:
0
,
message
:
'
消息发送成功
'
,
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
chat-room/src/app/api/room/delete/route.tsx
View file @
496f7668
...
...
@@ -14,7 +14,11 @@ interface RoomDeleteArgs {
*/
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
response
=
await
Db
.
DeleteRoom
({
user
,
roomId
});
const
response
=
await
Db
.
DeleteRoom
({
user
,
roomId
}
,
cookieData
.
userToken
);
return
NextResponse
.
json
(
response
);
}
\ No newline at end of file
chat-room/src/app/api/room/list/route.tsx
View file @
496f7668
...
...
@@ -11,8 +11,10 @@ interface RoomListRes {
rooms: RoomPreviewInfo[];
}
*/
export
async
function
GET
(
req
:
NextRequest
)
:
Promise
<
NextResponse
<
Ty
.
BackendResponse
<
Ty
.
RoomListRes
>>>
{
export
async
function
GET
(
req
:
NextRequest
)
:
Promise
<
NextResponse
<
Ty
.
BackendResponse
<
Ty
.
RoomListRes
|
null
>>>
{
const
cookieData
=
await
Db
.
VerifyCookie
(
req
.
cookies
);
if
(
'
code
'
in
cookieData
)
return
NextResponse
.
json
(
cookieData
);
const
response
=
await
Db
.
GetRoomList
();
return
NextResponse
.
json
(
response
);
}
\ No newline at end of file
chat-room/src/app/api/room/message/getUpdate/route.tsx
View file @
496f7668
...
...
@@ -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
result
=
await
Db
.
UpdateMessageList
({
roomId
:
Number
(
Params
.
get
(
'
roomId
'
)),
...
...
chat-room/src/app/api/room/message/list/route.tsx
View file @
496f7668
...
...
@@ -14,7 +14,10 @@ interface RoomMessageListRes {
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
response
=
await
Db
.
GetMessageList
({
roomId
:
Number
(
Params
.
get
(
'
roomId
'
))
});
return
NextResponse
.
json
(
response
);
...
...
Prev
1
2
Next
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment