This commit is contained in:
[Valentino Heman Budiarto] 2026-06-05 19:46:48 +07:00
parent 0a8ced6490
commit 16075c670a
2 changed files with 51 additions and 37 deletions

View File

@ -4,8 +4,8 @@ import (
"fmt" "fmt"
"log" "log"
"os" "os"
"strings" // TAMBAHAN: Untuk membersihkan spasi/enter gaib
"s-class-backend/models" "s-class-backend/models"
"strings"
"github.com/joho/godotenv" "github.com/joho/godotenv"
"gorm.io/driver/postgres" "gorm.io/driver/postgres"

View File

@ -8,6 +8,8 @@ import (
"os" "os"
"time" "time"
"s-class-backend/config" // IMPORT WAJIB UNTUK KONEKSI DATABASE
mqtt "github.com/eclipse/paho.mqtt.golang" mqtt "github.com/eclipse/paho.mqtt.golang"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@ -18,7 +20,6 @@ type DeviceControlRequest struct {
Action string `json:"action"` Action string `json:"action"`
} }
// Struct baru untuk menangkap input Token dari layar ESP32
type VerifyRequest struct { type VerifyRequest struct {
Token string `json:"token"` Token string `json:"token"`
} }
@ -29,7 +30,6 @@ type VerifyRequest struct {
func VerifyHardwareCode(c *gin.Context) { func VerifyHardwareCode(c *gin.Context) {
var req VerifyRequest var req VerifyRequest
// 1. Tangkap JSON { "token": "..." } dari ESP32
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Format token tidak valid"}) c.JSON(http.StatusBadRequest, gin.H{"error": "Format token tidak valid"})
return return
@ -37,41 +37,64 @@ func VerifyHardwareCode(c *gin.Context) {
tokenInput := req.Token tokenInput := req.Token
sekarang := time.Now() sekarang := time.Now()
var jamSelesai time.Time
isTokenValid := false
// --------------------------------------------------------------------- // Struktur penampung jam selesai dari database
// 2. LOGIKA DATABASE PENGGANTI (Silakan sesuaikan dengan model GORM-mu) type ResultTime struct {
// --------------------------------------------------------------------- JamSelesai time.Time `gorm:"column:end_time"` // PASTIKAN NAMA KOLOM 'end_time' SESUAI DENGAN YANG ADA DI DATABASE
/* // CONTOH ASLI JIKA SUDAH MENGHUBUNGKAN KE DATABASE: }
var jadwal models.Booking var result ResultTime
if err := db.Where("token = ?", tokenInput).First(&jadwal).Error; err != nil {
// 1. Cek di tabel bookings (Redeem Code)
errBooking := config.DB.Table("bookings").
Select("end_time").
Where("redeem_code = ?", tokenInput).
Scan(&result).Error
if errBooking == nil && !result.JamSelesai.IsZero() {
jamSelesai = result.JamSelesai
isTokenValid = true
fmt.Println("[VERIFY] Token ditemukan di tabel bookings!")
} else {
// 2. Cek di tabel class_schedules (Kode MK)
errSchedule := config.DB.Table("class_schedules").
Select("end_time").
Where("kode_mk = ?", tokenInput).
Scan(&result).Error
if errSchedule == nil && !result.JamSelesai.IsZero() {
jamSelesai = result.JamSelesai
isTokenValid = true
fmt.Println("[VERIFY] Token ditemukan di tabel class_schedules!")
}
}
// Jika token tidak ditemukan di kedua tabel (Token Acak/Salah)
if !isTokenValid {
fmt.Printf("[VERIFY] Token %s SALAH atau tidak ditemukan.\n", tokenInput)
c.JSON(http.StatusUnauthorized, gin.H{"error": "Token salah atau tidak ditemukan"}) c.JSON(http.StatusUnauthorized, gin.H{"error": "Token salah atau tidak ditemukan"})
return return
} }
jamSelesai := jadwal.JamSelesai
*/
// SIMULASI SEMENTARA (Hapus bagian ini jika query DB di atas sudah aktif):
// Mengatur jam selesai kelas pada jam 19:00:00 di hari ini sebagai sampel waktu
jamSelesai := time.Date(sekarang.Year(), sekarang.Month(), sekarang.Day(), 19, 0, 0, 0, sekarang.Location())
// Tampilkan log di terminal backend
fmt.Printf("[VERIFY] Token masuk: %s | Jam Selesai: %v\n", tokenInput, jamSelesai)
// ---------------------------------------------------------------------
// 3. Kalkulasi Selisih Waktu // 3. Kalkulasi Selisih Waktu
selisihWaktu := jamSelesai.Sub(sekarang) // Gabungkan tanggal hari ini dengan jam dari database
jamSelesaiHariIni := time.Date(sekarang.Year(), sekarang.Month(), sekarang.Day(),
jamSelesai.Hour(), jamSelesai.Minute(), jamSelesai.Second(), 0, sekarang.Location())
selisihWaktu := jamSelesaiHariIni.Sub(sekarang)
sisaMenit := int(selisihWaktu.Minutes()) sisaMenit := int(selisihWaktu.Minutes())
// 4. Proteksi Jika Waktu Sudah Habis / Kelas Selesai // 4. Proteksi Jika Waktu Sudah Habis (Lebih dari jam selesai)
if sisaMenit <= 0 { if sisaMenit <= 0 {
c.JSON(http.StatusForbidden, gin.H{"error": "Waktu peminjaman sudah habis"}) c.JSON(http.StatusForbidden, gin.H{"error": "Waktu peminjaman sudah habis"})
return return
} }
// 5. Kirim balasan ke ESP32 beserta nilai duration_minutes // 5. Kirim balasan sukses ke ESP32 beserta durasinya
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"status": "success", "status": "success",
"message": "Token Valid, Akses Diberikan", "message": "Token Valid",
"duration_minutes": sisaMenit, "duration_minutes": sisaMenit,
}) })
} }
@ -86,13 +109,11 @@ func ControlHardware(c *gin.Context) {
return return
} }
// --- SKENARIO 1: KONTROL LAMPU (Via MQTT ke ESP32 Relay) ---
if req.Device == "lampu1" || req.Device == "lampu2" { if req.Device == "lampu1" || req.Device == "lampu2" {
broker := os.Getenv("MQTT_BROKER") broker := os.Getenv("MQTT_BROKER")
user := os.Getenv("MQTT_USER") user := os.Getenv("MQTT_USER")
pass := os.Getenv("MQTT_PASSWORD") pass := os.Getenv("MQTT_PASSWORD")
// Setup Konfigurasi Klien MQTT Golang
opts := mqtt.NewClientOptions() opts := mqtt.NewClientOptions()
opts.AddBroker(broker) opts.AddBroker(broker)
opts.SetUsername(user) opts.SetUsername(user)
@ -106,10 +127,7 @@ func ControlHardware(c *gin.Context) {
} }
defer client.Disconnect(250) defer client.Disconnect(250)
// Tentukan Topik
topic := fmt.Sprintf("sclass/d101/%s", req.Device) topic := fmt.Sprintf("sclass/d101/%s", req.Device)
// Publish Pesan
token := client.Publish(topic, 0, false, req.Action) token := client.Publish(topic, 0, false, req.Action)
token.Wait() token.Wait()
@ -120,7 +138,6 @@ func ControlHardware(c *gin.Context) {
return return
} }
// --- SKENARIO 2: KONTROL AC & PROYEKTOR (Via Home Assistant) ---
if req.Device == "ac" || req.Device == "projector" { if req.Device == "ac" || req.Device == "projector" {
haURL := os.Getenv("HA_URL") haURL := os.Getenv("HA_URL")
haToken := os.Getenv("HA_TOKEN") haToken := os.Getenv("HA_TOKEN")
@ -169,7 +186,6 @@ func ControlHardware(c *gin.Context) {
return return
} }
// Jika device tidak dikenali
c.JSON(http.StatusBadRequest, gin.H{"error": "Device tidak dikenali sistem"}) c.JSON(http.StatusBadRequest, gin.H{"error": "Device tidak dikenali sistem"})
} }
@ -180,7 +196,6 @@ func GetPowerStatus(c *gin.Context) {
haURL := os.Getenv("HA_URL") haURL := os.Getenv("HA_URL")
haToken := os.Getenv("HA_TOKEN") haToken := os.Getenv("HA_TOKEN")
// Ganti dengan Entity ID sensor daya kamu di Home Assistant
entityID := "sensor.kwh_meter_power" entityID := "sensor.kwh_meter_power"
apiURL := fmt.Sprintf("%s/api/states/%s", haURL, entityID) apiURL := fmt.Sprintf("%s/api/states/%s", haURL, entityID)
@ -199,7 +214,6 @@ func GetPowerStatus(c *gin.Context) {
var result map[string]interface{} var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result) json.NewDecoder(resp.Body).Decode(&result)
// Ambil nilai state (angka watt)
powerStr, ok := result["state"].(string) powerStr, ok := result["state"].(string)
if !ok { if !ok {
c.JSON(http.StatusOK, gin.H{"power": 0}) c.JSON(http.StatusOK, gin.H{"power": 0})