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"
"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
}
}

View File

@ -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})