From 6402b4cfb13fad53521da5dc9b4fab32b848e804 Mon Sep 17 00:00:00 2001 From: Vadim Date: Mon, 23 Feb 2026 23:56:20 +0300 Subject: [PATCH] wip --- proto/message.proto | 85 ++++++++++++----- src/grpc/client.ts | 226 ++++++++++++++++++++++++++++++-------------- src/handles.ts | 25 ++--- src/index.ts | 18 ++-- src/types/types.ts | 2 +- 5 files changed, 238 insertions(+), 118 deletions(-) diff --git a/proto/message.proto b/proto/message.proto index f519c0b..995c6c2 100644 --- a/proto/message.proto +++ b/proto/message.proto @@ -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, }]; } diff --git a/src/grpc/client.ts b/src/grpc/client.ts index d29886c..4137e6c 100644 --- a/src/grpc/client.ts +++ b/src/grpc/client.ts @@ -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 { - data: T +interface Version { + id: number + version: string +} + +interface Callback { + (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) => void + updateUser: (data: UpdateUserDto, cb: Callback) => void + getUserByToken: (data: GetUserByTokenDto, cb: Callback) => void + getUserByEmail: (data: GetUserByEmailDto, cb: Callback) => void + getUser: (data: GetUserByIDDto, cb: Callback) => void + listUser: (data: ListUserDto, cb: Callback) => void + createChat: (data: CreateChatDto, cb: Callback) => void + updateChat: (data: UpdateChatDto, cb: Callback) => void + getChat: (data: GetChatDto, cb: Callback) => void + listChat: (data: ListChatDto, cb: Callback) => void + createMessage: (data: CreateMessageDto, cb: Callback) => void + updateMessage: (data: UpdateMessageDto, cb: Callback) => void + getMessage: (data: GetMessageDto, cb: Callback) => void + listMessage: (data: ListMessageDto, cb: Callback) => void + getVersion: (data: VersionDto, cb: Callback) => 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(client: any, methodName: ServiceName) { - const service = client[methodName] - - return (request: T): Promise> => { - return new Promise((resolve, reject) => { - service(request, (error: Error, response: Response) => { - 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(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(this.messageClient, ServiceName.GetUserByEmail)(dto) - // } - - getChatsByUser(dto: ListChatDto) { - return this.toPromise(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(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(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) + }) + }) } } diff --git a/src/handles.ts b/src/handles.ts index 02a3ff0..7a3e8ff 100644 --- a/src/handles.ts +++ b/src/handles.ts @@ -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( { diff --git a/src/index.ts b/src/index.ts index 2f61a1e..bfbfd64 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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 })) } } diff --git a/src/types/types.ts b/src/types/types.ts index 88ecbe7..449c4c7 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -26,7 +26,7 @@ interface ListMessages { } interface CreateMessage { - type: 'MESSAGE_CREATE' + type: 'CREATE_MESSAGE' data: { chat_id: string text: string