mirror of
https://github.com/sergeyyarkov/educt-server.git
synced 2026-10-11 10:19:20 +03:00
feat: added chat controller
This commit is contained in:
parent
c6472013a3
commit
97ed27a50a
38
app/Controllers/Http/Api/v1/ChatController.ts
Normal file
38
app/Controllers/Http/Api/v1/ChatController.ts
Normal 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);
|
||||||
@ -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
7
routes/apis/v1/chat.ts
Normal 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');
|
||||||
@ -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')
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user