Dev / main.go
Mhamdans17
feat: single session enforcement + UI improvements
c6e89e6
Raw
History Blame Contribute Delete
10.1 kB
package main
import (
"io"
"log"
"net/http"
"service-warungpos-go/config"
"service-warungpos-go/controllers"
"service-warungpos-go/middleware"
"service-warungpos-go/models"
"github.com/gin-gonic/gin"
)
func main() {
log.Println("Starting XOGM Go Service...")
// 1. Muat Config
config.LoadConfig()
// 2. Koneksi Database Supabase
config.ConnectDatabase()
// 3. Auto-Migration GORM untuk kelancaran Supabase DB
log.Println("Running Auto-Migration...")
err := config.DB.AutoMigrate(
&models.Company{},
&models.Branch{},
&models.User{},
&models.Category{},
&models.Product{},
&models.BranchProduct{},
&models.Order{},
&models.OrderItem{},
&models.OTP{},
&models.Setting{},
&models.Member{},
&models.PointRule{},
&models.WalletTransaction{},
&models.TopupRequest{},
&models.Voucher{},
&models.PointHistory{},
&models.UserSession{},
)
if err != nil {
log.Fatalf("Gagal melakukan Auto-Migration: %v", err)
}
log.Println("Auto-Migration Selesai dengan Sukses!")
// 4. Seeding super_admin default jika belum ada
controllers.SeedDefaultAdmin()
controllers.SeedDefaultSetting()
// 5. Tripay integration (No SDK init needed, we use plain HTTP Client!)
// 6. Router Setup menggunakan Gin
r := gin.New()
// Global Middlewares
r.Use(gin.Logger())
r.Use(gin.Recovery())
r.Use(middleware.CORSMiddleware())
// Health Checks & Health check root
r.GET("/", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"service": "XOGM Go API (Go Version)",
"version": "1.0.0",
"status": "running",
"docs": "/docs",
})
})
r.GET("/health", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"status": "healthy"})
})
r.GET("/api/ip", func(c *gin.Context) {
resp, err := http.Get("https://api.ipify.org")
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Gagal mendapatkan IP outbound: " + err.Error()})
return
}
defer resp.Body.Close()
ipBytes, err := io.ReadAll(resp.Body)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Gagal membaca IP response: " + err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{
"public_ip": string(ipBytes),
"info": "Gunakan IP ini untuk di-whitelist di Tripay",
})
})
// API Routing Group
api := r.Group("/api")
{
// === AUTHENTICATION ROUTES ===
auth := api.Group("/auth")
{
auth.POST("/login", controllers.Login)
auth.POST("/register-tenant", controllers.RegisterTenant)
auth.POST("/verify-otp", controllers.VerifyOTP)
auth.POST("/resend-otp", controllers.ResendOTP)
auth.POST("/forgot-password", controllers.ForgotPassword)
auth.POST("/reset-password", controllers.ResetPassword)
// Authenticated Auth Routes
auth.Use(middleware.GetCurrentUser())
{
auth.GET("/me", controllers.GetMe)
auth.POST("/logout", controllers.Logout)
auth.POST("/register", middleware.RequireSuperAdmin(), controllers.Register)
auth.GET("/users", middleware.RequireSuperAdmin(), controllers.ListUsers)
auth.PUT("/users/:user_id", middleware.RequireSuperAdmin(), controllers.UpdateUser)
// Owner routes for kasir
auth.GET("/kasir", middleware.RequireOwner(), controllers.ListKasir)
auth.POST("/kasir", middleware.RequireOwner(), controllers.CreateKasir)
auth.PUT("/kasir/:user_id", middleware.RequireOwner(), controllers.UpdateKasir)
}
}
// === COMPANY & BRANCH ROUTES ===
companies := api.Group("/companies")
{
companies.Use(middleware.GetCurrentUser())
{
companies.GET("/me", controllers.GetMyCompany)
companies.PUT("/me/points", controllers.UpdateMyCompanyPoints)
companies.GET("/", middleware.RequireSuperAdmin(), controllers.ListCompanies)
companies.GET("/:company_id", middleware.RequireSuperAdmin(), controllers.GetCompany)
companies.POST("/", middleware.RequireSuperAdmin(), controllers.CreateCompany)
companies.PUT("/:company_id", middleware.RequireSuperAdmin(), controllers.UpdateCompany)
companies.PUT("/:company_id/toggle-active", middleware.RequireSuperAdmin(), controllers.ToggleActiveCompany)
companies.DELETE("/:company_id", middleware.RequireSuperAdmin(), controllers.DeleteCompany)
companies.PUT("/:company_id/reject", middleware.RequireSuperAdmin(), controllers.RejectCompany)
companies.PUT("/:company_id/approve", middleware.RequireSuperAdmin(), controllers.ApproveCompany)
}
}
branches := api.Group("/branches")
{
branches.Use(middleware.GetCurrentUser(), middleware.RequireOwner())
{
branches.GET("/", controllers.ListBranches)
branches.POST("/", controllers.CreateBranch)
branches.PUT("/:branch_id", controllers.UpdateBranch)
branches.DELETE("/:branch_id", controllers.DeleteBranch)
branches.PUT("/:branch_id/toggle", controllers.ToggleBranchStatus)
branches.GET("/:branch_id/products", controllers.GetBranchProducts)
branches.PUT("/:branch_id/products/:product_id", controllers.ToggleProductAvailability)
}
}
// === MEMBERS ROUTES ===
members := api.Group("/members")
{
members.Use(middleware.GetCurrentUser(), middleware.RequireTenantContext())
{
members.GET("/check", controllers.CheckMember)
members.GET("/", controllers.ListMembers)
members.POST("/", controllers.CreateMember)
members.PUT("/:member_id", controllers.UpdateMember)
members.PUT("/:member_id/points", middleware.RequireOwner(), controllers.SetPoints)
members.GET("/:member_id/points/history", controllers.GetPointHistory)
members.DELETE("/:member_id", middleware.RequireOwner(), controllers.DeleteMember)
}
}
// === PRODUCTS & CATEGORIES ROUTES ===
products := api.Group("/products")
{
products.Use(middleware.GetCurrentUser())
{
products.POST("/upload-image", middleware.RequireOwner(), controllers.UploadProductImage)
products.GET("/", middleware.RequireTenantContext(), controllers.ListProducts)
products.GET("/:product_id", middleware.RequireTenantContext(), controllers.GetProduct)
products.POST("/", middleware.RequireOwner(), controllers.CreateProduct)
products.PUT("/:product_id", middleware.RequireOwner(), controllers.UpdateProduct)
products.DELETE("/:product_id", middleware.RequireOwner(), controllers.DeleteProduct)
}
}
categories := api.Group("/categories")
{
categories.Use(middleware.GetCurrentUser())
{
categories.GET("/", middleware.RequireTenantContext(), controllers.ListCategories)
categories.GET("/:category_id", middleware.RequireTenantContext(), controllers.GetCategory)
categories.POST("/", middleware.RequireOwner(), controllers.CreateCategory)
categories.PUT("/:category_id", middleware.RequireOwner(), controllers.UpdateCategory)
categories.DELETE("/:category_id", middleware.RequireOwner(), controllers.DeleteCategory)
}
}
// === ORDERS ROUTES ===
orders := api.Group("/orders")
{
orders.Use(middleware.GetCurrentUser(), middleware.RequireTenantContext())
{
orders.GET("/", controllers.ListOrders)
orders.GET("/:order_id", controllers.GetOrder)
orders.POST("/", controllers.CreateOrder)
orders.POST("/pay/cash", controllers.PayCash)
orders.POST("/pay/midtrans", controllers.PayTripay)
}
}
vouchers := api.Group("/vouchers")
{
vouchers.Use(middleware.GetCurrentUser())
{
vouchers.GET("/", controllers.GetVouchers)
vouchers.POST("/", controllers.CreateVoucher)
vouchers.PUT("/:id", controllers.UpdateVoucher)
vouchers.DELETE("/:id", controllers.DeleteVoucher)
}
}
// === PAYMENTS ROUTES ===
payments := api.Group("/payments")
{
// Webhook Tripay harus publik agar server Tripay bisa mengirim callbacks
payments.POST("/webhook", controllers.TripayWebhook)
payments.Use(middleware.GetCurrentUser(), middleware.RequireTenantContext())
{
payments.POST("/cash", controllers.PayCash)
payments.POST("/midtrans", controllers.PayTripay) // Keep /midtrans for backwards compatibility with front-end
payments.POST("/tripay", controllers.PayTripay) // Also expose /tripay natively
payments.GET("/:order_id/status", controllers.GetPaymentStatus)
payments.POST("/:order_id/cancel", controllers.CancelMidtransOrder)
}
}
// === REPORTS ROUTES ===
reports := api.Group("/reports")
{
reports.Use(middleware.GetCurrentUser(), middleware.RequireTenantContext())
{
reports.GET("/daily", controllers.DailyReport)
reports.GET("/summary", controllers.SummaryReport)
}
}
// === FEES (SUPER ADMIN ONLY) ROUTES ===
fees := api.Group("/fees")
{
fees.Use(middleware.GetCurrentUser(), middleware.RequireSuperAdmin())
{
fees.GET("/report", controllers.GetFeeReport)
fees.PUT("/companies/:company_id/set-fee", controllers.SetCompanyFee)
}
}
// === WALLET ROUTES ===
wallet := api.Group("/wallet")
{
wallet.Use(middleware.GetCurrentUser(), middleware.RequireTenantContext())
{
wallet.GET("", controllers.GetWalletDetail)
wallet.POST("/topup/manual", controllers.RequestTopupManual)
wallet.POST("/topup/qris", controllers.RequestTopupQris)
}
}
// === ADMIN TOPUP & WALLET ROUTES ===
adminTopups := api.Group("/admin/topups")
{
adminTopups.Use(middleware.GetCurrentUser(), middleware.RequireSuperAdmin())
{
adminTopups.GET("", controllers.AdminGetPendingTopups)
adminTopups.POST("/:id/approve", controllers.AdminApproveTopup)
adminTopups.POST("/:id/reject", controllers.AdminRejectTopup)
}
}
adminCompanies := api.Group("/admin/companies")
{
adminCompanies.Use(middleware.GetCurrentUser(), middleware.RequireSuperAdmin())
{
adminCompanies.POST("/:id/adjust-wallet", controllers.AdminAdjustWallet)
adminCompanies.PUT("/:id/settings", controllers.AdminUpdateCompanySettings)
}
}
}
// 7. Mulai Server (Membaca dinamis port Hugging Face Spaces jika ada)
serverPort := config.GlobalConfig.Port
if serverPort == "" {
serverPort = "8003"
}
log.Printf("XOGM Go Server berjalan di port: %s", serverPort)
if err := r.Run(":" + serverPort); err != nil {
log.Fatalf("Gagal menjalankan server Gin: %v", err)
}
}
// trigger rebuild