feat: added chat controller

This commit is contained in:
Sergey Yarkov 2022-03-01 18:10:04 +03:00
parent c6472013a3
commit 97ed27a50a
4 changed files with 92 additions and 1 deletions

View File

@ -0,0 +1,38 @@
import { inject, Ioc } from '@adonisjs/core/build/standalone';
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
import { ConversationType } from 'App/Store/MessageStore';
import IResponse from 'App/Datatypes/Interfaces/IResponse';
/**
* Enums
*/
import HttpStatusEnum from 'App/Datatypes/Enums/HttpStatusEnum';
/**
* Services
*/
import WsService from 'App/Services/WsService';
import BaseController from '../../BaseController';
@inject()
export default class ChatController extends BaseController {
/**
* Get list of conversations (users)
* GET /chat/conversations
*/
public async getConversations(ctx: HttpContextContract) {
const user = await ctx.auth.authenticate();
const data = await WsService.messageStore.getConversations(user.id);
const result: IResponse<ConversationType[]> = {
success: true,
status: HttpStatusEnum.OK,
message: 'Fetched conversations.',
data,
};
return this.sendResponse(ctx, result.data, result.message, result.status);
}
}
new Ioc().make(ChatController);

View File

@ -1,5 +1,6 @@
import { RedisConnectionContract } from '@ioc:Adonis/Addons/Redis'; import { RedisConnectionContract } from '@ioc:Adonis/Addons/Redis';
import Logger from '@ioc:Adonis/Core/Logger'; import Logger from '@ioc:Adonis/Core/Logger';
import User from 'App/Models/User';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import { nanoid } from 'nanoid'; import { nanoid } from 'nanoid';
@ -10,6 +11,12 @@ type MessageDataType = {
time: string; time: string;
}; };
export type ConversationType = {
userId: string;
fullname: string;
lastMessage: MessageDataType;
};
const MESSAGE_TTL = 185 * 24 * 60 * 60; // 185 days const MESSAGE_TTL = 185 * 24 * 60 * 60; // 185 days
const MAX_MESSAGES_HISTORY = 100; const MAX_MESSAGES_HISTORY = 100;
@ -62,6 +69,40 @@ class RedisMessageStore {
return messages.sort((m1, m2) => new Date(m1.time).valueOf() - new Date(m2.time).valueOf()); return messages.sort((m1, m2) => new Date(m1.time).valueOf() - new Date(m2.time).valueOf());
} }
public async getConversations(userId: string): Promise<ConversationType[]> {
/**
* Getting conversations that had some message history
*/
const conversations = await this.redisClient.keys(`conversation:*${userId}*`);
const userIds = conversations
.map(id => id.split(':')[1])
.map(id => (userId !== id.split('&')[0] ? id.split('&')[0] : id.split('&')[1]));
/**
* Fetch this users
*/
const users = await User.query().select('id', 'first_name', 'last_name').where('id', 'in', userIds);
/**
* Fetch last message of each conversation
*/
const result = await Promise.all(
users.map(async u => {
const convKey = this.createConversationKey(userId, u.id);
const msgKey = await this.redisClient.lindex(convKey, 0);
const lastMessage = await this.getMessage(`message:${msgKey}`);
return {
userId: u.id,
fullname: `${u.first_name} ${u.last_name}`,
lastMessage,
};
})
);
return result;
}
private async getMessage(key: string): Promise<MessageDataType> { private async getMessage(key: string): Promise<MessageDataType> {
const message = (await this.redisClient.hgetall(key)) as MessageDataType; const message = (await this.redisClient.hgetall(key)) as MessageDataType;
return message; return message;
@ -70,7 +111,7 @@ class RedisMessageStore {
// eslint-disable-next-line class-methods-use-this // eslint-disable-next-line class-methods-use-this
private createConversationKey(userId1: string, userId2: string): string { private createConversationKey(userId1: string, userId2: string): string {
const [id1, id2] = [userId1, userId2].sort(); const [id1, id2] = [userId1, userId2].sort();
return `conversation:${id1}-${id2}`; return `conversation:${id1}&${id2}`;
} }
} }

7
routes/apis/v1/chat.ts Normal file
View File

@ -0,0 +1,7 @@
import Route from '@ioc:Adonis/Core/Route';
Route.group(() => {
Route.get('/conversations', 'Api/v1/ChatController.getConversations')
.middleware('role:admin,teacher,student')
.as('chat.get-conversations');
}).prefix('chat');

View File

@ -68,6 +68,11 @@ Route.group(() => {
* Stat module * Stat module
*/ */
require('./stat'); require('./stat');
/**
* Chat module
*/
require('./chat');
}).middleware('auth'); }).middleware('auth');
}) })
.prefix('v1') .prefix('v1')