Initial commit
This commit is contained in:
+28
-26
@@ -1,25 +1,26 @@
|
||||
services:
|
||||
# Main Application
|
||||
app:
|
||||
container_name: websocket-qris
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
target: prod
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8070:8070"
|
||||
- "8030:8030"
|
||||
environment:
|
||||
# Server Configuration
|
||||
APP_ENV: production
|
||||
PORT: 8070
|
||||
PORT: 8030
|
||||
GIN_MODE: release
|
||||
|
||||
# Default Database Configuration (PostgreSQL)
|
||||
DB_CONNECTION: postgres
|
||||
DB_USERNAME: stim
|
||||
DB_PASSWORD: stim*RS54
|
||||
DB_HOST: 10.10.123.165
|
||||
DB_DATABASE: satu_db
|
||||
DB_USERNAME: simtest
|
||||
DB_PASSWORD: 12345
|
||||
DB_HOST: 10.10.123.223
|
||||
DB_DATABASE: simrsbackup
|
||||
DB_PORT: 5432
|
||||
DB_SSLMODE: disable
|
||||
|
||||
@@ -61,30 +62,30 @@ services:
|
||||
# MYSQL_MEDICAL_SSLMODE: disable
|
||||
|
||||
# Keycloak Configuration
|
||||
KEYCLOAK_ISSUER: https://auth.rssa.top/realms/sandbox
|
||||
KEYCLOAK_AUDIENCE: nuxtsim-pendaftaran
|
||||
KEYCLOAK_JWKS_URL: https://auth.rssa.top/realms/sandbox/protocol/openid-connect/certs
|
||||
KEYCLOAK_ENABLED: "true"
|
||||
# KEYCLOAK_ISSUER: https://auth.rssa.top/realms/sandbox
|
||||
# KEYCLOAK_AUDIENCE: nuxtsim-pendaftaran
|
||||
# KEYCLOAK_JWKS_URL: https://auth.rssa.top/realms/sandbox/protocol/openid-connect/certs
|
||||
# KEYCLOAK_ENABLED: "true"
|
||||
|
||||
# BPJS Configuration
|
||||
BPJS_BASEURL: https://apijkn.bpjs-kesehatan.go.id/vclaim-rest
|
||||
BPJS_CONSID: 5257
|
||||
BPJS_USERKEY: 4cf1cbef8c008440bbe9ef9ba789e482
|
||||
BPJS_SECRETKEY: 1bV363512D
|
||||
# BPJS_BASEURL: https://apijkn.bpjs-kesehatan.go.id/vclaim-rest
|
||||
# BPJS_CONSID: 5257
|
||||
# BPJS_USERKEY: 4cf1cbef8c008440bbe9ef9ba789e482
|
||||
# BPJS_SECRETKEY: 1bV363512D
|
||||
|
||||
# SatuSehat Configuration
|
||||
BRIDGING_SATUSEHAT_ORG_ID: 100026555
|
||||
BRIDGING_SATUSEHAT_FASYAKES_ID: 3573011
|
||||
BRIDGING_SATUSEHAT_CLIENT_ID: l1ZgJGW6K5pnrqGUikWM7fgIoquA2AQ5UUG0U8WqHaq2VEyZ
|
||||
BRIDGING_SATUSEHAT_CLIENT_SECRET: Al3PTYAW6axPiAFwaFlpn8qShLFW5YGMgG8w1qhexgCc7lGTEjjcR6zxa06ThPDy
|
||||
BRIDGING_SATUSEHAT_AUTH_URL: https://api-satusehat.kemkes.go.id/oauth2/v1
|
||||
BRIDGING_SATUSEHAT_BASE_URL: https://api-satusehat.kemkes.go.id/fhir-r4/v1
|
||||
BRIDGING_SATUSEHAT_CONSENT_URL: https://api-satusehat.dto.kemkes.go.id/consent/v1
|
||||
BRIDGING_SATUSEHAT_KFA_URL: https://api-satusehat.kemkes.go.id/kfa-v2
|
||||
# BRIDGING_SATUSEHAT_ORG_ID: 100026555
|
||||
# BRIDGING_SATUSEHAT_FASYAKES_ID: 3573011
|
||||
# BRIDGING_SATUSEHAT_CLIENT_ID: l1ZgJGW6K5pnrqGUikWM7fgIoquA2AQ5UUG0U8WqHaq2VEyZ
|
||||
# BRIDGING_SATUSEHAT_CLIENT_SECRET: Al3PTYAW6axPiAFwaFlpn8qShLFW5YGMgG8w1qhexgCc7lGTEjjcR6zxa06ThPDy
|
||||
# BRIDGING_SATUSEHAT_AUTH_URL: https://api-satusehat.kemkes.go.id/oauth2/v1
|
||||
# BRIDGING_SATUSEHAT_BASE_URL: https://api-satusehat.kemkes.go.id/fhir-r4/v1
|
||||
# BRIDGING_SATUSEHAT_CONSENT_URL: https://api-satusehat.dto.kemkes.go.id/consent/v1
|
||||
# BRIDGING_SATUSEHAT_KFA_URL: https://api-satusehat.kemkes.go.id/kfa-v2
|
||||
|
||||
# Swagger Configuration
|
||||
SWAGGER_TITLE: My Custom API Service
|
||||
SWAGGER_DESCRIPTION: This is a custom API service for managing various resources
|
||||
SWAGGER_TITLE: API Service QRIS
|
||||
SWAGGER_DESCRIPTION: Documentation SWAGGER API Service QRIS
|
||||
SWAGGER_VERSION: 2.0.0
|
||||
SWAGGER_CONTACT_NAME: Support Team
|
||||
SWAGGER_HOST: api.mycompany.com:8080
|
||||
@@ -92,9 +93,10 @@ services:
|
||||
SWAGGER_SCHEMES: https
|
||||
|
||||
# API Configuration
|
||||
API_TITLE: API Service UJICOBA
|
||||
API_DESCRIPTION: Dokumentation SWAGGER
|
||||
API_TITLE: API Service UJICOBA QRIS
|
||||
API_DESCRIPTION: Documentation SWAGGER
|
||||
API_VERSION: 3.0.0
|
||||
|
||||
# WebSocket Configuration
|
||||
WS_READ_TIMEOUT: 300s
|
||||
WS_WRITE_TIMEOUT: 30s
|
||||
|
||||
+58
-1812
File diff suppressed because it is too large
Load Diff
+58
-1812
File diff suppressed because it is too large
Load Diff
+52
-1208
File diff suppressed because it is too large
Load Diff
@@ -821,9 +821,7 @@
|
||||
);
|
||||
const ipBased = document.getElementById("ipBasedCheck").checked;
|
||||
|
||||
let url = `ws://meninjar.dev.rssa.id:8070/api/v1/ws?user_id=${encodeURIComponent(
|
||||
userId
|
||||
)}&room=${encodeURIComponent(room)}`;
|
||||
let url = `ws://localhost:8080/api/v1/ws?user_id=QRIS&room=BANKJATIM`;
|
||||
|
||||
if (ipBased) {
|
||||
url += "&ip_based=true";
|
||||
|
||||
+30
-30
@@ -761,18 +761,18 @@ func (c *Config) Validate() error {
|
||||
}
|
||||
}
|
||||
|
||||
if c.Bpjs.BaseURL == "" {
|
||||
log.Fatal("BPJS Base URL is required")
|
||||
}
|
||||
if c.Bpjs.ConsID == "" {
|
||||
log.Fatal("BPJS Consumer ID is required")
|
||||
}
|
||||
if c.Bpjs.UserKey == "" {
|
||||
log.Fatal("BPJS User Key is required")
|
||||
}
|
||||
if c.Bpjs.SecretKey == "" {
|
||||
log.Fatal("BPJS Secret Key is required")
|
||||
}
|
||||
// if c.Bpjs.BaseURL == "" {
|
||||
// log.Fatal("BPJS Base URL is required")
|
||||
// }
|
||||
// if c.Bpjs.ConsID == "" {
|
||||
// log.Fatal("BPJS Consumer ID is required")
|
||||
// }
|
||||
// if c.Bpjs.UserKey == "" {
|
||||
// log.Fatal("BPJS User Key is required")
|
||||
// }
|
||||
// if c.Bpjs.SecretKey == "" {
|
||||
// log.Fatal("BPJS Secret Key is required")
|
||||
// }
|
||||
|
||||
// Validate Keycloak configuration if enabled
|
||||
if c.Keycloak.Enabled {
|
||||
@@ -788,24 +788,24 @@ func (c *Config) Validate() error {
|
||||
}
|
||||
|
||||
// Validate SatuSehat configuration
|
||||
if c.SatuSehat.OrgID == "" {
|
||||
log.Fatal("SatuSehat Organization ID is required")
|
||||
}
|
||||
if c.SatuSehat.FasyakesID == "" {
|
||||
log.Fatal("SatuSehat Fasyankes ID is required")
|
||||
}
|
||||
if c.SatuSehat.ClientID == "" {
|
||||
log.Fatal("SatuSehat Client ID is required")
|
||||
}
|
||||
if c.SatuSehat.ClientSecret == "" {
|
||||
log.Fatal("SatuSehat Client Secret is required")
|
||||
}
|
||||
if c.SatuSehat.AuthURL == "" {
|
||||
log.Fatal("SatuSehat Auth URL is required")
|
||||
}
|
||||
if c.SatuSehat.BaseURL == "" {
|
||||
log.Fatal("SatuSehat Base URL is required")
|
||||
}
|
||||
// if c.SatuSehat.OrgID == "" {
|
||||
// log.Fatal("SatuSehat Organization ID is required")
|
||||
// }
|
||||
// if c.SatuSehat.FasyakesID == "" {
|
||||
// log.Fatal("SatuSehat Fasyankes ID is required")
|
||||
// }
|
||||
// if c.SatuSehat.ClientID == "" {
|
||||
// log.Fatal("SatuSehat Client ID is required")
|
||||
// }
|
||||
// if c.SatuSehat.ClientSecret == "" {
|
||||
// log.Fatal("SatuSehat Client Secret is required")
|
||||
// }
|
||||
// if c.SatuSehat.AuthURL == "" {
|
||||
// log.Fatal("SatuSehat Auth URL is required")
|
||||
// }
|
||||
// if c.SatuSehat.BaseURL == "" {
|
||||
// log.Fatal("SatuSehat Base URL is required")
|
||||
// }
|
||||
|
||||
// Validate WebSocket configuration
|
||||
if c.WebSocket.ReadTimeout <= 0 {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,8 @@ package websocket
|
||||
import (
|
||||
"api-service/internal/config"
|
||||
ws "api-service/internal/services/websocket"
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
@@ -88,7 +90,7 @@ func (h *WebSocketHandler) TestWebSocketConnection(c *gin.Context) {
|
||||
"user_id": "optional, for user identification",
|
||||
"room": "optional, for room-based messaging",
|
||||
},
|
||||
"example": "ws://meninjar.dev.rssa.id:8070/api/v1/ws?client_id=test_client&room=test_room",
|
||||
// "example": "ws://meninjar.dev.rssa.id:8070/api/v1/ws?client_id=test_client&room=test_room",
|
||||
"timestamp": time.Now().Unix(),
|
||||
})
|
||||
}
|
||||
@@ -134,3 +136,134 @@ func (h *WebSocketHandler) CleanupInactiveClients(c *gin.Context) {
|
||||
"timestamp": time.Now().Unix(),
|
||||
})
|
||||
}
|
||||
|
||||
func (h *WebSocketHandler) BroadcastQris(c *gin.Context) {
|
||||
var req struct {
|
||||
Data map[string]interface{} `json:"data"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
dbConn, err := h.hub.GetDatabaseConnection("simrs_backup")
|
||||
if err != nil || dbConn == nil {
|
||||
c.JSON(500, gin.H{"error": "Database connection failed"})
|
||||
return
|
||||
}
|
||||
|
||||
ip, _ := req.Data["ip"].(string)
|
||||
ctx, cancel := context.WithTimeout(c.Request.Context(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
posDevices, err := h.fetchPosDeviceByIP(ctx, dbConn, ip)
|
||||
|
||||
broadcaster := ws.NewBroadcaster(h.hub)
|
||||
|
||||
if err != nil || len(posDevices) == 0 {
|
||||
broadcaster.BroadcastQris("qris_posdevice", map[string]interface{}{
|
||||
"data": req.Data,
|
||||
"posdevice": nil,
|
||||
"message": "No posdevice found for this IP",
|
||||
"timestamp": time.Now().Unix(),
|
||||
})
|
||||
|
||||
c.JSON(404, gin.H{
|
||||
"error": "No posdevice found for this IP",
|
||||
"data": req.Data,
|
||||
"timestamp": time.Now().Unix(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
broadcaster.BroadcastQris("qris_posdevice", map[string]interface{}{
|
||||
"data": req.Data,
|
||||
"posdevice": posDevices,
|
||||
"timestamp": time.Now().Unix(),
|
||||
})
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
"status": "broadcast sent",
|
||||
"data": req.Data,
|
||||
"posdevice": posDevices,
|
||||
"timestamp": time.Now().Unix(),
|
||||
})
|
||||
}
|
||||
|
||||
func (h *WebSocketHandler) BroadcastCheck(c *gin.Context) {
|
||||
var req struct {
|
||||
Data map[string]interface{} `json:"data"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
dbConn, err := h.hub.GetDatabaseConnection("simrs_backup")
|
||||
if err != nil || dbConn == nil {
|
||||
c.JSON(500, gin.H{"error": "Database connection failed"})
|
||||
return
|
||||
}
|
||||
|
||||
ip, _ := req.Data["ip"].(string)
|
||||
ctx, cancel := context.WithTimeout(c.Request.Context(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
posDevices, err := h.fetchPosDeviceByIP(ctx, dbConn, ip)
|
||||
|
||||
broadcaster := ws.NewBroadcaster(h.hub)
|
||||
|
||||
if err != nil || len(posDevices) == 0 {
|
||||
broadcaster.BroadcastCheck("qris_check", map[string]interface{}{
|
||||
"data": req.Data,
|
||||
"posdevice": nil,
|
||||
"message": "No posdevice found for this IP",
|
||||
"timestamp": time.Now().Unix(),
|
||||
})
|
||||
|
||||
c.JSON(404, gin.H{
|
||||
"error": "No posdevice found for this IP",
|
||||
"data": req.Data,
|
||||
"timestamp": time.Now().Unix(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
broadcaster.BroadcastCheck("qris_check", map[string]interface{}{
|
||||
"data": req.Data,
|
||||
"posdevice": posDevices,
|
||||
"timestamp": time.Now().Unix(),
|
||||
})
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
"status": "broadcast sent",
|
||||
"data": req.Data,
|
||||
"posdevice": posDevices,
|
||||
"timestamp": time.Now().Unix(),
|
||||
})
|
||||
}
|
||||
|
||||
type Qris struct {
|
||||
PosDevice string `json:"posdevice"`
|
||||
}
|
||||
|
||||
func (h *WebSocketHandler) fetchPosDeviceByIP(ctx context.Context, db *sql.DB, ip string) ([]string, error) {
|
||||
query := `SELECT posdevice FROM m_deviceqris WHERE ip = $1 AND status = '1'`
|
||||
rows, err := db.QueryContext(ctx, query, ip)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var posDevices []string
|
||||
for rows.Next() {
|
||||
var posDevice string
|
||||
if err := rows.Scan(&posDevice); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
posDevices = append(posDevices, posDevice)
|
||||
}
|
||||
return posDevices, nil
|
||||
}
|
||||
|
||||
@@ -1,228 +0,0 @@
|
||||
package retribusi
|
||||
|
||||
import (
|
||||
"api-service/internal/models"
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Retribusi represents the data structure for the retribusi table
|
||||
// with proper null handling and optimized JSON marshaling
|
||||
type Retribusi struct {
|
||||
ID string `json:"id" db:"id"`
|
||||
Status string `json:"status" db:"status"`
|
||||
Sort models.NullableInt32 `json:"sort,omitempty" db:"sort"`
|
||||
UserCreated models.NullableString `json:"user_created,omitempty" db:"user_created"`
|
||||
DateCreated models.NullableTime `json:"date_created,omitempty" db:"date_created"`
|
||||
UserUpdated models.NullableString `json:"user_updated,omitempty" db:"user_updated"`
|
||||
DateUpdated models.NullableTime `json:"date_updated,omitempty" db:"date_updated"`
|
||||
Jenis models.NullableString `json:"jenis,omitempty" db:"Jenis"`
|
||||
Pelayanan models.NullableString `json:"pelayanan,omitempty" db:"Pelayanan"`
|
||||
Dinas models.NullableString `json:"dinas,omitempty" db:"Dinas"`
|
||||
KelompokObyek models.NullableString `json:"kelompok_obyek,omitempty" db:"Kelompok_obyek"`
|
||||
KodeTarif models.NullableString `json:"kode_tarif,omitempty" db:"Kode_tarif"`
|
||||
Tarif models.NullableString `json:"tarif,omitempty" db:"Tarif"`
|
||||
Satuan models.NullableString `json:"satuan,omitempty" db:"Satuan"`
|
||||
TarifOvertime models.NullableString `json:"tarif_overtime,omitempty" db:"Tarif_overtime"`
|
||||
SatuanOvertime models.NullableString `json:"satuan_overtime,omitempty" db:"Satuan_overtime"`
|
||||
RekeningPokok models.NullableString `json:"rekening_pokok,omitempty" db:"Rekening_pokok"`
|
||||
RekeningDenda models.NullableString `json:"rekening_denda,omitempty" db:"Rekening_denda"`
|
||||
Uraian1 models.NullableString `json:"uraian_1,omitempty" db:"Uraian_1"`
|
||||
Uraian2 models.NullableString `json:"uraian_2,omitempty" db:"Uraian_2"`
|
||||
Uraian3 models.NullableString `json:"uraian_3,omitempty" db:"Uraian_3"`
|
||||
}
|
||||
|
||||
// Custom JSON marshaling untuk Retribusi agar NULL values tidak muncul di response
|
||||
func (r Retribusi) MarshalJSON() ([]byte, error) {
|
||||
type Alias Retribusi
|
||||
aux := &struct {
|
||||
Sort *int `json:"sort,omitempty"`
|
||||
UserCreated *string `json:"user_created,omitempty"`
|
||||
DateCreated *time.Time `json:"date_created,omitempty"`
|
||||
UserUpdated *string `json:"user_updated,omitempty"`
|
||||
DateUpdated *time.Time `json:"date_updated,omitempty"`
|
||||
Jenis *string `json:"jenis,omitempty"`
|
||||
Pelayanan *string `json:"pelayanan,omitempty"`
|
||||
Dinas *string `json:"dinas,omitempty"`
|
||||
KelompokObyek *string `json:"kelompok_obyek,omitempty"`
|
||||
KodeTarif *string `json:"kode_tarif,omitempty"`
|
||||
Tarif *string `json:"tarif,omitempty"`
|
||||
Satuan *string `json:"satuan,omitempty"`
|
||||
TarifOvertime *string `json:"tarif_overtime,omitempty"`
|
||||
SatuanOvertime *string `json:"satuan_overtime,omitempty"`
|
||||
RekeningPokok *string `json:"rekening_pokok,omitempty"`
|
||||
RekeningDenda *string `json:"rekening_denda,omitempty"`
|
||||
Uraian1 *string `json:"uraian_1,omitempty"`
|
||||
Uraian2 *string `json:"uraian_2,omitempty"`
|
||||
Uraian3 *string `json:"uraian_3,omitempty"`
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(&r),
|
||||
}
|
||||
|
||||
// Convert NullableInt32 to pointer
|
||||
if r.Sort.Valid {
|
||||
sort := int(r.Sort.Int32)
|
||||
aux.Sort = &sort
|
||||
}
|
||||
if r.UserCreated.Valid {
|
||||
aux.UserCreated = &r.UserCreated.String
|
||||
}
|
||||
if r.DateCreated.Valid {
|
||||
aux.DateCreated = &r.DateCreated.Time
|
||||
}
|
||||
if r.UserUpdated.Valid {
|
||||
aux.UserUpdated = &r.UserUpdated.String
|
||||
}
|
||||
if r.DateUpdated.Valid {
|
||||
aux.DateUpdated = &r.DateUpdated.Time
|
||||
}
|
||||
if r.Jenis.Valid {
|
||||
aux.Jenis = &r.Jenis.String
|
||||
}
|
||||
if r.Pelayanan.Valid {
|
||||
aux.Pelayanan = &r.Pelayanan.String
|
||||
}
|
||||
if r.Dinas.Valid {
|
||||
aux.Dinas = &r.Dinas.String
|
||||
}
|
||||
if r.KelompokObyek.Valid {
|
||||
aux.KelompokObyek = &r.KelompokObyek.String
|
||||
}
|
||||
if r.KodeTarif.Valid {
|
||||
aux.KodeTarif = &r.KodeTarif.String
|
||||
}
|
||||
if r.Tarif.Valid {
|
||||
aux.Tarif = &r.Tarif.String
|
||||
}
|
||||
if r.Satuan.Valid {
|
||||
aux.Satuan = &r.Satuan.String
|
||||
}
|
||||
if r.TarifOvertime.Valid {
|
||||
aux.TarifOvertime = &r.TarifOvertime.String
|
||||
}
|
||||
if r.SatuanOvertime.Valid {
|
||||
aux.SatuanOvertime = &r.SatuanOvertime.String
|
||||
}
|
||||
if r.RekeningPokok.Valid {
|
||||
aux.RekeningPokok = &r.RekeningPokok.String
|
||||
}
|
||||
if r.RekeningDenda.Valid {
|
||||
aux.RekeningDenda = &r.RekeningDenda.String
|
||||
}
|
||||
if r.Uraian1.Valid {
|
||||
aux.Uraian1 = &r.Uraian1.String
|
||||
}
|
||||
if r.Uraian2.Valid {
|
||||
aux.Uraian2 = &r.Uraian2.String
|
||||
}
|
||||
if r.Uraian3.Valid {
|
||||
aux.Uraian3 = &r.Uraian3.String
|
||||
}
|
||||
|
||||
return json.Marshal(aux)
|
||||
}
|
||||
|
||||
// Helper methods untuk mendapatkan nilai yang aman
|
||||
func (r *Retribusi) GetJenis() string {
|
||||
if r.Jenis.Valid {
|
||||
return r.Jenis.String
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (r *Retribusi) GetDinas() string {
|
||||
if r.Dinas.Valid {
|
||||
return r.Dinas.String
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (r *Retribusi) GetTarif() string {
|
||||
if r.Tarif.Valid {
|
||||
return r.Tarif.String
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Response struct untuk GET by ID - diperbaiki struktur
|
||||
type RetribusiGetByIDResponse struct {
|
||||
Message string `json:"message"`
|
||||
Data *Retribusi `json:"data"`
|
||||
}
|
||||
|
||||
// Request struct untuk create - dioptimalkan dengan validasi
|
||||
type RetribusiCreateRequest struct {
|
||||
Status string `json:"status" validate:"required,oneof=draft active inactive"`
|
||||
Jenis *string `json:"jenis,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
Pelayanan *string `json:"pelayanan,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
Dinas *string `json:"dinas,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
KelompokObyek *string `json:"kelompok_obyek,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
KodeTarif *string `json:"kode_tarif,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
Uraian1 *string `json:"uraian_1,omitempty"`
|
||||
Uraian2 *string `json:"uraian_2,omitempty"`
|
||||
Uraian3 *string `json:"uraian_3,omitempty"`
|
||||
Tarif *string `json:"tarif,omitempty" validate:"omitempty,numeric"`
|
||||
Satuan *string `json:"satuan,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
TarifOvertime *string `json:"tarif_overtime,omitempty" validate:"omitempty,numeric"`
|
||||
SatuanOvertime *string `json:"satuan_overtime,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
RekeningPokok *string `json:"rekening_pokok,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
RekeningDenda *string `json:"rekening_denda,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
}
|
||||
|
||||
// Response struct untuk create
|
||||
type RetribusiCreateResponse struct {
|
||||
Message string `json:"message"`
|
||||
Data *Retribusi `json:"data"`
|
||||
}
|
||||
|
||||
// Update request - sama seperti create tapi dengan ID
|
||||
type RetribusiUpdateRequest struct {
|
||||
ID string `json:"-" validate:"required,uuid4"` // ID dari URL path
|
||||
Status string `json:"status" validate:"required,oneof=draft active inactive"`
|
||||
Jenis *string `json:"jenis,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
Pelayanan *string `json:"pelayanan,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
Dinas *string `json:"dinas,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
KelompokObyek *string `json:"kelompok_obyek,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
KodeTarif *string `json:"kode_tarif,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
Uraian1 *string `json:"uraian_1,omitempty"`
|
||||
Uraian2 *string `json:"uraian_2,omitempty"`
|
||||
Uraian3 *string `json:"uraian_3,omitempty"`
|
||||
Tarif *string `json:"tarif,omitempty" validate:"omitempty,numeric"`
|
||||
Satuan *string `json:"satuan,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
TarifOvertime *string `json:"tarif_overtime,omitempty" validate:"omitempty,numeric"`
|
||||
SatuanOvertime *string `json:"satuan_overtime,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
RekeningPokok *string `json:"rekening_pokok,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
RekeningDenda *string `json:"rekening_denda,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
}
|
||||
|
||||
// Response struct untuk update
|
||||
type RetribusiUpdateResponse struct {
|
||||
Message string `json:"message"`
|
||||
Data *Retribusi `json:"data"`
|
||||
}
|
||||
|
||||
// Response struct untuk delete
|
||||
type RetribusiDeleteResponse struct {
|
||||
Message string `json:"message"`
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
// Enhanced GET response dengan pagination dan aggregation
|
||||
type RetribusiGetResponse struct {
|
||||
Message string `json:"message"`
|
||||
Data []Retribusi `json:"data"`
|
||||
Meta models.MetaResponse `json:"meta"`
|
||||
Summary *models.AggregateData `json:"summary,omitempty"`
|
||||
}
|
||||
|
||||
// Filter struct untuk query parameters
|
||||
type RetribusiFilter struct {
|
||||
Status *string `json:"status,omitempty" form:"status"`
|
||||
Jenis *string `json:"jenis,omitempty" form:"jenis"`
|
||||
Dinas *string `json:"dinas,omitempty" form:"dinas"`
|
||||
KelompokObyek *string `json:"kelompok_obyek,omitempty" form:"kelompok_obyek"`
|
||||
Search *string `json:"search,omitempty" form:"search"`
|
||||
DateFrom *time.Time `json:"date_from,omitempty" form:"date_from"`
|
||||
DateTo *time.Time `json:"date_to,omitempty" form:"date_to"`
|
||||
}
|
||||
@@ -3,9 +3,7 @@ package v1
|
||||
import (
|
||||
"api-service/internal/config"
|
||||
"api-service/internal/database"
|
||||
authHandlers "api-service/internal/handlers/auth"
|
||||
healthcheckHandlers "api-service/internal/handlers/healthcheck"
|
||||
retribusiHandlers "api-service/internal/handlers/retribusi"
|
||||
websocketHandlers "api-service/internal/handlers/websocket"
|
||||
"api-service/internal/middleware"
|
||||
services "api-service/internal/services/auth"
|
||||
@@ -22,7 +20,7 @@ func RegisterRoutes(cfg *config.Config) *gin.Engine {
|
||||
router := gin.New()
|
||||
|
||||
// Initialize auth middleware configuration
|
||||
middleware.InitializeAuth(cfg)
|
||||
middleware.AuthJWTMiddleware()
|
||||
|
||||
// Add global middleware
|
||||
router.Use(middleware.CORSConfig())
|
||||
@@ -90,17 +88,17 @@ func RegisterRoutes(cfg *config.Config) *gin.Engine {
|
||||
// =============================================================================
|
||||
|
||||
// Authentication routes
|
||||
authHandler := authHandlers.NewAuthHandler(authService)
|
||||
tokenHandler := authHandlers.NewTokenHandler(authService)
|
||||
// authHandler := authHandlers.NewAuthHandler(authService)
|
||||
// tokenHandler := authHandlers.NewTokenHandler(authService)
|
||||
|
||||
// Basic auth routes
|
||||
v1.POST("/auth/login", authHandler.Login)
|
||||
v1.POST("/auth/register", authHandler.Register)
|
||||
v1.POST("/auth/refresh", authHandler.RefreshToken)
|
||||
// v1.POST("/auth/login", authHandler.Login)
|
||||
// v1.POST("/auth/register", authHandler.Register)
|
||||
// v1.POST("/auth/refresh", authHandler.RefreshToken)
|
||||
|
||||
// Token generation routes
|
||||
v1.POST("/token/generate", tokenHandler.GenerateToken)
|
||||
v1.POST("/token/generate-direct", tokenHandler.GenerateTokenDirect)
|
||||
// v1.POST("/token/generate", tokenHandler.GenerateToken)
|
||||
// v1.POST("/token/generate-direct", tokenHandler.GenerateTokenDirect)
|
||||
|
||||
// =============================================================================
|
||||
// WEBSOCKET ROUTES
|
||||
@@ -123,52 +121,72 @@ func RegisterRoutes(cfg *config.Config) *gin.Engine {
|
||||
v1.GET("/ws", websocketHandler.HandleWebSocket)
|
||||
v1.GET("/ws/test", websocketHandler.TestWebSocketConnection)
|
||||
v1.GET("/ws/stats", websocketHandler.GetWebSocketStats)
|
||||
v1.POST("/ws/broadcast/qris", websocketHandler.BroadcastQris)
|
||||
v1.POST("/ws/broadcast/check", websocketHandler.BroadcastCheck)
|
||||
|
||||
// Retribusi endpoints with WebSocket notifications
|
||||
retribusiHandler := retribusiHandlers.NewRetribusiHandler()
|
||||
retribusiGroup := v1.Group("/retribusi")
|
||||
{
|
||||
retribusiGroup.GET("", retribusiHandler.GetRetribusi)
|
||||
retribusiGroup.GET("/dynamic", retribusiHandler.GetRetribusiDynamic)
|
||||
retribusiGroup.GET("/search", retribusiHandler.SearchRetribusiAdvanced)
|
||||
retribusiGroup.GET("/id/:id", retribusiHandler.GetRetribusiByID)
|
||||
// retribusiHandler := retribusiHandlers.NewRetribusiHandler()
|
||||
// retribusiGroup := v1.Group("/retribusi")
|
||||
// {
|
||||
// retribusiGroup.GET("", retribusiHandler.GetRetribusi)
|
||||
// retribusiGroup.GET("/dynamic", retribusiHandler.GetRetribusiDynamic)
|
||||
// retribusiGroup.GET("/search", retribusiHandler.SearchRetribusiAdvanced)
|
||||
// retribusiGroup.GET("/id/:id", retribusiHandler.GetRetribusiByID)
|
||||
|
||||
// POST/PUT/DELETE with automatic WebSocket notifications
|
||||
retribusiGroup.POST("", func(c *gin.Context) {
|
||||
retribusiHandler.CreateRetribusi(c)
|
||||
// // POST/PUT/DELETE with automatic WebSocket notifications
|
||||
// retribusiGroup.POST("", func(c *gin.Context) {
|
||||
// retribusiHandler.CreateRetribusi(c)
|
||||
|
||||
// Trigger WebSocket notification after successful creation
|
||||
if c.Writer.Status() == 200 || c.Writer.Status() == 201 {
|
||||
// Notify database change via WebSocket
|
||||
// websocketHub.NotifyDatabaseChange("postgres_satudata", "retribusi_changes",
|
||||
// fmt.Sprintf(`{"action": "created", "timestamp": "%s"}`, time.Now().Format(time.RFC3339)))
|
||||
}
|
||||
})
|
||||
// // Trigger WebSocket notification after successful creation
|
||||
// if c.Writer.Status() == 200 || c.Writer.Status() == 201 {
|
||||
// // Notify database change via WebSocket
|
||||
// // websocketHub.NotifyDatabaseChange("postgres_satudata", "retribusi_changes",
|
||||
// // fmt.Sprintf(`{"action": "created", "timestamp": "%s"}`, time.Now().Format(time.RFC3339)))
|
||||
// }
|
||||
// })
|
||||
|
||||
retribusiGroup.PUT("/id/:id", func(c *gin.Context) {
|
||||
// id := c.Param("id")
|
||||
retribusiHandler.UpdateRetribusi(c)
|
||||
// retribusiGroup.PUT("/id/:id", func(c *gin.Context) {
|
||||
// // id := c.Param("id")
|
||||
// retribusiHandler.UpdateRetribusi(c)
|
||||
|
||||
// Trigger WebSocket notification after successful update
|
||||
if c.Writer.Status() == 200 {
|
||||
// Notify database change via WebSocket
|
||||
// websocketHub.NotifyDatabaseChange("postgres_satudata", "retribusi_changes",
|
||||
// fmt.Sprintf(`{"action": "updated", "id": "%s", "timestamp": "%s"}`, id, time.Now().Format(time.RFC3339)))
|
||||
}
|
||||
})
|
||||
// // Trigger WebSocket notification after successful update
|
||||
// if c.Writer.Status() == 200 {
|
||||
// // Notify database change via WebSocket
|
||||
// // websocketHub.NotifyDatabaseChange("postgres_satudata", "retribusi_changes",
|
||||
// // fmt.Sprintf(`{"action": "updated", "id": "%s", "timestamp": "%s"}`, id, time.Now().Format(time.RFC3339)))
|
||||
// }
|
||||
// })
|
||||
|
||||
retribusiGroup.DELETE("/id/:id", func(c *gin.Context) {
|
||||
// id := c.Param("id")
|
||||
retribusiHandler.DeleteRetribusi(c)
|
||||
// retribusiGroup.DELETE("/id/:id", func(c *gin.Context) {
|
||||
// // id := c.Param("id")
|
||||
// retribusiHandler.DeleteRetribusi(c)
|
||||
|
||||
// Trigger WebSocket notification after successful deletion
|
||||
if c.Writer.Status() == 200 {
|
||||
// Notify database change via WebSocket
|
||||
// websocketHub.NotifyDatabaseChange("postgres_satudata", "retribusi_changes",
|
||||
// fmt.Sprintf(`{"action": "deleted", "id": "%s", "timestamp": "%s"}`, id, time.Now().Format(time.RFC3339)))
|
||||
}
|
||||
})
|
||||
}
|
||||
// // Trigger WebSocket notification after successful deletion
|
||||
// if c.Writer.Status() == 200 {
|
||||
// // Notify database change via WebSocket
|
||||
// // websocketHub.NotifyDatabaseChange("postgres_satudata", "retribusi_changes",
|
||||
// // fmt.Sprintf(`{"action": "deleted", "id": "%s", "timestamp": "%s"}`, id, time.Now().Format(time.RFC3339)))
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
// =============================================================================
|
||||
// PROTECTED ROUTES (Authentication Required)
|
||||
// =============================================================================
|
||||
|
||||
// Create protected group with configurable authentication
|
||||
protected := v1.Group("/")
|
||||
protected.Use(middleware.AuthJWTMiddleware()) // Use configurable authentication
|
||||
|
||||
// User profile (protected)
|
||||
// protected.GET("/auth/me", authHandler.Me)
|
||||
|
||||
// Retribusi endpoints (CRUD operations - should be protected)
|
||||
// protectedQris := protected.Group("/ws")
|
||||
// {
|
||||
// protectedQris.POST("/qris/broadcast", websocketHandler.BroadcastQris) // POST /api/v1/ws/
|
||||
// protectedQris.POST("/check/broadcast", websocketHandler.BroadcastCheck) // POST /api/v1/ws/
|
||||
// }
|
||||
|
||||
return router
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"api-service/pkg/logger"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -108,6 +109,64 @@ func (b *Broadcaster) BroadcastMessage(messageType string, data interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
// BroadcastQris godoc
|
||||
// @Summary Broadcast a QRIS-related WebSocket message
|
||||
// @Description Creates and broadcasts a WebSocket message with the specified type and data for QRIS operations
|
||||
// @Tags WebSocket QRIS
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param messageType path string true "Type of the QRIS message to broadcast"
|
||||
// @Param data body interface{} true "QRIS data payload for the message"
|
||||
// @Success 200 {object} map[string]string "QRIS message successfully queued for broadcast"
|
||||
// @Failure 500 {object} map[string]string "Failed to queue QRIS message (queue full)"
|
||||
// @Router /api/v1/ws/broadcast/qris [post]
|
||||
func (b *Broadcaster) BroadcastQris(messageType string, data interface{}) {
|
||||
msg := NewWebSocketMessage(MessageType(messageType), data, "", "")
|
||||
|
||||
select {
|
||||
case b.hub.messageQueue <- msg:
|
||||
default:
|
||||
// Antrian penuh, abaikan pesan
|
||||
logger.Error("Message queue full, dropping message")
|
||||
}
|
||||
|
||||
// Show posdevice if present
|
||||
// if m, ok := data.(map[string]interface{}); ok {
|
||||
// fmt.Println("BroadcastQris called with IP display: ", m["posdevice"])
|
||||
// }
|
||||
|
||||
//tabel m_deviceqris
|
||||
//kolom posdevice dari nama lokasi jadi ip display
|
||||
//kolom ip dari ip simrs
|
||||
}
|
||||
|
||||
// BroadcastCheck godoc
|
||||
// @Summary Broadcast a WebSocket message
|
||||
// @Description Creates and broadcasts a WebSocket message with the specified type and data
|
||||
// @Tags WebSocket QRIS
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param messageType path string true "Type of the message to broadcast"
|
||||
// @Param data body interface{} true "Data payload for the message"
|
||||
// @Success 200 {object} map[string]string "Message successfully queued for broadcast"
|
||||
// @Failure 500 {object} map[string]string "Failed to queue message (queue full)"
|
||||
// @Router /api/v1/ws/broadcast/check [post]
|
||||
func (b *Broadcaster) BroadcastCheck(messageType string, data interface{}) {
|
||||
msg := NewWebSocketMessage(MessageType(messageType), data, "", "")
|
||||
|
||||
select {
|
||||
case b.hub.messageQueue <- msg:
|
||||
default:
|
||||
// Antrian penuh, abaikan pesan
|
||||
logger.Error("Message queue full, dropping message")
|
||||
}
|
||||
|
||||
// Show posdevice if present
|
||||
// if m, ok := data.(map[string]interface{}); ok {
|
||||
// fmt.Println("BroadcastCheck called with IP display: ", m["posdevice"])
|
||||
// }
|
||||
}
|
||||
|
||||
// BroadcastToRoom mengirim pesan ke ruangan tertentu
|
||||
func (b *Broadcaster) BroadcastToRoom(room string, messageType string, data interface{}) {
|
||||
msg := NewWebSocketMessage(
|
||||
|
||||
@@ -40,7 +40,7 @@ func (h *DatabaseHandler) handleDatabaseInsert(client *Client, message WebSocket
|
||||
return nil
|
||||
}
|
||||
|
||||
table, ok := data["table"].(string)
|
||||
table, ok := data["m_deviceqris"].(string)
|
||||
if !ok || table == "" {
|
||||
client.sendErrorResponse("Invalid table name", "table is required")
|
||||
return nil
|
||||
@@ -55,7 +55,7 @@ func (h *DatabaseHandler) handleDatabaseInsert(client *Client, message WebSocket
|
||||
// Perform actual database insert
|
||||
if h.hub.dbService != nil {
|
||||
// Get database connection
|
||||
db, err := h.hub.GetDatabaseConnection("postgres_satudata")
|
||||
db, err := h.hub.GetDatabaseConnection("simrs_backup")
|
||||
if err != nil {
|
||||
client.sendErrorResponse("Database connection error", err.Error())
|
||||
return nil
|
||||
@@ -110,14 +110,14 @@ func (h *DatabaseHandler) handleDatabaseQuery(client *Client, message WebSocketM
|
||||
return nil
|
||||
}
|
||||
|
||||
table, ok := data["table"].(string)
|
||||
table, ok := data["m_deviceqris"].(string)
|
||||
if !ok || table == "" {
|
||||
client.sendErrorResponse("Invalid table name", "table is required")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Execute query
|
||||
results, err := h.hub.ExecuteDatabaseQuery("postgres_satudata", fmt.Sprintf("SELECT * FROM %s LIMIT 100", table))
|
||||
results, err := h.hub.ExecuteDatabaseQuery("simrs_backup", fmt.Sprintf("SELECT * FROM %s LIMIT 100", table))
|
||||
if err != nil {
|
||||
client.sendErrorResponse("Database query error", err.Error())
|
||||
return nil
|
||||
@@ -142,7 +142,7 @@ func (h *DatabaseHandler) handleDatabaseCustomQuery(client *Client, message WebS
|
||||
|
||||
database, ok := data["database"].(string)
|
||||
if !ok || database == "" {
|
||||
database = "postgres_satudata"
|
||||
database = "simrs_backup"
|
||||
}
|
||||
|
||||
query, ok := data["query"].(string)
|
||||
|
||||
@@ -64,7 +64,7 @@ type ActivityLog struct {
|
||||
// DatabaseService mendefinisikan interface untuk layanan database
|
||||
type DatabaseService interface {
|
||||
Health() map[string]interface{}
|
||||
ListDBs() []string
|
||||
//ListDBs() []string
|
||||
ListenForChanges(ctx context.Context, dbName string, channels []string, callback func(channel, payload string)) error
|
||||
NotifyChange(dbName, channel, payload string) error
|
||||
GetDB(name string) (*sql.DB, error)
|
||||
@@ -345,9 +345,9 @@ func (h *Hub) GetStats() map[string]interface{} {
|
||||
// setupDatabaseListeners sets up database change listeners for real-time updates
|
||||
func (h *Hub) setupDatabaseListeners() {
|
||||
// Listen for changes on retribusi table
|
||||
channels := []string{"retribusi_changes", "data_changes"}
|
||||
channels := []string{"data_changes"}
|
||||
|
||||
err := h.dbService.ListenForChanges(h.ctx, "postgres_satudata", channels, func(channel, payload string) {
|
||||
err := h.dbService.ListenForChanges(h.ctx, "simrs_backup", channels, func(channel, payload string) {
|
||||
h.handleDatabaseChange(channel, payload)
|
||||
})
|
||||
|
||||
|
||||
@@ -4,17 +4,67 @@ global:
|
||||
enable_swagger: true
|
||||
enable_logging: true
|
||||
|
||||
# services:
|
||||
# retribusi:
|
||||
# name: "Retribusi"
|
||||
# category: "retribusi"
|
||||
# package: "retribusi"
|
||||
# description: "Retribusi service for tariff and billing management"
|
||||
# base_url: ""
|
||||
# timeout: 30
|
||||
# retry_count: 3
|
||||
|
||||
services:
|
||||
retribusi:
|
||||
name: "Retribusi"
|
||||
category: "retribusi"
|
||||
package: "retribusi"
|
||||
description: "Retribusi service for tariff and billing management"
|
||||
qris:
|
||||
name: "QRIS"
|
||||
category: "qris"
|
||||
package: "qris"
|
||||
description: "QRIS service for QR code payment management"
|
||||
base_url: ""
|
||||
timeout: 30
|
||||
retry_count: 3
|
||||
|
||||
endpoints:
|
||||
qris:
|
||||
description: "QRIS management"
|
||||
handler_folder: "qris"
|
||||
handler_file: "qris.go"
|
||||
handler_name: "QRIS"
|
||||
table_name: "m_deviceqris"
|
||||
functions:
|
||||
create:
|
||||
methods: ["POST"]
|
||||
path: "/broadcast/qris"
|
||||
post_routes: "/broadcast/qris"
|
||||
post_path: "/broadcast/qris"
|
||||
model: "QrisCreateRequest"
|
||||
response_model: "QrisCreateResponse"
|
||||
request_model: "QrisCreateRequest"
|
||||
description: "Send new QRIS broadcast"
|
||||
summary: "Send QRIS"
|
||||
tags: ["QRIS"]
|
||||
require_auth: true
|
||||
cache_enabled: false
|
||||
enable_database: true
|
||||
cache_ttl: 0
|
||||
|
||||
create:
|
||||
methods: ["POST"]
|
||||
path: "/broadcast/check"
|
||||
post_routes: "/broadcast/check"
|
||||
post_path: "/broadcast/check"
|
||||
model: "QrisCreateRequest"
|
||||
response_model: "QrisCreateResponse"
|
||||
request_model: "QrisCreateRequest"
|
||||
description: "Send new QRIS check broadcast"
|
||||
summary: "Send QRIS check"
|
||||
tags: ["QRIS"]
|
||||
require_auth: true
|
||||
cache_enabled: false
|
||||
enable_database: true
|
||||
cache_ttl: 0
|
||||
|
||||
# endpoints:
|
||||
# retribusi:
|
||||
# description: "Retribusi tariff management"
|
||||
# handler_folder: "retribusi"
|
||||
@@ -152,99 +202,99 @@ services:
|
||||
# has_stats: true
|
||||
|
||||
# Example of another service
|
||||
user:
|
||||
name: "User"
|
||||
category: "user"
|
||||
package: "user"
|
||||
description: "User management service"
|
||||
base_url: ""
|
||||
timeout: 30
|
||||
retry_count: 3
|
||||
# user:
|
||||
# name: "User"
|
||||
# category: "user"
|
||||
# package: "user"
|
||||
# description: "User management service"
|
||||
# base_url: ""
|
||||
# timeout: 30
|
||||
# retry_count: 3
|
||||
|
||||
endpoints:
|
||||
user:
|
||||
description: "User management endpoints"
|
||||
handler_folder: "retribusi"
|
||||
handler_file: "user.go"
|
||||
handler_name: "User"
|
||||
table_name: "data_user"
|
||||
functions:
|
||||
list:
|
||||
methods: ["GET"]
|
||||
path: "/"
|
||||
get_routes: "/"
|
||||
get_path: "/"
|
||||
model: "User"
|
||||
response_model: "UserGetResponse"
|
||||
description: "Get user list with pagination"
|
||||
summary: "Get User List"
|
||||
tags: ["User"]
|
||||
require_auth: true
|
||||
cache_enabled: true
|
||||
enable_database: true
|
||||
cache_ttl: 300
|
||||
has_pagination: true
|
||||
has_filter: true
|
||||
has_search: true
|
||||
# endpoints:
|
||||
# user:
|
||||
# description: "User management endpoints"
|
||||
# handler_folder: "retribusi"
|
||||
# handler_file: "user.go"
|
||||
# handler_name: "User"
|
||||
# table_name: "data_user"
|
||||
# functions:
|
||||
# list:
|
||||
# methods: ["GET"]
|
||||
# path: "/"
|
||||
# get_routes: "/"
|
||||
# get_path: "/"
|
||||
# model: "User"
|
||||
# response_model: "UserGetResponse"
|
||||
# description: "Get user list with pagination"
|
||||
# summary: "Get User List"
|
||||
# tags: ["User"]
|
||||
# require_auth: true
|
||||
# cache_enabled: true
|
||||
# enable_database: true
|
||||
# cache_ttl: 300
|
||||
# has_pagination: true
|
||||
# has_filter: true
|
||||
# has_search: true
|
||||
|
||||
get:
|
||||
methods: ["GET"]
|
||||
path: "/:id"
|
||||
get_routes: "/:id"
|
||||
get_path: "/:id"
|
||||
model: "User"
|
||||
response_model: "UserGetByIDResponse"
|
||||
description: "Get user by ID"
|
||||
summary: "Get User by ID"
|
||||
tags: ["User"]
|
||||
require_auth: true
|
||||
cache_enabled: true
|
||||
enable_database: true
|
||||
cache_ttl: 300
|
||||
# get:
|
||||
# methods: ["GET"]
|
||||
# path: "/:id"
|
||||
# get_routes: "/:id"
|
||||
# get_path: "/:id"
|
||||
# model: "User"
|
||||
# response_model: "UserGetByIDResponse"
|
||||
# description: "Get user by ID"
|
||||
# summary: "Get User by ID"
|
||||
# tags: ["User"]
|
||||
# require_auth: true
|
||||
# cache_enabled: true
|
||||
# enable_database: true
|
||||
# cache_ttl: 300
|
||||
|
||||
create:
|
||||
methods: ["POST"]
|
||||
path: "/"
|
||||
post_routes: "/"
|
||||
post_path: "/"
|
||||
model: "UserCreateRequest"
|
||||
response_model: "UserCreateResponse"
|
||||
request_model: "UserCreateRequest"
|
||||
description: "Create new user"
|
||||
summary: "Create User"
|
||||
tags: ["User"]
|
||||
require_auth: true
|
||||
cache_enabled: false
|
||||
enable_database: true
|
||||
cache_ttl: 0
|
||||
# create:
|
||||
# methods: ["POST"]
|
||||
# path: "/"
|
||||
# post_routes: "/"
|
||||
# post_path: "/"
|
||||
# model: "UserCreateRequest"
|
||||
# response_model: "UserCreateResponse"
|
||||
# request_model: "UserCreateRequest"
|
||||
# description: "Create new user"
|
||||
# summary: "Create User"
|
||||
# tags: ["User"]
|
||||
# require_auth: true
|
||||
# cache_enabled: false
|
||||
# enable_database: true
|
||||
# cache_ttl: 0
|
||||
|
||||
update:
|
||||
methods: ["PUT"]
|
||||
path: "/:id"
|
||||
put_routes: "/:id"
|
||||
put_path: "/:id"
|
||||
model: "UserUpdateRequest"
|
||||
response_model: "UserUpdateResponse"
|
||||
request_model: "UserUpdateRequest"
|
||||
description: "Update user"
|
||||
summary: "Update User"
|
||||
tags: ["User"]
|
||||
require_auth: true
|
||||
cache_enabled: false
|
||||
enable_database: true
|
||||
cache_ttl: 0
|
||||
# update:
|
||||
# methods: ["PUT"]
|
||||
# path: "/:id"
|
||||
# put_routes: "/:id"
|
||||
# put_path: "/:id"
|
||||
# model: "UserUpdateRequest"
|
||||
# response_model: "UserUpdateResponse"
|
||||
# request_model: "UserUpdateRequest"
|
||||
# description: "Update user"
|
||||
# summary: "Update User"
|
||||
# tags: ["User"]
|
||||
# require_auth: true
|
||||
# cache_enabled: false
|
||||
# enable_database: true
|
||||
# cache_ttl: 0
|
||||
|
||||
delete:
|
||||
methods: ["DELETE"]
|
||||
path: "/:id"
|
||||
delete_routes: "/:id"
|
||||
delete_path: "/:id"
|
||||
model: "User"
|
||||
response_model: "UserDeleteResponse"
|
||||
description: "Delete user"
|
||||
summary: "Delete User"
|
||||
tags: ["User"]
|
||||
require_auth: true
|
||||
cache_enabled: false
|
||||
enable_database: true
|
||||
cache_ttl: 0
|
||||
# delete:
|
||||
# methods: ["DELETE"]
|
||||
# path: "/:id"
|
||||
# delete_routes: "/:id"
|
||||
# delete_path: "/:id"
|
||||
# model: "User"
|
||||
# response_model: "UserDeleteResponse"
|
||||
# description: "Delete user"
|
||||
# summary: "Delete User"
|
||||
# tags: ["User"]
|
||||
# require_auth: true
|
||||
# cache_enabled: false
|
||||
# enable_database: true
|
||||
# cache_ttl: 0
|
||||
|
||||
Reference in New Issue
Block a user