Task
clicktasks/
├── client/ # Frontend (HTML/CSS/JS)
├── server/ # Backend Node.js
│ ├── config/ # Configurações do banco e JWT
│ ├── controllers/ # Lógica de negócios
│ ├── middlewares/ # Autenticação e validações
│ ├── models/ # Modelos do banco de dados
│ ├── routes/ # Rotas da API
│ ├── services/ # Serviços (email, OAuth, etc.)
│ └── server.js # Ponto de entrada
├── .env # Variáveis de ambiente
└── package.json # Dependências do Node.js
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const { Sequelize } = require('sequelize');
const authRoutes = require('./routes/auth');
const profileRoutes = require('./routes/profiles');
const app = express();
// Middlewares de segurança
app.use(helmet());
app.use(cors({ origin: process.env.CLIENT_URL }));
app.use(express.json());
// Limitar requisições (prevenção de brute force)
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutos
max: 100 // limite por IP
});
app.use(limiter);
// Conexão com MySQL
const sequelize = new Sequelize(
process.env.DB_NAME,
process.env.DB_USER,
process.env.DB_PASSWORD,
{
host: process.env.DB_HOST,
dialect: 'mysql',
logging: false
}
);
// Testar conexão com o banco
sequelize.authenticate()
.then(() => console.log('✅ Conectado ao MySQL'))
.catch(err => console.error('❌ Erro na conexão:', err));
// Rotas
app.use('/api/auth', authRoutes);
app.use('/api/profiles', profileRoutes);
// Iniciar servidor
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
console.log(`🚀 Servidor rodando em http://localhost:${PORT}`);
});
const { DataTypes } = require('sequelize');
const sequelize = require('../config/database');
const bcrypt = require('bcryptjs');
const User = sequelize.define('User', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING,
allowNull: false
},
email: {
type: DataTypes.STRING,
unique: true,
allowNull: false,
validate: { isEmail: true }
},
password: {
type: DataTypes.STRING,
allowNull: false,
set(value) {
// Hash automático da senha
const salt = bcrypt.genSaltSync(10);
this.setDataValue('password', bcrypt.hashSync(value, salt));
}
},
userType: {
type: DataTypes.ENUM('profissional', 'empresa'),
allowNull: false
},
isVerified: {
type: DataTypes.BOOLEAN,
defaultValue: false
}
}, {
timestamps: true,
paranoid: true // Soft delete
});
module.exports = User;
const jwt = require('jsonwebtoken');
const { jwtSecret, jwtExpiresIn } = require('./config');
const generateToken = (userId) => {
return jwt.sign({ id: userId }, jwtSecret, {
expiresIn: jwtExpiresIn
});
};
const verifyToken = (token) => {
try {
return jwt.verify(token, jwtSecret);
} catch (err) {
return null;
}
};
module.exports = { generateToken, verifyToken };
const { User } = require('../models');
const { generateToken } = require('../config/jwt');
const { sendVerificationEmail } = require('../services/emailService');
const { verifyRecaptcha } = require('../services/recaptchaService');
exports.register = async (req, res) => {
try {
// Verificar reCAPTCHA
const recaptchaValid = await verifyRecaptcha(req.body.recaptchaToken);
if (!recaptchaValid) {
return res.status(400).json({ error: 'Falha na verificação reCAPTCHA' });
}
const { name, email, password, userType } = req.body;
// Criar usuário
const user = await User.create({ name, email, password, userType });
// Enviar e-mail de verificação
await sendVerificationEmail(user);
// Gerar token JWT
const token = generateToken(user.id);
res.status(201).json({ token, user: { id: user.id, name: user.name, email: user.email } });
} catch (error) {
res.status(400).json({ error: error.message });
}
};
exports.login = async (req, res) => {
try {
const { email, password } = req.body;
const user = await User.findOne({ where: { email } });
if (!user || !bcrypt.compareSync(password, user.password)) {
return res.status(401).json({ error: 'Credenciais inválidas' });
}
const token = generateToken(user.id);
res.json({ token, user: { id: user.id, name: user.name, email: user.email } });
} catch (error) {
res.status(400).json({ error: error.message });
}
};
// Login Social (OAuth 2.0)
exports.socialAuth = async (req, res) => {
// Implementação para Google/LinkedIn OAuth
};
const { verifyToken } = require('../config/jwt');
exports.authenticate = async (req, res, next) => {
const token = req.header('Authorization')?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({ error: 'Acesso não autorizado' });
}
const decoded = verifyToken(token);
if (!decoded) {
return res.status(401).json({ error: 'Token inválido' });
}
req.userId = decoded.id;
next();
};
const axios = require('axios');
exports.verifyRecaptcha = async (token) => {
try {
const response = await axios.post(
'https://www.google.com/recaptcha/api/siteverify',
`secret=${process.env.RECAPTCHA_SECRET_KEY}&response=${token}`
);
return response.data.success && response.data.score >= 0.5;
} catch (error) {
console.error('Erro na verificação reCAPTCHA:', error);
return false;
}
};
CREATE DATABASE clicktasks_db;
CREATE TABLE Users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
userType ENUM('profissional', 'empresa') NOT NULL,
isVerified BOOLEAN DEFAULT false,
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deletedAt TIMESTAMP NULL
);
# Banco de Dados
DB_HOST=localhost
DB_NAME=clicktasks_db
DB_USER=root
DB_PASSWORD=senha_segura
# Autenticação
JWT_SECRET=super_secret_key
JWT_EXPIRES_IN=7d
# reCAPTCHA
RECAPTCHA_SITE_KEY=sua_chave_site
RECAPTCHA_SECRET_KEY=sua_chave_secreta
# OAuth (Google)
GOOGLE_CLIENT_ID=seu_client_id
GOOGLE_CLIENT_SECRET=seu_client_secret
# SMTP (E-mails)
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=seu_email@example.com
SMTP_PASS=sua_senha
bash
cd server
npm install express sequelize mysql2 bcryptjs jsonwebtoken axios helmet cors dotenv
node server.js
POST /api/auth/register
POST /api/auth/login
GET /api/profiles
Comentários
Postar um comentário
Sejam Bem Vindos ao Passo Seguinte