This commit is contained in:
2026-02-23 23:56:20 +03:00
parent d675d3ab9f
commit 6402b4cfb1
5 changed files with 238 additions and 118 deletions

View File

@@ -14,6 +14,7 @@ service MessageService {
rpc GetUserByToken(GetUserByTokenRequest) returns(GetUserResponse);
rpc GetUserByEmail(GetUserByEmailRequest) returns(GetUserResponse);
rpc ListUser(ListUserRequest) returns(ListUserResponse);
rpc GetUser(GetUserRequest) returns(GetUserResponse);
rpc CreateChat(CreateChatRequest) returns(CreateChatResponse);
rpc UpdateChat(UpdateChatRequest) returns(UpdateChatResponse);
@@ -31,7 +32,10 @@ service MessageService {
// User
message CreateUserRequest {
string email = 1 [(validate.rules).string.email = true];
optional string name = 2;
optional string name = 2 [(validate.rules).string = {
ignore_empty: true, // ⭐ Главное правило
max_len: 200
}];
}
message CreateUserResponse {
@@ -41,7 +45,10 @@ message CreateUserResponse {
message UpdateUserRequest {
int32 id = 1 [(validate.rules).int32.gt = 0];
optional string token = 2; // todo
optional string description = 3;
optional string description = 3 [(validate.rules).string = {
ignore_empty: true,
max_len: 200
}];
}
message UpdateUserResponse {
@@ -56,6 +63,10 @@ message GetUserByEmailRequest {
string email = 1 [(validate.rules).string.email = true];
}
message GetUserRequest {
int32 id = 1 [(validate.rules).int32.gt = 0];
}
message GetUserResponse {
User data = 1;
}
@@ -91,11 +102,13 @@ message UserForChatResponse {
// Chat
message CreateChatRequest {
optional string name = 1;
repeated UserForChat users = 2;
repeated UserForChat users = 2 [(validate.rules).repeated = {
min_items: 1,
}];
}
message UserForChat {
int32 user_id = 1;
int32 user_id = 1 [(validate.rules).int32.gt = 0];
optional bool is_admin = 2;
}
@@ -105,11 +118,11 @@ message CreateChatResponse {
message UpdateChatRequest {
string id = 1 [(validate.rules).string = {
pattern: "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$",
min_len: 36,
max_len: 36
uuid: true,
}];
repeated UserForChat users = 2 [(validate.rules).repeated = {
min_items: 1,
}];
repeated UserForChat users = 2;
}
message UpdateChatResponse {
@@ -117,7 +130,9 @@ message UpdateChatResponse {
}
message GetChatRequest {
string id = 1;
string id = 1 [(validate.rules).string = {
uuid: true,
}];
}
message GetChatResponse {
@@ -144,15 +159,25 @@ message Chat {
// Message
message CreateMessageRequest {
string chat_id = 2 [(validate.rules).string = {
pattern: "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$",
min_len: 36,
max_len: 36
uuid: true,
}];
int32 user_id = 3 [(validate.rules).int32.gt = 0];
optional string text = 4;
optional string image = 5;
optional string video = 6;
optional string file = 7;
optional string text = 4 [(validate.rules).string = {
ignore_empty: true,
max_len: 200
}];
optional string image = 5 [(validate.rules).string = {
ignore_empty: true,
max_len: 200
}];
optional string video = 6 [(validate.rules).string = {
ignore_empty: true,
max_len: 200
}];
optional string file = 7 [(validate.rules).string = {
ignore_empty: true,
max_len: 200
}];
}
message CreateMessageResponse {
@@ -162,14 +187,24 @@ message CreateMessageResponse {
message UpdateMessageRequest {
int32 id = 1 [(validate.rules).int32.gt = 0];
string chat_id = 2 [(validate.rules).string = {
pattern: "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$",
min_len: 36,
max_len: 36
uuid: true,
}];
optional string text = 3 [(validate.rules).string = {
ignore_empty: true,
max_len: 200
}];
optional string image = 4 [(validate.rules).string = {
ignore_empty: true,
max_len: 200
}];
optional string video = 5 [(validate.rules).string = {
ignore_empty: true,
max_len: 200
}];
optional string file = 6 [(validate.rules).string = {
ignore_empty: true,
max_len: 200
}];
optional string text = 3;
optional string image = 4;
optional string video = 5;
optional string file = 6;
}
message UpdateMessageResponse {
@@ -187,9 +222,7 @@ message GetMessageResponse {
message ListMessageRequest {
int32 page = 1;
string chat_id = 2 [(validate.rules).string = {
pattern: "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$",
min_len: 36,
max_len: 36
uuid: true,
}];
}

View File

@@ -22,6 +22,10 @@ interface GetUserByEmailDto {
email: string
}
interface GetUserByIDDto {
id: number
}
interface ListUserDto {
page: number
iser_ids: number[]
@@ -32,7 +36,7 @@ interface CreateChatDto {
users: { user_id: number; is_admin?: boolean }[]
}
interface UpdateChat {
interface UpdateChatDto {
id: number
users: { user_id: number; is_admin?: boolean }[]
}
@@ -70,11 +74,6 @@ interface ListMessageDto {
chat_id: string
}
interface Version {
id: number
version: string
}
interface VersionDto {}
export interface User {
@@ -103,53 +102,38 @@ interface Message {
updated_at: string
}
interface Response<T> {
data: T
interface Version {
id: number
version: string
}
interface Callback<T> {
(error: Error, res: { data: T }): void
}
interface Services {
createUser: (data: CreateUserDto) => { data: User }
updateUser: (data: UpdateUserDto) => { data: User }
getUserByToken: (data: GetUserByTokenDto) => { data: User }
getUserByEmail: (data: GetUserByEmailDto) => { data: User }
listUser: (data: ListUserDto) => { data: User[] }
createChat: (data: CreateChatDto) => { data: Chat }
updateChat: (data: UpdateChat) => { data: Chat }
getChat: (data: GetChatDto) => { data: Chat }
listChat: (data: ListChatDto) => { data: Chat[] }
createMessage: (data: CreateMessageDto) => { data: Message }
updateMessage: (data: UpdateMessageDto) => { data: Message }
getMessage: (data: GetMessageDto) => { data: Message }
listMessage: (data: ListMessageDto) => { data: Message[] }
getVersion: (data: VersionDto) => { data: Version }
}
enum ServiceName {
CreateUser = 'createUser',
UpdateUser = 'updateUser',
GetUserByToken = 'getUserByToken',
GetUserByEmail = 'getUserByEmail',
ListUser = 'listUser',
CreateChat = 'createChat',
UpdateChat = 'updateChat',
GetChat = 'getChat',
ListChat = 'listChat',
CreateMessage = 'createMessage',
UpdateMessage = 'updateMessage',
GetMessage = 'getMessage',
ListMessage = 'listMessage',
GetVersion = 'getVersion',
createUser: (data: CreateUserDto, cb: Callback<User>) => void
updateUser: (data: UpdateUserDto, cb: Callback<User>) => void
getUserByToken: (data: GetUserByTokenDto, cb: Callback<User>) => void
getUserByEmail: (data: GetUserByEmailDto, cb: Callback<User>) => void
getUser: (data: GetUserByIDDto, cb: Callback<User>) => void
listUser: (data: ListUserDto, cb: Callback<User[]>) => void
createChat: (data: CreateChatDto, cb: Callback<Chat>) => void
updateChat: (data: UpdateChatDto, cb: Callback<Chat>) => void
getChat: (data: GetChatDto, cb: Callback<Chat>) => void
listChat: (data: ListChatDto, cb: Callback<Chat[]>) => void
createMessage: (data: CreateMessageDto, cb: Callback<Message>) => void
updateMessage: (data: UpdateMessageDto, cb: Callback<Message>) => void
getMessage: (data: GetMessageDto, cb: Callback<Message>) => void
listMessage: (data: ListMessageDto, cb: Callback<Message[]>) => void
getVersion: (data: VersionDto, cb: Callback<Version>) => void
}
class GrpcClient {
messageClient: Services
private messageClient: Services
constructor() {
console.log('Grpc Client init')
const PROTO_PATH = path.resolve('./proto/message.proto')
const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
keepCase: true,
@@ -161,43 +145,147 @@ class GrpcClient {
const protoDescriptor = grpc.loadPackageDefinition(packageDefinition)
const messageProto = protoDescriptor.message as any
this.messageClient = new messageProto.MessageService('localhost:8070', grpc.credentials.createInsecure())
// console.log(this.messageClient)
}
private toPromise<T, R>(client: any, methodName: ServiceName) {
const service = client[methodName]
return (request: T): Promise<Response<R>> => {
return new Promise((resolve, reject) => {
service(request, (error: Error, response: Response<R>) => {
if (error) reject(error.message)
else resolve(response)
})
// User
createUser(dto: CreateUserDto): Promise<{ data: User }> {
return new Promise((resolve, reject) => {
this.messageClient.createUser(dto, (error, data) => {
if (error) reject(error?.message)
else resolve(data)
})
}
})
}
getVersion(dto: VersionDto) {
const service = this.messageClient[ServiceName.GetVersion]
return this.toPromise<VersionDto, Version>(this.messageClient, ServiceName.GetVersion)(dto)
updateUser(dto: UpdateUserDto): Promise<{ data: User }> {
return new Promise((resolve, reject) => {
this.messageClient.updateUser(dto, (error, data) => {
if (error) reject(error?.message)
else resolve(data)
})
})
}
// getUserByEmail(dto: GetUserByEmail) {
// return this.toPromise<GetUserByEmail, User>(this.messageClient, ServiceName.GetUserByEmail)(dto)
// }
getChatsByUser(dto: ListChatDto) {
return this.toPromise<ListChatDto, Chat[]>(this.messageClient, ServiceName.ListChat)(dto)
getUserByToken(dto: GetUserByTokenDto): Promise<{ data: User }> {
return new Promise((resolve, reject) => {
this.messageClient.getUserByToken(dto, (error, data) => {
if (error) reject(error?.message)
else resolve(data)
})
})
}
getMessagesByChatId(dto: ListMessageDto) {
return this.toPromise<ListMessageDto, Message[]>(this.messageClient, ServiceName.ListMessage)(dto)
getUserByEmail(dto: GetUserByEmailDto): Promise<{ data: User }> {
return new Promise((resolve, reject) => {
this.messageClient.getUserByEmail(dto, (error, data) => {
if (error) reject(error?.message)
else resolve(data)
})
})
}
createMessage(dto: CreateMessageDto) {
return this.toPromise<CreateMessageDto, Message>(this.messageClient, ServiceName.CreateMessage)(dto)
getUser(dto: GetUserByIDDto): Promise<{ data: User }> {
return new Promise((resolve, reject) => {
this.messageClient.getUser(dto, (error, data) => {
if (error) reject(error?.message)
else resolve(data)
})
})
}
listUser(dto: ListUserDto): Promise<{ data: User[] }> {
return new Promise((resolve, reject) => {
this.messageClient.listUser(dto, (error, data) => {
if (error) reject(error?.message)
else resolve(data)
})
})
}
// Chat
createChat(dto: CreateChatDto): Promise<{ data: Chat }> {
return new Promise((resolve, reject) => {
this.messageClient.createChat(dto, (error, data) => {
if (error) reject(error?.message)
else resolve(data)
})
})
}
updateChat(dto: UpdateChatDto): Promise<{ data: Chat }> {
return new Promise((resolve, reject) => {
this.messageClient.updateChat(dto, (error, data) => {
if (error) reject(error?.message)
else resolve(data)
})
})
}
getChat(dto: GetChatDto): Promise<{ data: Chat }> {
return new Promise((resolve, reject) => {
this.messageClient.getChat(dto, (error, data) => {
if (error) reject(error?.message)
else resolve(data)
})
})
}
listChat(dto: ListChatDto): Promise<{ data: Chat[] }> {
return new Promise((resolve, reject) => {
this.messageClient.listChat(dto, (error, data) => {
if (error) reject(error?.message)
else resolve(data)
})
})
}
// message
createMessage(dto: CreateMessageDto): Promise<{ data: Message }> {
return new Promise((resolve, reject) => {
this.messageClient.createMessage(dto, (error, data) => {
if (error) reject(error?.message)
else resolve(data)
})
})
}
updateMessage(dto: UpdateMessageDto): Promise<{ data: Message }> {
return new Promise((resolve, reject) => {
this.messageClient.updateMessage(dto, (error, data) => {
if (error) reject(error?.message)
else resolve(data)
})
})
}
getMessage(dto: GetMessageDto): Promise<{ data: Message }> {
return new Promise((resolve, reject) => {
this.messageClient.getMessage(dto, (error, data) => {
if (error) reject(error?.message)
else resolve(data)
})
})
}
listMessage(dto: ListMessageDto): Promise<{ data: Message[] }> {
return new Promise((resolve, reject) => {
this.messageClient.listMessage(dto, (error, data) => {
if (error) reject(error?.message)
else resolve(data)
})
})
}
// version
getVersion(dto: VersionDto): Promise<{ data: Version }> {
return new Promise((resolve, reject) => {
this.messageClient.getVersion(dto, (error, data) => {
if (error) reject(error?.message)
else resolve(data)
})
})
}
}

View File

@@ -2,34 +2,35 @@ import { HttpStatusCodes } from './constants.ts'
import { errors } from 'jose'
import type { LoginDto, WebSocketData } from './types/types.ts'
import { createAccessToken, verifyAccessToken } from './utils/jwt.ts'
import { grpcClient } from './grpc/client.ts'
import { grpcClient as client } from './grpc/client.ts'
import { config } from './config.ts'
export async function login(req: Request) {
try {
const body: LoginDto = await req.json()
const versionResponse = await grpcClient.getVersion({})
console.log(versionResponse.data)
const versionResponse = await client.getVersion({})
console.log(versionResponse?.data)
const { email } = body
if (!email) return Response.json({ message: 'email required' }, { status: HttpStatusCodes.BAD_REQUEST })
const userResponse = await grpcClient.getUserByEmail({ email: 'vadim.olonin@gmail.com' })
const userResponse = await client.getUserByEmail({ email: body.email })
const user = userResponse.data
if (!user) return Response.json({ message: 'Invalid email or password' }, { status: HttpStatusCodes.NOT_FOUND })
const accessToken = await createAccessToken(user.id, user.email)
const expires = new Date(Date.now() + config.cookieExpiry * 1000)
const sessionCookie = new Bun.Cookie('token', accessToken.token, {
path: '/',
expires: expires,
// maxAge: config.cookieExpiry,
httpOnly: true,
// secure: true,
sameSite: 'strict',
})
// const sessionCookie = new Bun.Cookie('token', accessToken.token, {
// path: '/',
// expires: expires,
// maxAge: config.cookieExpiry,
// httpOnly: true,
// secure: true,
// sameSite: 'strict',
// })
return Response.json(
{

View File

@@ -3,7 +3,6 @@ import type { WebSocketData, WsData } from './types/types.ts'
import { grpcClient as client } from './grpc/client.ts'
import { login, upgrade } from './handles.ts'
const GROUP = 'group'
const PORT = 3000
const server = Bun.serve({
@@ -28,12 +27,11 @@ const server = Bun.serve({
const user = ws.data
const chatResponse = await client.getChatsByUser({ page: 0, user_ids: [1] })
const userResponse = await client.getUser({ id: user.userId })
ws.send(JSON.stringify({ type: 'USER', ...userResponse }))
const chatResponse = await client.listChat({ page: 0, user_id: user.userId })
chatResponse.data.forEach((el) => ws.subscribe(el.id))
// console.log('chats', chatResponse.data)
// console.log('subscriptions', ws.subscriptions)
ws.send(JSON.stringify({ type: 'CHATS', ...chatResponse }))
} catch (error) {
console.log(error)
@@ -59,20 +57,20 @@ const server = Bun.serve({
return
}
if (o.type === 'MESSAGE_CREATE') {
if (o.type === 'CREATE_MESSAGE') {
console.log('create')
const message = await client.createMessage({
const messageResponse = await client.createMessage({
chat_id: o.data.chat_id,
user_id: ws.data.userId,
text: o.data.text,
})
server.publish(o.data.chat_id, JSON.stringify({ type: 'CREATE_MESSAGE', ...message }))
server.publish(o.data.chat_id, JSON.stringify({ type: 'MESSAGE', ...messageResponse }))
}
if (o.type === 'GET_MESSAGES') {
console.log('GET_MESSAGES')
const messages = await client.getMessagesByChatId({ chat_id: o.data.chat_id, page: 1 })
const messages = await client.listMessage({ chat_id: o.data.chat_id, page: 1 })
server.publish(o.data.chat_id, JSON.stringify({ type: 'MESSAGES', ...messages }))
}
}

View File

@@ -26,7 +26,7 @@ interface ListMessages {
}
interface CreateMessage {
type: 'MESSAGE_CREATE'
type: 'CREATE_MESSAGE'
data: {
chat_id: string
text: string