Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/c3' into nicolas
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolasf96 committed Mar 11, 2024
2 parents 33d9f66 + e7082de commit d4f33c3
Show file tree
Hide file tree
Showing 16 changed files with 146 additions and 139 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ package-lock.json
.vscode
.personal
/storage/img/*
!/storage/img/user.webp
!/storage/img/user.webp
.env
2 changes: 2 additions & 0 deletions api/v1/ia.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// TODO Refactor: Ver si se puede evitar axios. Si es estrictamente necesario, escribirlo en un comentario.
import axios from 'axios'
import event from 'events'
event.EventEmitter.defaultMaxListeners=50;

// TODO Security: Usar .env
const apiKey = 'B0ayZrLmS19aqobZA2wsYToeSqDz9cTm';
const base_url = 'https://api.deepinfra.com/v1/openai';

Expand Down
5 changes: 4 additions & 1 deletion api/v1/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -586,8 +586,10 @@ Respuesta.pagina = ({ pagina = 0, DNI } = {}) => {
where: {
"$respuestas.post.duenio.DNI$": DNI,
},
subQuery: false,
order: [[Post, "fecha", "DESC"]],
// ,raw:true,nest:true
limit: PAGINACION.resultadosPorPagina,
offset: +pagina * PAGINACION.resultadosPorPagina
});
};

Expand Down Expand Up @@ -789,6 +791,7 @@ Pregunta.pagina=({pagina=0,duenioID,filtrar,formatoCorto}={})=>{

if (filtrar) {
if (filtrar.texto) {
// TODO Security: cadena literal en consulta
opciones.where = Sequelize.or(
Sequelize.literal(
'match(post.cuerpo) against ("' +
Expand Down
67 changes: 46 additions & 21 deletions api/v1/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@ import * as express from "express";
import * as bcrypt from "bcrypt";
import multer from "multer";
import path from "path"
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './storage/img')
},
filename: function (req, file, cb) {
cb(null, "imagenPerfil-" + req.session.usuario.DNI+".jpg")
},

})
var upload = multer({storage:storage,
import nodemailer from "nodemailer";

const upload = multer({
storage:multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './storage/img')
},
filename: function (req, file, cb) {
cb(null, "imagenPerfil-" + req.session.usuario.DNI+".jpg")
},

}),
fileFilter: function(req, file, cb){
const allowedExtensions = ['.jpg', '.png']; // Add more extensions as needed
const fileExtension = path.extname(file.originalname).toLowerCase();
Expand All @@ -20,7 +22,8 @@ var upload = multer({storage:storage,
} else {
cb(null, false); // Reject the file
}
}})
}
})
const router = express.Router();
import {
Usuario,
Expand Down Expand Up @@ -64,6 +67,8 @@ Parametro.findAll().then((ps) => {
});
});

// TODO Refactor: Separar este archivo de más de 2k líneas de código en diferentes archivos segpun la entidad accedida. Ejemplo: https://github.com/c3r38r170/tp-fullstack/blob/master/backend/rutas/todas.js

// sesiones

router.post("/sesion", function (req, res) {
Expand Down Expand Up @@ -327,11 +332,31 @@ router.post("/usuario/:DNI/contrasenia", function (req, res) {
res.status(404).send("DNI inexistente");
return;
}

let contraseniaNueva = generarContrasenia();
usu.contrasenia = contraseniaNueva;

//TODO Feature: mandar mail
usu.save().then(res.status(200).send("DNI encontrado, correo enviado"));
Promise.all([
nodemailer
.createTransport({
host: process.env.CORREO_HOST,
port: process.env.CORREO_PORT,
secure: true, // Use `true` for port 465, `false` for all other ports
auth: {
user: process.env.CORREO_USER,
pass: process.env.CORREO_PASS,
},
})
.sendMail({
from: '"UTN FAQ - Recuperación de contraseña" <[email protected]>', // sender address
to: usu.correo, // list of receivers
subject: "UTN FAQ - Recuperación de contraseña", // Subject line
text: `¡Saludos, ${usu.nombre}! Tu contraseña temporal es "${contraseniaNueva}" (sin comillas).`, // plain text body
html: `<big>¡Saludos, ${usu.nombre}!</big><br/><p>Se ha reestablecido tu contraseña, tu nueva contraseña temporal es:</p><pre>${contraseniaNueva}}</pre><p>No olvides cambiarla por otra / personalizarla cuando entres.</p><p><a href=${process.env.DIRECCION}>¡Te esperamos!</a></p>`, // html body
})
,usu.save()
])
.then(res.status(200).send("Se ha reestablecido la contraseña. Revise su correo electrónico para poder acceder."));
});
});

Expand Down Expand Up @@ -474,13 +499,14 @@ router.patch("/usuario", upload.single("image"), function (req, res) {
}
Usuario.findByPk(req.session.usuario.DNI)
.then((usuario) => {
if(bcrypt.compare(req.body.contraseniaAnterior, usuario.contrasenia)){
usuario.contrasenia = req.body.contraseniaNueva;
usuario.save();
res.status(200).send("Datos actualizados exitosamente");
if(!bcrypt.compare(req.body.contraseniaAnterior, usuario.contrasenia)){
res.status(401).send("Contraseña anterior no válida")
return;
}
res.status(402).send("Contraseña anterior no válida")

usuario.contrasenia = req.body.contraseniaNueva;
usuario.save();
res.status(200).send("Datos actualizados exitosamente");
})
.catch((err) => {
res.status(500).send(err);
Expand Down Expand Up @@ -1393,9 +1419,8 @@ router.get("/categorias", async (req, res) => {
try {
let categorias;
// TODO Refactor: raw? nest?
console.log(req.query);
if(!!+req.query.etiquetas){
categorias=await Categoria.findAll({include:/* Etiqueta */{model:Etiqueta, as:'etiquetas'}})
categorias=await Categoria.findAll({include:{model:Etiqueta, as:'etiquetas'}})
}else{
categorias = await Categoria.findAll();
}
Expand Down Expand Up @@ -1781,7 +1806,7 @@ router.get('/notificacion', function(req,res){

router.patch("/notificacion", function (req, res) {
if (!req.session.usuario) {
res.status(402).send();
res.status(401).send();
return;
}

Expand Down
16 changes: 10 additions & 6 deletions frontend/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,16 +211,20 @@ router.get("/pregunta/:id?", async (req, res) => {
pagina.globales.preguntaID = preguntaID;

res.send(pagina.render());
} else {
let usu = req.session;
if (!usu.usuario) {
let pagina = SinPermisos(usu, "No está logueado");
} else {
let sesion=req.session;
if (!sesion.usuario) {
let pagina = SinPermisos(sesion, "No está logueado");
res.send(pagina.render());
return;
}

// * Nueva pregunta.
let pagina = PantallaNuevaPregunta(req.path, req.session);
res.send(pagina.render());
Categoria.findAll({include:{model:EtiquetaDAO, as:'etiquetas'}})
.then(categorias=>{
let pagina = PantallaNuevaPregunta(req.path, sesion,categorias);
res.send(pagina.render());
})
}
} catch (error) {
console.error(error);
Expand Down
9 changes: 5 additions & 4 deletions frontend/static/componentes/formulario.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ class Campo{
#extra = null;
#placeholder='';

constructor({name,textoEtiqueta,type,required=true,value='',extra,placeholder, clasesInput}){
constructor({name,textoEtiqueta,type,required=true,value=''/* TODO Refactor: null? */,extra,placeholder, clasesInput}){
// TODO Feature: Tirar error si no estan los necesarios.
this.#name=name;
this.#textoEtiqueta=textoEtiqueta;
Expand All @@ -154,6 +154,7 @@ class Campo{
break;
case 'lista-etiquetas':
html+=' data-type = "tags" data-placeholder="Etiquetas" data-selectable="false" multiple '
// ! no break;
case 'select':
html=html.replace('input','select');
endTag=`>${this.#extra}</select>`;
Expand All @@ -175,11 +176,11 @@ class Campo{
}
if(this.#required)
html+=` required`;
if(this.#value)
if(this.#value) // * Este no aplica en caso de lista-etiqueta o select.
html+=` value="${this.#value}"`;
if(this.#placeholder)
if(this.#placeholder){
html+=` placeholder="${this.#placeholder}"`;

}

return html+endTag+'</label>';

Expand Down
31 changes: 4 additions & 27 deletions frontend/static/componentes/notificacion.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,17 @@ class Notificacion{
#fecha;
#idPregunta;
#ID;
//ppregunta ajena es notificacion por etiqueta suscripta
//pregunta ajena es notificacion por etiqueta suscripta
//respuesta ajena es notificacion por respuesta a pregunta propia o suscripta
//respuesta o pregunta propia es notificación por valoración

/*
/* TODO Docs: Actualizar estos comentarios.
notificacion
post
usuario
respuesta
pregunta
pregunta
*/

constructor(id,{
Expand Down Expand Up @@ -50,27 +49,6 @@ class Notificacion{
this.#texto = 'Nuevas respuestas en la pregunta'
}
}


/*
// TODO Refactor: Preguntar una sola vez.
if(post.pregunta.ID){ // * Es una notificación de una pregunta
this.#tituloPregunta=post.pregunta.titulo;
}else{ // * Es una notificación de una respuesta
// TODO UX: Considerar mostrar algo de texto de la respuesta o no.
this.#tituloPregunta=post.respuesta.pregunta.titulo;
}
if(post.duenio.DNI==usuarioActualDNI){
this.#texto='Recibiste un nuevo voto positivo: ';
}else{
if(post.pregunta.ID){ // * Es una notificación de una pregunta
this.#texto='Nueva pregunta que te puede interesar: ';
}else{ // * Es una notificación de una respuesta
this.#texto='Nuevas respuestas en la pregunta ';
}
}*/
}

static verNotificacion(e){
Expand All @@ -82,6 +60,7 @@ class Notificacion{


const url= `http://localhost:8080/api/notificacion`;
// TODO Refactor: Ver si esto choca o es equivalente al hecho de que las notificaciones se ven al entrar en la página.
fetch(url, {
method: 'PATCH',
headers: {
Expand All @@ -97,9 +76,7 @@ class Notificacion{
}

render(){
// TODO UX: Estilos, visto no visto, al enlace, etc. (.notificacion)
// TODO Feature: Implementar registro de visto. onclick
// TODO Feature: Marcar como visto. onclick="Notificacion.verNotificacion()"
// TODO Feature: Matar el user.webp viejo, meter acá el endpoint de imagen de usuario.
return`
<div class="chip-notificacion notificacion ${this.visto==0? 'noti-no-vista': ''}" id='chip-notificacion-${this.#ID}' data-id='${this.#ID}' data-idPregunta='${this.#idPregunta}'>
<div class="img-container">
Expand Down
1 change: 0 additions & 1 deletion frontend/static/componentes/pagina.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ class Pagina {
<script>${Object.entries(this.globales)
.map(([k, v]) => `var ${k} = ${JSON.stringify(v)}`)
.join(";")}</script>
<!-- <script src="https://unpkg.com/@c3r38r170/[email protected]" type="module"></script> -->
<script src="/main.js" type=module></script>
<link rel="stylesheet" href="/main.css">
Expand Down
4 changes: 1 addition & 3 deletions frontend/static/pantallas/editar-pregunta.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@ function crearPagina(ruta,sesion, pregunta, categorias){
,{
textoEnviar:'Editar Pregunta', verbo: 'PATCH', clasesBoton:'is-link is-rounded mt-3'
}
),
// new ListaEtiquetas('editando-pregunta')

)
]
});
return pagina;
Expand Down
12 changes: 6 additions & 6 deletions frontend/static/pantallas/nueva-pregunta.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { Pagina, Formulario } from "../componentes/todos.js";

import { Pagina, Formulario} from "../componentes/todos.js";

// TODO refactor: Usar campo de Lista
function crearPagina(ruta,sesion){
function crearPagina(ruta,sesion,categorias){
let pagina=new Pagina({
ruta:ruta
,titulo:'Nueva Pregunta'
Expand All @@ -12,10 +11,11 @@ function crearPagina(ruta,sesion){
'nueva-pregunta'
,'/api/pregunta'
,[
/* {name,textoEtiqueta,type,required=true,value,extra,clasesBoton} */
{name:'titulo',textoEtiqueta:'Título'}
// TODO UX: Detalles? ¿O Cuerpo? ¿O algo...? Ver algún ejemplo. Also, mostrar más grande (rows) y limitar texto (max?)
,{name:'cuerpo',textoEtiqueta:'Detalles',type:'textarea'}
// TODO Refactor: DRY en el extra de options? encontrar la forma de no repetir ese map largo... ¿quizá hacer Categorias.render? Considerar todas las funciones, hay 2 sin valor predeterminado (nueva pregunta y busqueda sin hacer) y 2 con (busqueda hecha y editar pregunta)
,{name:'etiquetasIDs',textoEtiqueta:'Etiquetas',type:'lista-etiquetas', extra:categorias.map(cat => cat.etiquetas.map(eti => `<option value=${eti.ID} data-color="${cat.color}" data-categoria="${cat.descripcion}">${cat.descripcion} - ${eti.descripcion}</option>`)).flat().join('')}
]
,(respuesta,info)=>{
if(info.ok){
Expand All @@ -29,10 +29,10 @@ function crearPagina(ruta,sesion){
}
)
// TODO Feature: Formulario de creación de preguntas
// Campo de Título. Tiene que sugerir preguntar relacionadas.
// Campo de Título. Tiene que sugerir preguntar relacionadas.
// ✅ Campo de etiquetas. Se deben obtener las etiquetas, mostrarlas, permitir elegirlas.
// ✅ Campo de cuerpo. Texto largo con un máximo y ya.
// Las sugerencias pueden ser un panel abajo, o abajo del título... que se vaya actualizando a medida que se escribe el cuerpo.
// Las sugerencias pueden ser un panel abajo, o abajo del título... que se vaya actualizando a medida que se escribe el cuerpo.
// Botón de crear pregunta. Se bloquea, si hay un error salta cartel (como por moderación), si no lleva a la página de la pregunta. Reemplaza, así volver para atrás va al inicio y no a la creación de preguntas.
]
});
Expand Down
Loading

0 comments on commit d4f33c3

Please sign in to comment.