diff --git a/.gitea/workflows/demo.yaml b/.gitea/workflows/deploy.yaml similarity index 79% rename from .gitea/workflows/demo.yaml rename to .gitea/workflows/deploy.yaml index 145b5ed..e679b3e 100644 --- a/.gitea/workflows/demo.yaml +++ b/.gitea/workflows/deploy.yaml @@ -1,6 +1,9 @@ name: Gitea Actions Demo run-name: ${{ gitea.actor }} is testing out Gitea Actions 🚀 -on: [push] +on: + push: + branches: + - dev jobs: Explore-Gitea-Actions: @@ -13,6 +16,10 @@ jobs: uses: actions/checkout@v3 - run: echo "💡 The ${{ gitea.repository }} repository has been cloned to the runner." - run: echo "🖥️ The workflow is now ready to test your code on the runner!" +# - name: List files in the repository +# run: | +# ls ${{ gitea.workspace }} +# docker run -d --name tracker -p 3300:3000 git.madsky.ru/vadim/tracker:latest - name: List files in the repository run: | ls ${{ gitea.workspace }} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4365359 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +#FROM node:lts-alpine +FROM oven/bun:canary-alpine +WORKDIR /app + +COPY package*.json ./ +RUN npm install +COPY . . + +RUN npm run build + +RUN apk add --no-cache tzdata +ENV NODE_ENV=production +ENV TZ=Europe/Moscow + +CMD ["node", "dist/main"] +EXPOSE 3000/tcp +ENTRYPOINT [ "bun", "run", "index.ts" ] diff --git a/src/grpc/client.ts b/src/grpc/client2.ts similarity index 100% rename from src/grpc/client.ts rename to src/grpc/client2.ts diff --git a/src/grpc/message.ts b/src/grpc/message.ts new file mode 100644 index 0000000..f1ea722 --- /dev/null +++ b/src/grpc/message.ts @@ -0,0 +1,73 @@ +import grpc, { type ServiceError } from '@grpc/grpc-js' +import type { Empty } from '../generated/google/protobuf/empty.ts' +import { + type CreateChatRequest, + type CreateChatResponse, + type CreateMessageRequest, + type CreateMessageResponse, + type GetUserByEmailRequest, + type GetUserRequest, + GetUserResponse, + type GetVersionResponse, + type ListChatRequest, + type ListChatResponse, + type ListMessageRequest, + type ListMessageResponse, + type ListUserRequest, + type ListUserResponse, + MessageServiceClient, +} from '../generated/message.ts' + +class MessageService { + private client: MessageServiceClient + + constructor() { + this.client = new MessageServiceClient('192.168.1.11:8070', grpc.credentials.createInsecure()) + } + + private toPromise( + request: T, + service: (request: T, callback: (error: ServiceError | null, response: R) => void) => void, + ): Promise { + return new Promise((resolve, reject) => { + service(request, (error, data) => { + if (error) reject(error?.message) + else resolve(data) + }) + }) + } + + getUser(req: GetUserRequest) { + return this.toPromise(req, this.client.getUser) + } + + listChat(req: ListChatRequest) { + return this.toPromise(req, this.client.listChat) + } + + createChat(req: CreateChatRequest) { + return this.toPromise(req, this.client.createChat) + } + + createMessage(req: CreateMessageRequest) { + return this.toPromise(req, this.client.createMessage) + } + + listMessage(req: ListMessageRequest) { + return this.toPromise(req, this.client.listMessage) + } + + listUser(req: ListUserRequest) { + return this.toPromise(req, this.client.listUser) + } + + getUserByEmail(req: GetUserByEmailRequest) { + return this.toPromise(req, this.client.getUserByEmail) + } + + getVersion() { + return this.toPromise({}, this.client.getVersion) + } +} + +export const messageService = new MessageService() diff --git a/src/handles.ts b/src/handles.ts index b56e7a7..9e94e3a 100644 --- a/src/handles.ts +++ b/src/handles.ts @@ -1,48 +1,55 @@ import { HttpStatusCodes } from './constants.ts' import { errors } from 'jose' -import type { LoginDto, WebSocketData } from './types/types.ts' +import type { LoginDto, WebSocketCtx, WebSocketData } from './types/types.ts' import { createAccessToken, verifyAccessToken } from './utils/jwt.ts' -import { grpcClient as client } from './grpc/client.ts' import { config } from './config.ts' +import { messageService } from './grpc/message.ts' -export async function login(req: Request) { - try { - const body: LoginDto = await req.json() +export const handlers = { + async getVersion() { + try { + const res = await messageService.getVersion() + return Response.json(res) + } catch (error) { + return Response.json({ message: 'Error' }, { status: HttpStatusCodes.BAD_REQUEST }) + } + }, + async login(req: Request) { + try { + const body: LoginDto = await req.json() - 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 { email } = body - if (!email) return Response.json({ message: 'email required' }, { status: HttpStatusCodes.BAD_REQUEST }) + const userResponse = await messageService.getUserByEmail({ email: body.email }) + const user = userResponse.data + if (!user) return Response.json({ message: 'Invalid email or password' }, { status: HttpStatusCodes.NOT_FOUND }) - const userResponse = await client.getUserByEmail({ email: body.email }) - const user = userResponse.data + const accessToken = await createAccessToken(user.id, user.email) + const expires = new Date(Date.now() + config.cookieExpiry * 1000) - 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) - return Response.json( - { - accessToken: accessToken.token, - tokenType: 'Bearer', - expires: expires, - }, - { status: HttpStatusCodes.CREATED }, - ) - } catch (error) { - console.log({ error }) - return Response.json({ message: 'Login failed' }, { status: HttpStatusCodes.BAD_REQUEST }) - } + return Response.json( + { + accessToken: accessToken.token, + tokenType: 'Bearer', + expires: expires, + }, + { status: HttpStatusCodes.CREATED }, + ) + } catch (error) { + console.log({ error }) + return Response.json({ message: 'Login failed' }, { status: HttpStatusCodes.BAD_REQUEST }) + } + }, } -export async function upgrade(req: Request, server: Bun.Server) { +export async function upgrade(req: Request, server: Bun.Server) { const payload = await checkRequest(req) if (!payload) return new Response('Invalid token', { status: HttpStatusCodes.NOT_FOUND }) const success = server.upgrade(req, { data: { userId: +payload.sub, email: payload.email } }) if (success) return undefined + return new Response('Upgrade failed', { status: HttpStatusCodes.BAD_REQUEST }) } diff --git a/src/index.ts b/src/index.ts index 5cd2a40..31ff2d7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,8 @@ import { HttpStatusCodes } from './constants.ts' -import type { WebSocketData, WsData } from './types/types.ts' -import { grpcClient as client } from './grpc/client.ts' -import { login, upgrade } from './handles.ts' +import type { WebSocketCtx, WebSocketData } from './types/types.ts' + +import { handlers, upgrade } from './handles.ts' +import { messageService } from './grpc/message.ts' const PORT = 3000 @@ -13,13 +14,14 @@ const server = Bun.serve({ const pathname = url.pathname const method = req.method - if (pathname === '/login' && method === 'POST') return login(req) + if (pathname === '/version' && method === 'GET') return handlers.getVersion() + if (pathname === '/login' && method === 'POST') return handlers.login(req) if (pathname === '/ws') return upgrade(req, server) return new Response('Not found', { status: HttpStatusCodes.NOT_FOUND }) }, websocket: { - data: {} as WebSocketData, + data: {} as WebSocketCtx, async open(ws) { try { const ipAddr = ws.remoteAddress @@ -27,10 +29,10 @@ const server = Bun.serve({ const user = ws.data - const userResponse = await client.getUser({ id: user.userId }) + const userResponse = await messageService.getUser({ id: user.userId }) ws.send(JSON.stringify({ type: 'USER', ...userResponse })) - const chatResponse = await client.listChat({ page: 0, user_id: user.userId }) + const chatResponse = await messageService.listChat({ page: 0, userId: user.userId }) chatResponse.data.forEach((el) => ws.subscribe(el.id)) ws.send(JSON.stringify({ type: 'CHATS', ...chatResponse })) } catch (error) { @@ -47,34 +49,37 @@ const server = Bun.serve({ }, async message(ws, message) { console.log('Websocket message', message) - // const result = ws.send(message); try { if (typeof message === 'string') { - const o = JSON.parse(message) as WsData - if (!o) return + const webSocketData = JSON.parse(message) as WebSocketData + if (!webSocketData) return - if (o.type === 'CREATE_CHAT') { - const chat = await client.createChat({ users: [{ user_id: o.data.userId }, { user_id: ws.data.userId }] }) + if (webSocketData.type === 'CREATE_CHAT') { + const chat = await messageService.createChat({ users: [{ userId: webSocketData.data.userId }, { userId: ws.data.userId }] }) ws.send(JSON.stringify({ type: 'CHATS', ...chat })) } - if (o.type === 'CREATE_MESSAGE') { - const messageResponse = await client.createMessage({ - chat_id: o.data.chat_id, - user_id: ws.data.userId, - text: o.data.text, + if (webSocketData.type === 'CREATE_MESSAGE') { + const messageResponse = await messageService.createMessage({ + chatId: webSocketData.data.chatId, + userId: ws.data.userId, + message: webSocketData.data.message, }) - server.publish(o.data.chat_id, JSON.stringify({ type: 'MESSAGE', ...messageResponse })) + server.publish(webSocketData.data.chatId, JSON.stringify({ type: 'MESSAGE', ...messageResponse })) } - if (o.type === 'GET_MESSAGES') { - const messages = await client.listMessage({ user_id: ws.data.userId, chat_id: o.data.chatId, page: 1 }) - server.publish(o.data.chatId, JSON.stringify({ type: 'MESSAGES', ...messages })) + if (webSocketData.type === 'GET_MESSAGES') { + const messages = await messageService.listMessage({ + userId: ws.data.userId, + chatId: webSocketData.data.chatId, + page: 1, + }) + server.publish(webSocketData.data.chatId, JSON.stringify({ type: 'MESSAGES', ...messages })) } - if (o.type === 'GET_USERS') { - const users = await client.listUser({}) + if (webSocketData.type === 'GET_USERS') { + const users = await messageService.listUser({ page: 1, userIds: [] }) ws.send(JSON.stringify({ type: 'USERS', ...users })) } } diff --git a/src/types/types.ts b/src/types/types.ts index af0e6c7..5b9491c 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -1,4 +1,4 @@ -export interface WebSocketData { +export interface WebSocketCtx { chatId?: string token?: string userId: number @@ -28,8 +28,8 @@ interface ListMessages { interface CreateMessage { type: 'CREATE_MESSAGE' data: { - chat_id: string - text: string + chatId: string + message: string } } @@ -37,7 +37,7 @@ interface ListUsers { type: 'GET_USERS' data: { page?: number - user_ids?: number[] + userIds?: number[] } } @@ -48,4 +48,4 @@ interface CreateChat { } } -export type WsData = ListMessages | CreateMessage | ListUsers | CreateChat +export type WebSocketData = ListMessages | CreateMessage | ListUsers | CreateChat