This commit is contained in:
2026-03-22 22:40:26 +03:00
parent e876bcfeaa
commit aec1be6179
7 changed files with 72 additions and 16 deletions

1
.gitignore vendored
View File

@@ -2,3 +2,4 @@ node_modules/
dist/ dist/
.idea/ .idea/
.env .env
secrets.json

View File

@@ -1,4 +1,4 @@
const EXPIRE = 30 * 60 // 30min const EXPIRE = 300 * 60 // 300min
export const config = { export const config = {
accessSecret: process.env.JWT_SECRET ?? 'JWTAccessSecret', accessSecret: process.env.JWT_SECRET ?? 'JWTAccessSecret',

View File

@@ -4,6 +4,8 @@ import type { LoginDto, WebSocketCtx, WebSocketData } from './types/types.ts'
import { createAccessToken, verifyAccessToken } from './utils/jwt.ts' import { createAccessToken, verifyAccessToken } from './utils/jwt.ts'
import { config } from './config.ts' import { config } from './config.ts'
import { messageService } from './grpc/message.ts' import { messageService } from './grpc/message.ts'
import { storage } from './storage'
import { randomUUIDv7 } from 'bun'
export const handlers = { export const handlers = {
async getVersion(req: Request) { async getVersion(req: Request) {
@@ -44,6 +46,20 @@ export const handlers = {
return Response.json({ message: 'Login failed' }, { status: HttpStatusCodes.BAD_REQUEST }) return Response.json({ message: 'Login failed' }, { status: HttpStatusCodes.BAD_REQUEST })
} }
}, },
async setAvatar(req: Request) {
const formdata = await req.formData()
const file = formdata.get('file') as File
if (!file) {
return Response.json({ message: 'Invalid image' }, { status: HttpStatusCodes.BAD_REQUEST })
}
const uuid = randomUUIDv7()
await storage.upload(file,`${uuid}.${file.type.split('/')[1]}`)
},
async getAvatar(req: Request) {
const url = storage.presign('avatar.jpg')
return Response.json({ url })
}
} }
export async function upgrade(req: Request, server: Bun.Server<WebSocketCtx>) { export async function upgrade(req: Request, server: Bun.Server<WebSocketCtx>) {

View File

@@ -15,6 +15,8 @@ const server = Bun.serve({
const method = req.method const method = req.method
if (pathname === '/api/version' && method === 'GET') return handlers.getVersion(req) if (pathname === '/api/version' && method === 'GET') return handlers.getVersion(req)
if (pathname === '/api/avatar' && method === 'POST') return handlers.setAvatar(req)
if (pathname === '/api/avatar' && method === 'GET') return handlers.getAvatar(req)
if (pathname === '/api/login' && method === 'POST') return handlers.login(req) if (pathname === '/api/login' && method === 'POST') return handlers.login(req)
if (pathname === '/ws') return upgrade(req, server) if (pathname === '/ws') return upgrade(req, server)
@@ -55,16 +57,30 @@ const server = Bun.serve({
const webSocketData = JSON.parse(message) as WebSocketData const webSocketData = JSON.parse(message) as WebSocketData
if (!webSocketData) return if (!webSocketData) return
if(webSocketData.type === 'GET_CHATS') {
const chatResponse = await messageService.listChat({ page: 0, userId: ws.data.userId })
chatResponse.data.forEach((el) => ws.subscribe(el.id))
ws.send(JSON.stringify({ type: 'CHATS', ...chatResponse }))
}
if (webSocketData.type === 'CREATE_CHAT') { if (webSocketData.type === 'CREATE_CHAT') {
const chat = await messageService.createChat({ users: [{ userId: webSocketData.data.userId }, { userId: ws.data.userId }] }) const chat = await messageService.createChat({
users: [
{ userId: webSocketData.data.userId },
{ userId: ws.data.userId }
] })
if (chat?.data) {
ws.subscribe(chat.data.id)
ws.send(JSON.stringify({ type: 'CHATS', ...chat })) ws.send(JSON.stringify({ type: 'CHATS', ...chat }))
} }
}
if (webSocketData.type === 'CREATE_MESSAGE') { if (webSocketData.type === 'CREATE_MESSAGE') {
const messageResponse = await messageService.createMessage({ const messageResponse = await messageService.createMessage({
chatId: webSocketData.data.chatId, chatId: webSocketData.data.chatId,
userId: ws.data.userId,
message: webSocketData.data.message, message: webSocketData.data.message,
userId: ws.data.userId,
}) })
server.publish(webSocketData.data.chatId, JSON.stringify({ type: 'MESSAGE', ...messageResponse })) server.publish(webSocketData.data.chatId, JSON.stringify({ type: 'MESSAGE', ...messageResponse }))
} }

View File

@@ -1,11 +0,0 @@
import { S3Client } from "@aws-sdk/client-s3";
const rustfs_client = new S3Client({
region: "cn-east-1",
credentials: {
accessKeyId: process.env.RUSTFS_ACCESS_KEY_ID!,
secretAccessKey: process.env.RUSTFS_SECRET_ACCESS_KEY!,
},
endpoint: process.env.RUSTFS_ENDPOINT_URL!,
});

30
src/storage/index.ts Normal file
View File

@@ -0,0 +1,30 @@
// import { ListBucketsCommand, PutObjectCommand, S3Client } from '@aws-sdk/client-s3'
import { S3Client, type S3File } from 'bun'
class Storage {
client: S3Client
constructor() {
this.client = new S3Client({
region: 'cn-east-1',
accessKeyId: process.env.RUSTFS_ACCESS_KEY_ID!,
secretAccessKey: process.env.RUSTFS_SECRET_ACCESS_KEY!,
bucket: 'stor1',
endpoint: process.env.RUSTFS_ENDPOINT_URL!,
})
}
async upload(file: Blob, name: string): Promise<void> {
const s3File: S3File = this.client.file(name)
const result = await s3File.write(file)
console.log({result})
}
presign(name: string) {
const s3File = this.client.file(name)
return s3File.presign({method: 'GET'})
}
}
export const storage = new Storage()

View File

@@ -25,6 +25,10 @@ interface ListMessages {
} }
} }
interface ListChats {
type: 'GET_CHATS'
}
interface CreateMessage { interface CreateMessage {
type: 'CREATE_MESSAGE' type: 'CREATE_MESSAGE'
data: { data: {
@@ -48,4 +52,4 @@ interface CreateChat {
} }
} }
export type WebSocketData = ListMessages | CreateMessage | ListUsers | CreateChat export type WebSocketData = ListMessages | CreateMessage | ListUsers | CreateChat | ListChats