created API For Aplication Absensi

This commit is contained in:
2025-10-14 14:08:11 +07:00
commit 96d206d892
56 changed files with 6533 additions and 0 deletions

View File

@@ -0,0 +1,390 @@
require('dotenv').config()
const bcrypt = require('bcrypt')
const jwt = require('jsonwebtoken')
const moment = require('moment')
const crypto = require('crypto')
const { Sequelize } = require('sequelize')
const { sequelize } = require('../../../models/migration.js')
const response = require('../../helpers/responses')
const { sendOTP, generateOTP, sendForgotPassword, normalizePhone } = require('../../helpers/helpers')
const UserResource = require('../resources/user.resource')
const { sendMail } = require('../../config/mail.config')
const db = require('../../../models/migration.js')
const { password } = require('../../config/db.config.js')
const errorHandler = require('../../middlewares/errorHandler.js')
const { v4: uuidv4 } = require("uuid");
const User = db.User
const Branch = db.Branch
const UserOtp = db.UserOtp
const PasswordReset = db.PasswordReset
const { Op } = require('sequelize')
const { OAuth2Client } = require("google-auth-library");
const signIn = async (req, res) => {
const client = new OAuth2Client("GOOGLE_CLIENT_ID");
try {
const { email, phone, password, token: tokenGoogle, login_via } = req.body;
let user;
if (email) {
user = await User.findOne({ where: { email } });
if (login_via == 'GOOGLE') {
// Verify Google token
const ticket = await client.verifyIdToken({
idToken: tokenGoogle, // Changed from tokenGoogle to idToken
audience: process.env.GOOGLE_CLIENT_ID,
});
const payload = ticket.getPayload();
const googleEmail = payload.email;
// Find or create user with Google credentials
user = await User.findOne({
where: {
email: googleEmail
}
});
if (!user) {
// Create new user if doesn't exist
user = await User.create({
email: googleEmail,
name: payload.name,
login_via: 'GOOGLE',
token: tokenGoogle,
google_id: payload.sub,
avatar_url: payload.picture,
});
}else{
// Update user's Google token
user.google_id = payload.sub;
user.avatar_url = payload.picture;
user.token = tokenGoogle;
await user.save();
}
}
} else if (phone) {
const normalizedPhone = normalizePhone(phone);
user = await User.findOne({ where: { phone: normalizedPhone } });
}
if (login_via === 'GOOGLE') {
} else {
if (!user || !(await bcrypt.compare(password, user.password))) {
return res.status(400).json({ error: "Email / Phone atau Password salah" });
}
}
if (user.is_suspended) {
return res.status(403).json({ error: "Akun Anda telah ditangguhkan" });
}
const now = new Date();
// === Generate JWT mirip Supabase ===
const token = jwt.sign(
{
name: user.name,
id: user.id,
email: user.email,
phone: user.phone || "",
role: user.role || "user",
},
process.env.JWT_SECRET_KEY
);
// Update last login
user.last_login = now;
user.is_first_login = false;
await user.save();
return res.json({
access_token: token,
token_type: "bearer",
user: {
id: user.id,
name: user.name,
email: user.email,
role: user.role || "user",
created_at: user.created_at.toISOString(),
},
});
} catch (error) {
errorHandler(error, req, res)
return response.failed(res, 500, error.message)
}
};
const sendOtp = async (req, res) => {
try {
const { email, phone, via } = req.body
const otp = generateOTP(6)
const hash = await bcrypt.hash(otp, 10)
const data = via === 'WHATSAPP' ? email : phone
const otpData = await UserOtp.create({ data, otp, token: hash, expire_in: 60, via })
await sendOTP('OTP', otp, via, phone, email)
return response.success(res, { otpData }, `OTP dikirim ${via}`)
} catch (error) {
return response.failed(res, 500, error.message)
}
}
const checkOtp = async (req, res) => {
try {
const { otp, token } = req.body
const data = await UserOtp.findOne({ where: { otp, token } })
if (!data) {
return response.failed(res, 404, 'OTP tidak valid')
}
const userCondition = data.via === 'WHATSAPP' ? { email: data.data } : { phone: data.data }
let user = await User.findOne({ where: userCondition })
if (!user) {
user = await User.create(userCondition)
}
const jwtToken = jwt.sign({ id: user.id }, process.env.JWT_SECRET_KEY, { expiresIn: '1d' })
return response.success(res, { user: new UserResource(user), token: jwtToken }, 'Login OTP berhasil')
} catch (error) {
return response.failed(res, 500, error.message)
}
}
const signUp = async (req, res) => {
try {
const { name, email, password, role, branch_id } = req.body;
// Cek apakah email / phone sudah ada
const existingUser = await User.findOne({
where: {
[Sequelize.Op.or]: [{ email }],
},
});
if (existingUser) {
return res.status(400).json({ error: "Email sudah terdaftar" });
}
const branch = await Branch.findOne({ where: { id: branch_id } });
if (!branch) {
await t.rollback();
return response.failed(res, 404, "Branch tidak ditemukan");
}
// Hash password
const hashedPassword = await bcrypt.hash(password, 10);
// Buat user baru dalam transaksi
const user = await sequelize.transaction(async (t) => {
const newUser = await User.create(
{
name,
email,
branch_id,
password: hashedPassword,
role: role || "user",
},
{ transaction: t }
);
return newUser;
});
// Generate token JWT
const token = jwt.sign(
{
name: user.name,
id: user.id,
email: user.email,
phone: user.phone || "",
role: user.role || "user",
},
process.env.JWT_SECRET_KEY
);
const refreshToken = uuidv4();
const now = new Date();
// Update last_login setelah register
user.last_login = now;
user.is_first_login = false;
await user.save();
return res.json({
access_token: token,
token_type: "bearer",
user: {
id: user.id,
name: user.name,
email: user.email,
branch: branch.name,
role: user.role || "user",
created_at: user.created_at.toISOString(),
},
});
} catch (error) {
errorHandler(error, req, res)
return response.failed(res, 500, error.message)
}
};
const getUserLogin = async (token, res) => {
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET_KEY)
const user = await User.findByPk(decoded.id, {
attributes: { exclude: ['password'] }
})
if (!user) {
return response.failed(res, 404, 'User Tidak Ditemukan')
}
return response.success(res, new UserResource(user), 'User ditemukan')
} catch (error) {
return response.failed(res, 401, 'Token Tidak Valid atau expired')
}
}
const forgotPassword = async (body, res) => {
const [user, resetPassword] = await Promise.all([
User.findOne({
where: {
email: body.email,
},
}),
PasswordReset.findOne({
where: {
email: body.email,
is_used: false,
},
}),
]);
if (!user) {
return response.error(res, 1307, 400);
}
if (resetPassword) {
resetPassword.token = crypto.randomBytes(20).toString('hex');
resetPassword.expires_at = moment().add(15, 'minutes').toISOString();
await resetPassword.save();
await sequelize.transaction(async () => {
await sendForgotPassword(body.email, resetPassword.token);
});
const tokenUpdated = {
user,
forgot_password: resetPassword,
};
return response.success(res, tokenUpdated, 'Token Updated');
}
const emailToken = crypto.randomBytes(20).toString('hex');
const expiresAt = moment().add(15, 'minutes').toISOString();
const dataPasswordReset = await PasswordReset.create({
email: body.email,
token: emailToken,
expires_at: expiresAt,
});
const tokenCreated = {
user,
forgot_password: dataPasswordReset,
};
return response.success(res, tokenCreated, 'Token Created');
};
const resetPassword = async (body, token, res) => {
try {
const resetPassword = await PasswordReset.findOne({
where: {
is_used: false,
token: token,
},
});
console.log('resetPassword data:', resetPassword);
if (!resetPassword) {
return response.error(res, 1306, 400);
}
const now = moment();
const expiresAt = moment(resetPassword.expires_at);
if (now.isAfter(expiresAt)) {
return response.error(res, 1306, 400);
}
const password = body.new_password;
const confirm_password = body.confirm_password;
if (password !== confirm_password) {
return response.failed(res, 1105, 400);
}
const salt = await bcrypt.genSalt(10);
const hash = await bcrypt.hash(password, salt);
const user = await User.findOne({
where: {
email: resetPassword.email,
},
});
user.password = hash;
user.is_default = false;
await user.save();
resetPassword.is_used = true;
await resetPassword.save();
return response.success(res, null, 'Password reset successfully');
} catch (error) {
console.error('Reset password error:', error);
return response.failed(res, 500, error.message || 'Something went wrong');
}
};
module.exports = {
signIn,
sendOtp,
checkOtp,
signUp,
getUserLogin,
resetPassword,
forgotPassword
}