Skip to content

Commit

Permalink
OV-0: + logs
Browse files Browse the repository at this point in the history
  • Loading branch information
anton-otroshchenko committed Sep 28, 2024
1 parent 431c83f commit 4ed9d3c
Show file tree
Hide file tree
Showing 102 changed files with 1,691 additions and 0 deletions.
2 changes: 2 additions & 0 deletions backend/knexfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import { database } from '~/common/database/database.js';
export default database.environmentsConfig;
114 changes: 114 additions & 0 deletions backend/src/bundles/auth/auth.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { userSignInValidationSchema, } from '~/bundles/users/users.js';
import { userSignUpValidationSchema } from '~/bundles/users/users.js';
import { BaseController, } from '~/common/controller/controller.js';
import { ApiPath } from '~/common/enums/enums.js';
import { HttpCode } from '~/common/http/http.js';
import { AuthApiPath } from './enums/enums.js';
class AuthController extends BaseController {
authService;
constructor(logger, authService) {
super(logger, ApiPath.AUTH);
this.authService = authService;
this.addRoute({
path: AuthApiPath.SIGN_IN,
method: 'POST',
validation: {
body: userSignInValidationSchema,
},
handler: (options) => this.signIn(options),
});
this.addRoute({
path: AuthApiPath.SIGN_UP,
method: 'POST',
validation: {
body: userSignUpValidationSchema,
},
handler: (options) => this.signUp(options),
});
}
/**
* @swagger
* /auth/sign-in:
* post:
* description: Sign in user into the application
* requestBody:
* description: User auth data
* required: true
* content:
* application/json:
* schema:
* type: object
* required: [email, password]
* properties:
* email:
* type: string
* format: email
* password:
* type: string
* responses:
* 200:
* description: Successful operation
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/User'
* 401:
* description: Failed operation. Unauthorized.
* content:
* application/json:
* schema:
* type: object
* $ref: '#/components/schemas/Error'
*/
async signIn(options) {
return {
payload: await this.authService.signIn(options.body),
status: HttpCode.OK,
};
}
/**
* @swagger
* /auth/sign-up:
* post:
* description: Sign up user into the application
* requestBody:
* description: User auth data
* required: true
* content:
* application/json:
* schema:
* type: object
* required: [fullName, email, password, confirmPassword]
* properties:
* fullName:
* type: string
* email:
* type: string
* format: email
* password:
* type: string
* confirmPassword:
* type: string
* responses:
* 201:
* description: Successful operation
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/User'
* 400:
* description: Failed operation
* content:
* application/json:
* schema:
* type: object
* $ref: '#/components/schemas/Error'
*/
async signUp(options) {
return {
status: HttpCode.CREATED,
payload: await this.authService.signUp(options.body),
};
}
}
export { AuthController };
7 changes: 7 additions & 0 deletions backend/src/bundles/auth/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { userService } from '~/bundles/users/users.js';
import { logger } from '~/common/logger/logger.js';
import { AuthController } from './auth.controller.js';
import { AuthService } from './auth.service.js';
const authService = new AuthService(userService);
const authController = new AuthController(logger, authService);
export { authController, authService };
46 changes: 46 additions & 0 deletions backend/src/bundles/auth/auth.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { HttpCode, HttpError } from '~/common/http/http.js';
import { cryptService, tokenService } from '~/common/services/services.js';
import { UserValidationMessage } from './enums/enums.js';
class AuthService {
userService;
constructor(userService) {
this.userService = userService;
}
async signIn(userRequestDto) {
const { email, password } = userRequestDto;
const user = await this.userService.findByEmail(email);
if (!user) {
throw new HttpError({
message: UserValidationMessage.WRONG_CREDENTIALS,
status: HttpCode.BAD_REQUEST,
});
}
const { passwordHash } = user.toNewObject();
const isPwdCorrect = cryptService.compareSyncPassword(password, passwordHash);
if (!isPwdCorrect) {
throw new HttpError({
message: UserValidationMessage.WRONG_CREDENTIALS,
status: HttpCode.BAD_REQUEST,
});
}
const userObject = user.toObject();
const { id } = userObject;
const token = await tokenService.createToken(id);
return { ...userObject, token };
}
async signUp(userRequestDto) {
const { email } = userRequestDto;
const emailExists = await this.userService.findByEmail(email);
if (emailExists) {
throw new HttpError({
message: UserValidationMessage.EMAIL_ALREADY_EXISTS,
status: HttpCode.BAD_REQUEST,
});
}
const user = await this.userService.create(userRequestDto);
const { id } = user;
const token = await tokenService.createToken(id);
return { ...user, token };
}
}
export { AuthService };
1 change: 1 addition & 0 deletions backend/src/bundles/auth/enums/enums.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { AuthApiPath, UserValidationMessage } from 'shared';
109 changes: 109 additions & 0 deletions backend/src/bundles/chat/chat.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { BaseController, } from '~/common/controller/controller.js';
import { ApiPath } from '~/common/enums/enums.js';
import { HttpCode, HTTPMethod } from '~/common/http/http.js';
import { MAX_TOKEN } from '~/common/services/open-ai/libs/constants/constants.js';
import { ChatPath, OpenAIRole, } from '~/common/services/open-ai/libs/enums/enums.js';
import { textGenerationValidationSchema } from './libs/validation-schemas/validation-schemas.js';
class ChatController extends BaseController {
openAIService;
chatService;
constructor(logger, openAIService, chatService) {
super(logger, ApiPath.CHAT);
this.openAIService = openAIService;
this.chatService = chatService;
this.addRoute({
path: ChatPath.ROOT,
method: HTTPMethod.POST,
validation: {
body: textGenerationValidationSchema,
},
handler: (options) => this.generateChatAnswer(options),
});
this.addRoute({
path: ChatPath.ROOT,
method: HTTPMethod.DELETE,
handler: (options) => this.deleteSession(options),
});
}
/**
* @swagger
* /chat/:
* post:
* description: Returns generated text by Open AI
* requestBody:
* description: User message
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* message:
* type: string
* responses:
* 200:
* description: Successful operation
* content:
* application/json:
* schema:
* type: object
* properties:
* generatedText:
* type: string
*/
async generateChatAnswer(options) {
const { body, session } = options;
session.chatHistory = this.chatService.addMessageToHistory(session.chatHistory, body.message, OpenAIRole.USER);
session.chatHistory = this.chatService.deleteOldMessages(session.chatHistory, MAX_TOKEN);
const generatedText = await this.openAIService.generateText(session.chatHistory);
session.chatHistory = this.chatService.addMessageToHistory(session.chatHistory, generatedText, OpenAIRole.ASSISTANT);
return {
payload: { generatedText },
status: HttpCode.OK,
};
}
/**
* @swagger
* /chat/:
* delete:
* description: Clears chat history
* requestBody:
* description: User message
* required: false
* responses:
* 200:
* description: Successful operation
* content:
* application/json:
* schema:
* type: object
* properties:
* isDeleted:
* type: boolean
* 500:
* description: Failed operation
* content:
* application/json:
* schema:
* type: object
* properties:
* isDeleted:
* type: boolean
*/
deleteSession(options) {
const { session } = options;
session.destroy((error) => {
if (error) {
return {
payload: { isDeleted: false },
status: HttpCode.INTERNAL_SERVER_ERROR,
};
}
});
return {
payload: { isDeleted: true },
status: HttpCode.OK,
};
}
}
export { ChatController };
7 changes: 7 additions & 0 deletions backend/src/bundles/chat/chat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { logger } from '~/common/logger/logger.js';
import { openAIService } from '~/common/services/services.js';
import { ChatController } from './chat.controller.js';
import { ChatService } from './chat.service.js';
const chatService = new ChatService();
const chatController = new ChatController(logger, openAIService, chatService);
export { chatController };
32 changes: 32 additions & 0 deletions backend/src/bundles/chat/chat.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { encoding_for_model } from 'tiktoken';
import { CHAT_MODEL } from '~/common/services/open-ai/libs/constants/constants.js';
class ChatService {
modelEncoding;
constructor() {
this.modelEncoding = encoding_for_model(CHAT_MODEL);
}
addMessageToHistory(chatHistory, userMessage, role) {
const newUserMessage = {
content: userMessage,
role,
};
return [...chatHistory, newUserMessage];
}
countTokens(messages) {
return messages.reduce((sum, message) => sum + this.modelEncoding.encode(message.content).length, 0);
}
deleteOldMessages(messages, maxTokens) {
let totalTokens = this.countTokens(messages);
let updatedMessages = [...messages];
while (totalTokens > maxTokens && updatedMessages.length > 0) {
const [removedMessage, ...rest] = updatedMessages;
updatedMessages = rest;
if (!removedMessage) {
break;
}
totalTokens -= this.modelEncoding.encode(removedMessage.content).length;
}
return updatedMessages;
}
}
export { ChatService };
1 change: 1 addition & 0 deletions backend/src/bundles/chat/libs/types/chat-service.type.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {};
1 change: 1 addition & 0 deletions backend/src/bundles/chat/libs/types/message.type.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {};
1 change: 1 addition & 0 deletions backend/src/bundles/chat/libs/types/types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { textGenerationValidationSchema } from 'shared';
1 change: 1 addition & 0 deletions backend/src/bundles/users/enums/enums.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { UsersApiPath } from 'shared';
1 change: 1 addition & 0 deletions backend/src/bundles/users/types/types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {};
57 changes: 57 additions & 0 deletions backend/src/bundles/users/user.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { BaseController, } from '~/common/controller/controller.js';
import { ApiPath } from '~/common/enums/enums.js';
import { HttpCode } from '~/common/http/http.js';
import { UsersApiPath } from './enums/enums.js';
/**
* @swagger
* components:
* schemas:
* User:
* type: object
* properties:
* id:
* type: string
* format: uuid
* email:
* type: string
* format: email
* fullName:
* type: string
*/
class UserController extends BaseController {
userService;
constructor(logger, userService) {
super(logger, ApiPath.USERS);
this.userService = userService;
this.addRoute({
path: UsersApiPath.ROOT,
method: 'GET',
handler: () => this.findAll(),
});
}
/**
* @swagger
* /users:
* get:
* description: Returns an array of users
* responses:
* 200:
* description: Successful operation
* content:
* application/json:
* schema:
* type: object
* properties:
* items:
* type: array
* items:
* $ref: '#/components/schemas/User'
*/
async findAll() {
return {
status: HttpCode.OK,
payload: await this.userService.findAll(),
};
}
}
export { UserController };
Loading

0 comments on commit 4ed9d3c

Please sign in to comment.