220 lines
8.6 KiB
JavaScript
220 lines
8.6 KiB
JavaScript
const Absensi = require('../models/Absensi');
|
|
const LokasiAbsensi = require('../models/LokasiAbsensi');
|
|
const { KodePembelajaran, Pelajaran, JadwalPelajaran } = require('../models');
|
|
const jwt = require('jsonwebtoken');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const multer = require('multer');
|
|
|
|
// Konfigurasi penyimpanan untuk multer
|
|
const storage = multer.diskStorage({
|
|
destination: function (req, file, cb) {
|
|
const uploadDir = path.join(__dirname, '../public/uploads/absensi');
|
|
|
|
// Cek apakah direktori ada, jika tidak ada maka buat
|
|
if (!fs.existsSync(uploadDir)) {
|
|
fs.mkdirSync(uploadDir, { recursive: true });
|
|
}
|
|
cb(null, uploadDir);
|
|
},
|
|
filename: function (req, file, cb) {
|
|
// Membuat nama file unik dengan timestamp
|
|
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
|
|
const ext = path.extname(file.originalname);
|
|
cb(null, 'absensi-' + uniqueSuffix + ext);
|
|
}
|
|
});
|
|
|
|
// Filter untuk memastikan hanya file gambar yang di-upload
|
|
const fileFilter = (req, file, cb) => {
|
|
if (file.mimetype.startsWith('image/')) {
|
|
cb(null, true);
|
|
} else {
|
|
cb(new Error('Hanya file gambar yang diperbolehkan!'), false);
|
|
}
|
|
};
|
|
|
|
// Inisialisasi multer
|
|
const upload = multer({
|
|
storage: storage,
|
|
fileFilter: fileFilter,
|
|
limits: {
|
|
fileSize: 5 * 1024 * 1024 // Batas ukuran file (5MB)
|
|
}
|
|
}).single('foto_absen');
|
|
|
|
// Fungsi untuk menghitung jarak antara dua koordinat
|
|
function hitungJarak(lat1, lon1, lat2, lon2) {
|
|
const R = 6371; // Radius bumi dalam km
|
|
const dLat = (lat2 - lat1) * (Math.PI / 180);
|
|
const dLon = (lon2 - lon1) * (Math.PI / 180);
|
|
const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
|
Math.cos(lat1 * (Math.PI / 180)) * Math.cos(lat2 * (Math.PI / 180)) *
|
|
Math.sin(dLon / 2) * Math.sin(dLon / 2);
|
|
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
return R * c * 1000; // Jarak dalam meter
|
|
}
|
|
|
|
// ✅ GET Absensi by ID Siswa
|
|
exports.getAbsensiByToken = async (req, res) => {
|
|
try {
|
|
// Ambil token dari header
|
|
const token = req.header("Authorization")?.replace("Bearer ", "");
|
|
if (!token) {
|
|
return res.status(401).json({ status: "error", message: "Token tidak ditemukan" });
|
|
}
|
|
|
|
// Decode token
|
|
const decoded = jwt.verify(token, "secret_key");
|
|
const id_siswa = decoded.id_siswa;
|
|
|
|
// Ambil data absensi
|
|
const absensi = await Absensi.findAll({
|
|
where: { id_siswa },
|
|
attributes: [
|
|
'id_absensi',
|
|
'id_siswa',
|
|
'id_kodepembelajaran',
|
|
'waktu_absen',
|
|
'status',
|
|
'batas_waktu_absen',
|
|
'foto_absen' // ✅ Tambahkan atribut foto_absen
|
|
],
|
|
include: [
|
|
{
|
|
model: KodePembelajaran,
|
|
as: "kode_pembelajaran",
|
|
attributes: ["id_pelajaran"],
|
|
include: [
|
|
{
|
|
model: Pelajaran,
|
|
as: "Pelajaran",
|
|
attributes: ["nama_pelajaran"]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
});
|
|
|
|
if (!absensi.length) {
|
|
return res.status(404).json({ status: "error", message: "Data absensi tidak ditemukan" });
|
|
}
|
|
|
|
// Buat URL lengkap untuk foto absensi
|
|
const baseUrl = `${req.protocol}://${req.get('host')}`;
|
|
|
|
// ✅ Pastikan batas_waktu_absen tetap ada dalam response
|
|
const formattedAbsensi = absensi.map(item => ({
|
|
id_absensi: item.id_absensi,
|
|
id_siswa: item.id_siswa,
|
|
id_kodepembelajaran: item.id_kodepembelajaran,
|
|
waktu_absen: item.waktu_absen,
|
|
status: item.status,
|
|
batas_waktu_absen: item.batas_waktu_absen || "Tidak ada batas waktu", // ✅ Jika null, berikan default value
|
|
nama_pelajaran: item.kode_pembelajaran?.Pelajaran?.nama_pelajaran || "Tidak diketahui",
|
|
foto_absen: item.foto_absen ? `${baseUrl}/uploads/absensi/${item.foto_absen}` : null
|
|
}));
|
|
|
|
res.status(200).json({ status: "success", data: formattedAbsensi });
|
|
|
|
} catch (error) {
|
|
res.status(500).json({ status: "error", message: "Terjadi kesalahan pada server", error: error.message });
|
|
}
|
|
};
|
|
|
|
// ✅ PUT Update Absensi dengan Upload Gambar
|
|
exports.updateAbsensi = async (req, res) => {
|
|
try {
|
|
// Handle upload file dengan multer
|
|
upload(req, res, async function(err) {
|
|
if (err instanceof multer.MulterError) {
|
|
return res.status(400).json({ status: "error", message: "Kesalahan saat upload file", error: err.message });
|
|
} else if (err) {
|
|
return res.status(400).json({ status: "error", message: err.message });
|
|
}
|
|
|
|
// Proses data setelah upload berhasil
|
|
const { id_siswa, id_kodepembelajaran, waktu_absen, latitude, longitude, status, keterangan_absen } = req.body;
|
|
|
|
console.log("Body request:", req.body);
|
|
console.log("File:", req.file);
|
|
|
|
// Cek apakah absensi tersedia
|
|
const absensi = await Absensi.findOne({ where: { id_siswa, id_kodepembelajaran } });
|
|
if (!absensi) {
|
|
// Hapus file jika absensi tidak ditemukan
|
|
if (req.file) {
|
|
fs.unlinkSync(req.file.path);
|
|
}
|
|
return res.status(404).json({ status: "error", message: "Data absensi tidak ditemukan" });
|
|
}
|
|
|
|
// Ambil lokasi sekolah yang aktif
|
|
const lokasi = await LokasiAbsensi.findOne({ where: { status: 'aktif' } });
|
|
if (!lokasi) {
|
|
// Hapus file jika lokasi tidak tersedia
|
|
if (req.file) {
|
|
fs.unlinkSync(req.file.path);
|
|
}
|
|
return res.status(400).json({ status: "error", message: "Lokasi absensi tidak tersedia" });
|
|
}
|
|
|
|
// Hitung jarak siswa ke lokasi sekolah
|
|
const jarak = hitungJarak(latitude, longitude, parseFloat(lokasi.latitude), parseFloat(lokasi.longitude));
|
|
|
|
if (jarak > lokasi.radius) {
|
|
// Hapus file jika siswa diluar radius
|
|
if (req.file) {
|
|
fs.unlinkSync(req.file.path);
|
|
}
|
|
return res.status(400).json({ status: "error", message: "Anda tidak berada di radius sekolah" });
|
|
}
|
|
|
|
// Hapus foto lama jika ada dan jika ada foto baru
|
|
if (absensi.foto_absen && req.file) {
|
|
try {
|
|
// Pastikan foto_absen adalah string
|
|
if (typeof absensi.foto_absen === 'string') {
|
|
const oldFilePath = path.join(__dirname, '../public/uploads/absensi', absensi.foto_absen);
|
|
console.log("Mencoba menghapus file:", oldFilePath);
|
|
|
|
if (fs.existsSync(oldFilePath)) {
|
|
fs.unlinkSync(oldFilePath);
|
|
console.log("File lama berhasil dihapus");
|
|
} else {
|
|
console.log("File lama tidak ditemukan");
|
|
}
|
|
} else {
|
|
console.log("foto_absen bukan string:", typeof absensi.foto_absen);
|
|
}
|
|
} catch (err) {
|
|
console.error("Error saat menghapus file lama:", err);
|
|
// Lanjutkan proses meskipun ada error saat menghapus
|
|
}
|
|
}
|
|
|
|
// Update absensi
|
|
await Absensi.update(
|
|
{
|
|
waktu_absen: waktu_absen || new Date(),
|
|
foto_absen: req.file ? req.file.filename : absensi.foto_absen,
|
|
status,
|
|
keterangan_absen
|
|
},
|
|
{ where: { id_siswa, id_kodepembelajaran } }
|
|
);
|
|
|
|
res.status(200).json({
|
|
status: "success",
|
|
message: "Absensi berhasil diperbarui",
|
|
data: {
|
|
foto_absen: req.file ? req.file.filename : absensi.foto_absen,
|
|
foto_url: req.file ? `${req.protocol}://${req.get('host')}/uploads/absensi/${req.file.filename}` : null
|
|
}
|
|
});
|
|
});
|
|
} catch (error) {
|
|
console.error("Error:", error);
|
|
res.status(500).json({ status: "error", message: "Terjadi kesalahan pada server", error: error.message });
|
|
}
|
|
}; |