feat: add projects files
commit
4f8e2efd85
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"extends": ["@commitlint/config-conventional"]
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
/node_modules
|
||||||
|
.env
|
|
@ -0,0 +1,4 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
. "$(dirname -- "$0")/_/husky.sh"
|
||||||
|
|
||||||
|
npx commitlint --edit
|
|
@ -0,0 +1,62 @@
|
||||||
|
require('dotenv').config()
|
||||||
|
require('express-async-errors')
|
||||||
|
|
||||||
|
// express
|
||||||
|
const express = require('express')
|
||||||
|
const app = express()
|
||||||
|
|
||||||
|
// rest of the packages
|
||||||
|
const morgan = require('morgan')
|
||||||
|
// const fileUpload = require('express-fileupload')
|
||||||
|
|
||||||
|
const rateLimiter = require('express-rate-limit')
|
||||||
|
const helmet = require('helmet')
|
||||||
|
const xss = require('xss-clean')
|
||||||
|
const cors = require('cors')
|
||||||
|
|
||||||
|
// database
|
||||||
|
const connectDB = require('./db/connect')
|
||||||
|
|
||||||
|
// routers
|
||||||
|
const nlRouter = require('./routes/naturalLanguageRoute')
|
||||||
|
|
||||||
|
|
||||||
|
const notFoundMiddlware = require('./middleware/not-found')
|
||||||
|
const errorHandlerMiddleware = require('./middleware/error-handler')
|
||||||
|
|
||||||
|
//middleware
|
||||||
|
app.set('trust proxy', 1)
|
||||||
|
app.use(rateLimiter({
|
||||||
|
windowMs: 15 * 60 * 1000,
|
||||||
|
max: 60,
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Security packages
|
||||||
|
app.use(helmet())
|
||||||
|
app.use(cors())
|
||||||
|
app.use(xss())
|
||||||
|
|
||||||
|
app.use(morgan('tiny'))
|
||||||
|
app.use(express.json())
|
||||||
|
|
||||||
|
app.use(express.static('./public'))
|
||||||
|
// app.use(fileUpload())
|
||||||
|
|
||||||
|
app.get('/', (req, res) => {
|
||||||
|
res.send('e-commerce api')
|
||||||
|
})
|
||||||
|
|
||||||
|
// app.get('/api/v1/', (req, res) => {
|
||||||
|
// console.log(req.signedCookies)
|
||||||
|
// res.send('e-commerce api')
|
||||||
|
// })
|
||||||
|
|
||||||
|
app.use('/api/v1/nl', nlRouter)
|
||||||
|
|
||||||
|
app.use(notFoundMiddlware)
|
||||||
|
app.use(errorHandlerMiddleware)
|
||||||
|
|
||||||
|
const port = process.env.PORT || 3000
|
||||||
|
|
||||||
|
app.listen(port, console.log(`Listening on port: ${port}...`))
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
const User = require('../models/User')
|
||||||
|
const { StatusCodes } = require('http-status-codes')
|
||||||
|
const CustomError = require('../errors')
|
||||||
|
|
||||||
|
const { attachCookiesToResponse, createTokenUser } = require('../utils')
|
||||||
|
|
||||||
|
const register = async (req, res) => {
|
||||||
|
|
||||||
|
const { email, name, password } = req.body
|
||||||
|
|
||||||
|
const emailAlreadyExists = await User.findOne({ email })
|
||||||
|
if (emailAlreadyExists) {
|
||||||
|
throw new CustomError.BadRequestError('Email already exists')
|
||||||
|
}
|
||||||
|
|
||||||
|
// first register user is an admin
|
||||||
|
const isFirstAccount = await User.countDocuments({}) === 0
|
||||||
|
|
||||||
|
const role = isFirstAccount ? 'admin' : 'user'
|
||||||
|
|
||||||
|
const user = await User.create({ name, email, password, role })
|
||||||
|
|
||||||
|
const tokenUser = createTokenUser(user)
|
||||||
|
|
||||||
|
attachCookiesToResponse({ res, user: tokenUser })
|
||||||
|
|
||||||
|
res.status(StatusCodes.CREATED).json({ user: tokenUser })
|
||||||
|
}
|
||||||
|
const login = async (req, res) => {
|
||||||
|
const { email, password } = req.body
|
||||||
|
|
||||||
|
if (!email || !password) {
|
||||||
|
throw new CustomError.BadRequestError('Please provide email and password')
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await User.findOne({ email })
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new CustomError.UnauthenticatedError('Ivalid Credentials')
|
||||||
|
}
|
||||||
|
|
||||||
|
const isPasswordCorret = await user.comparePassword(password)
|
||||||
|
|
||||||
|
if (!isPasswordCorret) {
|
||||||
|
throw new CustomError.UnauthenticatedError('Ivalid Credentials')
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const tokenUser = {
|
||||||
|
name: user.name,
|
||||||
|
userId: user._id,
|
||||||
|
role: user.role
|
||||||
|
}
|
||||||
|
|
||||||
|
attachCookiesToResponse({ res, user: tokenUser })
|
||||||
|
|
||||||
|
res.status(StatusCodes.OK).json({ user: tokenUser })
|
||||||
|
|
||||||
|
}
|
||||||
|
const logout = async (req, res) => {
|
||||||
|
res.cookie('token', 'logout', {
|
||||||
|
httpOnly: true,
|
||||||
|
expires: new Date(Date.now())
|
||||||
|
})
|
||||||
|
|
||||||
|
res.status(StatusCodes.OK).json({
|
||||||
|
msg: 'user logged out!'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
register,
|
||||||
|
login,
|
||||||
|
logout
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
const { StatusCodes } = require("http-status-codes")
|
||||||
|
const { sentiment, convertTextToSpeech, listVoice } = require("../utils")
|
||||||
|
const language = require('@google-cloud/language').v2
|
||||||
|
const CustomError = require('../errors')
|
||||||
|
const voiceConfigList = require('../mockData/voice.json')
|
||||||
|
|
||||||
|
const getSentiment = async (req, res) => {
|
||||||
|
|
||||||
|
const { text, } = req.body
|
||||||
|
|
||||||
|
const status = await sentiment(text)
|
||||||
|
|
||||||
|
res.status(StatusCodes.OK).json({ status })
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAudioFromText = async (req, res) => {
|
||||||
|
|
||||||
|
const { text, voice_name, voice_gender, languageCode } = req.query
|
||||||
|
|
||||||
|
if ((voice_name || voice_gender || languageCode) && languageCode == 'pt-BR') {
|
||||||
|
|
||||||
|
const config = { voice_name, voice_gender, languageCode }
|
||||||
|
|
||||||
|
for (const key in config) {
|
||||||
|
if (config.hasOwnProperty(key) && config[key] === undefined) {
|
||||||
|
throw new CustomError.BadRequestError(`The key ${key} is required when setted one of the three configuration parameters: voice_name, voice_gender and languageCode`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const voice = voiceConfigList.find(({ name, ssmlGender, languageCode }) => {
|
||||||
|
if (name == config.voice_name && ssmlGender == config.voice_gender && languageCode == config.languageCode) return { name, ssmlGender, languageCode }
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!voice)
|
||||||
|
throw new CustomError.BadRequestError(`Wrong config voice combination! Check the endpoint(http://localhost:3000/api/v1/nl/voice-config) to display the available configurations to a language`)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const audioBuffer = await convertTextToSpeech(text, voice_name, voice_gender, languageCode)
|
||||||
|
|
||||||
|
res.contentType('audio/mpeg')
|
||||||
|
|
||||||
|
res.status(StatusCodes.OK).send(audioBuffer)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const getVoiceConfig = async (req, res) => {
|
||||||
|
|
||||||
|
const { languageCode } = req.query
|
||||||
|
|
||||||
|
console.log(languageCode)
|
||||||
|
|
||||||
|
const configs = await listVoice(languageCode)
|
||||||
|
|
||||||
|
res.status(StatusCodes.OK).json({ configs })
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getSentiment,
|
||||||
|
getAudioFromText,
|
||||||
|
getVoiceConfig
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
const mongoose = require('mongoose');
|
||||||
|
|
||||||
|
const connectDB = (url) => {
|
||||||
|
return mongoose.connect(url);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = connectDB;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
const { StatusCodes } = require('http-status-codes');
|
||||||
|
const CustomAPIError = require('./custom-api');
|
||||||
|
|
||||||
|
class BadRequestError extends CustomAPIError {
|
||||||
|
constructor(message) {
|
||||||
|
super(message);
|
||||||
|
this.statusCode = StatusCodes.BAD_REQUEST;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = BadRequestError;
|
|
@ -0,0 +1,7 @@
|
||||||
|
class CustomAPIError extends Error {
|
||||||
|
constructor(message) {
|
||||||
|
super(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = CustomAPIError
|
|
@ -0,0 +1,13 @@
|
||||||
|
const CustomAPIError = require('./custom-api');
|
||||||
|
const UnauthenticatedError = require('./unauthenticated');
|
||||||
|
const NotFoundError = require('./not-found');
|
||||||
|
const BadRequestError = require('./bad-request');
|
||||||
|
const UnauthorizedError = require('./unauthorized');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
CustomAPIError,
|
||||||
|
UnauthenticatedError,
|
||||||
|
NotFoundError,
|
||||||
|
BadRequestError,
|
||||||
|
UnauthorizedError,
|
||||||
|
};
|
|
@ -0,0 +1,11 @@
|
||||||
|
const { StatusCodes } = require('http-status-codes');
|
||||||
|
const CustomAPIError = require('./custom-api');
|
||||||
|
|
||||||
|
class NotFoundError extends CustomAPIError {
|
||||||
|
constructor(message) {
|
||||||
|
super(message);
|
||||||
|
this.statusCode = StatusCodes.NOT_FOUND;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = NotFoundError;
|
|
@ -0,0 +1,11 @@
|
||||||
|
const { StatusCodes } = require('http-status-codes');
|
||||||
|
const CustomAPIError = require('./custom-api');
|
||||||
|
|
||||||
|
class UnauthenticatedError extends CustomAPIError {
|
||||||
|
constructor(message) {
|
||||||
|
super(message);
|
||||||
|
this.statusCode = StatusCodes.UNAUTHORIZED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = UnauthenticatedError;
|
|
@ -0,0 +1,11 @@
|
||||||
|
const { StatusCodes } = require('http-status-codes');
|
||||||
|
const CustomAPIError = require('./custom-api');
|
||||||
|
|
||||||
|
class UnauthorizedError extends CustomAPIError {
|
||||||
|
constructor(message) {
|
||||||
|
super(message);
|
||||||
|
this.statusCode = StatusCodes.FORBIDDEN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = UnauthorizedError;
|
|
@ -0,0 +1,41 @@
|
||||||
|
const CustomError = require('../errors')
|
||||||
|
|
||||||
|
const authorization = async (req, res, next) => {
|
||||||
|
|
||||||
|
const authHeader = req.headers.authorization
|
||||||
|
|
||||||
|
if (!authHeader) {
|
||||||
|
throw new CustomError.BadRequestError('Authorization not found into header!')
|
||||||
|
}
|
||||||
|
|
||||||
|
const [, token] = authHeader.split(" ");
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
|
throw new CustomError.BadRequestError('Authorization token not found into header!')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token != process.env.TOKEN){
|
||||||
|
throw new CustomError.UnauthorizedError('Authorization token Invalid')
|
||||||
|
}
|
||||||
|
|
||||||
|
next()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const authorizePermissions = (...roles) => {
|
||||||
|
|
||||||
|
return (req, res, next) => {
|
||||||
|
|
||||||
|
if (!roles.includes(req.user.role)) {
|
||||||
|
throw new CustomError.UnauthorizedError('Unauthorized do access to this routes')
|
||||||
|
}
|
||||||
|
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
authorization,
|
||||||
|
authorizePermissions,
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
const { StatusCodes } = require('http-status-codes');
|
||||||
|
const errorHandlerMiddleware = (err, req, res, next) => {
|
||||||
|
let customError = {
|
||||||
|
// set default
|
||||||
|
statusCode: err.statusCode || StatusCodes.INTERNAL_SERVER_ERROR,
|
||||||
|
msg: err.message || 'Something went wrong try again later',
|
||||||
|
};
|
||||||
|
if (err.name === 'ValidationError') {
|
||||||
|
customError.msg = Object.values(err.errors)
|
||||||
|
.map((item) => item.message)
|
||||||
|
.join(',');
|
||||||
|
customError.statusCode = 400;
|
||||||
|
}
|
||||||
|
if (err.code && err.code === 11000) {
|
||||||
|
customError.msg = `Duplicate value entered for ${Object.keys(
|
||||||
|
err.keyValue
|
||||||
|
)} field, please choose another value`;
|
||||||
|
customError.statusCode = 400;
|
||||||
|
}
|
||||||
|
if (err.name === 'CastError') {
|
||||||
|
customError.msg = `No item found with id : ${err.value}`;
|
||||||
|
customError.statusCode = 404;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(customError.statusCode).json({ msg: customError.msg });
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = errorHandlerMiddleware;
|
|
@ -0,0 +1,3 @@
|
||||||
|
const notFound = (req, res) => res.status(404).send('Route does not exist')
|
||||||
|
|
||||||
|
module.exports = notFound
|
|
@ -0,0 +1,35 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"tax": 399,
|
||||||
|
"shippingFee": 499,
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"name": "accent chair",
|
||||||
|
"price": 2599,
|
||||||
|
"image": "https://dl.airtable.com/.attachmentThumbnails/e8bc3791196535af65f40e36993b9e1f/438bd160",
|
||||||
|
"amount": 34,
|
||||||
|
"product": "6126ad3424d2bd09165a68c8"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tax": 499,
|
||||||
|
"shippingFee": 799,
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"name": "bed",
|
||||||
|
"price": 2699,
|
||||||
|
"image": "https://dl.airtable.com/.attachmentThumbnails/e8bc3791196535af65f40e36993b9e1f/438bd160",
|
||||||
|
"amount": 3,
|
||||||
|
"product": "6126ad3424d2bd09165a68c7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "chair",
|
||||||
|
"price": 2999,
|
||||||
|
"image": "https://dl.airtable.com/.attachmentThumbnails/e8bc3791196535af65f40e36993b9e1f/438bd160",
|
||||||
|
"amount": 2,
|
||||||
|
"product": "6126ad3424d2bd09165a68c4"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,38 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "accent chair",
|
||||||
|
"price": 25999,
|
||||||
|
"image": "https://dl.airtable.com/.attachmentThumbnails/e8bc3791196535af65f40e36993b9e1f/438bd160",
|
||||||
|
"colors": ["#ff0000", "#00ff00", "#0000ff"],
|
||||||
|
"company": "marcos",
|
||||||
|
"description": "Cloud bread VHS hell of banjo bicycle rights jianbing umami mumblecore etsy 8-bit pok pok +1 wolf. Vexillologist yr dreamcatcher waistcoat, authentic chillwave trust fund. Viral typewriter fingerstache pinterest pork belly narwhal. Schlitz venmo everyday carry kitsch pitchfork chillwave iPhone taiyaki trust fund hashtag kinfolk microdosing gochujang live-edge",
|
||||||
|
"category": "office"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "albany sectional",
|
||||||
|
"price": 109999,
|
||||||
|
"image": "https://dl.airtable.com/.attachmentThumbnails/0be1af59cf889899b5c9abb1e4db38a4/d631ac52",
|
||||||
|
"colors": ["#000", "#ffb900"],
|
||||||
|
"company": "liddy",
|
||||||
|
"description": "Cloud bread VHS hell of banjo bicycle rights jianbing umami mumblecore etsy 8-bit pok pok +1 wolf. Vexillologist yr dreamcatcher waistcoat, authentic chillwave trust fund. Viral typewriter fingerstache pinterest pork belly narwhal. Schlitz venmo everyday carry kitsch pitchfork chillwave iPhone taiyaki trust fund hashtag kinfolk microdosing gochujang live-edge",
|
||||||
|
"category": "kitchen"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "armchair",
|
||||||
|
"price": 12599,
|
||||||
|
"image": "https://dl.airtable.com/.attachmentThumbnails/530c07c5ade5acd9934c8dd334458b86/cf91397f",
|
||||||
|
"colors": ["#000", "#00ff00", "#0000ff"],
|
||||||
|
"company": "marcos",
|
||||||
|
"description": "Cloud bread VHS hell of banjo bicycle rights jianbing umami mumblecore etsy 8-bit pok pok +1 wolf. Vexillologist yr dreamcatcher waistcoat, authentic chillwave trust fund. Viral typewriter fingerstache pinterest pork belly narwhal. Schlitz venmo everyday carry kitsch pitchfork chillwave iPhone taiyaki trust fund hashtag kinfolk microdosing gochujang live-edge",
|
||||||
|
"category": "bedroom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "emperor bed",
|
||||||
|
"price": 23999,
|
||||||
|
"image": "https://dl.airtable.com/.attachmentThumbnails/0446e84c5bca9643de3452a61b2d6195/1b32f48b",
|
||||||
|
"colors": ["#0000ff", "#000"],
|
||||||
|
"company": "ikea",
|
||||||
|
"description": "Cloud bread VHS hell of banjo bicycle rights jianbing umami mumblecore etsy 8-bit pok pok +1 wolf. Vexillologist yr dreamcatcher waistcoat, authentic chillwave trust fund. Viral typewriter fingerstache pinterest pork belly narwhal. Schlitz venmo everyday carry kitsch pitchfork chillwave iPhone taiyaki trust fund hashtag kinfolk microdosing gochujang live-edge",
|
||||||
|
"category": "bedroom"
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,46 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "pt-BR-Wavenet-A",
|
||||||
|
"ssmlGender": "FEMALE",
|
||||||
|
"languageCode": "pt-BR"
|
||||||
|
},
|
||||||
|
{ "name": "pt-BR-Wavenet-B", "ssmlGender": "MALE", "languageCode": "pt-BR" },
|
||||||
|
{
|
||||||
|
"name": "pt-BR-Wavenet-C",
|
||||||
|
"ssmlGender": "FEMALE",
|
||||||
|
"languageCode": "pt-BR"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pt-BR-Standard-A",
|
||||||
|
"ssmlGender": "FEMALE",
|
||||||
|
"languageCode": "pt-BR"
|
||||||
|
},
|
||||||
|
{ "name": "pt-BR-Standard-B", "ssmlGender": "MALE", "languageCode": "pt-BR" },
|
||||||
|
{
|
||||||
|
"name": "pt-BR-Standard-C",
|
||||||
|
"ssmlGender": "FEMALE",
|
||||||
|
"languageCode": "pt-BR"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pt-BR-Neural2-A",
|
||||||
|
"ssmlGender": "FEMALE",
|
||||||
|
"languageCode": "pt-BR"
|
||||||
|
},
|
||||||
|
{ "name": "pt-BR-Neural2-B", "ssmlGender": "MALE", "languageCode": "pt-BR" },
|
||||||
|
{
|
||||||
|
"name": "pt-BR-Neural2-C",
|
||||||
|
"ssmlGender": "FEMALE",
|
||||||
|
"languageCode": "pt-BR"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pt-BR-Standard-A",
|
||||||
|
"ssmlGender": "FEMALE",
|
||||||
|
"languageCode": "pt-BR"
|
||||||
|
},
|
||||||
|
{ "name": "pt-BR-Standard-B", "ssmlGender": "MALE", "languageCode": "pt-BR" },
|
||||||
|
{
|
||||||
|
"name": "pt-BR-Standard-C",
|
||||||
|
"ssmlGender": "FEMALE",
|
||||||
|
"languageCode": "pt-BR"
|
||||||
|
}
|
||||||
|
]
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,38 @@
|
||||||
|
{
|
||||||
|
"name": "sentiment_api",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "app.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node app.js",
|
||||||
|
"dev": "nodemon app.js"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"@google-cloud/language": "^6.1.0",
|
||||||
|
"@google-cloud/text-to-speech": "^5.0.1",
|
||||||
|
"bcryptjs": "^2.4.3",
|
||||||
|
"cookie-parser": "^1.4.5",
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"dotenv": "^10.0.0",
|
||||||
|
"express": "^4.17.1",
|
||||||
|
"express-async-errors": "^3.1.1",
|
||||||
|
"express-fileupload": "^1.2.1",
|
||||||
|
"express-mongo-sanitize": "^2.1.0",
|
||||||
|
"express-rate-limit": "^5.4.1",
|
||||||
|
"helmet": "^4.6.0",
|
||||||
|
"http-status-codes": "^2.1.4",
|
||||||
|
"joi": "^17.4.0",
|
||||||
|
"mongoose": "^7.3.1",
|
||||||
|
"morgan": "^1.10.0",
|
||||||
|
"validator": "^13.6.0",
|
||||||
|
"xss-clean": "^0.1.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@commitlint/cli": "^17.7.2",
|
||||||
|
"@commitlint/config-conventional": "^17.7.0",
|
||||||
|
"husky": "^8.0.3",
|
||||||
|
"nodemon": "^2.0.9"
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
After Width: | Height: | Size: 127 KiB |
Binary file not shown.
After Width: | Height: | Size: 205 KiB |
|
@ -0,0 +1,11 @@
|
||||||
|
const express = require('express')
|
||||||
|
const router = express.Router()
|
||||||
|
const { authorization, } = require('../middleware/authentication')
|
||||||
|
|
||||||
|
const { getSentiment, getAudioFromText, getVoiceConfig } = require('../controllers/naturalLanguageController')
|
||||||
|
|
||||||
|
router.route('/sentiment').post(authorization, getSentiment)
|
||||||
|
router.route('/text-to-speech').get(getAudioFromText)
|
||||||
|
router.route('/voice-config').get(getVoiceConfig)
|
||||||
|
|
||||||
|
module.exports = router
|
|
@ -0,0 +1,14 @@
|
||||||
|
// const { createJWT, isTokenValid, attachCookiesToResponse } = require('./jwt')
|
||||||
|
// const createTokenUser = require('./createTokenUser')
|
||||||
|
// const checkPermissions = require('./checkPermissions')
|
||||||
|
|
||||||
|
const sentiment = require('./sentiment')
|
||||||
|
const convertTextToSpeech = require('./textToSpeech')
|
||||||
|
const listVoice = require('./listVoice')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
sentiment,
|
||||||
|
convertTextToSpeech,
|
||||||
|
listVoice
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
const textToSpeech = require('@google-cloud/text-to-speech')
|
||||||
|
|
||||||
|
const listVoice = async (languageCode) => {
|
||||||
|
|
||||||
|
const client = new textToSpeech.TextToSpeechClient()
|
||||||
|
|
||||||
|
const [result] = await client.listVoices({ languageCode })
|
||||||
|
|
||||||
|
const voices = result.voices
|
||||||
|
|
||||||
|
return voices.map(({ name, ssmlGender, languageCodes }) => { return { name, ssmlGender, languageCodes: languageCodes[0] } })
|
||||||
|
}
|
||||||
|
module.exports = listVoice
|
|
@ -0,0 +1,42 @@
|
||||||
|
// Imports the Google Cloud client library
|
||||||
|
const language = require('@google-cloud/language').v2
|
||||||
|
|
||||||
|
// Creates a client
|
||||||
|
const client = new language.LanguageServiceClient()
|
||||||
|
|
||||||
|
const sentiment = async (text) => {
|
||||||
|
|
||||||
|
// Prepares a document, representing the provided text
|
||||||
|
const document = {
|
||||||
|
content: text,
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
}
|
||||||
|
|
||||||
|
let status = null
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Detects the sentiment of the document
|
||||||
|
const [result] = await client.analyzeSentiment({ document })
|
||||||
|
|
||||||
|
const sentiment = result.documentSentiment
|
||||||
|
|
||||||
|
if (sentiment.score <= -0.25)
|
||||||
|
status = 'negative'
|
||||||
|
else if (sentiment.score <= 0.25)
|
||||||
|
status = 'neutral'
|
||||||
|
else
|
||||||
|
status = 'positive'
|
||||||
|
|
||||||
|
console.log(` Text: ${document.content}`)
|
||||||
|
console.log(` Score: ${sentiment.score}`)
|
||||||
|
console.log(` Magnitude: ${sentiment.magnitude}`)
|
||||||
|
console.log(` languageCode: ${result.languageCode}`)
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.log(`Error in sentiment fuction on sentiment.js file: ${error}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = sentiment
|
|
@ -0,0 +1,47 @@
|
||||||
|
// Imports the Google Cloud client library
|
||||||
|
const textToSpeech = require('@google-cloud/text-to-speech')
|
||||||
|
const fs = require('fs')
|
||||||
|
const util = require('util')
|
||||||
|
// Creates a client
|
||||||
|
const client = new textToSpeech.TextToSpeechClient()
|
||||||
|
|
||||||
|
const convertTextToSpeech = async (
|
||||||
|
text,
|
||||||
|
voice_name = 'pt-BR-Standard-B',
|
||||||
|
voice_gender = 'MALE',
|
||||||
|
languageCode = 'pt-BR'
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
|
||||||
|
const request = {
|
||||||
|
input: { text: text },
|
||||||
|
voice: {
|
||||||
|
languageCode: languageCode,
|
||||||
|
name: voice_name,
|
||||||
|
ssmlGender: voice_gender,
|
||||||
|
},
|
||||||
|
audioConfig: { audioEncoding: 'MP3' },
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log('REQUEST: ', request)
|
||||||
|
|
||||||
|
// Performs the text-to-speech request
|
||||||
|
const [response] = await client.synthesizeSpeech(request)
|
||||||
|
|
||||||
|
return response.audioContent
|
||||||
|
|
||||||
|
// Write the binary audio content to a local file
|
||||||
|
// const writeFile = util.promisify(fs.writeFile)
|
||||||
|
|
||||||
|
// await writeFile('output.mp3', response.audioContent, 'binary')
|
||||||
|
|
||||||
|
// console.log('Audio content written to file: output.mp3')
|
||||||
|
} catch (error) {
|
||||||
|
console.log(
|
||||||
|
'There was an error on textToSpeech.ts file. Error in convertTextToSpeech function: ',
|
||||||
|
error
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = convertTextToSpeech
|
Loading…
Reference in New Issue