Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Logs
page/
logs
*.log
npm-debug.log*
Expand Down
4 changes: 2 additions & 2 deletions api/Exercise.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ const exerciseSchema = new mongoose.Schema({
en: { type: String, required: true },
pt: { type: String, required: true }
},
images: { type: [String], required: true }, // Imagens permanecem inalteradas
id: { type: String, required: true, unique: true } // ID permanece inalterado
images: { type: [String], required: true },
id: { type: String, required: true, unique: true }
});

module.exports = mongoose.model("Exercise", exerciseSchema);
256 changes: 193 additions & 63 deletions api/exerciseRoutes.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ const Fuse = require("fuse.js");
const router = express.Router();
const Exercise = require("./Exercise");

// Configurações do Fuse.js
const fuseOptions = {
includeScore: true,
threshold: 0.4,
keys: ["name.en", "name.pt"], // Busca em ambos os idiomas
keys: ["name.en", "name.pt"],
};

// Mensagens de erro localizadas
Expand All @@ -15,13 +16,33 @@ const errorMessages = {
en: "Invalid language. Use 'en' or 'pt'.",
pt: "Idioma inválido. Use 'en' ou 'pt'.",
},
invalideParametters: {
en: "The field(s) parameter(s) cannot be empty. Valid fields are:",
pt: "O(s) parâmetro(s) de campo(s) não pode(m) estar vazio(s). Campos válidos são:",
},
invalidFields: {
en: "Invalid field(s). Valid fields are:",
pt: "Campo(s) inválido(s). Campos válidos são:",
},
missingQuery: {
en: "Please provide a search term.",
pt: "Por favor, insira um termo de pesquisa.",
},
invalidPage: {
en: "parameter 'page' is invalid. use a value greater than or equal to 0.",
pt: "Parâmetro 'page' inválido. use um valor maior ou igual a 0.",
},
invalidLimit: {
en: "parameter 'limit' is invalid. use a value greater than 0.",
pt: "Parâmetro 'limit' inválido. use um valor maior que 0",
},
resultesFound: {
en: "Exercises found:",
pt: "Exercícios encontrados:",
},
noResults: {
en: "Exercise not found.",
pt: "Exercício não encontrado.",
en: "No exercises found.",
pt: "Nenhum exercício encontrado.",
},
fetchError: {
en: "Error fetching exercises.",
Expand All @@ -33,117 +54,226 @@ const errorMessages = {
},
};

// Rota para obter exercícios paginados em um idioma específico
// Lista de campos válidos
const validFields = [
"force",
"level",
"mechanic",
"equipment",
"primaryMuscles",
"secondaryMuscles",
"instructions",
"category",
"images",
"name",
];

