From 16075c670ae7ca2fadeae5ec9cf9d3f0790bef7a Mon Sep 17 00:00:00 2001 From: "[Valentino Heman Budiarto]" <[hemanvalentino@gmail.com]> Date: Fri, 5 Jun 2026 19:46:48 +0700 Subject: [PATCH] . --- backend/config/database.go | 8 +-- backend/controllers/hardwarecontroller.go | 80 +++++++++++++---------- 2 files changed, 51 insertions(+), 37 deletions(-) diff --git a/backend/config/database.go b/backend/config/database.go index 9101bc0..23930df 100644 --- a/backend/config/database.go +++ b/backend/config/database.go @@ -4,8 +4,8 @@ import ( "fmt" "log" "os" - "strings" // TAMBAHAN: Untuk membersihkan spasi/enter gaib "s-class-backend/models" + "strings" "github.com/joho/godotenv" "gorm.io/driver/postgres" @@ -32,13 +32,13 @@ func ConnectDatabase() { fmt.Printf("[DEBUG] Host: %s | Port: %s | User: %s | DB: %s\n", dbHost, dbPort, dbUser, dbName) fmt.Printf("[DEBUG] Panjang karakter password yang dibaca Golang: %d\n", len(dbPassword)) - dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s sslmode=disable TimeZone=Asia/Jakarta", + dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s sslmode=disable TimeZone=Asia/Jakarta", dbHost, dbUser, dbPassword, dbName, dbPort) database, err := gorm.Open(postgres.Open(dsn), &gorm.Config{ DisableForeignKeyConstraintWhenMigrating: true, }) - + if err != nil { fmt.Println("[ERROR FATAL KONEKSI DB]:", err) panic("Gagal koneksi ke database!") @@ -54,4 +54,4 @@ func ConnectDatabase() { fmt.Println("Sukses terkoneksi ke Database PostgreSQL!") DB = database -} \ No newline at end of file +} diff --git a/backend/controllers/hardwarecontroller.go b/backend/controllers/hardwarecontroller.go index 4c9e5f3..9150906 100644 --- a/backend/controllers/hardwarecontroller.go +++ b/backend/controllers/hardwarecontroller.go @@ -8,6 +8,8 @@ import ( "os" "time" + "s-class-backend/config" // IMPORT WAJIB UNTUK KONEKSI DATABASE + mqtt "github.com/eclipse/paho.mqtt.golang" "github.com/gin-gonic/gin" ) @@ -18,7 +20,6 @@ type DeviceControlRequest struct { Action string `json:"action"` } -// Struct baru untuk menangkap input Token dari layar ESP32 type VerifyRequest struct { Token string `json:"token"` } @@ -29,7 +30,6 @@ type VerifyRequest struct { func VerifyHardwareCode(c *gin.Context) { var req VerifyRequest - // 1. Tangkap JSON { "token": "..." } dari ESP32 if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Format token tidak valid"}) return @@ -37,41 +37,64 @@ func VerifyHardwareCode(c *gin.Context) { tokenInput := req.Token sekarang := time.Now() + var jamSelesai time.Time + isTokenValid := false - // --------------------------------------------------------------------- - // 2. LOGIKA DATABASE PENGGANTI (Silakan sesuaikan dengan model GORM-mu) - // --------------------------------------------------------------------- - /* // CONTOH ASLI JIKA SUDAH MENGHUBUNGKAN KE DATABASE: - var jadwal models.Booking - if err := db.Where("token = ?", tokenInput).First(&jadwal).Error; err != nil { - c.JSON(http.StatusUnauthorized, gin.H{"error": "Token salah atau tidak ditemukan"}) - return - } - jamSelesai := jadwal.JamSelesai - */ + // Struktur penampung jam selesai dari database + type ResultTime struct { + JamSelesai time.Time `gorm:"column:end_time"` // PASTIKAN NAMA KOLOM 'end_time' SESUAI DENGAN YANG ADA DI DATABASE + } + var result ResultTime - // 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) - // --------------------------------------------------------------------- + // 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"}) + return + } // 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()) - // 4. Proteksi Jika Waktu Sudah Habis / Kelas Selesai + // 4. Proteksi Jika Waktu Sudah Habis (Lebih dari jam selesai) if sisaMenit <= 0 { c.JSON(http.StatusForbidden, gin.H{"error": "Waktu peminjaman sudah habis"}) return } - // 5. Kirim balasan ke ESP32 beserta nilai duration_minutes + // 5. Kirim balasan sukses ke ESP32 beserta durasinya c.JSON(http.StatusOK, gin.H{ "status": "success", - "message": "Token Valid, Akses Diberikan", + "message": "Token Valid", "duration_minutes": sisaMenit, }) } @@ -86,13 +109,11 @@ func ControlHardware(c *gin.Context) { return } - // --- SKENARIO 1: KONTROL LAMPU (Via MQTT ke ESP32 Relay) --- if req.Device == "lampu1" || req.Device == "lampu2" { broker := os.Getenv("MQTT_BROKER") user := os.Getenv("MQTT_USER") pass := os.Getenv("MQTT_PASSWORD") - // Setup Konfigurasi Klien MQTT Golang opts := mqtt.NewClientOptions() opts.AddBroker(broker) opts.SetUsername(user) @@ -106,10 +127,7 @@ func ControlHardware(c *gin.Context) { } defer client.Disconnect(250) - // Tentukan Topik topic := fmt.Sprintf("sclass/d101/%s", req.Device) - - // Publish Pesan token := client.Publish(topic, 0, false, req.Action) token.Wait() @@ -120,7 +138,6 @@ func ControlHardware(c *gin.Context) { return } - // --- SKENARIO 2: KONTROL AC & PROYEKTOR (Via Home Assistant) --- if req.Device == "ac" || req.Device == "projector" { haURL := os.Getenv("HA_URL") haToken := os.Getenv("HA_TOKEN") @@ -169,7 +186,6 @@ func ControlHardware(c *gin.Context) { return } - // Jika device tidak dikenali 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") haToken := os.Getenv("HA_TOKEN") - // Ganti dengan Entity ID sensor daya kamu di Home Assistant entityID := "sensor.kwh_meter_power" apiURL := fmt.Sprintf("%s/api/states/%s", haURL, entityID) @@ -199,7 +214,6 @@ func GetPowerStatus(c *gin.Context) { var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) - // Ambil nilai state (angka watt) powerStr, ok := result["state"].(string) if !ok { c.JSON(http.StatusOK, gin.H{"power": 0})