// Endpoint para obter exercícios com filtros dinâmicos e campos opcionais
router.get("/", async (req, res) => {
const lang = req.query.lang || "en";
const fields = req.query.fields ? req.query.fields.split(",") : null;
const query = {};

// Validar paremettro 'lang
if (!["en", "pt"].includes(lang)) {
return res
.status(400)
.json({ message: errorMessages.invalidLang[lang] });
}

// Validar parametros 'fields'
if (req.query.fields === "") {
return res.status(400).json({
message: `${errorMessages.invalideParametters[lang] } ${validFields.join(", ")}.`,
});
}

if (fields) {
const invalidFields = fields.filter((field) => !validFields.includes(field));
if (invalidFields.length > 0) {
return res.status(400).json({
message: `${errorMessages.invalidFields[lang] || errorMessages.invalidFields.en} ${validFields.join(", ")}.`,
invalidFields,
})
}
}

try {
const page = parseInt(req.query.page) || 0;
const limit = parseInt(req.query.limit) || 50;
const lang = req.query.lang || "en"; // Idioma padrão é inglês

if (!["en", "pt"].includes(lang)) {
return res
.status(400)
.json({ message: errorMessages.invalidLang[lang] || errorMessages.invalidLang.en });
// Filtros dinâmicos
for (const [key, value] of Object.entries(req.query)) {
if (!["lang", "fields", "page", "limit"].includes(key)) {
if (["primaryMuscles", "secondaryMuscles", "level"].includes(key)) {
query[`${key}.${lang}`] = value; // Campo multilíngue
} else {
query[key] = value; // Outros filtros
}
}
}

// Validação para 'page' e 'limit'
let page = req.query.page ? parseInt(req.query.page, 10) : 0;
let limit = req.query.limit ? parseInt(req.query.limit, 10) : 50;

if (
(req.query.page && (!Number.isInteger(page) || page < 0))
) {
return res.status(400).json({ message: errorMessages.invalidPage[lang] });
};

if (
(req.query.limit && (!Number.isInteger(limit) || limit < 1))
) {
return res.status(400).json({ message: errorMessages.invalidLimit[lang] });
}

const exercises = await Exercise.find()
page = page || 0;
limit = limit || 50;

const exercises = await Exercise.find(query)
.skip(page * limit)
.limit(limit)
.lean();

// Formatar os resultados com base no idioma solicitado
const formattedExercises = exercises.map((exercise) => ({
_id: exercise._id,
name: exercise.name[lang],
force: exercise.force[lang],
level: exercise.level[lang],
mechanic: exercise.mechanic[lang],
equipment: exercise.equipment[lang],
primaryMuscles: exercise.primaryMuscles[lang],
secondaryMuscles: exercise.secondaryMuscles[lang],
instructions: exercise.instructions[lang],
category: exercise.category[lang],
images: exercise.images,
id: exercise.id,
}));
if (exercises.length === 0) {
return res.status(404).json({
message: errorMessages.noResults[lang],
});
}

const formattedExercises = exercises.map((exercise) => {
if (fields) {
// Retorna apenas os campos especificados em 'fields'
const result = {
_id: exercise._id,
name: exercise.name[lang],
};

fields.forEach((field) => {
if (exercise[field]) {
result[field] = exercise[field][lang] || exercise[field];
}
});

return result;
}

// Retorna todos os campos se 'fields' não estiver presente
return {
...exercise,
_id: exercise._id,
name: exercise.name[lang],
force: exercise.force[lang],
level: exercise.level[lang],
mechanic: exercise.mechanic[lang],
equipment: exercise.equipment[lang],
primaryMuscles: exercise.primaryMuscles[lang],
secondaryMuscles: exercise.secondaryMuscles[lang],
instructions: exercise.instructions[lang],
category: exercise.category[lang],
images: exercise.images,
id: exercise.id,
};
});

res.json(formattedExercises);
} catch (err) {
console.error(
lang === "pt"
? `${errorMessages.fetchError.pt}: ${err.message}`
: `${errorMessages.fetchError.en}: ${err.message}`
);
res.status(500).json({
message: errorMessages.fetchError[lang] || errorMessages.fetchError.en,
res.status(500).json({
message: errorMessages.fetchError[lang],
error: err.message,
});
}
});

// Rota para buscar exercícios com idioma
// Rota para buscar exercícios
router.get("/search", async (req, res) => {
const { query, lang = "en" } = req.query;
const { query, lang = "en", fields } = req.query;

// Validar idioma
if (!["en", "pt"].includes(lang)) {
return res
.status(400)
.json({ message: errorMessages.invalidLang[lang] || errorMessages.invalidLang.en });
.json({ message: errorMessages.invalidLang[lang] });
}

// Validar termo de busca
if (!query) {
return res
.status(400)
.json({ message: errorMessages.missingQuery[lang] || errorMessages.missingQuery.en });
.json({ message: errorMessages.missingQuery[lang] });
}

try {
const exercises = await Exercise.find().lean(); // Obtém todos os exercícios
// Recuperar todos os exercícios
const exercises = await Exercise.find().lean();

// Configurar Fuse.js com base no idioma
const fuse = new Fuse(exercises, {
...fuseOptions,
keys: [`name.${lang}`], // Busca apenas no idioma solicitado
keys: [`name.${lang}`],
});

// Realizar busca
const results = fuse.search(query);

// Filtrar os exercícios correspondentes
// Verificar se há resultados
if (results.length === 0) {
return res.json({ message: errorMessages.noResults[lang] });
}

// Converter resultados e aplicar `fields`, se fornecido
const fieldList = fields ? fields.split(",") : null;
const matchedExercises = results.map((result) => {
const exercise = result.item;
return {

// Montar resposta com todos os campos ou apenas os selecionados
const response = {
_id: exercise._id,
name: exercise.name[lang],
force: exercise.force[lang],
level: exercise.level[lang],
mechanic: exercise.mechanic[lang],
equipment: exercise.equipment[lang],
primaryMuscles: exercise.primaryMuscles[lang],
secondaryMuscles: exercise.secondaryMuscles[lang],
instructions: exercise.instructions[lang],
category: exercise.category[lang],
images: [
};

if (!fieldList || fieldList.includes("force")) {
response.force = exercise.force[lang];
}
if (!fieldList || fieldList.includes("level")) {
response.level = exercise.level[lang];
}
if (!fieldList || fieldList.includes("mechanic")) {
response.mechanic = exercise.mechanic[lang];
}
if (!fieldList || fieldList.includes("equipment")) {
response.equipment = exercise.equipment[lang];
}
if (!fieldList || fieldList.includes("primaryMuscles")) {
response.primaryMuscles = exercise.primaryMuscles[lang];
}
if (!fieldList || fieldList.includes("secondaryMuscles")) {
response.secondaryMuscles = exercise.secondaryMuscles[lang];
}
if (!fieldList || fieldList.includes("instructions")) {
response.instructions = exercise.instructions[lang];
}
if (!fieldList || fieldList.includes("category")) {
response.category = exercise.category[lang];
}
if (!fieldList || fieldList.includes("images")) {
response.images = [
`/exercises/${exercise.id}/0.jpg`,
`/exercises/${exercise.id}/1.jpg`,
],
id: exercise.id,
};
});
];
}

// Se nenhum exercício foi encontrado
if (matchedExercises.length === 0) {
return res.json({ message: errorMessages.noResults[lang] || errorMessages.noResults.en });
}
return response;
});

// Retornar os exercícios encontrados
res.json({ message: "Exercises found:", exercises: matchedExercises });
res.json({ message: errorMessages.resultesFound[lang], exercises: matchedExercises });
} catch (err) {
console.error(
lang === "pt"
? `${errorMessages.searchError.pt}: ${err.message}`
: `${errorMessages.searchError.en}: ${err.message}`
);
res.status(500).json({
message: errorMessages.searchError[lang] || errorMessages.searchError.en,
message: errorMessages.searchError[lang],
error: err.message,
});
}
Expand Down
Loading