This commit is contained in:
teguh nugroho 2026-08-16 01:44:29 +07:00
parent 0b52645074
commit 8855495bb4
46 changed files with 6188 additions and 0 deletions

27
.env.example Normal file
View File

@ -0,0 +1,27 @@
# Database Configuration (Master - Write via PgBouncer)
DB_HOST=145.79.11.200
DB_PORT=6432
DB_USER=cargo_admin
DB_PASSWORD=your-password
DB_NAME=cargo_erp
DB_SSLMODE=disable
# Database Configuration (Slave - Read via PgBouncer Replicas)
DB_READ_HOST=145.79.11.200
DB_READ_PORT=6433
# Redis Configuration
REDIS_URL=redis://:your-redis-password@145.79.11.200:6379
# JWT Configuration
JWT_SECRET=your-secret-key-change-in-production
# Application Configuration
APP_PORT=8080
APP_ENV=development
# MinIO Configuration
MINIO_ENDPOINT=145.79.11.200:9000
MINIO_ACCESS_KEY=minio_admin
MINIO_SECRET_KEY=Minio!2026qwe
MINIO_BUCKET=cargo-attachments

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
notes.txt
_internal/
**/*.json

22
Dockerfile Normal file
View File

@ -0,0 +1,22 @@
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum* ./
RUN go mod download
COPY . .
RUN go mod tidy
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o /app/server ./cmd/api
FROM alpine:latest
RUN apk --no-cache add ca-certificates wget
WORKDIR /root/
COPY --from=builder /app/server .
EXPOSE 8080
CMD ["./server"]

View File

@ -0,0 +1,35 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package domain
import (
"time"
)
const TableNameCity = "city"
// City mapped from table <city>
type City struct {
ID string `gorm:"column:id;type:character(26);primaryKey;default:generate_ulid()" json:"id"`
Name *string `gorm:"column:name;type:character varying(255);index:city_name_idx,priority:1" json:"name"`
ProvinceID *string `gorm:"column:province_id;type:character(26);index:city_province_id_idx,priority:1" json:"province_id"`
CreatedBy *string `gorm:"column:created_by;type:character(26);index:city_created_by_idx,priority:1" json:"created_by"`
CreatedOn *time.Time `gorm:"column:created_on;type:timestamp without time zone" json:"created_on"`
UpdatedBy *string `gorm:"column:updated_by;type:character(26)" json:"updated_by"`
UpdatedOn *time.Time `gorm:"column:updated_on;type:timestamp without time zone" json:"updated_on"`
MerchantID *string `gorm:"column:merchant_id;type:character(26)" json:"merchant_id"`
PostalCodes_Relation []PostalCode `gorm:"foreignKey:city_id;references:id" json:"postal_codes_relation"`
Subdistricts_Relation []Subdistrict `gorm:"foreignKey:city_id;references:id" json:"subdistricts_relation"`
Districts_Relation []District `gorm:"foreignKey:city_id;references:id" json:"districts_relation"`
Province_Relation Province `gorm:"foreignKey:province_id;references:id" json:"province_relation"`
Merchants_Relation []Merchant `gorm:"foreignKey:city_id;references:id" json:"merchants_relation"`
CreatedBy_Relation User `gorm:"foreignKey:created_by;references:id" json:"created_by_relation"`
Merchant_Relation Merchant `gorm:"foreignKey:merchant_id;references:id" json:"merchant_relation"`
}
// TableName City's table name
func (*City) TableName() string {
return TableNameCity
}

View File

@ -0,0 +1,29 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package domain
import (
"time"
)
const TableNameCostComponent = "cost_component"
// CostComponent mapped from table <cost_component>
type CostComponent struct {
ID string `gorm:"column:id;type:character(26);primaryKey;default:generate_ulid()" json:"id"`
Name *string `gorm:"column:name;type:character varying(255);index:cost_component_name_idx,priority:1" json:"name"`
MerchantID *string `gorm:"column:merchant_id;type:character(26);index:cost_component_merchant_id_idx,priority:1" json:"merchant_id"`
CreatedBy *string `gorm:"column:created_by;type:character(26);index:cost_component_created_by_idx,priority:1" json:"created_by"`
CreatedOn *time.Time `gorm:"column:created_on;type:timestamp without time zone;default:now()" json:"created_on"`
UpdatedBy *string `gorm:"column:updated_by;type:character(26)" json:"updated_by"`
UpdatedOn *time.Time `gorm:"column:updated_on;type:timestamp without time zone;default:now()" json:"updated_on"`
Inc *string `gorm:"column:inc;type:character varying" json:"inc"`
Code *string `gorm:"column:code;type:character varying(100)" json:"code"`
}
// TableName CostComponent's table name
func (*CostComponent) TableName() string {
return TableNameCostComponent
}

View File

@ -0,0 +1,36 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package domain
import (
"time"
)
const TableNameDistrict = "district"
// District mapped from table <district>
type District struct {
ID string `gorm:"column:id;type:character(26);primaryKey;default:generate_ulid()" json:"id"`
Name *string `gorm:"column:name;type:character varying(255);index:district_name_idx,priority:1" json:"name"`
ProvinceID *string `gorm:"column:province_id;type:character(26);index:district_province_id_idx,priority:1" json:"province_id"`
CityID *string `gorm:"column:city_id;type:character(26);index:district_city_id_idx,priority:1" json:"city_id"`
CreatedBy *string `gorm:"column:created_by;type:character(26);index:district_created_by_idx,priority:1" json:"created_by"`
CreatedOn *time.Time `gorm:"column:created_on;type:timestamp without time zone" json:"created_on"`
UpdatedBy *string `gorm:"column:updated_by;type:character(26)" json:"updated_by"`
UpdatedOn *time.Time `gorm:"column:updated_on;type:timestamp without time zone" json:"updated_on"`
MerchantID *string `gorm:"column:merchant_id;type:character(26)" json:"merchant_id"`
PostalCodes_Relation []PostalCode `gorm:"foreignKey:district_id;references:id" json:"postal_codes_relation"`
Subdistricts_Relation []Subdistrict `gorm:"foreignKey:district_id;references:id" json:"subdistricts_relation"`
Province_Relation Province `gorm:"foreignKey:province_id;references:id" json:"province_relation"`
City_Relation City `gorm:"foreignKey:city_id;references:id" json:"city_relation"`
Merchants_Relation []Merchant `gorm:"foreignKey:district_id;references:id" json:"merchants_relation"`
CreatedBy_Relation User `gorm:"foreignKey:created_by;references:id" json:"created_by_relation"`
Merchant_Relation Merchant `gorm:"foreignKey:merchant_id;references:id" json:"merchant_relation"`
}
// TableName District's table name
func (*District) TableName() string {
return TableNameDistrict
}

View File

@ -0,0 +1,50 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package domain
import (
"time"
)
const TableNameMerchant = "merchant"
// Merchant mapped from table <merchant>
type Merchant struct {
ID string `gorm:"column:id;type:character(26);primaryKey;default:generate_ulid()" json:"id"`
Name *string `gorm:"column:name;type:character varying(255);index:merchant_name_idx,priority:3" json:"name"`
Address *string `gorm:"column:address;type:character varying(500);index:merchant_name_idx,priority:1" json:"address"`
Email *string `gorm:"column:email;type:character varying(200);index:merchant_name_idx,priority:2" json:"email"`
Telp *string `gorm:"column:telp;type:character varying(50);index:merchant_name_idx,priority:4" json:"telp"`
AdditionalInformation *string `gorm:"column:additional_information;type:jsonb;index:merchant_additional_information_idx,priority:1;default:{}" json:"additional_information"`
Active *bool `gorm:"column:active;type:boolean;default:true" json:"active"`
CreatedBy *string `gorm:"column:created_by;type:character(26)" json:"created_by"`
CreatedOn *time.Time `gorm:"column:created_on;type:timestamp without time zone;default:now()" json:"created_on"`
UpdatedBy *string `gorm:"column:updated_by;type:character(26)" json:"updated_by"`
UpdatedOn *time.Time `gorm:"column:updated_on;type:timestamp without time zone;default:now()" json:"updated_on"`
ProvinceID *string `gorm:"column:province_id;type:character(26);index:merchant_province_id_idx,priority:1" json:"province_id"`
CityID *string `gorm:"column:city_id;type:character(26);index:merchant_city_id_idx,priority:1" json:"city_id"`
DistrictID *string `gorm:"column:district_id;type:character(26);index:merchant_district_id_idx,priority:1" json:"district_id"`
SubdistrictID *string `gorm:"column:subdistrict_id;type:character(26);index:merchant_subdistrict_id_idx,priority:1" json:"subdistrict_id"`
PostalCode *string `gorm:"column:postal_code;type:character(26);index:merchant_postal_code_idx,priority:1" json:"postal_code"`
Province_Relation *Province `gorm:"foreignKey:province_id;references:id" json:"province_relation"`
City_Relation *City `gorm:"foreignKey:city_id;references:id" json:"city_relation"`
District_Relation *District `gorm:"foreignKey:district_id;references:id" json:"district_relation"`
Subdistrict_Relation *Subdistrict `gorm:"foreignKey:subdistrict_id;references:id" json:"subdistrict_relation"`
UsersRoless_Relation []*UsersRole `gorm:"foreignKey:merchant_id;references:id" json:"users_roless_relation"`
Modas_Relation []*Moda `gorm:"foreignKey:merchant_id;references:id" json:"modas_relation"`
PackageTypes_Relation []*PackageType `gorm:"foreignKey:merchant_id;references:id" json:"package_types_relation"`
Userss_Relation []*User `gorm:"foreignKey:merchant_id;references:id" json:"userss_relation"`
CreatedBy_Relation *User `gorm:"foreignKey:created_by;references:id" json:"created_by_relation"`
Provinces_Relation []*Province `gorm:"foreignKey:merchant_id;references:id" json:"provinces_relation"`
Citys_Relation []*City `gorm:"foreignKey:merchant_id;references:id" json:"citys_relation"`
Districts_Relation []*District `gorm:"foreignKey:merchant_id;references:id" json:"districts_relation"`
Subdistricts_Relation []*Subdistrict `gorm:"foreignKey:merchant_id;references:id" json:"subdistricts_relation"`
PostalCodes_Relation []*PostalCode `gorm:"foreignKey:merchant_id;references:id" json:"postal_codes_relation"`
}
// TableName Merchant's table name
func (*Merchant) TableName() string {
return TableNameMerchant
}

View File

@ -0,0 +1,28 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package domain
import (
"time"
)
const TableNameModa = "moda"
// Moda mapped from table <moda>
type Moda struct {
ID string `gorm:"column:id;type:character(26);primaryKey;default:generate_ulid()" json:"id"`
Name *string `gorm:"column:name;type:character varying(255);index:moda_name_idx,priority:1" json:"name"`
MerchantID *string `gorm:"column:merchant_id;type:character(26);index:moda_merchant_id_idx,priority:1" json:"merchant_id"`
CreatedBy *string `gorm:"column:created_by;type:character(26);index:moda_created_by_idx,priority:1" json:"created_by"`
CreatedOn *time.Time `gorm:"column:created_on;type:timestamp without time zone;default:now()" json:"created_on"`
UpdatedBy *string `gorm:"column:updated_by;type:character(26)" json:"updated_by"`
UpdatedOn *time.Time `gorm:"column:updated_on;type:timestamp without time zone;default:now()" json:"updated_on"`
Merchant_Relation Merchant `gorm:"foreignKey:merchant_id;references:id" json:"merchant_relation"`
}
// TableName Moda's table name
func (*Moda) TableName() string {
return TableNameModa
}

View File

@ -0,0 +1,28 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package domain
import (
"time"
)
const TableNamePackageType = "package_type"
// PackageType mapped from table <package_type>
type PackageType struct {
ID string `gorm:"column:id;type:character(26);primaryKey;default:generate_ulid()" json:"id"`
Name *string `gorm:"column:name;type:character varying(255);index:package_type_name_idx,priority:1" json:"name"`
MerchantID *string `gorm:"column:merchant_id;type:character(26);index:package_type_merchant_id_idx,priority:1" json:"merchant_id"`
CreatedBy *string `gorm:"column:created_by;type:character(26)" json:"created_by"`
CreatedOn *time.Time `gorm:"column:created_on;type:timestamp without time zone;default:now()" json:"created_on"`
UpdatedBy *string `gorm:"column:updated_by;type:character(26)" json:"updated_by"`
UpdatedOn *time.Time `gorm:"column:updated_on;type:timestamp without time zone" json:"updated_on"`
Merchant_Relation Merchant `gorm:"foreignKey:merchant_id;references:id" json:"merchant_relation"`
}
// TableName PackageType's table name
func (*PackageType) TableName() string {
return TableNamePackageType
}

View File

@ -0,0 +1,37 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package domain
import (
"time"
)
const TableNamePostalCode = "postal_code"
// PostalCode mapped from table <postal_code>
type PostalCode struct {
ID string `gorm:"column:id;type:character(26);primaryKey;default:generate_ulid()" json:"id"`
PostalCode *string `gorm:"column:postal_code;type:character varying(255);index:postal_code_postal_code_idx,priority:1" json:"postal_code"`
ProvinceID *string `gorm:"column:province_id;type:character(26);index:postal_code_province_id_idx,priority:1" json:"province_id"`
CityID *string `gorm:"column:city_id;type:character(26);index:postal_code_city_id_idx,priority:1" json:"city_id"`
DistrictID *string `gorm:"column:district_id;type:character(26);index:postal_code_district_id_idx,priority:1" json:"district_id"`
SubdistrictID *string `gorm:"column:subdistrict_id;type:character(26);index:postal_code_subdistrict_id_idx,priority:1" json:"subdistrict_id"`
CreatedBy *string `gorm:"column:created_by;type:character(26);index:postal_code_created_by_idx,priority:1" json:"created_by"`
CreatedOn *time.Time `gorm:"column:created_on;type:timestamp without time zone" json:"created_on"`
UpdatedBy *string `gorm:"column:updated_by;type:character(26)" json:"updated_by"`
UpdatedOn *string `gorm:"column:updated_on;type:character varying" json:"updated_on"`
MerchantID *string `gorm:"column:merchant_id;type:character(26)" json:"merchant_id"`
Province_Relation Province `gorm:"foreignKey:province_id;references:id" json:"province_relation"`
City_Relation City `gorm:"foreignKey:city_id;references:id" json:"city_relation"`
District_Relation District `gorm:"foreignKey:district_id;references:id" json:"district_relation"`
Subdistrict_Relation Subdistrict `gorm:"foreignKey:subdistrict_id;references:id" json:"subdistrict_relation"`
CreatedBy_Relation User `gorm:"foreignKey:created_by;references:id" json:"created_by_relation"`
Merchant_Relation Merchant `gorm:"foreignKey:merchant_id;references:id" json:"merchant_relation"`
}
// TableName PostalCode's table name
func (*PostalCode) TableName() string {
return TableNamePostalCode
}

View File

@ -0,0 +1,34 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package domain
import (
"time"
)
const TableNameProvince = "province"
// Province mapped from table <province>
type Province struct {
ID string `gorm:"column:id;type:character(26);primaryKey;default:generate_ulid()" json:"id"`
Name *string `gorm:"column:name;type:character varying(255);index:province_name_idx,priority:1" json:"name"`
CreatedBy *string `gorm:"column:created_by;type:character(26);index:province_created_by_idx,priority:1" json:"created_by"`
CreatedOn *time.Time `gorm:"column:created_on;type:timestamp without time zone" json:"created_on"`
UpdatedBy *string `gorm:"column:updated_by;type:character(26)" json:"updated_by"`
UpdatedOn *time.Time `gorm:"column:updated_on;type:timestamp without time zone" json:"updated_on"`
MerchantID *string `gorm:"column:merchant_id;type:character(26)" json:"merchant_id"`
PostalCodes_Relation []PostalCode `gorm:"foreignKey:province_id;references:id" json:"postal_codes_relation"`
Subdistricts_Relation []Subdistrict `gorm:"foreignKey:province_id;references:id" json:"subdistricts_relation"`
Districts_Relation []District `gorm:"foreignKey:province_id;references:id" json:"districts_relation"`
Citys_Relation []City `gorm:"foreignKey:province_id;references:id" json:"citys_relation"`
Merchants_Relation []Merchant `gorm:"foreignKey:province_id;references:id" json:"merchants_relation"`
CreatedBy_Relation User `gorm:"foreignKey:created_by;references:id" json:"created_by_relation"`
Merchant_Relation Merchant `gorm:"foreignKey:merchant_id;references:id" json:"merchant_relation"`
}
// TableName Province's table name
func (*Province) TableName() string {
return TableNameProvince
}

View File

@ -0,0 +1,37 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package domain
import (
"time"
)
const TableNameSubdistrict = "subdistrict"
// Subdistrict mapped from table <subdistrict>
type Subdistrict struct {
ID string `gorm:"column:id;type:character(26);primaryKey;default:generate_ulid()" json:"id"`
Name *string `gorm:"column:name;type:character varying(255);index:subdistrit_name_idx,priority:1" json:"name"`
ProvinceID *string `gorm:"column:province_id;type:character(26);index:subdistrit_province_id_idx,priority:1" json:"province_id"`
CityID *string `gorm:"column:city_id;type:character(26);index:subdistrit_city_id_idx,priority:1" json:"city_id"`
DistrictID *string `gorm:"column:district_id;type:character(26);index:subdistrit_district_id_idx,priority:1" json:"district_id"`
CreatedBy *string `gorm:"column:created_by;type:character(26);index:subdistrit_created_by_idx,priority:1" json:"created_by"`
CreatedOn *time.Time `gorm:"column:created_on;type:timestamp without time zone" json:"created_on"`
UpdatedBy *string `gorm:"column:updated_by;type:character(26)" json:"updated_by"`
UpdatedOn *time.Time `gorm:"column:updated_on;type:timestamp without time zone" json:"updated_on"`
MerchantID *string `gorm:"column:merchant_id;type:character(26)" json:"merchant_id"`
PostalCodes_Relation []PostalCode `gorm:"foreignKey:subdistrict_id;references:id" json:"postal_codes_relation"`
Province_Relation Province `gorm:"foreignKey:province_id;references:id" json:"province_relation"`
City_Relation City `gorm:"foreignKey:city_id;references:id" json:"city_relation"`
District_Relation District `gorm:"foreignKey:district_id;references:id" json:"district_relation"`
Merchants_Relation []Merchant `gorm:"foreignKey:subdistrict_id;references:id" json:"merchants_relation"`
CreatedBy_Relation User `gorm:"foreignKey:created_by;references:id" json:"created_by_relation"`
Merchant_Relation Merchant `gorm:"foreignKey:merchant_id;references:id" json:"merchant_relation"`
}
// TableName Subdistrict's table name
func (*Subdistrict) TableName() string {
return TableNameSubdistrict
}

View File

@ -0,0 +1,41 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package domain
import (
"time"
)
const TableNameUser = "users"
// User mapped from table <users>
type User struct {
ID string `gorm:"column:id;type:character(26);primaryKey;default:generate_ulid()" json:"id"`
MerchantID *string `gorm:"column:merchant_id;type:character(26);index:users_merchant_id_idx,priority:1" json:"merchant_id"`
NickName *string `gorm:"column:nick_name;type:character varying(100);index:users_nick_name_idx,priority:2" json:"nick_name"`
Email *string `gorm:"column:email;type:character varying(200);index:users_nick_name_idx,priority:1" json:"email"`
Password *string `gorm:"column:password;type:character varying(300)" json:"password"`
IsActive *bool `gorm:"column:is_active;type:boolean;default:true" json:"is_active"`
UsersRoleID *string `gorm:"column:users_role_id;type:character(26)" json:"users_role_id"`
Blocked *bool `gorm:"column:blocked;type:boolean" json:"blocked"`
BlockedNotes *string `gorm:"column:blocked_notes;type:character varying(500)" json:"blocked_notes"`
CreatedBy *string `gorm:"column:created_by;type:character(26);index:users_created_by_idx,priority:1" json:"created_by"`
CreatedOn *time.Time `gorm:"column:created_on;type:timestamp without time zone;default:now()" json:"created_on"`
UpdatedBy *string `gorm:"column:updated_by;type:character(26)" json:"updated_by"`
UpdatedOn *time.Time `gorm:"column:updated_on;type:timestamp without time zone;default:now()" json:"updated_on"`
Merchant_Relation *Merchant `gorm:"foreignKey:merchant_id;references:id" json:"merchant_relation"`
UsersRole_Relation UsersRole `gorm:"foreignKey:users_role_id;references:id" json:"users_role_relation"`
Merchants_Relation []Merchant `gorm:"foreignKey:created_by;references:id" json:"merchants_relation"`
Provinces_Relation []Province `gorm:"foreignKey:created_by;references:id" json:"provinces_relation"`
Citys_Relation []City `gorm:"foreignKey:created_by;references:id" json:"citys_relation"`
Districts_Relation []District `gorm:"foreignKey:created_by;references:id" json:"districts_relation"`
Subdistricts_Relation []Subdistrict `gorm:"foreignKey:created_by;references:id" json:"subdistricts_relation"`
PostalCodes_Relation []PostalCode `gorm:"foreignKey:created_by;references:id" json:"postal_codes_relation"`
}
// TableName User's table name
func (*User) TableName() string {
return TableNameUser
}

View File

@ -0,0 +1,29 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package domain
import (
"time"
)
const TableNameUsers = "users_"
// Users mapped from table <users_>
type Users struct {
ID string `gorm:"column:id;type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
MerchantID *string `gorm:"column:merchant_id;type:uuid;index:idx_users_merchant,priority:1" json:"merchant_id"`
Username string `gorm:"column:username;type:character varying(50);not null;index:idx_users_username,priority:1" json:"username"`
Email string `gorm:"column:email;type:character varying(100);not null" json:"email"`
Password string `gorm:"column:password;type:character varying(200);not null" json:"password"`
Role string `gorm:"column:role;type:character varying(20);not null;default:cashier" json:"role"`
IsActive *bool `gorm:"column:is_active;type:boolean;default:true" json:"is_active"`
CreatedAt *time.Time `gorm:"column:created_at;type:timestamp without time zone;default:now()" json:"created_at"`
UpdatedAt *time.Time `gorm:"column:updated_at;type:timestamp without time zone;default:now()" json:"updated_at"`
}
// TableName Users's table name
func (*Users) TableName() string {
return TableNameUsers
}

View File

@ -0,0 +1,30 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package domain
import (
"time"
)
const TableNameUsersRole = "users_roles"
// UsersRole mapped from table <users_roles>
type UsersRole struct {
ID string `gorm:"column:id;type:character(26);primaryKey;default:generate_ulid()" json:"id"`
Name *string `gorm:"column:name;type:character varying(255)" json:"name"`
MerchantID *string `gorm:"column:merchant_id;type:character(26)" json:"merchant_id"`
CreatedBy *string `gorm:"column:created_by;type:character(26)" json:"created_by"`
CreatedOn *time.Time `gorm:"column:created_on;type:timestamp without time zone;default:now()" json:"created_on"`
UpdatedBy *string `gorm:"column:updated_by;type:character(26)" json:"updated_by"`
UpdatedOn *time.Time `gorm:"column:updated_on;type:timestamp without time zone;default:now()" json:"updated_on"`
RoleKey string `gorm:"column:role_key;type:character varying(20);not null" json:"role_key"`
Merchant_Relation Merchant `gorm:"foreignKey:merchant_id;references:id" json:"merchant_relation"`
Userss_Relation []User `gorm:"foreignKey:users_role_id;references:id" json:"userss_relation"`
}
// TableName UsersRole's table name
func (*UsersRole) TableName() string {
return TableNameUsersRole
}

35
cmd/api/main.go Normal file
View File

@ -0,0 +1,35 @@
package main
import (
"cargo-erp-backend/internal/config"
"cargo-erp-backend/internal/database"
"cargo-erp-backend/internal/handlers"
"cargo-erp-backend/internal/logger"
"cargo-erp-backend/pkg/helpers"
)
func main() {
//load config
AppConfig := config.NewConfig()
AppConfig.Load()
//logger load
LoggerApp := logger.NewLogger(AppConfig)
LoggerApp.Init()
DBLogger := LoggerApp.Get("DB")
APPLogger := LoggerApp.Get("APP")
//db connection
db := database.NewDatabase(AppConfig, DBLogger, APPLogger)
db.Connect()
db.RegisterCallback()
//redis
redisDB := database.NewRedis(AppConfig, DBLogger)
redisDB.Connect()
redisDB.RegisterCallback()
// migrations.GenerateModel(nil, db.Get("master"))
Helper := helpers.NewHelper(AppConfig, db, APPLogger, redisDB)
app := handlers.NewHandlers(Helper)
app.Run()
}

72
go.mod Normal file
View File

@ -0,0 +1,72 @@
module cargo-erp-backend
go 1.24
require (
github.com/fsnotify/fsnotify v1.9.0
github.com/gin-gonic/gin v1.9.1
github.com/golang-jwt/jwt/v4 v4.5.2
github.com/redis/go-redis/v9 v9.22.0
github.com/spf13/viper v1.21.0
go.uber.org/zap v1.28.0
golang.org/x/crypto v0.40.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gorm.io/driver/postgres v1.5.9
gorm.io/gen v0.3.28
gorm.io/gorm v1.25.12
)
require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/bytedance/sonic v1.9.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.14.0 // indirect
github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect
github.com/jackc/pgx/v5 v5.5.5 // indirect
github.com/jackc/puddle/v2 v2.2.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/rogpeppe/go-internal v1.10.0 // indirect
github.com/sagikazarmark/locafero v0.11.0 // indirect
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
github.com/spf13/afero v1.15.0 // indirect
github.com/spf13/cast v1.10.0 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.10.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect
golang.org/x/mod v0.26.0 // indirect
golang.org/x/net v0.42.0 // indirect
golang.org/x/sync v0.16.0 // indirect
golang.org/x/sys v0.34.0 // indirect
golang.org/x/text v0.28.0 // indirect
golang.org/x/tools v0.35.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/datatypes v1.2.4 // indirect
gorm.io/driver/mysql v1.5.7 // indirect
gorm.io/hints v1.1.0 // indirect
gorm.io/plugin/dbresolver v1.5.3 // indirect
)

222
go.sum Normal file
View File

@ -0,0 +1,222 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA=
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw=
github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE=
github.com/microsoft/go-mssqldb v0.17.0/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOaO4S0d2sB5aeGQ=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/redis/go-redis/v9 v9.22.0 h1:laDvpYXTJtZLloinw1fA5Kqd6HAEH2XKxOkG/PDq2F0=
github.com/redis/go-redis/v9 v9.22.0/go.mod h1:y2g0Wj8rQvuK0ELM+oxSudcLtC09JScs98I/X9gRWY4=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs=
github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo=
go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o=
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/datatypes v1.2.4 h1:uZmGAcK/QZ0uyfCuVg0VQY1ZmV9h1fuG0tMwKByO1z4=
gorm.io/datatypes v1.2.4/go.mod h1:f4BsLcFAX67szSv8svwLRjklArSHAvHLeE3pXAS5DZI=
gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo=
gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
gorm.io/driver/postgres v1.5.9 h1:DkegyItji119OlcaLjqN11kHoUgZ/j13E0jkJZgD6A8=
gorm.io/driver/postgres v1.5.9/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI=
gorm.io/driver/sqlite v1.1.6/go.mod h1:W8LmC/6UvVbHKah0+QOC7Ja66EaZXHwUTjgXY8YNWX8=
gorm.io/driver/sqlite v1.4.3 h1:HBBcZSDnWi5BW3B3rwvVTc510KGkBkexlOg0QrmLUuU=
gorm.io/driver/sqlite v1.4.3/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0=
gorm.io/driver/sqlserver v1.4.1/go.mod h1:DJ4P+MeZbc5rvY58PnmN1Lnyvb5gw5NPzGshHDnJLig=
gorm.io/gen v0.3.28 h1:wnTvsd0a22Qsa06Am8oEjKy8KIB3+kowZx4MzijZ6wc=
gorm.io/gen v0.3.28/go.mod h1:bJdK2/7BJaRdLBY7Vf8+FxWLC5WWYRfG+ZY3NQT/AbU=
gorm.io/gorm v1.21.15/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
gorm.io/gorm v1.22.2/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
gorm.io/hints v1.1.0 h1:Lp4z3rxREufSdxn4qmkK3TLDltrM10FLTHiuqwDPvXw=
gorm.io/hints v1.1.0/go.mod h1:lKQ0JjySsPBj3uslFzY3JhYDtqEwzm+G1hv8rWujB6Y=
gorm.io/plugin/dbresolver v1.5.3 h1:wFwINGZZmttuu9h7XpvbDHd8Lf9bb8GNzp/NpAMV2wU=
gorm.io/plugin/dbresolver v1.5.3/go.mod h1:TSrVhaUg2DZAWP3PrHlDlITEJmNOkL0tFTjvTEsQ4XE=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

67
internal/config/config.go Normal file
View File

@ -0,0 +1,67 @@
package config
import (
"fmt"
"log"
"path"
"path/filepath"
"sync"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
)
type ConfigInterface interface {
Load()
RegisterOnChangeCallback(f func())
Get(key string) interface{}
}
type Config struct {
cbLock sync.Mutex
Callbacks []func()
Env string
}
func NewConfig() ConfigInterface {
return &Config{}
}
func (c *Config) Load() {
pathConfig := "./../config.json"
realPath, err := filepath.Abs(pathConfig)
if err != nil {
log.Fatal(err.Error())
}
viper.SetConfigName("config")
viper.SetConfigType("json")
viper.AddConfigPath(path.Dir(realPath))
if err := viper.ReadInConfig(); err != nil {
log.Fatalf("Failed To Read Configuration File: %v", err)
}
c.SetEnv()
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
c.SetEnv()
log.Printf("Configuration File Changed : %s", e.Name)
c.cbLock.Lock()
for _, callback := range c.Callbacks {
if callback != nil {
callback()
}
}
c.cbLock.Unlock()
})
}
func (c *Config) RegisterOnChangeCallback(f func()) {
c.cbLock.Lock()
defer c.cbLock.Unlock()
c.Callbacks = append(c.Callbacks, f)
}
func (c *Config) SetEnv() {
envString := viper.GetString("env")
log.Println(envString)
c.Env = envString
}
func (c *Config) Get(key string) interface{} {
envString := fmt.Sprintf("%v.%v", "config", c.Env)
return viper.Get(fmt.Sprintf("%v.%v", envString, key))
}

View File

@ -0,0 +1,115 @@
package database
import (
"cargo-erp-backend/internal/config"
"cargo-erp-backend/internal/logger"
"fmt"
"net/url"
"sync"
"time"
"go.uber.org/zap"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
type DatabaseInterface interface {
Connect()
Close()
Get(key string) *gorm.DB
RegisterCallback()
}
type Database struct {
Config config.ConfigInterface
DB map[string]*gorm.DB
dbLock sync.RWMutex
Logger *zap.Logger
AppLogger *zap.Logger
}
func NewDatabase(config config.ConfigInterface, logger *zap.Logger, app_logger *zap.Logger) DatabaseInterface {
return &Database{
Config: config,
Logger: logger,
AppLogger: app_logger,
}
}
func (d *Database) Connect() {
db := make(map[string]*gorm.DB)
username := d.Config.Get("database.username").(string)
password := d.Config.Get("database.password").(string)
dbname := d.Config.Get("database.database").(string)
host_master := fmt.Sprintf("%v", d.Config.Get("database.master.host"))
port_master := fmt.Sprintf("%v", d.Config.Get("database.master.port"))
ssl_master := d.Config.Get("database.master.ssl")
dsnMaster := fmt.Sprintf("postgres://%s:%s@%s:%s/%s?sslmode=%s",
url.PathEscape(username),
url.PathEscape(password),
host_master,
port_master,
dbname,
ssl_master,
)
d.AppLogger.Info("Connect To Master DB")
gormLogger := logger.NewGormZapLogger(d.Logger, 5*time.Second)
dbMaster, err := gorm.Open(postgres.Open(dsnMaster), &gorm.Config{
Logger: gormLogger,
})
if err != nil {
d.AppLogger.Fatal(err.Error())
return
}
db["master"] = dbMaster
d.AppLogger.Info("DB Master Connected")
host_slave := fmt.Sprintf("%v", d.Config.Get("database.slave.host"))
port_slave := fmt.Sprintf("%v", d.Config.Get("database.slave.port"))
ssl_slave := d.Config.Get("database.slave.ssl")
dsnSlave := fmt.Sprintf("postgres://%s:%s@%s:%s/%s?sslmode=%s",
url.PathEscape(username),
url.PathEscape(password),
host_slave,
port_slave,
dbname,
ssl_slave,
)
d.AppLogger.Info("Connect To Slave DB")
dbSlave, err := gorm.Open(postgres.Open(dsnSlave), &gorm.Config{
Logger: gormLogger,
})
if err != nil {
d.AppLogger.Fatal(err.Error())
return
}
db["slave"] = dbSlave
d.DB = db
d.AppLogger.Info("DB Connection Done")
}
func (d *Database) RegisterCallback() {
d.Config.RegisterOnChangeCallback(d.ReloadDB)
}
func (d *Database) Get(key string) *gorm.DB {
if v, ok := d.DB[key]; ok {
return v
}
return nil
}
func (d *Database) Close() {
d.dbLock.Lock()
defer d.dbLock.Unlock()
d.AppLogger.Info("Close Database Connection Start")
for k, db := range d.DB {
if db != nil {
sqlDb, _ := db.DB()
sqlDb.Close()
d.AppLogger.Info(fmt.Sprintf("Close Database Connection %s", k))
}
}
d.AppLogger.Info("Close Database Connection Done")
}
func (d *Database) ReloadDB() {
d.AppLogger.Info("Reload Connecting Database")
d.Close()
d.Connect()
d.AppLogger.Info("Reload Connecting Database Done")
}

View File

@ -0,0 +1,93 @@
package database
import (
"cargo-erp-backend/internal/config"
"context"
"fmt"
"github.com/redis/go-redis/v9"
"go.uber.org/zap"
)
type RedisInterface interface {
Connect()
Get(key string) *redis.Client
RegisterCallback()
}
type Redis struct {
Config config.ConfigInterface
Logger *zap.Logger
DB map[string]*redis.Client
}
func NewRedis(config config.ConfigInterface, logger *zap.Logger) RedisInterface {
return &Redis{
Config: config,
Logger: logger,
}
}
func (r *Redis) Connect() {
redisConfig := r.Config.Get("redis")
dbRedis := make(map[string]*redis.Client)
if v, ok := redisConfig.(map[string]interface{}); ok {
for kNode, vNode := range v {
r.Logger.Info("Parse config " + kNode)
if params, ok := vNode.(map[string]interface{}); ok {
password := ""
host := ""
port := ""
if val, ok := params["password"]; ok {
password = fmt.Sprintf("%v", val)
}
if val, ok := params["host"]; ok {
host = fmt.Sprintf("%v", val)
}
if val, ok := params["port"]; ok {
port = fmt.Sprintf("%v", val)
}
url := fmt.Sprintf("redis://:%v@%v:%v", password, host, port)
r.Logger.Info(fmt.Sprintf("Connecting to Redis %v", kNode))
opts, err := redis.ParseURL(url)
if err != nil {
r.Logger.Error(fmt.Sprintf("unable to parse redis URL %v", err.Error()))
} else {
client := redis.NewClient(opts)
if err := client.Ping(context.Background()).Err(); err != nil {
r.Logger.Error(fmt.Sprintf("unable to connect to redis: %v", err.Error()))
} else {
dbRedis[kNode] = client
}
}
r.Logger.Info("Connected")
} else {
r.Logger.Fatal("Parse paramater redis config error")
}
}
} else {
r.Logger.Fatal("Parse paramater redis config error")
}
r.DB = dbRedis
}
func (r *Redis) Close() {
for k, v := range r.DB {
r.Logger.Info(fmt.Sprintf("Close Connection %v", k))
v.Close()
r.Logger.Info("Close Connection Done")
}
r.DB = make(map[string]*redis.Client)
}
func (r *Redis) RegisterCallback() {
r.Config.RegisterOnChangeCallback(r.Reconnecting)
}
func (r *Redis) Reconnecting() {
r.Close()
r.Connect()
}
func (r *Redis) Get(key string) *redis.Client {
if v, ok := r.DB[key]; ok {
return v
}
return nil
}

35
internal/domain/city.go Normal file
View File

@ -0,0 +1,35 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package domain
import (
"time"
)
const TableNameCity = "city"
// City mapped from table <city>
type City struct {
ID string `gorm:"column:id;type:character(26);primaryKey;default:generate_ulid()" json:"id"`
Name *string `gorm:"column:name;type:character varying(255);index:city_name_idx,priority:1" json:"name"`
ProvinceID *string `gorm:"column:province_id;type:character(26);index:city_province_id_idx,priority:1" json:"province_id"`
CreatedBy *string `gorm:"column:created_by;type:character(26);index:city_created_by_idx,priority:1" json:"created_by"`
CreatedOn *time.Time `gorm:"column:created_on;type:timestamp without time zone" json:"created_on"`
UpdatedBy *string `gorm:"column:updated_by;type:character(26)" json:"updated_by"`
UpdatedOn *time.Time `gorm:"column:updated_on;type:timestamp without time zone" json:"updated_on"`
MerchantID *string `gorm:"column:merchant_id;type:character(26)" json:"merchant_id"`
PostalCodes_Relation []PostalCode `gorm:"foreignKey:city_id;references:id" json:"postal_codes_relation"`
Subdistricts_Relation []Subdistrict `gorm:"foreignKey:city_id;references:id" json:"subdistricts_relation"`
Districts_Relation []District `gorm:"foreignKey:city_id;references:id" json:"districts_relation"`
Province_Relation Province `gorm:"foreignKey:province_id;references:id" json:"province_relation"`
Merchants_Relation []Merchant `gorm:"foreignKey:city_id;references:id" json:"merchants_relation"`
CreatedBy_Relation User `gorm:"foreignKey:created_by;references:id" json:"created_by_relation"`
Merchant_Relation Merchant `gorm:"foreignKey:merchant_id;references:id" json:"merchant_relation"`
}
// TableName City's table name
func (*City) TableName() string {
return TableNameCity
}

View File

@ -0,0 +1,29 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package domain
import (
"time"
)
const TableNameCostComponent = "cost_component"
// CostComponent mapped from table <cost_component>
type CostComponent struct {
ID string `gorm:"column:id;type:character(26);primaryKey;default:generate_ulid()" json:"id"`
Name *string `gorm:"column:name;type:character varying(255);index:cost_component_name_idx,priority:1" json:"name"`
MerchantID *string `gorm:"column:merchant_id;type:character(26);index:cost_component_merchant_id_idx,priority:1" json:"merchant_id"`
CreatedBy *string `gorm:"column:created_by;type:character(26);index:cost_component_created_by_idx,priority:1" json:"created_by"`
CreatedOn *time.Time `gorm:"column:created_on;type:timestamp without time zone;default:now()" json:"created_on"`
UpdatedBy *string `gorm:"column:updated_by;type:character(26)" json:"updated_by"`
UpdatedOn *time.Time `gorm:"column:updated_on;type:timestamp without time zone;default:now()" json:"updated_on"`
Inc *string `gorm:"column:inc;type:character varying" json:"inc"`
Code *string `gorm:"column:code;type:character varying(100)" json:"code"`
}
// TableName CostComponent's table name
func (*CostComponent) TableName() string {
return TableNameCostComponent
}

View File

@ -0,0 +1,36 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package domain
import (
"time"
)
const TableNameDistrict = "district"
// District mapped from table <district>
type District struct {
ID string `gorm:"column:id;type:character(26);primaryKey;default:generate_ulid()" json:"id"`
Name *string `gorm:"column:name;type:character varying(255);index:district_name_idx,priority:1" json:"name"`
ProvinceID *string `gorm:"column:province_id;type:character(26);index:district_province_id_idx,priority:1" json:"province_id"`
CityID *string `gorm:"column:city_id;type:character(26);index:district_city_id_idx,priority:1" json:"city_id"`
CreatedBy *string `gorm:"column:created_by;type:character(26);index:district_created_by_idx,priority:1" json:"created_by"`
CreatedOn *time.Time `gorm:"column:created_on;type:timestamp without time zone" json:"created_on"`
UpdatedBy *string `gorm:"column:updated_by;type:character(26)" json:"updated_by"`
UpdatedOn *time.Time `gorm:"column:updated_on;type:timestamp without time zone" json:"updated_on"`
MerchantID *string `gorm:"column:merchant_id;type:character(26)" json:"merchant_id"`
PostalCodes_Relation []PostalCode `gorm:"foreignKey:district_id;references:id" json:"postal_codes_relation"`
Subdistricts_Relation []Subdistrict `gorm:"foreignKey:district_id;references:id" json:"subdistricts_relation"`
Province_Relation Province `gorm:"foreignKey:province_id;references:id" json:"province_relation"`
City_Relation City `gorm:"foreignKey:city_id;references:id" json:"city_relation"`
Merchants_Relation []Merchant `gorm:"foreignKey:district_id;references:id" json:"merchants_relation"`
CreatedBy_Relation User `gorm:"foreignKey:created_by;references:id" json:"created_by_relation"`
Merchant_Relation Merchant `gorm:"foreignKey:merchant_id;references:id" json:"merchant_relation"`
}
// TableName District's table name
func (*District) TableName() string {
return TableNameDistrict
}

View File

@ -0,0 +1,50 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package domain
import (
"time"
)
const TableNameMerchant = "merchant"
// Merchant mapped from table <merchant>
type Merchant struct {
ID string `gorm:"column:id;type:character(26);primaryKey;default:generate_ulid()" json:"id"`
Name *string `gorm:"column:name;type:character varying(255);index:merchant_name_idx,priority:3" json:"name"`
Address *string `gorm:"column:address;type:character varying(500);index:merchant_name_idx,priority:1" json:"address"`
Email *string `gorm:"column:email;type:character varying(200);index:merchant_name_idx,priority:2" json:"email"`
Telp *string `gorm:"column:telp;type:character varying(50);index:merchant_name_idx,priority:4" json:"telp"`
AdditionalInformation *string `gorm:"column:additional_information;type:jsonb;index:merchant_additional_information_idx,priority:1;default:{}" json:"additional_information"`
Active *bool `gorm:"column:active;type:boolean;default:true" json:"active"`
CreatedBy *string `gorm:"column:created_by;type:character(26)" json:"created_by"`
CreatedOn *time.Time `gorm:"column:created_on;type:timestamp without time zone;default:now()" json:"created_on"`
UpdatedBy *string `gorm:"column:updated_by;type:character(26)" json:"updated_by"`
UpdatedOn *time.Time `gorm:"column:updated_on;type:timestamp without time zone;default:now()" json:"updated_on"`
ProvinceID *string `gorm:"column:province_id;type:character(26);index:merchant_province_id_idx,priority:1" json:"province_id"`
CityID *string `gorm:"column:city_id;type:character(26);index:merchant_city_id_idx,priority:1" json:"city_id"`
DistrictID *string `gorm:"column:district_id;type:character(26);index:merchant_district_id_idx,priority:1" json:"district_id"`
SubdistrictID *string `gorm:"column:subdistrict_id;type:character(26);index:merchant_subdistrict_id_idx,priority:1" json:"subdistrict_id"`
PostalCode *string `gorm:"column:postal_code;type:character(26);index:merchant_postal_code_idx,priority:1" json:"postal_code"`
Province_Relation *Province `gorm:"foreignKey:province_id;references:id" json:"province_relation"`
City_Relation *City `gorm:"foreignKey:city_id;references:id" json:"city_relation"`
District_Relation *District `gorm:"foreignKey:district_id;references:id" json:"district_relation"`
Subdistrict_Relation *Subdistrict `gorm:"foreignKey:subdistrict_id;references:id" json:"subdistrict_relation"`
UsersRoless_Relation []*UsersRole `gorm:"foreignKey:merchant_id;references:id" json:"users_roless_relation"`
Modas_Relation []*Moda `gorm:"foreignKey:merchant_id;references:id" json:"modas_relation"`
PackageTypes_Relation []*PackageType `gorm:"foreignKey:merchant_id;references:id" json:"package_types_relation"`
Userss_Relation []*User `gorm:"foreignKey:merchant_id;references:id" json:"userss_relation"`
CreatedBy_Relation *User `gorm:"foreignKey:created_by;references:id" json:"created_by_relation"`
Provinces_Relation []*Province `gorm:"foreignKey:merchant_id;references:id" json:"provinces_relation"`
Citys_Relation []*City `gorm:"foreignKey:merchant_id;references:id" json:"citys_relation"`
Districts_Relation []*District `gorm:"foreignKey:merchant_id;references:id" json:"districts_relation"`
Subdistricts_Relation []*Subdistrict `gorm:"foreignKey:merchant_id;references:id" json:"subdistricts_relation"`
PostalCodes_Relation []*PostalCode `gorm:"foreignKey:merchant_id;references:id" json:"postal_codes_relation"`
}
// TableName Merchant's table name
func (*Merchant) TableName() string {
return TableNameMerchant
}

28
internal/domain/moda.go Normal file
View File

@ -0,0 +1,28 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package domain
import (
"time"
)
const TableNameModa = "moda"
// Moda mapped from table <moda>
type Moda struct {
ID string `gorm:"column:id;type:character(26);primaryKey;default:generate_ulid()" json:"id"`
Name *string `gorm:"column:name;type:character varying(255);index:moda_name_idx,priority:1" json:"name"`
MerchantID *string `gorm:"column:merchant_id;type:character(26);index:moda_merchant_id_idx,priority:1" json:"merchant_id"`
CreatedBy *string `gorm:"column:created_by;type:character(26);index:moda_created_by_idx,priority:1" json:"created_by"`
CreatedOn *time.Time `gorm:"column:created_on;type:timestamp without time zone;default:now()" json:"created_on"`
UpdatedBy *string `gorm:"column:updated_by;type:character(26)" json:"updated_by"`
UpdatedOn *time.Time `gorm:"column:updated_on;type:timestamp without time zone;default:now()" json:"updated_on"`
Merchant_Relation Merchant `gorm:"foreignKey:merchant_id;references:id" json:"merchant_relation"`
}
// TableName Moda's table name
func (*Moda) TableName() string {
return TableNameModa
}

View File

@ -0,0 +1,28 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package domain
import (
"time"
)
const TableNamePackageType = "package_type"
// PackageType mapped from table <package_type>
type PackageType struct {
ID string `gorm:"column:id;type:character(26);primaryKey;default:generate_ulid()" json:"id"`
Name *string `gorm:"column:name;type:character varying(255);index:package_type_name_idx,priority:1" json:"name"`
MerchantID *string `gorm:"column:merchant_id;type:character(26);index:package_type_merchant_id_idx,priority:1" json:"merchant_id"`
CreatedBy *string `gorm:"column:created_by;type:character(26)" json:"created_by"`
CreatedOn *time.Time `gorm:"column:created_on;type:timestamp without time zone;default:now()" json:"created_on"`
UpdatedBy *string `gorm:"column:updated_by;type:character(26)" json:"updated_by"`
UpdatedOn *time.Time `gorm:"column:updated_on;type:timestamp without time zone" json:"updated_on"`
Merchant_Relation Merchant `gorm:"foreignKey:merchant_id;references:id" json:"merchant_relation"`
}
// TableName PackageType's table name
func (*PackageType) TableName() string {
return TableNamePackageType
}

View File

@ -0,0 +1,37 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package domain
import (
"time"
)
const TableNamePostalCode = "postal_code"
// PostalCode mapped from table <postal_code>
type PostalCode struct {
ID string `gorm:"column:id;type:character(26);primaryKey;default:generate_ulid()" json:"id"`
PostalCode *string `gorm:"column:postal_code;type:character varying(255);index:postal_code_postal_code_idx,priority:1" json:"postal_code"`
ProvinceID *string `gorm:"column:province_id;type:character(26);index:postal_code_province_id_idx,priority:1" json:"province_id"`
CityID *string `gorm:"column:city_id;type:character(26);index:postal_code_city_id_idx,priority:1" json:"city_id"`
DistrictID *string `gorm:"column:district_id;type:character(26);index:postal_code_district_id_idx,priority:1" json:"district_id"`
SubdistrictID *string `gorm:"column:subdistrict_id;type:character(26);index:postal_code_subdistrict_id_idx,priority:1" json:"subdistrict_id"`
CreatedBy *string `gorm:"column:created_by;type:character(26);index:postal_code_created_by_idx,priority:1" json:"created_by"`
CreatedOn *time.Time `gorm:"column:created_on;type:timestamp without time zone" json:"created_on"`
UpdatedBy *string `gorm:"column:updated_by;type:character(26)" json:"updated_by"`
UpdatedOn *string `gorm:"column:updated_on;type:character varying" json:"updated_on"`
MerchantID *string `gorm:"column:merchant_id;type:character(26)" json:"merchant_id"`
Province_Relation Province `gorm:"foreignKey:province_id;references:id" json:"province_relation"`
City_Relation City `gorm:"foreignKey:city_id;references:id" json:"city_relation"`
District_Relation District `gorm:"foreignKey:district_id;references:id" json:"district_relation"`
Subdistrict_Relation Subdistrict `gorm:"foreignKey:subdistrict_id;references:id" json:"subdistrict_relation"`
CreatedBy_Relation User `gorm:"foreignKey:created_by;references:id" json:"created_by_relation"`
Merchant_Relation Merchant `gorm:"foreignKey:merchant_id;references:id" json:"merchant_relation"`
}
// TableName PostalCode's table name
func (*PostalCode) TableName() string {
return TableNamePostalCode
}

View File

@ -0,0 +1,34 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package domain
import (
"time"
)
const TableNameProvince = "province"
// Province mapped from table <province>
type Province struct {
ID string `gorm:"column:id;type:character(26);primaryKey;default:generate_ulid()" json:"id"`
Name *string `gorm:"column:name;type:character varying(255);index:province_name_idx,priority:1" json:"name"`
CreatedBy *string `gorm:"column:created_by;type:character(26);index:province_created_by_idx,priority:1" json:"created_by"`
CreatedOn *time.Time `gorm:"column:created_on;type:timestamp without time zone" json:"created_on"`
UpdatedBy *string `gorm:"column:updated_by;type:character(26)" json:"updated_by"`
UpdatedOn *time.Time `gorm:"column:updated_on;type:timestamp without time zone" json:"updated_on"`
MerchantID *string `gorm:"column:merchant_id;type:character(26)" json:"merchant_id"`
PostalCodes_Relation []PostalCode `gorm:"foreignKey:province_id;references:id" json:"postal_codes_relation"`
Subdistricts_Relation []Subdistrict `gorm:"foreignKey:province_id;references:id" json:"subdistricts_relation"`
Districts_Relation []District `gorm:"foreignKey:province_id;references:id" json:"districts_relation"`
Citys_Relation []City `gorm:"foreignKey:province_id;references:id" json:"citys_relation"`
Merchants_Relation []Merchant `gorm:"foreignKey:province_id;references:id" json:"merchants_relation"`
CreatedBy_Relation User `gorm:"foreignKey:created_by;references:id" json:"created_by_relation"`
Merchant_Relation Merchant `gorm:"foreignKey:merchant_id;references:id" json:"merchant_relation"`
}
// TableName Province's table name
func (*Province) TableName() string {
return TableNameProvince
}

View File

@ -0,0 +1,37 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package domain
import (
"time"
)
const TableNameSubdistrict = "subdistrict"
// Subdistrict mapped from table <subdistrict>
type Subdistrict struct {
ID string `gorm:"column:id;type:character(26);primaryKey;default:generate_ulid()" json:"id"`
Name *string `gorm:"column:name;type:character varying(255);index:subdistrit_name_idx,priority:1" json:"name"`
ProvinceID *string `gorm:"column:province_id;type:character(26);index:subdistrit_province_id_idx,priority:1" json:"province_id"`
CityID *string `gorm:"column:city_id;type:character(26);index:subdistrit_city_id_idx,priority:1" json:"city_id"`
DistrictID *string `gorm:"column:district_id;type:character(26);index:subdistrit_district_id_idx,priority:1" json:"district_id"`
CreatedBy *string `gorm:"column:created_by;type:character(26);index:subdistrit_created_by_idx,priority:1" json:"created_by"`
CreatedOn *time.Time `gorm:"column:created_on;type:timestamp without time zone" json:"created_on"`
UpdatedBy *string `gorm:"column:updated_by;type:character(26)" json:"updated_by"`
UpdatedOn *time.Time `gorm:"column:updated_on;type:timestamp without time zone" json:"updated_on"`
MerchantID *string `gorm:"column:merchant_id;type:character(26)" json:"merchant_id"`
PostalCodes_Relation []PostalCode `gorm:"foreignKey:subdistrict_id;references:id" json:"postal_codes_relation"`
Province_Relation Province `gorm:"foreignKey:province_id;references:id" json:"province_relation"`
City_Relation City `gorm:"foreignKey:city_id;references:id" json:"city_relation"`
District_Relation District `gorm:"foreignKey:district_id;references:id" json:"district_relation"`
Merchants_Relation []Merchant `gorm:"foreignKey:subdistrict_id;references:id" json:"merchants_relation"`
CreatedBy_Relation User `gorm:"foreignKey:created_by;references:id" json:"created_by_relation"`
Merchant_Relation Merchant `gorm:"foreignKey:merchant_id;references:id" json:"merchant_relation"`
}
// TableName Subdistrict's table name
func (*Subdistrict) TableName() string {
return TableNameSubdistrict
}

41
internal/domain/users.go Normal file
View File

@ -0,0 +1,41 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package domain
import (
"time"
)
const TableNameUser = "users"
// User mapped from table <users>
type User struct {
ID string `gorm:"column:id;type:character(26);primaryKey;default:generate_ulid()" json:"id"`
MerchantID *string `gorm:"column:merchant_id;type:character(26);index:users_merchant_id_idx,priority:1" json:"merchant_id"`
NickName *string `gorm:"column:nick_name;type:character varying(100);index:users_nick_name_idx,priority:2" json:"nick_name"`
Email *string `gorm:"column:email;type:character varying(200);index:users_nick_name_idx,priority:1" json:"email"`
Password *string `gorm:"column:password;type:character varying(300)" json:"password"`
IsActive *bool `gorm:"column:is_active;type:boolean;default:true" json:"is_active"`
UsersRoleID *string `gorm:"column:users_role_id;type:character(26)" json:"users_role_id"`
Blocked *bool `gorm:"column:blocked;type:boolean" json:"blocked"`
BlockedNotes *string `gorm:"column:blocked_notes;type:character varying(500)" json:"blocked_notes"`
CreatedBy *string `gorm:"column:created_by;type:character(26);index:users_created_by_idx,priority:1" json:"created_by"`
CreatedOn *time.Time `gorm:"column:created_on;type:timestamp without time zone;default:now()" json:"created_on"`
UpdatedBy *string `gorm:"column:updated_by;type:character(26)" json:"updated_by"`
UpdatedOn *time.Time `gorm:"column:updated_on;type:timestamp without time zone;default:now()" json:"updated_on"`
Merchant_Relation *Merchant `gorm:"foreignKey:merchant_id;references:id" json:"merchant_relation"`
UsersRole_Relation UsersRole `gorm:"foreignKey:users_role_id;references:id" json:"users_role_relation"`
Merchants_Relation []Merchant `gorm:"foreignKey:created_by;references:id" json:"merchants_relation"`
Provinces_Relation []Province `gorm:"foreignKey:created_by;references:id" json:"provinces_relation"`
Citys_Relation []City `gorm:"foreignKey:created_by;references:id" json:"citys_relation"`
Districts_Relation []District `gorm:"foreignKey:created_by;references:id" json:"districts_relation"`
Subdistricts_Relation []Subdistrict `gorm:"foreignKey:created_by;references:id" json:"subdistricts_relation"`
PostalCodes_Relation []PostalCode `gorm:"foreignKey:created_by;references:id" json:"postal_codes_relation"`
}
// TableName User's table name
func (*User) TableName() string {
return TableNameUser
}

View File

@ -0,0 +1,30 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package domain
import (
"time"
)
const TableNameUsersRole = "users_roles"
// UsersRole mapped from table <users_roles>
type UsersRole struct {
ID string `gorm:"column:id;type:character(26);primaryKey;default:generate_ulid()" json:"id"`
Name *string `gorm:"column:name;type:character varying(255)" json:"name"`
MerchantID *string `gorm:"column:merchant_id;type:character(26)" json:"merchant_id"`
CreatedBy *string `gorm:"column:created_by;type:character(26)" json:"created_by"`
CreatedOn *time.Time `gorm:"column:created_on;type:timestamp without time zone;default:now()" json:"created_on"`
UpdatedBy *string `gorm:"column:updated_by;type:character(26)" json:"updated_by"`
UpdatedOn *time.Time `gorm:"column:updated_on;type:timestamp without time zone;default:now()" json:"updated_on"`
RoleKey string `gorm:"column:role_key;type:character varying(20);not null" json:"role_key"`
Merchant_Relation Merchant `gorm:"foreignKey:merchant_id;references:id" json:"merchant_relation"`
Userss_Relation []User `gorm:"foreignKey:users_role_id;references:id" json:"userss_relation"`
}
// TableName UsersRole's table name
func (*UsersRole) TableName() string {
return TableNameUsersRole
}

View File

@ -0,0 +1,10 @@
package dto
type AuthRequest struct {
Email string `json:"email" form:"email" binding:"required,email"`
Password string `json:"password" form:"password" binding:"required,min=5"`
}
type AuthResponse struct {
Token string `json:"token"`
}

View File

@ -0,0 +1,83 @@
package handlers
import (
"cargo-erp-backend/internal/handlers/router/auth"
"cargo-erp-backend/pkg/helpers"
"fmt"
"net/http"
"time"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type HandlersInfterface interface {
Run()
}
type Handlers struct {
Helper helpers.HelperInterface
}
func NewHandlers(helper helpers.HelperInterface) HandlersInfterface {
return &Handlers{
Helper: helper,
}
}
func (h *Handlers) ZapLoggerMiddleware(logger *zap.Logger) gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
path := c.Request.URL.Path
query := c.Request.URL.RawQuery
// Lanjutkan eksekusi ke handler utama
c.Next()
// Hitung durasi response eksekusi
latency := time.Since(start)
status := c.Writer.Status()
// Siapkan field data log terstruktur
fields := []zap.Field{
zap.Int("status", status),
zap.String("method", c.Request.Method),
zap.String("path", path),
zap.String("query", query),
zap.String("ip", c.ClientIP()),
zap.Duration("latency", latency),
zap.String("user-agent", c.Request.UserAgent()),
}
// Jika ada error internal dari Gin, masukkan ke log
if len(c.Errors) > 0 {
for _, e := range c.Errors.Errors() {
logger.Error(e, fields...)
}
return
}
// Tentukan level log berdasarkan HTTP Status Code
if status >= 500 {
logger.Error("Server Error", fields...)
} else if status >= 400 {
logger.Warn("Client Error", fields...)
} else {
logger.Info("Request Success", fields...)
}
}
}
func (h *Handlers) Run() {
host := fmt.Sprintf("%v", h.Helper.Config().Get("app.host"))
port := fmt.Sprintf("%v", h.Helper.Config().Get("app.port"))
r := gin.Default()
r.Use(h.ZapLoggerMiddleware(h.Helper.Log()))
r.Use(gin.Recovery())
r.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
//auth
auth.NewAuthHandler(h.Helper, r).Router()
h.Helper.Log().Info("Starting Apps")
r.Run(fmt.Sprintf("%v:%v", host, port))
}

View File

@ -0,0 +1,77 @@
package auth
import (
"cargo-erp-backend/internal/handlers/dto"
"cargo-erp-backend/internal/middleware"
"cargo-erp-backend/internal/usecases"
"cargo-erp-backend/pkg/helpers"
"cargo-erp-backend/pkg/response"
"context"
"fmt"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
type AuthHandlerInterface interface {
Router()
}
type AuthHandler struct {
Helper helpers.HelperInterface
Engine *gin.Engine
}
func NewAuthHandler(helper helpers.HelperInterface, e *gin.Engine) AuthHandlerInterface {
return &AuthHandler{
Helper: helper,
Engine: e,
}
}
func (h *AuthHandler) Router() {
h.Engine.POST("login", h.Login)
h.Engine.POST("logout", middleware.NewAuthMiddleware(h.Helper).Check(), h.Logout)
}
func (h *AuthHandler) Login(c *gin.Context) {
u := usecases.NewAuthUsecase(h.Helper)
var parameter dto.AuthRequest
if err := c.BindJSON(&parameter); err != nil {
response.Error(c, http.StatusBadRequest, err.Error())
return
}
token, err := u.Login(parameter.Email, parameter.Password)
if err != nil {
response.Error(c, http.StatusUnauthorized, err.Error())
return
}
cookieName := fmt.Sprintf("%v", h.Helper.Config().Get("cookie.name"))
exp_time := fmt.Sprintf("%v", h.Helper.Config().Get("cookie.expiry_time"))
t, _ := strconv.Atoi(exp_time)
c.SetCookie(
cookieName, // name
token, // value
t, // max age: 30 menit
"/", // path
"", // domain
false, // secure
true, // httpOnly
)
response.Success(c, map[string]interface{}{"token": token})
}
func (h *AuthHandler) Logout(c *gin.Context) {
auth_key := c.MustGet("auth_key")
ctx := context.Background()
h.Helper.GetRedis("master").Del(ctx, fmt.Sprintf("%v", auth_key))
cookieName := fmt.Sprintf("%v", h.Helper.Config().Get("cookie.name"))
c.SetCookie(
cookieName, // name
"", // value
-3600, // max age: 30 menit
"/", // path
"", // domain
false, // secure
true, // httpOnly
)
response.Success(c, map[string]interface{}{"message": "You are logged out"})
}

View File

@ -0,0 +1,92 @@
package logger
import (
"context"
"errors"
"time"
"go.uber.org/zap"
"gorm.io/gorm"
gormlogger "gorm.io/gorm/logger"
"gorm.io/gorm/utils"
)
// GormZapLogger adalah custom logger GORM yang menggunakan Zap
type GormZapLogger struct {
ZapLogger *zap.Logger
LogLevel gormlogger.LogLevel
SlowThreshold time.Duration
}
// NewGormZapLogger menginisialisasi logger baru
func NewGormZapLogger(zapLog *zap.Logger, slowThreshold time.Duration) *GormZapLogger {
return &GormZapLogger{
ZapLogger: zapLog,
LogLevel: gormlogger.Info, // Atur default ke Info, bisa disesuaikan
SlowThreshold: slowThreshold, // Waktu batas query dianggap lambat (misal 200ms)
}
}
// LogMode mengubah level log GORM
func (l *GormZapLogger) LogMode(level gormlogger.LogLevel) gormlogger.Interface {
newLogger := *l
newLogger.LogLevel = level
return &newLogger
}
// Info mencetak log informasi biasa
func (l *GormZapLogger) Info(ctx context.Context, msg string, data ...interface{}) {
if l.LogLevel >= gormlogger.Info {
l.ZapLogger.Sugar().Infof(msg, data...)
}
}
// Warn mencetak log peringatan
func (l *GormZapLogger) Warn(ctx context.Context, msg string, data ...interface{}) {
if l.LogLevel >= gormlogger.Warn {
l.ZapLogger.Sugar().Warnf(msg, data...)
}
}
// Error mencetak log kesalahan
func (l *GormZapLogger) Error(ctx context.Context, msg string, data ...interface{}) {
if l.LogLevel >= gormlogger.Error {
l.ZapLogger.Sugar().Errorf(msg, data...)
}
}
// Trace mencetak log internal query SQL dari GORM
func (l *GormZapLogger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {
if l.LogLevel <= gormlogger.Silent {
return
}
elapsed := time.Since(begin)
sql, rows := fc()
// Ambil lokasi file baris kode Go yang mengeksekusi query ini
line := utils.FileWithLineNum()
// Siapkan fields bawaan Zap agar log terstruktur dengan baik
fields := []zap.Field{
zap.String("line", line),
zap.Duration("elapsed", elapsed),
zap.Int64("rows", rows),
zap.String("sql", sql),
}
switch {
// Kasus 1: Terjadi Error dan Error tersebut BUKAN Record NotFound
case err != nil && l.LogLevel >= gormlogger.Error && !errors.Is(err, gorm.ErrRecordNotFound):
fields = append(fields, zap.Error(err))
l.ZapLogger.Error("[GORM] query error", fields...)
// Kasus 2: Query berjalan lambat melewati batas SlowThreshold
case elapsed > l.SlowThreshold && l.SlowThreshold != 0 && l.LogLevel >= gormlogger.Warn:
l.ZapLogger.Warn("[GORM] slow query warning", fields...)
// Kasus 3: Log info query normal
case l.LogLevel >= gormlogger.Info:
l.ZapLogger.Info("[GORM] query execution", fields...)
}
}

151
internal/logger/logger.go Normal file
View File

@ -0,0 +1,151 @@
package logger
import (
"cargo-erp-backend/internal/config"
"fmt"
"log"
"os"
"path/filepath"
"strconv"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
)
type LoggerInterface interface {
Init()
Get(key string) *zap.Logger
}
type Logger struct {
Config config.ConfigInterface
Logger map[string]*zap.Logger
}
func NewLogger(config config.ConfigInterface) LoggerInterface {
return &Logger{
Config: config,
}
}
func (l *Logger) Init() {
log.Println("initial database logger")
l.Logger = make(map[string]*zap.Logger)
l.Logger["DB"] = nil
l.Logger["APP"] = nil
if v, ok := l.Config.Get("database.logger").(map[string]interface{}); ok {
if val, ok := v["filename"]; ok {
if filename, ok := val.(string); ok {
file_location, err := filepath.Abs(filename)
MaxSize := 10
MaxBackups := 5
MaxAge := 30
Compress := true
if err == nil {
if s, ok := v["MaxSize"]; ok {
size, err := strconv.Atoi(fmt.Sprintf("%v", s))
if err != nil {
MaxSize = size
}
}
if s, ok := v["max_backups"]; ok {
size, err := strconv.Atoi(fmt.Sprintf("%v", s))
if err != nil {
MaxBackups = size
}
}
if s, ok := v["max_age"]; ok {
size, err := strconv.Atoi(fmt.Sprintf("%v", s))
if err != nil {
MaxAge = size
}
}
if s, ok := v["compress"]; ok {
if sv, ok := s.(bool); ok {
Compress = sv
}
}
}
l.Logger["DB"] = l.NewLog(file_location, MaxSize, MaxBackups, MaxAge, Compress)
}
}
}
if v, ok := l.Config.Get("logger").(map[string]interface{}); ok {
if val, ok := v["filename"]; ok {
if filename, ok := val.(string); ok {
file_location, err := filepath.Abs(filename)
MaxSize := 10
MaxBackups := 5
MaxAge := 30
Compress := true
if err == nil {
if s, ok := v["MaxSize"]; ok {
size, err := strconv.Atoi(fmt.Sprintf("%v", s))
if err != nil {
MaxSize = size
}
}
if s, ok := v["max_backups"]; ok {
size, err := strconv.Atoi(fmt.Sprintf("%v", s))
if err != nil {
MaxBackups = size
}
}
if s, ok := v["max_age"]; ok {
size, err := strconv.Atoi(fmt.Sprintf("%v", s))
if err != nil {
MaxAge = size
}
}
if s, ok := v["compress"]; ok {
if sv, ok := s.(bool); ok {
Compress = sv
}
}
}
l.Logger["APP"] = l.NewLog(file_location, MaxSize, MaxBackups, MaxAge, Compress)
if l.Logger["DB"] == nil {
l.Logger["DB"] = l.Logger["APP"]
}
}
}
}
}
func (l *Logger) Get(key string) *zap.Logger {
if v, ok := l.Logger[key]; ok {
return v
}
return nil
}
func (l *Logger) NewLog(filename string, max_size int, max_backup int, max_age int, compress bool) *zap.Logger {
lumberjackLogger := &lumberjack.Logger{
Filename: filename, // Lokasi file
MaxSize: max_size, // Megabytes sebelum rotasi
MaxBackups: max_backup, // Jumlah file cadangan
MaxAge: max_age, // Retention dalam HARI
Compress: compress, // Otomatis kompres ke file .gz
}
encoderConfig := zap.NewProductionEncoderConfig()
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder // Format waktu: 2026-08-13T...
jsonEncoder := zapcore.NewJSONEncoder(encoderConfig)
consoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())
// Hanya meloloskan level DEBUG dan INFO
isInfoOrDebug := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
return lvl == zap.DebugLevel || lvl == zap.InfoLevel
})
// Hanya meloloskan level WARN, ERROR, PANIC, FATAL
isErrorOrHigher := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
return lvl >= zap.WarnLevel
})
core := zapcore.NewTee(
zapcore.NewCore(jsonEncoder, zapcore.AddSync(lumberjackLogger), isInfoOrDebug),
// Masuk ke file error (Ganti os.Stderr dengan errorFileLogger Anda)
zapcore.NewCore(jsonEncoder, zapcore.AddSync(lumberjackLogger), isErrorOrHigher),
// CONSOLE LOGS (Terminal): Biasanya ingin melihat semua level dari DEBUG sampai FATAL
zapcore.NewCore(consoleEncoder, zapcore.AddSync(os.Stdout), zap.DebugLevel),
)
logger := zap.New(core, zap.AddCaller())
defer logger.Sync()
return logger
}

View File

@ -0,0 +1,95 @@
package middleware
import (
"cargo-erp-backend/pkg/helpers"
"cargo-erp-backend/pkg/response"
"context"
"fmt"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/redis/go-redis/v9"
)
type AuthMiddlewareInterface interface {
Check() gin.HandlerFunc
}
type AuthMiddleware struct {
Helper helpers.HelperInterface
}
func NewAuthMiddleware(helper helpers.HelperInterface) AuthMiddlewareInterface {
return &AuthMiddleware{
Helper: helper,
}
}
func (m *AuthMiddleware) Check() gin.HandlerFunc {
return func(c *gin.Context) {
// rechecking auth header
authHeader := c.GetHeader("Authorization")
authData := ""
authKey := ""
err_auth_header := false
if authHeader == "" {
err_auth_header = true
}
parts := strings.Fields(authHeader)
token_string := ""
if len(parts) != 2 || !strings.EqualFold(parts[0], "Bearer") {
err_auth_header = true
} else {
token_string = parts[1]
}
if err_auth_header {
cookieName := fmt.Sprintf("%v", m.Helper.Config().Get("cookie.name"))
value, err := c.Cookie(cookieName)
if err != nil {
response.Error(c, http.StatusUnauthorized, "Unauthorized")
c.Abort()
return
}
if len(authHeader) > 0 && len(value) > 0 && parts[1] != value {
response.Error(c, http.StatusUnauthorized, "Unauthorized")
c.Abort()
return
}
token_string = value
}
ctx := context.Background()
redis_exist := make(map[string]string)
m.Helper.Log().Info("session_login : " + token_string)
iter := m.Helper.GetRedis("slave").Scan(ctx, 0, "session_login:*:"+token_string, 1000).Iterator()
for iter.Next(ctx) {
keys := iter.Val()
value, err := m.Helper.GetRedis("slave").Get(ctx, keys).Result()
if err != nil {
if err == redis.Nil {
continue
}
}
redis_exist[keys] = value
}
if err := iter.Err(); err != nil {
response.Error(c, http.StatusUnauthorized, "Unauthorized")
c.Abort()
return
}
if len(redis_exist) != 1 {
response.Error(c, http.StatusUnauthorized, "Unauthorized")
c.Abort()
return
}
for k, v := range redis_exist {
authData = v
authKey = k
}
m.Helper.Log().Info("Set Auth : " + authData)
c.Set("auth", authData)
c.Set("auth_key", authKey)
c.Next()
}
}

73
internal/usecases/auth.go Normal file
View File

@ -0,0 +1,73 @@
package usecases
import (
"cargo-erp-backend/internal/domain"
"cargo-erp-backend/pkg/helpers"
"cargo-erp-backend/pkg/jwt"
"context"
"fmt"
"strconv"
"strings"
"time"
)
type AuthUsecaseInterface interface {
Login(email string, password string) (token string, err error)
}
type AuthUsecase struct {
Helper helpers.HelperInterface
}
func NewAuthUsecase(helper helpers.HelperInterface) AuthUsecaseInterface {
return &AuthUsecase{
Helper: helper,
}
}
func (u *AuthUsecase) Login(email string, password string) (token string, err error) {
var user []domain.User
db := u.Helper.GetDB("slave")
db.Model(&user).Where("email=?", email).Find(&user)
for _, v := range user {
pass := u.Helper.StringFromPtr(v.Password)
if u.Helper.VerifyPassword(pass, password) {
ctx := context.Background()
rd := u.Helper.GetRedis("master")
secretJwt := jwt.NewJWT(u.Helper.Config(), u.Helper.Log())
token, err := secretJwt.GenerateToken(v.ID)
if err != nil {
return "", err
}
extime_str := fmt.Sprintf("%v", u.Helper.Config().Get("jwt.expiry_time"))
t, err := strconv.Atoi(extime_str)
if err != nil {
return "", err
}
//
key := fmt.Sprintf("session_login:%v:*", v.ID)
iter := u.Helper.GetRedis("slave").Scan(ctx, 0, key, 1000).Iterator()
for iter.Next(ctx) {
keys := iter.Val()
keys_ex := strings.Split(keys, ":")
//change to del if using single login
if keys_ex[1] == v.ID {
u.Helper.GetRedis("slave").Expire(ctx, keys, time.Duration(t)*time.Second)
return keys_ex[2], nil
}
}
key = fmt.Sprintf("session_login:%v:%v", v.ID, token)
clonner, err := u.Helper.CloneStructWithoutField(v, "password")
if err != nil {
return "", err
}
bdata, _ := u.Helper.StructTobyte(clonner)
status := rd.Set(ctx, key, bdata, time.Duration(t)*time.Second)
if status.Err() != nil {
return "", status.Err()
}
return token, nil
}
}
return "", fmt.Errorf("User Not Found")
}

100
migrations/001_init.sql Normal file
View File

@ -0,0 +1,100 @@
-- Cargo ERP Database Schema
-- Provinces table
CREATE TABLE IF NOT EXISTS provinces (
id SERIAL PRIMARY KEY,
code VARCHAR(10) NOT NULL UNIQUE,
name VARCHAR(100) NOT NULL
);
-- Cities table
CREATE TABLE IF NOT EXISTS cities (
id SERIAL PRIMARY KEY,
code VARCHAR(10) NOT NULL,
name VARCHAR(100) NOT NULL,
province_id INTEGER REFERENCES provinces(id) ON DELETE CASCADE
);
-- Districts table
CREATE TABLE IF NOT EXISTS districts (
id SERIAL PRIMARY KEY,
code VARCHAR(10) NOT NULL,
name VARCHAR(100) NOT NULL,
city_id INTEGER REFERENCES cities(id) ON DELETE CASCADE
);
-- Subdistricts table
CREATE TABLE IF NOT EXISTS subdistricts (
id SERIAL PRIMARY KEY,
code VARCHAR(10) NOT NULL,
name VARCHAR(100) NOT NULL,
district_id INTEGER REFERENCES districts(id) ON DELETE CASCADE
);
-- Merchants table
CREATE TABLE IF NOT EXISTS merchants (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
code VARCHAR(50) NOT NULL UNIQUE,
address TEXT NOT NULL,
phone VARCHAR(20) NOT NULL,
email VARCHAR(255) NOT NULL,
province_id INTEGER REFERENCES provinces(id),
city_id INTEGER REFERENCES cities(id),
district_id INTEGER REFERENCES districts(id),
subdistrict_id INTEGER REFERENCES subdistricts(id),
postcode VARCHAR(10),
status VARCHAR(20) NOT NULL DEFAULT 'active',
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Users table
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
merchant_id INTEGER REFERENCES merchants(id) ON DELETE SET NULL,
username VARCHAR(100) NOT NULL UNIQUE,
email VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
role VARCHAR(20) NOT NULL CHECK (role IN ('admin', 'administrasi', 'warehouse', 'cashier')),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Orders table
CREATE TABLE IF NOT EXISTS orders (
id SERIAL PRIMARY KEY,
merchant_id INTEGER NOT NULL REFERENCES merchants(id) ON DELETE CASCADE,
order_number VARCHAR(50) NOT NULL UNIQUE,
customer_name VARCHAR(255) NOT NULL,
customer_phone VARCHAR(20) NOT NULL,
pickup_address TEXT NOT NULL,
delivery_address TEXT NOT NULL,
pickup_subdistrict_id INTEGER REFERENCES subdistricts(id),
delivery_subdistrict_id INTEGER REFERENCES subdistricts(id),
status VARCHAR(20) NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'picked_up', 'in_transit', 'delivered', 'cancelled')),
weight DECIMAL(10,2) NOT NULL,
price DECIMAL(15,2) NOT NULL,
notes TEXT,
created_by INTEGER REFERENCES users(id),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Indexes
CREATE INDEX IF NOT EXISTS idx_cities_province_id ON cities(province_id);
CREATE INDEX IF NOT EXISTS idx_districts_city_id ON districts(city_id);
CREATE INDEX IF NOT EXISTS idx_subdistricts_district_id ON subdistricts(district_id);
CREATE INDEX IF NOT EXISTS idx_merchants_status ON merchants(status);
CREATE INDEX IF NOT EXISTS idx_users_username ON users(username);
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
CREATE INDEX IF NOT EXISTS idx_orders_merchant_id ON orders(merchant_id);
CREATE INDEX IF NOT EXISTS idx_orders_order_number ON orders(order_number);
CREATE INDEX IF NOT EXISTS idx_orders_status ON orders(status);
CREATE INDEX IF NOT EXISTS idx_orders_created_at ON orders(created_at);
-- Insert sample admin user (password: admin123)
-- The password hash below is for "admin123" with bcrypt cost 10
INSERT INTO users (username, email, password, role) VALUES
('admin', 'admin@cargo-erp.com', '$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy', 'admin')
ON CONFLICT (username) DO NOTHING;

223
migrations/generatemodel.go Normal file
View File

@ -0,0 +1,223 @@
package migrations
import (
"database/sql"
"fmt"
"log"
"os"
"path/filepath"
"strings"
"gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/gorm"
)
// Struktur untuk menampung metadata Foreign Key dari database
type ForeignKeyMeta struct {
TableName string
ColumnName string
RefTable string
RefColumn string
}
func GenerateModel(table *string, db *gorm.DB) {
currentDir, err := os.Getwd()
if err != nil {
log.Fatalf("Gagal mendapatkan working directory: %v", err)
}
// Folder penampung sementara kueri (akan dihapus di akhir)
queryOutPath := filepath.Join(currentDir, "internal", "domain", "query_utils")
// Target folder Struct Model murni Anda
modelOutPath := filepath.Join(currentDir, "internal", "domain")
_ = os.MkdirAll(queryOutPath, os.ModePerm)
_ = os.MkdirAll(modelOutPath, os.ModePerm)
sqlDB, err := db.DB()
if err != nil {
log.Fatalf("Gagal mengambil instance sql.DB: %v", err)
}
fkMetas := fetchForeignKeys(sqlDB)
log.Printf("Berhasil mendeteksi %d relasi Foreign Key fisik di database.\n", len(fkMetas))
g := gen.NewGenerator(gen.Config{
OutPath: queryOutPath,
ModelPkgPath: modelOutPath,
Mode: gen.WithoutContext,
FieldWithIndexTag: true,
FieldWithTypeTag: true,
FieldNullable: true,
FieldSignable: true,
})
g.UseDB(db)
rows, err := sqlDB.Query("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'")
if err != nil {
log.Fatalf("Gagal mengambil daftar tabel: %v", err)
}
defer rows.Close()
var tableNames []string
for rows.Next() {
var tName string
if err := rows.Scan(&tName); err == nil && !strings.HasPrefix(tName, "gorm_") {
tableNames = append(tableNames, tName)
}
}
// Map untuk mengumpulkan seluruh konfigurasi gen.ModelOpt dari tiap tabel
tableRelationOpts := make(map[string][]gen.ModelOpt)
// Isi opsi relasi berdasarkan foreign key yang terdeteksi
for _, tName := range tableNames {
var relationOptions []gen.ModelOpt
for _, fk := range fkMetas {
// A. Relasi BELONGS TO
if fk.TableName == tName {
baseName := snakeToPascalCase(strings.TrimSuffix(fk.ColumnName, "_id"))
if baseName == "" {
baseName = snakeToPascalCase(fk.RefTable)
}
// KUSTOMISASI 1: Tambahkan akhiran _Relation pada properti
propertyName := fmt.Sprintf("%s_Relation", baseName)
relationOptions = append(relationOptions, gen.FieldRelate(
field.BelongsTo,
propertyName,
g.GenerateModel(fk.RefTable),
&field.RelateConfig{
GORMTag: field.GormTag{
"foreignKey": []string{fk.ColumnName},
"references": []string{fk.RefColumn},
},
},
))
}
// B. Relasi HAS MANY
if fk.RefTable == tName {
// KUSTOMISASI 1: Tambahkan akhiran _Relation pada properti jamak
propertyName := fmt.Sprintf("%ss_Relation", snakeToPascalCase(fk.TableName))
relationOptions = append(relationOptions, gen.FieldRelate(
field.HasMany,
propertyName,
g.GenerateModel(fk.TableName),
&field.RelateConfig{
GORMTag: field.GormTag{
"foreignKey": []string{fk.ColumnName},
"references": []string{fk.RefColumn},
},
},
))
}
}
tableRelationOpts[tName] = relationOptions
}
var finalModels []any
allTableModels := make(map[string]any)
for _, tName := range tableNames {
opts := tableRelationOpts[tName]
compiledModel := g.GenerateModel(tName, opts...)
allTableModels[tName] = compiledModel
finalModels = append(finalModels, compiledModel)
}
if table != nil && *table != "" {
if specModel, ok := allTableModels[*table]; ok {
g.ApplyBasic(specModel)
} else {
log.Fatalf("Tabel %s tidak ditemukan di database!", *table)
}
} else {
g.ApplyBasic(finalModels...)
}
// Eksekusi pembuatan file standar GORM Gen (.gen.go)
g.Execute()
// Bersihkan utility kueri yang tidak dibutuhkan
_ = os.RemoveAll(queryOutPath)
// KUSTOMISASI 2: Hilangkan ekstensi ".gen.go" menjadi ".go" murni
err = renameGeneratedFiles(modelOutPath)
if err != nil {
log.Printf("Peringatan: Gagal mengubah nama beberapa file model: %v\n", err)
}
log.Println("Selesai! Model berhasil dibuat dengan format nama tanpa .gen dan field bertanda _Relation.")
}
// Fungsi untuk mencari berkas .gen.go dan mengubah namanya menjadi .go murni
func renameGeneratedFiles(dir string) error {
files, err := os.ReadDir(dir)
if err != nil {
return err
}
for _, file := range files {
if !file.IsDir() && strings.HasSuffix(file.Name(), ".gen.go") {
oldPath := filepath.Join(dir, file.Name())
newName := strings.Replace(file.Name(), ".gen.go", ".go", 1)
newPath := filepath.Join(dir, newName)
// Proses penggantian nama file fisik
if err := os.Rename(oldPath, newPath); err != nil {
return err
}
}
}
return nil
}
// Fungsi pembantu menarik metadata FK Postgres
func fetchForeignKeys(db *sql.DB) []ForeignKeyMeta {
query := `
SELECT
kcu.table_name AS table_name,
kcu.column_name AS column_name,
ccu.table_name AS referenced_table_name,
ccu.column_name AS referenced_column_name
FROM
information_schema.table_constraints AS tc
JOIN information_schema.key_column_usage AS kcu
ON tc.constraint_name = kcu.constraint_name
AND tc.table_schema = kcu.table_schema
JOIN information_schema.constraint_column_usage AS ccu
ON ccu.constraint_name = tc.constraint_name
AND ccu.table_schema = tc.table_schema
WHERE tc.constraint_type = 'FOREIGN KEY' AND tc.table_schema = 'public';
`
rows, err := db.Query(query)
if err != nil {
log.Printf("Gagal membaca metadata Foreign Key: %v\n", err)
return nil
}
defer rows.Close()
var metas []ForeignKeyMeta
for rows.Next() {
var m ForeignKeyMeta
if err := rows.Scan(&m.TableName, &m.ColumnName, &m.RefTable, &m.RefColumn); err == nil {
metas = append(metas, m)
}
}
return metas
}
func snakeToPascalCase(s string) string {
parts := strings.Split(s, "_")
for i, part := range parts {
if len(part) > 0 {
parts[i] = strings.ToUpper(part[:1]) + part[1:]
}
}
return strings.Join(parts, "")
}

92
pkg/helpers/helpers.go Normal file
View File

@ -0,0 +1,92 @@
package helpers
import (
"cargo-erp-backend/internal/config"
"cargo-erp-backend/internal/database"
"encoding/json"
"github.com/redis/go-redis/v9"
"go.uber.org/zap"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
)
type HelperInterface interface {
GetDB(key string) *gorm.DB
GetRedis(key string) *redis.Client
Config() config.ConfigInterface
Log() *zap.Logger
HashPassword(password string) string
VerifyPassword(hashedPassword, password string) bool
StringFromPtr(ptr *string) string
StructTobyte(data interface{}) ([]byte, error)
CloneStructWithoutField(input interface{}, field string) (map[string]interface{}, error)
}
type Helper struct {
Conf config.ConfigInterface
DB database.DatabaseInterface
Logger *zap.Logger
RedisDB database.RedisInterface
}
func NewHelper(config config.ConfigInterface, db database.DatabaseInterface, logger *zap.Logger, dbredis database.RedisInterface) HelperInterface {
return &Helper{
Conf: config,
DB: db,
Logger: logger,
RedisDB: dbredis,
}
}
func (h *Helper) GetDB(key string) *gorm.DB {
return h.DB.Get(key)
}
func (h *Helper) GetRedis(key string) *redis.Client {
return h.RedisDB.Get(key)
}
func (h *Helper) Config() config.ConfigInterface {
return h.Conf
}
func (h *Helper) Log() *zap.Logger {
return h.Logger
}
func (h *Helper) HashPassword(password string) string {
bytes, _ := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
return string(bytes)
}
func (h *Helper) VerifyPassword(hashedPassword, password string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
return err == nil
}
func (h *Helper) StringFromPtr(ptr *string) string {
if ptr == nil {
return ""
}
return *ptr
}
func (h *Helper) StructTobyte(data interface{}) ([]byte, error) {
var output []byte
s, err := json.Marshal(data)
if err != nil {
return output, err
}
output = s
return output, nil
}
func (h *Helper) CloneStructWithoutField(input interface{}, field string) (map[string]interface{}, error) {
data, err := json.Marshal(input)
if err != nil {
return nil, err
}
var result map[string]interface{}
if err := json.Unmarshal(data, &result); err != nil {
return nil, err
}
if len(field) > 0 {
delete(result, field)
}
return result, nil
}

85
pkg/jwt/jwt.go Normal file
View File

@ -0,0 +1,85 @@
package jwt
import (
"cargo-erp-backend/internal/config"
"fmt"
"strconv"
"time"
"github.com/golang-jwt/jwt/v4"
"go.uber.org/zap"
)
type JWTinterface interface {
GenerateToken(secret string) (string, error)
VerifyToken(tokenString string) (*Claims, error)
}
type JWT struct {
Config config.ConfigInterface
Log *zap.Logger
}
func NewJWT(config config.ConfigInterface, logger *zap.Logger) JWTinterface {
return &JWT{
Config: config,
Log: logger,
}
}
type Claims struct {
UserID string `json:"uid"`
jwt.RegisteredClaims
}
func (j *JWT) GenerateToken(tokenUser string) (string, error) {
secret := fmt.Sprintf("%v", j.Config.Get("jwt.secret"))
expiry_time_string := fmt.Sprintf("%v", j.Config.Get("jwt.expiry_time"))
expiry_time, err := strconv.Atoi(expiry_time_string)
if err != nil {
return "", err
}
claims := Claims{
UserID: tokenUser,
RegisteredClaims: jwt.RegisteredClaims{
Issuer: "cargo-platform",
Subject: tokenUser,
IssuedAt: jwt.NewNumericDate(time.Now()),
ExpiresAt: jwt.NewNumericDate(
time.Now().Add(time.Duration(expiry_time) * time.Second),
),
},
}
token := jwt.NewWithClaims(
jwt.SigningMethodHS256,
claims,
)
return token.SignedString([]byte(secret))
}
func (j *JWT) VerifyToken(tokenString string) (*Claims, error) {
secret := fmt.Sprintf("%v", j.Config.Get("jwt.secret"))
token, err := jwt.ParseWithClaims(
tokenString,
&Claims{},
func(token *jwt.Token) (interface{}, error) {
if token.Method != jwt.SigningMethodHS256 {
return nil, fmt.Errorf("invalid signing method")
}
return []byte(secret), nil
},
)
if err != nil {
return nil, err
}
claims, ok := token.Claims.(*Claims)
if !ok || !token.Valid {
return nil, fmt.Errorf("invalid token")
}
return claims, nil
}

68
pkg/response/response.go Normal file
View File

@ -0,0 +1,68 @@
package response
import (
"net/http"
"github.com/gin-gonic/gin"
)
type APIResponse struct {
Success bool `json:"success"`
Message string `json:"message,omitempty"`
Data interface{} `json:"data,omitempty"`
}
type PaginatedData struct {
Items interface{} `json:"items"`
Total int `json:"total"`
Page int `json:"page"`
Limit int `json:"limit"`
TotalPages int `json:"total_pages"`
}
func Success(c *gin.Context, data interface{}) {
c.JSON(http.StatusOK, APIResponse{
Success: true,
Data: data,
})
}
func Created(c *gin.Context, data interface{}) {
c.JSON(http.StatusCreated, APIResponse{
Success: true,
Data: data,
})
}
func Error(c *gin.Context, code int, message string) {
c.JSON(code, APIResponse{
Success: false,
Message: message,
})
}
func SuccessWithMessage(c *gin.Context, message string, data interface{}) {
c.JSON(http.StatusOK, APIResponse{
Success: true,
Message: message,
Data: data,
})
}
func PaginatedResponse(c *gin.Context, items interface{}, total, page, limit int) {
totalPages := total / limit
if total%limit > 0 {
totalPages++
}
c.JSON(http.StatusOK, APIResponse{
Success: true,
Data: PaginatedData{
Items: items,
Total: total,
Page: page,
Limit: limit,
TotalPages: totalPages,
},
})
}

458
storage/logs/app.log Normal file
View File

@ -0,0 +1,458 @@
{"level":"info","ts":"2026-08-13T21:46:17.630+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-13T21:46:17.787+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-13T21:46:17.941+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-13T21:46:17.941+0700","caller":"handlers/handlers.go:36","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-13T21:46:43.223+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-13T21:46:43.494+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-13T21:46:43.618+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-13T21:46:43.618+0700","caller":"handlers/handlers.go:36","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-13T22:00:01.955+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-13T22:00:02.256+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-13T22:00:02.487+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-13T22:00:02.487+0700","caller":"handlers/handlers.go:38","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-13T22:15:06.240+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-13T22:15:06.365+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-13T22:15:06.445+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-13T22:15:06.445+0700","caller":"handlers/handlers.go:40","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-13T22:27:50.679+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-13T22:27:50.768+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-13T22:27:50.851+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-13T22:27:50.851+0700","caller":"handlers/handlers.go:85","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-13T22:28:27.312+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-13T22:28:27.404+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-13T22:28:27.475+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-13T22:28:27.476+0700","caller":"handlers/handlers.go:84","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-13T22:29:31.604+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-13T22:29:31.689+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-13T22:29:31.760+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-13T22:29:31.760+0700","caller":"handlers/handlers.go:85","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-13T22:30:59.523+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-13T22:30:59.609+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-13T22:30:59.703+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-13T22:30:59.703+0700","caller":"handlers/handlers.go:90","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-13T22:31:13.213+0700","caller":"handlers/handlers.go:72","msg":"Request Success","status":200,"method":"GET","path":"/ping","query":"","ip":"127.0.0.1","latency":0.002712167,"user-agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:153.0) Gecko/20100101 Firefox/153.0"}
{"level":"warn","ts":"2026-08-13T22:31:13.526+0700","caller":"handlers/handlers.go:70","msg":"Client Error","status":404,"method":"GET","path":"/favicon.ico","query":"","ip":"127.0.0.1","latency":0.000858875,"user-agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:153.0) Gecko/20100101 Firefox/153.0"}
{"level":"info","ts":"2026-08-13T22:31:20.833+0700","caller":"handlers/handlers.go:72","msg":"Request Success","status":200,"method":"GET","path":"/login","query":"","ip":"127.0.0.1","latency":0.002228875,"user-agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:153.0) Gecko/20100101 Firefox/153.0"}
{"level":"warn","ts":"2026-08-13T22:31:20.869+0700","caller":"handlers/handlers.go:70","msg":"Client Error","status":404,"method":"GET","path":"/favicon.ico","query":"","ip":"127.0.0.1","latency":0.000000417,"user-agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:153.0) Gecko/20100101 Firefox/153.0"}
{"level":"info","ts":"2026-08-13T23:00:21.130+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-13T23:00:21.243+0700","caller":"database/database.go:74","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-13T23:00:21.342+0700","caller":"database/database.go:84","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-13T23:01:44.195+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-13T23:01:44.278+0700","caller":"database/database.go:74","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-13T23:01:44.366+0700","caller":"database/database.go:84","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-13T23:05:29.670+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-13T23:05:29.755+0700","caller":"database/database.go:74","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-13T23:05:29.831+0700","caller":"database/database.go:84","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-13T23:07:40.483+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-13T23:07:40.584+0700","caller":"database/database.go:74","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-13T23:07:40.663+0700","caller":"database/database.go:84","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-13T23:13:19.014+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"fatal","ts":"2026-08-13T23:13:19.082+0700","caller":"database/database.go:58","msg":"failed to connect to `host=145.79.11.200 user=cargo database=cargo_erp`: server error (FATAL: bouncer config error (SQLSTATE 08P01))"}
{"level":"info","ts":"2026-08-13T23:14:16.466+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"fatal","ts":"2026-08-13T23:14:16.562+0700","caller":"database/database.go:58","msg":"failed to connect to `host=145.79.11.200 user=cargo_admin database=cargo_erp`: failed SASL auth (FATAL: SASL authentication failed (SQLSTATE 08P01))"}
{"level":"info","ts":"2026-08-13T23:14:35.650+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"fatal","ts":"2026-08-13T23:14:35.734+0700","caller":"database/database.go:58","msg":"failed to connect to `host=145.79.11.200 user=cargo database=cargo_erp`: server error (FATAL: bouncer config error (SQLSTATE 08P01))"}
{"level":"info","ts":"2026-08-13T23:15:10.765+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"fatal","ts":"2026-08-13T23:15:10.824+0700","caller":"database/database.go:58","msg":"failed to connect to `host=145.79.11.200 user=cargo database=cargo_erp`: server error (FATAL: bouncer config error (SQLSTATE 08P01))"}
{"level":"info","ts":"2026-08-13T23:15:20.833+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-13T23:15:20.917+0700","caller":"database/database.go:62","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-13T23:15:20.918+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-13T23:15:21.006+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-13T23:16:39.066+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-13T23:16:39.173+0700","caller":"database/database.go:62","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-13T23:16:39.173+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-13T23:16:39.252+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-13T23:18:32.972+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-13T23:18:33.064+0700","caller":"database/database.go:62","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-13T23:18:33.064+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-13T23:18:33.142+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-13T23:22:52.364+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-13T23:22:52.450+0700","caller":"database/database.go:62","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-13T23:22:52.451+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-13T23:22:52.528+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-13T23:25:38.058+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-13T23:25:38.157+0700","caller":"database/database.go:62","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-13T23:25:38.157+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-13T23:25:49.271+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-13T23:25:49.370+0700","caller":"database/database.go:62","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-13T23:25:49.370+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-13T23:25:49.457+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-14T00:09:28.596+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-14T00:09:28.682+0700","caller":"database/database.go:62","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-14T00:09:28.682+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-14T00:09:28.760+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-14T00:11:14.945+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-14T00:11:15.037+0700","caller":"database/database.go:62","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-14T00:11:15.037+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-14T00:11:15.125+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-14T00:14:12.844+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-14T00:14:12.933+0700","caller":"database/database.go:62","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-14T00:14:12.934+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-14T00:14:13.014+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-14T00:16:03.378+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-14T00:16:03.472+0700","caller":"database/database.go:62","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-14T00:16:03.472+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-14T00:16:03.564+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T10:33:04.359+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T10:33:04.796+0700","caller":"database/database.go:62","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T10:33:04.797+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T10:33:05.036+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T10:33:05.036+0700","caller":"handlers/handlers.go:93","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-15T10:35:31.431+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T10:35:31.521+0700","caller":"database/database.go:62","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T10:35:31.521+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T10:35:31.617+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T10:35:31.617+0700","caller":"handlers/handlers.go:93","msg":"Starting Apps"}
{"level":"error","ts":"2026-08-15T10:36:29.127+0700","caller":"handlers/handlers.go:64","msg":"Key: 'AuthRequest.Email' Error:Field validation for 'Email' failed on the 'required' tag\nKey: 'AuthRequest.Password' Error:Field validation for 'Password' failed on the 'required' tag","status":400,"method":"POST","path":"/login","query":"","ip":"127.0.0.1","latency":0.0028055,"user-agent":"insomnia/13.1.0"}
{"level":"error","ts":"2026-08-15T10:36:45.528+0700","caller":"handlers/handlers.go:64","msg":"Key: 'AuthRequest.Password' Error:Field validation for 'Password' failed on the 'required' tag","status":400,"method":"POST","path":"/login","query":"","ip":"127.0.0.1","latency":0.004973209,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-15T10:37:31.365+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T10:37:31.451+0700","caller":"database/database.go:62","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T10:37:31.451+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T10:37:31.535+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T10:37:31.535+0700","caller":"handlers/handlers.go:93","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-15T10:37:34.953+0700","caller":"handlers/handlers.go:75","msg":"Request Success","status":200,"method":"POST","path":"/login","query":"","ip":"127.0.0.1","latency":0.032455291,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-15T10:54:09.472+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T10:54:09.555+0700","caller":"database/database.go:62","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T10:54:09.555+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T10:54:09.631+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T10:54:09.631+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"error","ts":"2026-08-15T10:54:13.941+0700","caller":"handlers/handlers.go:60","msg":"Server Error","status":500,"method":"POST","path":"/login","query":"","ip":"127.0.0.1","latency":0.044428542,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-15T10:57:14.222+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T10:57:14.313+0700","caller":"database/database.go:62","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T10:57:14.313+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T10:57:14.410+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T10:57:14.410+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"warn","ts":"2026-08-15T10:57:17.986+0700","caller":"handlers/handlers.go:62","msg":"Client Error","status":400,"method":"POST","path":"/login","query":"","ip":"127.0.0.1","latency":0.03831625,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-15T11:09:37.233+0700","caller":"database/database.go:108","msg":"Reload Connecting Database"}
{"level":"info","ts":"2026-08-15T11:09:37.235+0700","caller":"database/database.go:97","msg":"Close Database Connection Start"}
{"level":"info","ts":"2026-08-15T11:09:37.238+0700","caller":"database/database.go:102","msg":"Close Database Connection master"}
{"level":"info","ts":"2026-08-15T11:09:37.239+0700","caller":"database/database.go:102","msg":"Close Database Connection slave"}
{"level":"info","ts":"2026-08-15T11:09:37.239+0700","caller":"database/database.go:105","msg":"Close Database Connection Done"}
{"level":"info","ts":"2026-08-15T11:09:37.239+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T11:09:37.352+0700","caller":"database/database.go:62","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T11:09:37.352+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T11:09:37.430+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T11:11:40.726+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T11:11:40.829+0700","caller":"database/database.go:62","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T11:11:40.829+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T11:11:40.973+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T11:11:40.973+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-15T11:12:09.727+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T11:12:09.812+0700","caller":"database/database.go:62","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T11:12:09.812+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T11:12:09.898+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T11:12:09.898+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-15T21:41:50.394+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T21:41:50.668+0700","caller":"database/database.go:62","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T21:41:50.668+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T21:41:50.840+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T21:42:17.260+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T21:42:17.351+0700","caller":"database/database.go:62","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T21:42:17.351+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T21:42:17.428+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T21:43:45.063+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T21:43:45.199+0700","caller":"database/database.go:62","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T21:43:45.199+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T21:43:45.292+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T21:44:12.818+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T21:44:12.918+0700","caller":"database/database.go:62","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T21:44:12.918+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T21:44:12.992+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T21:44:31.989+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T21:44:32.077+0700","caller":"database/database.go:62","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T21:44:32.077+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T21:44:32.162+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T21:45:18.090+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T21:45:18.217+0700","caller":"database/database.go:62","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T21:45:18.217+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T21:45:18.296+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T21:45:18.419+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-15T21:46:06.762+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T21:46:06.985+0700","caller":"database/database.go:62","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T21:46:06.985+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T21:46:07.079+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T21:46:07.198+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"warn","ts":"2026-08-15T21:46:33.811+0700","caller":"handlers/handlers.go:62","msg":"Client Error","status":400,"method":"POST","path":"/login","query":"","ip":"127.0.0.1","latency":0.147348833,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-15T21:49:20.652+0700","caller":"database/database.go:108","msg":"Reload Connecting Database"}
{"level":"info","ts":"2026-08-15T21:49:20.656+0700","caller":"database/database.go:97","msg":"Close Database Connection Start"}
{"level":"info","ts":"2026-08-15T21:49:20.661+0700","caller":"database/database.go:102","msg":"Close Database Connection slave"}
{"level":"info","ts":"2026-08-15T21:49:20.661+0700","caller":"database/database.go:102","msg":"Close Database Connection master"}
{"level":"info","ts":"2026-08-15T21:49:20.661+0700","caller":"database/database.go:105","msg":"Close Database Connection Done"}
{"level":"info","ts":"2026-08-15T21:49:20.662+0700","caller":"database/database.go:52","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T21:49:20.771+0700","caller":"database/database.go:62","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T21:49:20.771+0700","caller":"database/database.go:75","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T21:49:20.993+0700","caller":"database/database.go:85","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T21:56:53.219+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T21:56:53.360+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T21:56:53.360+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T21:56:53.443+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T21:56:53.659+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-15T21:57:14.361+0700","caller":"database/database.go:111","msg":"Reload Connecting Database"}
{"level":"info","ts":"2026-08-15T21:57:14.365+0700","caller":"database/database.go:100","msg":"Close Database Connection Start"}
{"level":"info","ts":"2026-08-15T21:57:14.369+0700","caller":"database/database.go:105","msg":"Close Database Connection master"}
{"level":"info","ts":"2026-08-15T21:57:14.370+0700","caller":"database/database.go:105","msg":"Close Database Connection slave"}
{"level":"info","ts":"2026-08-15T21:57:14.370+0700","caller":"database/database.go:108","msg":"Close Database Connection Done"}
{"level":"info","ts":"2026-08-15T21:57:14.371+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T21:57:14.521+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T21:57:14.521+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T21:57:14.601+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T21:57:14.601+0700","caller":"database/database.go:114","msg":"Reload Connecting Database Done"}
{"level":"info","ts":"2026-08-15T21:57:21.394+0700","caller":"database/database.go:111","msg":"Reload Connecting Database"}
{"level":"info","ts":"2026-08-15T21:57:21.396+0700","caller":"database/database.go:100","msg":"Close Database Connection Start"}
{"level":"info","ts":"2026-08-15T21:57:21.396+0700","caller":"database/database.go:105","msg":"Close Database Connection master"}
{"level":"info","ts":"2026-08-15T21:57:21.397+0700","caller":"database/database.go:105","msg":"Close Database Connection slave"}
{"level":"info","ts":"2026-08-15T21:57:21.397+0700","caller":"database/database.go:108","msg":"Close Database Connection Done"}
{"level":"info","ts":"2026-08-15T21:57:21.397+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T21:57:21.544+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T21:57:21.545+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T21:57:21.642+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T21:57:21.642+0700","caller":"database/database.go:114","msg":"Reload Connecting Database Done"}
{"level":"warn","ts":"2026-08-15T21:57:38.325+0700","caller":"handlers/handlers.go:62","msg":"Client Error","status":401,"method":"POST","path":"/login","query":"","ip":"127.0.0.1","latency":0.132527625,"user-agent":"insomnia/13.1.0"}
{"level":"warn","ts":"2026-08-15T22:00:08.985+0700","caller":"handlers/handlers.go:62","msg":"Client Error","status":401,"method":"POST","path":"/login","query":"","ip":"127.0.0.1","latency":0.183630167,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-15T22:00:52.456+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T22:00:52.576+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T22:00:52.576+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T22:00:52.654+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T22:00:52.785+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-15T22:00:55.679+0700","caller":"handlers/handlers.go:64","msg":"Request Success","status":200,"method":"POST","path":"/login","query":"","ip":"127.0.0.1","latency":0.135076584,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-15T22:01:45.193+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T22:01:45.291+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T22:01:45.291+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T22:01:45.365+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T22:01:45.570+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"warn","ts":"2026-08-15T22:01:51.501+0700","caller":"handlers/handlers.go:62","msg":"Client Error","status":401,"method":"POST","path":"/login","query":"","ip":"127.0.0.1","latency":0.031749625,"user-agent":"insomnia/13.1.0"}
{"level":"warn","ts":"2026-08-15T22:02:26.847+0700","caller":"handlers/handlers.go:62","msg":"Client Error","status":401,"method":"POST","path":"/login","query":"","ip":"127.0.0.1","latency":0.069505792,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-15T22:04:05.942+0700","caller":"handlers/handlers.go:64","msg":"Request Success","status":200,"method":"POST","path":"/login","query":"","ip":"127.0.0.1","latency":0.148913625,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-15T22:09:54.081+0700","caller":"database/database.go:111","msg":"Reload Connecting Database"}
{"level":"info","ts":"2026-08-15T22:09:54.085+0700","caller":"database/database.go:100","msg":"Close Database Connection Start"}
{"level":"info","ts":"2026-08-15T22:09:54.090+0700","caller":"database/database.go:105","msg":"Close Database Connection master"}
{"level":"info","ts":"2026-08-15T22:09:54.090+0700","caller":"database/database.go:105","msg":"Close Database Connection slave"}
{"level":"info","ts":"2026-08-15T22:09:54.090+0700","caller":"database/database.go:108","msg":"Close Database Connection Done"}
{"level":"info","ts":"2026-08-15T22:09:54.091+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T22:09:54.211+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T22:09:54.211+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T22:09:54.305+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T22:09:54.305+0700","caller":"database/database.go:114","msg":"Reload Connecting Database Done"}
{"level":"info","ts":"2026-08-15T22:43:30.370+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T22:43:30.798+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T22:43:30.799+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T22:43:30.877+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T22:43:31.003+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-15T22:43:35.544+0700","caller":"handlers/handlers.go:64","msg":"Request Success","status":200,"method":"POST","path":"/login","query":"","ip":"127.0.0.1","latency":0.146430833,"user-agent":"insomnia/13.1.0"}
{"level":"warn","ts":"2026-08-15T22:44:04.550+0700","caller":"handlers/handlers.go:62","msg":"Client Error","status":401,"method":"POST","path":"/logout","query":"","ip":"127.0.0.1","latency":0.015486292,"user-agent":"insomnia/13.1.0"}
{"level":"warn","ts":"2026-08-15T22:44:50.975+0700","caller":"handlers/handlers.go:62","msg":"Client Error","status":401,"method":"POST","path":"/logout","query":"","ip":"127.0.0.1","latency":0.025776708,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-15T22:46:11.954+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T22:46:12.051+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T22:46:12.054+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T22:46:12.130+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T22:46:12.233+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"warn","ts":"2026-08-15T22:46:14.780+0700","caller":"handlers/handlers.go:62","msg":"Client Error","status":401,"method":"POST","path":"/logout","query":"","ip":"127.0.0.1","latency":0.017874792,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-15T22:51:47.045+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T22:51:47.135+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T22:51:47.135+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T22:51:47.203+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T22:51:47.324+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"warn","ts":"2026-08-15T22:51:51.438+0700","caller":"handlers/handlers.go:62","msg":"Client Error","status":401,"method":"POST","path":"/logout","query":"","ip":"127.0.0.1","latency":0.024666291,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-15T22:51:58.922+0700","caller":"handlers/handlers.go:64","msg":"Request Success","status":200,"method":"POST","path":"/login","query":"","ip":"127.0.0.1","latency":0.144759625,"user-agent":"insomnia/13.1.0"}
{"level":"warn","ts":"2026-08-15T22:52:14.778+0700","caller":"handlers/handlers.go:62","msg":"Client Error","status":401,"method":"POST","path":"/logout","query":"","ip":"127.0.0.1","latency":0.065118833,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-15T22:55:01.888+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T22:55:02.013+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T22:55:02.013+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T22:55:02.113+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T22:55:02.250+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"warn","ts":"2026-08-15T22:55:05.639+0700","caller":"handlers/handlers.go:62","msg":"Client Error","status":401,"method":"POST","path":"/logout","query":"","ip":"127.0.0.1","latency":0.016216792,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-15T22:56:00.788+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T22:56:00.907+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T22:56:00.907+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T22:56:00.991+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T22:56:01.137+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"warn","ts":"2026-08-15T22:56:03.126+0700","caller":"handlers/handlers.go:62","msg":"Client Error","status":401,"method":"POST","path":"/logout","query":"","ip":"127.0.0.1","latency":0.016038125,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-15T22:56:30.984+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T22:56:31.084+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T22:56:31.084+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T22:56:31.154+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T22:56:31.263+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"warn","ts":"2026-08-15T22:56:36.131+0700","caller":"handlers/handlers.go:62","msg":"Client Error","status":401,"method":"POST","path":"/logout","query":"","ip":"127.0.0.1","latency":0.018368625,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-15T22:57:26.021+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T22:57:26.349+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T22:57:26.349+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T22:57:26.469+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T22:57:26.589+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"warn","ts":"2026-08-15T22:57:28.380+0700","caller":"handlers/handlers.go:62","msg":"Client Error","status":401,"method":"POST","path":"/logout","query":"","ip":"127.0.0.1","latency":0.010972333,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-15T22:58:34.166+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T22:58:34.264+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T22:58:34.264+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T22:58:34.352+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T22:58:34.462+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-15T22:58:36.784+0700","caller":"middleware/auth.go:43","msg":"Session : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIxOWZmMTAzMWExMWMwYzAxM2Y2MDA0NGY1NiIsImlzcyI6ImNhcmdvLXBsYXRmb3JtIiwic3ViIjoiMTlmZjEwMzFhMTFjMGMwMTNmNjAwNDRmNTYiLCJleHAiOjE3ODY4MTI3MTgsImlhdCI6MTc4NjgwOTExOH0.G_EigVQilWqK_HVIehF9n7aAu-8T_ANKQg6Kge2JUxA"}
{"level":"warn","ts":"2026-08-15T22:58:36.796+0700","caller":"handlers/handlers.go:62","msg":"Client Error","status":401,"method":"POST","path":"/logout","query":"","ip":"127.0.0.1","latency":0.012448291,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-15T23:00:12.979+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T23:00:13.068+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T23:00:13.068+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T23:00:13.151+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T23:00:13.270+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-15T23:00:15.329+0700","caller":"handlers/handlers.go:64","msg":"Request Success","status":200,"method":"POST","path":"/login","query":"","ip":"127.0.0.1","latency":0.141631917,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-15T23:00:26.344+0700","caller":"middleware/auth.go:44","msg":"Session : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIxOWZmMTAzMWExMWMwYzAxM2Y2MDA0NGY1NiIsImlzcyI6ImNhcmdvLXBsYXRmb3JtIiwic3ViIjoiMTlmZjEwMzFhMTFjMGMwMTNmNjAwNDRmNTYiLCJleHAiOjE3ODY4MTMyMTUsImlhdCI6MTc4NjgwOTYxNX0.RyfPuq_BNl6RU8msz_LWC_ZzReo71_VlLzcjnELomBU"}
{"level":"info","ts":"2026-08-15T23:01:16.688+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T23:01:16.771+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T23:01:16.771+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T23:01:16.886+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T23:01:17.001+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-15T23:01:19.639+0700","caller":"middleware/auth.go:44","msg":"Session : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIxOWZmMTAzMWExMWMwYzAxM2Y2MDA0NGY1NiIsImlzcyI6ImNhcmdvLXBsYXRmb3JtIiwic3ViIjoiMTlmZjEwMzFhMTFjMGMwMTNmNjAwNDRmNTYiLCJleHAiOjE3ODY4MTMyMTUsImlhdCI6MTc4NjgwOTYxNX0.RyfPuq_BNl6RU8msz_LWC_ZzReo71_VlLzcjnELomBU"}
{"level":"info","ts":"2026-08-15T23:01:36.822+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T23:01:36.910+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T23:01:36.910+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T23:01:36.991+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T23:01:37.098+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-15T23:01:38.777+0700","caller":"middleware/auth.go:44","msg":"Session : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIxOWZmMTAzMWExMWMwYzAxM2Y2MDA0NGY1NiIsImlzcyI6ImNhcmdvLXBsYXRmb3JtIiwic3ViIjoiMTlmZjEwMzFhMTFjMGMwMTNmNjAwNDRmNTYiLCJleHAiOjE3ODY4MTMyMTUsImlhdCI6MTc4NjgwOTYxNX0.RyfPuq_BNl6RU8msz_LWC_ZzReo71_VlLzcjnELomBU"}
{"level":"info","ts":"2026-08-15T23:02:12.263+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T23:02:12.370+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T23:02:12.371+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T23:02:12.447+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T23:02:12.562+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-15T23:02:15.161+0700","caller":"middleware/auth.go:44","msg":"Session : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIxOWZmMTAzMWExMWMwYzAxM2Y2MDA0NGY1NiIsImlzcyI6ImNhcmdvLXBsYXRmb3JtIiwic3ViIjoiMTlmZjEwMzFhMTFjMGMwMTNmNjAwNDRmNTYiLCJleHAiOjE3ODY4MTMyMTUsImlhdCI6MTc4NjgwOTYxNX0.RyfPuq_BNl6RU8msz_LWC_ZzReo71_VlLzcjnELomBU"}
{"level":"info","ts":"2026-08-15T23:02:38.791+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T23:02:38.869+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T23:02:38.869+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T23:02:38.937+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T23:02:39.067+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-15T23:02:41.220+0700","caller":"middleware/auth.go:44","msg":"session_login : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIxOWZmMTAzMWExMWMwYzAxM2Y2MDA0NGY1NiIsImlzcyI6ImNhcmdvLXBsYXRmb3JtIiwic3ViIjoiMTlmZjEwMzFhMTFjMGMwMTNmNjAwNDRmNTYiLCJleHAiOjE3ODY4MTMyMTUsImlhdCI6MTc4NjgwOTYxNX0.RyfPuq_BNl6RU8msz_LWC_ZzReo71_VlLzcjnELomBU"}
{"level":"info","ts":"2026-08-15T23:02:56.824+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T23:02:56.914+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T23:02:56.914+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T23:02:56.988+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T23:02:57.104+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-15T23:02:58.684+0700","caller":"middleware/auth.go:44","msg":"session_login : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIxOWZmMTAzMWExMWMwYzAxM2Y2MDA0NGY1NiIsImlzcyI6ImNhcmdvLXBsYXRmb3JtIiwic3ViIjoiMTlmZjEwMzFhMTFjMGMwMTNmNjAwNDRmNTYiLCJleHAiOjE3ODY4MTMyMTUsImlhdCI6MTc4NjgwOTYxNX0.RyfPuq_BNl6RU8msz_LWC_ZzReo71_VlLzcjnELomBU"}
{"level":"info","ts":"2026-08-15T23:16:20.283+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T23:16:20.369+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T23:16:20.369+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T23:16:20.452+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T23:18:31.629+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T23:18:31.719+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T23:18:31.719+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T23:18:31.789+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T23:18:31.903+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-15T23:18:39.291+0700","caller":"handlers/handlers.go:64","msg":"Request Success","status":200,"method":"POST","path":"/login","query":"","ip":"127.0.0.1","latency":0.140461083,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-15T23:20:19.696+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T23:20:19.784+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T23:20:19.785+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T23:20:19.857+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T23:20:19.962+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"warn","ts":"2026-08-15T23:20:22.753+0700","caller":"handlers/handlers.go:62","msg":"Client Error","status":401,"method":"POST","path":"/login","query":"","ip":"127.0.0.1","latency":0.1520805,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-15T23:22:36.618+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T23:22:36.732+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T23:22:36.732+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T23:22:36.808+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T23:22:36.921+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-15T23:22:41.571+0700","caller":"handlers/handlers.go:64","msg":"Request Success","status":200,"method":"POST","path":"/login","query":"","ip":"127.0.0.1","latency":0.14289375,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-15T23:23:53.871+0700","caller":"middleware/auth.go:44","msg":"session_login : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIxOWZmMTAzMWExMWMwYzAxM2Y2MDA0NGY1NiIsImlzcyI6ImNhcmdvLXBsYXRmb3JtIiwic3ViIjoiMTlmZjEwMzFhMTFjMGMwMTNmNjAwNDRmNTYiLCJleHAiOjE3ODY4MTQ1NjEsImlhdCI6MTc4NjgxMDk2MX0.no9KKVGl_khgBf8tGE8FAl9_zBdcWkEUiUFzmFmOacc"}
{"level":"info","ts":"2026-08-15T23:25:24.065+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T23:25:24.147+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T23:25:24.147+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T23:25:24.214+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T23:25:24.320+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-15T23:25:26.242+0700","caller":"middleware/auth.go:43","msg":"session_login : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIxOWZmMTAzMWExMWMwYzAxM2Y2MDA0NGY1NiIsImlzcyI6ImNhcmdvLXBsYXRmb3JtIiwic3ViIjoiMTlmZjEwMzFhMTFjMGMwMTNmNjAwNDRmNTYiLCJleHAiOjE3ODY4MTQ1NjEsImlhdCI6MTc4NjgxMDk2MX0.no9KKVGl_khgBf8tGE8FAl9_zBdcWkEUiUFzmFmOacc"}
{"level":"info","ts":"2026-08-15T23:25:26.255+0700","caller":"middleware/auth.go:55","msg":"Set Auth : {\"id\":\"19ff1031a11c0c013f60044f56\",\"merchant_id\":null,\"nick_name\":\"Super Admin\",\"email\":\"crackers.biscuits@gmail.com\",\"password\":\"$2a$10$A0R8A2CDxxcQuGbTW/r2merI4rymcbe27Wh5kPH..qNMJUzmtAb/e\",\"is_active\":true,\"users_role_id\":\"19fe813c54d12bf48f1d7c710e\",\"blocked\":false,\"blocked_notes\":null,\"created_by\":null,\"created_on\":\"2026-08-11T20:29:00.18279Z\",\"updated_by\":null,\"updated_on\":\"2026-08-11T20:29:00.18279Z\",\"merchant_relation\":null,\"users_role_relation\":{\"id\":\"\",\"name\":null,\"merchant_id\":null,\"created_by\":null,\"created_on\":null,\"updated_by\":null,\"updated_on\":null,\"role_key\":\"\",\"merchant_relation\":{\"id\":\"\",\"name\":null,\"address\":null,\"email\":null,\"telp\":null,\"additional_information\":null,\"active\":null,\"created_by\":null,\"created_on\":null,\"updated_by\":null,\"updated_on\":null,\"province_id\":null,\"city_id\":null,\"district_id\":null,\"subdistrict_id\":null,\"postal_code\":null,\"province_relation\":null,\"city_relation\":null,\"district_relation\":null,\"subdistrict_relation\":null,\"users_roless_relation\":null,\"modas_relation\":null,\"package_types_relation\":null,\"userss_relation\":null,\"created_by_relation\":null,\"provinces_relation\":null,\"citys_relation\":null,\"districts_relation\":null,\"subdistricts_relation\":null,\"postal_codes_relation\":null},\"userss_relation\":null},\"merchants_relation\":null,\"provinces_relation\":null,\"citys_relation\":null,\"districts_relation\":null,\"subdistricts_relation\":null,\"postal_codes_relation\":null}"}
{"level":"info","ts":"2026-08-15T23:25:26.257+0700","caller":"handlers/handlers.go:64","msg":"Request Success","status":200,"method":"POST","path":"/logout","query":"","ip":"127.0.0.1","latency":0.014630542,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-15T23:30:07.616+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T23:30:07.728+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T23:30:07.728+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T23:30:07.806+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T23:30:07.936+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-15T23:30:12.541+0700","caller":"middleware/auth.go:43","msg":"session_login : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIxOWZmMTAzMWExMWMwYzAxM2Y2MDA0NGY1NiIsImlzcyI6ImNhcmdvLXBsYXRmb3JtIiwic3ViIjoiMTlmZjEwMzFhMTFjMGMwMTNmNjAwNDRmNTYiLCJleHAiOjE3ODY4MTQ1NjEsImlhdCI6MTc4NjgxMDk2MX0.no9KKVGl_khgBf8tGE8FAl9_zBdcWkEUiUFzmFmOacc"}
{"level":"info","ts":"2026-08-15T23:30:12.551+0700","caller":"middleware/auth.go:55","msg":"Set Auth : {\"id\":\"19ff1031a11c0c013f60044f56\",\"merchant_id\":null,\"nick_name\":\"Super Admin\",\"email\":\"crackers.biscuits@gmail.com\",\"password\":\"$2a$10$A0R8A2CDxxcQuGbTW/r2merI4rymcbe27Wh5kPH..qNMJUzmtAb/e\",\"is_active\":true,\"users_role_id\":\"19fe813c54d12bf48f1d7c710e\",\"blocked\":false,\"blocked_notes\":null,\"created_by\":null,\"created_on\":\"2026-08-11T20:29:00.18279Z\",\"updated_by\":null,\"updated_on\":\"2026-08-11T20:29:00.18279Z\",\"merchant_relation\":null,\"users_role_relation\":{\"id\":\"\",\"name\":null,\"merchant_id\":null,\"created_by\":null,\"created_on\":null,\"updated_by\":null,\"updated_on\":null,\"role_key\":\"\",\"merchant_relation\":{\"id\":\"\",\"name\":null,\"address\":null,\"email\":null,\"telp\":null,\"additional_information\":null,\"active\":null,\"created_by\":null,\"created_on\":null,\"updated_by\":null,\"updated_on\":null,\"province_id\":null,\"city_id\":null,\"district_id\":null,\"subdistrict_id\":null,\"postal_code\":null,\"province_relation\":null,\"city_relation\":null,\"district_relation\":null,\"subdistrict_relation\":null,\"users_roless_relation\":null,\"modas_relation\":null,\"package_types_relation\":null,\"userss_relation\":null,\"created_by_relation\":null,\"provinces_relation\":null,\"citys_relation\":null,\"districts_relation\":null,\"subdistricts_relation\":null,\"postal_codes_relation\":null},\"userss_relation\":null},\"merchants_relation\":null,\"provinces_relation\":null,\"citys_relation\":null,\"districts_relation\":null,\"subdistricts_relation\":null,\"postal_codes_relation\":null}"}
{"level":"info","ts":"2026-08-15T23:30:12.566+0700","caller":"handlers/handlers.go:64","msg":"Request Success","status":200,"method":"POST","path":"/logout","query":"","ip":"127.0.0.1","latency":0.024830708,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-15T23:30:21.452+0700","caller":"middleware/auth.go:43","msg":"session_login : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIxOWZmMTAzMWExMWMwYzAxM2Y2MDA0NGY1NiIsImlzcyI6ImNhcmdvLXBsYXRmb3JtIiwic3ViIjoiMTlmZjEwMzFhMTFjMGMwMTNmNjAwNDRmNTYiLCJleHAiOjE3ODY4MTQ1NjEsImlhdCI6MTc4NjgxMDk2MX0.no9KKVGl_khgBf8tGE8FAl9_zBdcWkEUiUFzmFmOacc"}
{"level":"warn","ts":"2026-08-15T23:30:21.472+0700","caller":"handlers/handlers.go:62","msg":"Client Error","status":401,"method":"POST","path":"/logout","query":"","ip":"127.0.0.1","latency":0.020868,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-15T23:30:23.607+0700","caller":"middleware/auth.go:43","msg":"session_login : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIxOWZmMTAzMWExMWMwYzAxM2Y2MDA0NGY1NiIsImlzcyI6ImNhcmdvLXBsYXRmb3JtIiwic3ViIjoiMTlmZjEwMzFhMTFjMGMwMTNmNjAwNDRmNTYiLCJleHAiOjE3ODY4MTQ1NjEsImlhdCI6MTc4NjgxMDk2MX0.no9KKVGl_khgBf8tGE8FAl9_zBdcWkEUiUFzmFmOacc"}
{"level":"warn","ts":"2026-08-15T23:30:23.625+0700","caller":"handlers/handlers.go:62","msg":"Client Error","status":401,"method":"POST","path":"/logout","query":"","ip":"127.0.0.1","latency":0.017106458,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-15T23:36:21.508+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T23:36:21.606+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T23:36:21.606+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T23:36:21.701+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T23:36:21.807+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-15T23:37:59.688+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-15T23:37:59.767+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-15T23:37:59.767+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-15T23:37:59.842+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-15T23:37:59.948+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-15T23:39:05.986+0700","caller":"handlers/handlers.go:64","msg":"Request Success","status":200,"method":"POST","path":"/login","query":"","ip":"127.0.0.1","latency":0.152333541,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-15T23:39:18.222+0700","caller":"middleware/auth.go:43","msg":"session_login : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIxOWZmMTAzMWExMWMwYzAxM2Y2MDA0NGY1NiIsImlzcyI6ImNhcmdvLXBsYXRmb3JtIiwic3ViIjoiMTlmZjEwMzFhMTFjMGMwMTNmNjAwNDRmNTYiLCJleHAiOjE3ODY4MTU1NDUsImlhdCI6MTc4NjgxMTk0NX0.U-AGAkbba7kij5iIPjV7x7VY6TUJ3ehO1UN4qqjMM7k"}
{"level":"info","ts":"2026-08-15T23:39:18.233+0700","caller":"middleware/auth.go:79","msg":"Set Auth : {\"id\":\"19ff1031a11c0c013f60044f56\",\"merchant_id\":null,\"nick_name\":\"Super Admin\",\"email\":\"crackers.biscuits@gmail.com\",\"password\":\"$2a$10$A0R8A2CDxxcQuGbTW/r2merI4rymcbe27Wh5kPH..qNMJUzmtAb/e\",\"is_active\":true,\"users_role_id\":\"19fe813c54d12bf48f1d7c710e\",\"blocked\":false,\"blocked_notes\":null,\"created_by\":null,\"created_on\":\"2026-08-11T20:29:00.18279Z\",\"updated_by\":null,\"updated_on\":\"2026-08-11T20:29:00.18279Z\",\"merchant_relation\":null,\"users_role_relation\":{\"id\":\"\",\"name\":null,\"merchant_id\":null,\"created_by\":null,\"created_on\":null,\"updated_by\":null,\"updated_on\":null,\"role_key\":\"\",\"merchant_relation\":{\"id\":\"\",\"name\":null,\"address\":null,\"email\":null,\"telp\":null,\"additional_information\":null,\"active\":null,\"created_by\":null,\"created_on\":null,\"updated_by\":null,\"updated_on\":null,\"province_id\":null,\"city_id\":null,\"district_id\":null,\"subdistrict_id\":null,\"postal_code\":null,\"province_relation\":null,\"city_relation\":null,\"district_relation\":null,\"subdistrict_relation\":null,\"users_roless_relation\":null,\"modas_relation\":null,\"package_types_relation\":null,\"userss_relation\":null,\"created_by_relation\":null,\"provinces_relation\":null,\"citys_relation\":null,\"districts_relation\":null,\"subdistricts_relation\":null,\"postal_codes_relation\":null},\"userss_relation\":null},\"merchants_relation\":null,\"provinces_relation\":null,\"citys_relation\":null,\"districts_relation\":null,\"subdistricts_relation\":null,\"postal_codes_relation\":null}"}
{"level":"info","ts":"2026-08-15T23:39:18.247+0700","caller":"handlers/handlers.go:64","msg":"Request Success","status":200,"method":"POST","path":"/logout","query":"","ip":"127.0.0.1","latency":0.024412875,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-15T23:39:20.977+0700","caller":"middleware/auth.go:43","msg":"session_login : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIxOWZmMTAzMWExMWMwYzAxM2Y2MDA0NGY1NiIsImlzcyI6ImNhcmdvLXBsYXRmb3JtIiwic3ViIjoiMTlmZjEwMzFhMTFjMGMwMTNmNjAwNDRmNTYiLCJleHAiOjE3ODY4MTU1NDUsImlhdCI6MTc4NjgxMTk0NX0.U-AGAkbba7kij5iIPjV7x7VY6TUJ3ehO1UN4qqjMM7k"}
{"level":"warn","ts":"2026-08-15T23:39:20.997+0700","caller":"handlers/handlers.go:62","msg":"Client Error","status":401,"method":"POST","path":"/logout","query":"","ip":"127.0.0.1","latency":0.020748084,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-16T00:15:07.911+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-16T00:15:08.007+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-16T00:15:08.007+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-16T00:15:08.078+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-16T00:15:08.194+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-16T00:28:48.223+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-16T00:28:48.343+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-16T00:28:48.358+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-16T00:28:48.457+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-16T00:28:48.560+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-16T00:28:52.225+0700","caller":"handlers/handlers.go:64","msg":"Request Success","status":200,"method":"POST","path":"/login","query":"","ip":"127.0.0.1","latency":0.146153875,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-16T00:29:06.165+0700","caller":"middleware/auth.go:43","msg":"session_login : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIxOWZmMTAzMWExMWMwYzAxM2Y2MDA0NGY1NiIsImlzcyI6ImNhcmdvLXBsYXRmb3JtIiwic3ViIjoiMTlmZjEwMzFhMTFjMGMwMTNmNjAwNDRmNTYiLCJleHAiOjE3ODY4MTg1MzIsImlhdCI6MTc4NjgxNDkzMn0.La4ZzjebMg4OaAcLd-CBWR1HgVZnqpKuo3y_DyFbhm0"}
{"level":"warn","ts":"2026-08-16T00:29:06.199+0700","caller":"handlers/handlers.go:62","msg":"Client Error","status":401,"method":"POST","path":"/logout","query":"","ip":"127.0.0.1","latency":0.033537125,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-16T00:31:49.605+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-16T00:31:49.711+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-16T00:31:49.711+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-16T00:31:49.784+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-16T00:31:49.888+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-16T00:31:52.717+0700","caller":"middleware/auth.go:43","msg":"session_login : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIxOWZmMTAzMWExMWMwYzAxM2Y2MDA0NGY1NiIsImlzcyI6ImNhcmdvLXBsYXRmb3JtIiwic3ViIjoiMTlmZjEwMzFhMTFjMGMwMTNmNjAwNDRmNTYiLCJleHAiOjE3ODY4MTg1MzIsImlhdCI6MTc4NjgxNDkzMn0.La4ZzjebMg4OaAcLd-CBWR1HgVZnqpKuo3y_DyFbhm0"}
{"level":"warn","ts":"2026-08-16T00:31:52.740+0700","caller":"handlers/handlers.go:62","msg":"Client Error","status":401,"method":"POST","path":"/logout","query":"","ip":"127.0.0.1","latency":0.022636333,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-16T00:32:38.076+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-16T00:32:38.197+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-16T00:32:38.197+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-16T00:32:38.287+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-16T00:32:38.399+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-16T00:32:42.956+0700","caller":"middleware/auth.go:43","msg":"session_login : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIxOWZmMTAzMWExMWMwYzAxM2Y2MDA0NGY1NiIsImlzcyI6ImNhcmdvLXBsYXRmb3JtIiwic3ViIjoiMTlmZjEwMzFhMTFjMGMwMTNmNjAwNDRmNTYiLCJleHAiOjE3ODY4MTg1MzIsImlhdCI6MTc4NjgxNDkzMn0.La4ZzjebMg4OaAcLd-CBWR1HgVZnqpKuo3y_DyFbhm0"}
{"level":"warn","ts":"2026-08-16T00:32:42.978+0700","caller":"handlers/handlers.go:62","msg":"Client Error","status":401,"method":"POST","path":"/logout","query":"","ip":"127.0.0.1","latency":0.022303667,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-16T00:37:33.768+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-16T00:37:33.851+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-16T00:37:33.851+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-16T00:37:33.920+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-16T00:37:34.026+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-16T00:37:36.633+0700","caller":"middleware/auth.go:44","msg":"session_login : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIxOWZmMTAzMWExMWMwYzAxM2Y2MDA0NGY1NiIsImlzcyI6ImNhcmdvLXBsYXRmb3JtIiwic3ViIjoiMTlmZjEwMzFhMTFjMGMwMTNmNjAwNDRmNTYiLCJleHAiOjE3ODY4MTg1MzIsImlhdCI6MTc4NjgxNDkzMn0.La4ZzjebMg4OaAcLd-CBWR1HgVZnqpKuo3y_DyFbhm0"}
{"level":"info","ts":"2026-08-16T00:56:59.720+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-16T00:56:59.810+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-16T00:56:59.810+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-16T00:56:59.887+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-16T00:57:00.001+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-16T00:57:46.069+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-16T00:57:46.161+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-16T00:57:46.161+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-16T00:57:46.233+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-16T00:57:46.334+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-16T00:57:50.872+0700","caller":"middleware/auth.go:62","msg":"session_login : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIxOWZmMTAzMWExMWMwYzAxM2Y2MDA0NGY1NiIsImlzcyI6ImNhcmdvLXBsYXRmb3JtIiwic3ViIjoiMTlmZjEwMzFhMTFjMGMwMTNmNjAwNDRmNTYiLCJleHAiOjE3ODY4MTg1MzIsImlhdCI6MTc4NjgxNDkzMn0.La4ZzjebMg4OaAcLd-CBWR1HgVZnqpKuo3y_DyFbhm0"}
{"level":"info","ts":"2026-08-16T00:57:50.895+0700","caller":"middleware/auth.go:90","msg":"Set Auth : {\"blocked\":false,\"blocked_notes\":null,\"citys_relation\":null,\"created_by\":null,\"created_on\":\"2026-08-11T20:29:00.18279Z\",\"districts_relation\":null,\"email\":\"crackers.biscuits@gmail.com\",\"id\":\"19ff1031a11c0c013f60044f56\",\"is_active\":true,\"merchant_id\":null,\"merchant_relation\":null,\"merchants_relation\":null,\"nick_name\":\"Super Admin\",\"postal_codes_relation\":null,\"provinces_relation\":null,\"subdistricts_relation\":null,\"updated_by\":null,\"updated_on\":\"2026-08-11T20:29:00.18279Z\",\"users_role_id\":\"19fe813c54d12bf48f1d7c710e\",\"users_role_relation\":{\"created_by\":null,\"created_on\":null,\"id\":\"\",\"merchant_id\":null,\"merchant_relation\":{\"active\":null,\"additional_information\":null,\"address\":null,\"city_id\":null,\"city_relation\":null,\"citys_relation\":null,\"created_by\":null,\"created_by_relation\":null,\"created_on\":null,\"district_id\":null,\"district_relation\":null,\"districts_relation\":null,\"email\":null,\"id\":\"\",\"modas_relation\":null,\"name\":null,\"package_types_relation\":null,\"postal_code\":null,\"postal_codes_relation\":null,\"province_id\":null,\"province_relation\":null,\"provinces_relation\":null,\"subdistrict_id\":null,\"subdistrict_relation\":null,\"subdistricts_relation\":null,\"telp\":null,\"updated_by\":null,\"updated_on\":null,\"users_roless_relation\":null,\"userss_relation\":null},\"name\":null,\"role_key\":\"\",\"updated_by\":null,\"updated_on\":null,\"userss_relation\":null}}"}
{"level":"info","ts":"2026-08-16T00:57:50.911+0700","caller":"handlers/handlers.go:64","msg":"Request Success","status":200,"method":"POST","path":"/logout","query":"","ip":"127.0.0.1","latency":0.038014083,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-16T00:57:53.419+0700","caller":"middleware/auth.go:62","msg":"session_login : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIxOWZmMTAzMWExMWMwYzAxM2Y2MDA0NGY1NiIsImlzcyI6ImNhcmdvLXBsYXRmb3JtIiwic3ViIjoiMTlmZjEwMzFhMTFjMGMwMTNmNjAwNDRmNTYiLCJleHAiOjE3ODY4MTg1MzIsImlhdCI6MTc4NjgxNDkzMn0.La4ZzjebMg4OaAcLd-CBWR1HgVZnqpKuo3y_DyFbhm0"}
{"level":"warn","ts":"2026-08-16T00:57:53.429+0700","caller":"handlers/handlers.go:62","msg":"Client Error","status":401,"method":"POST","path":"/logout","query":"","ip":"127.0.0.1","latency":0.010037542,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-16T01:06:48.679+0700","caller":"middleware/auth.go:62","msg":"session_login : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIxOWZmMTAzMWExMWMwYzAxM2Y2MDA0NGY1NiIsImlzcyI6ImNhcmdvLXBsYXRmb3JtIiwic3ViIjoiMTlmZjEwMzFhMTFjMGMwMTNmNjAwNDRmNTYiLCJleHAiOjE3ODY4MTg1MzIsImlhdCI6MTc4NjgxNDkzMn0.La4ZzjebMg4OaAcLd-CBWR1HgVZnqpKuo3y_DyFbhm0"}
{"level":"warn","ts":"2026-08-16T01:06:48.704+0700","caller":"handlers/handlers.go:62","msg":"Client Error","status":401,"method":"POST","path":"/logout","query":"","ip":"127.0.0.1","latency":0.025659833,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-16T01:07:01.070+0700","caller":"handlers/handlers.go:64","msg":"Request Success","status":200,"method":"POST","path":"/login","query":"","ip":"127.0.0.1","latency":0.165668709,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-16T01:07:05.663+0700","caller":"handlers/handlers.go:64","msg":"Request Success","status":200,"method":"POST","path":"/login","query":"","ip":"127.0.0.1","latency":0.099613708,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-16T01:07:41.632+0700","caller":"middleware/auth.go:62","msg":"session_login : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIxOWZmMTAzMWExMWMwYzAxM2Y2MDA0NGY1NiIsImlzcyI6ImNhcmdvLXBsYXRmb3JtIiwic3ViIjoiMTlmZjEwMzFhMTFjMGMwMTNmNjAwNDRmNTYiLCJleHAiOjE3ODY4MjA4MjUsImlhdCI6MTc4NjgxNzIyNX0.00s8ApCLAmsJwbHVyAYyDjwRV3ht4Pu8YmzQFaRymnQ"}
{"level":"info","ts":"2026-08-16T01:07:41.662+0700","caller":"middleware/auth.go:90","msg":"Set Auth : {\"blocked\":false,\"blocked_notes\":null,\"citys_relation\":null,\"created_by\":null,\"created_on\":\"2026-08-11T20:29:00.18279Z\",\"districts_relation\":null,\"email\":\"crackers.biscuits@gmail.com\",\"id\":\"19ff1031a11c0c013f60044f56\",\"is_active\":true,\"merchant_id\":null,\"merchant_relation\":null,\"merchants_relation\":null,\"nick_name\":\"Super Admin\",\"postal_codes_relation\":null,\"provinces_relation\":null,\"subdistricts_relation\":null,\"updated_by\":null,\"updated_on\":\"2026-08-11T20:29:00.18279Z\",\"users_role_id\":\"19fe813c54d12bf48f1d7c710e\",\"users_role_relation\":{\"created_by\":null,\"created_on\":null,\"id\":\"\",\"merchant_id\":null,\"merchant_relation\":{\"active\":null,\"additional_information\":null,\"address\":null,\"city_id\":null,\"city_relation\":null,\"citys_relation\":null,\"created_by\":null,\"created_by_relation\":null,\"created_on\":null,\"district_id\":null,\"district_relation\":null,\"districts_relation\":null,\"email\":null,\"id\":\"\",\"modas_relation\":null,\"name\":null,\"package_types_relation\":null,\"postal_code\":null,\"postal_codes_relation\":null,\"province_id\":null,\"province_relation\":null,\"provinces_relation\":null,\"subdistrict_id\":null,\"subdistrict_relation\":null,\"subdistricts_relation\":null,\"telp\":null,\"updated_by\":null,\"updated_on\":null,\"users_roless_relation\":null,\"userss_relation\":null},\"name\":null,\"role_key\":\"\",\"updated_by\":null,\"updated_on\":null,\"userss_relation\":null}}"}
{"level":"info","ts":"2026-08-16T01:07:41.676+0700","caller":"handlers/handlers.go:64","msg":"Request Success","status":200,"method":"POST","path":"/logout","query":"","ip":"127.0.0.1","latency":0.044218125,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-16T01:07:47.376+0700","caller":"middleware/auth.go:62","msg":"session_login : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIxOWZmMTAzMWExMWMwYzAxM2Y2MDA0NGY1NiIsImlzcyI6ImNhcmdvLXBsYXRmb3JtIiwic3ViIjoiMTlmZjEwMzFhMTFjMGMwMTNmNjAwNDRmNTYiLCJleHAiOjE3ODY4MjA4MjUsImlhdCI6MTc4NjgxNzIyNX0.00s8ApCLAmsJwbHVyAYyDjwRV3ht4Pu8YmzQFaRymnQ"}
{"level":"warn","ts":"2026-08-16T01:07:47.399+0700","caller":"handlers/handlers.go:62","msg":"Client Error","status":401,"method":"POST","path":"/logout","query":"","ip":"127.0.0.1","latency":0.02364325,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-16T01:08:29.877+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-16T01:08:29.969+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-16T01:08:29.969+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-16T01:08:30.043+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-16T01:08:30.145+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-16T01:08:33.218+0700","caller":"handlers/handlers.go:64","msg":"Request Success","status":200,"method":"POST","path":"/login","query":"","ip":"127.0.0.1","latency":0.123252208,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-16T01:08:35.002+0700","caller":"handlers/handlers.go:64","msg":"Request Success","status":200,"method":"POST","path":"/login","query":"","ip":"127.0.0.1","latency":0.108842167,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-16T01:08:55.108+0700","caller":"database/database.go:53","msg":"Connect To Master DB"}
{"level":"info","ts":"2026-08-16T01:08:55.196+0700","caller":"database/database.go:63","msg":"DB Master Connected"}
{"level":"info","ts":"2026-08-16T01:08:55.196+0700","caller":"database/database.go:76","msg":"Connect To Slave DB"}
{"level":"info","ts":"2026-08-16T01:08:55.267+0700","caller":"database/database.go:86","msg":"DB Connection Done"}
{"level":"info","ts":"2026-08-16T01:08:55.372+0700","caller":"handlers/handlers.go:81","msg":"Starting Apps"}
{"level":"info","ts":"2026-08-16T01:08:55.959+0700","caller":"handlers/handlers.go:64","msg":"Request Success","status":200,"method":"POST","path":"/login","query":"","ip":"127.0.0.1","latency":0.128654042,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-16T01:09:49.767+0700","caller":"middleware/auth.go:62","msg":"session_login : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIxOWZmMTAzMWExMWMwYzAxM2Y2MDA0NGY1NiIsImlzcyI6ImNhcmdvLXBsYXRmb3JtIiwic3ViIjoiMTlmZjEwMzFhMTFjMGMwMTNmNjAwNDRmNTYiLCJleHAiOjE3ODY4MjA5MTMsImlhdCI6MTc4NjgxNzMxM30.2bT0Odo8WsWMuBD5NX4e1pV0_WEFxz3_e8IDazDpxvo"}
{"level":"info","ts":"2026-08-16T01:09:49.793+0700","caller":"middleware/auth.go:90","msg":"Set Auth : {\"blocked\":false,\"blocked_notes\":null,\"citys_relation\":null,\"created_by\":null,\"created_on\":\"2026-08-11T20:29:00.18279Z\",\"districts_relation\":null,\"email\":\"crackers.biscuits@gmail.com\",\"id\":\"19ff1031a11c0c013f60044f56\",\"is_active\":true,\"merchant_id\":null,\"merchant_relation\":null,\"merchants_relation\":null,\"nick_name\":\"Super Admin\",\"postal_codes_relation\":null,\"provinces_relation\":null,\"subdistricts_relation\":null,\"updated_by\":null,\"updated_on\":\"2026-08-11T20:29:00.18279Z\",\"users_role_id\":\"19fe813c54d12bf48f1d7c710e\",\"users_role_relation\":{\"created_by\":null,\"created_on\":null,\"id\":\"\",\"merchant_id\":null,\"merchant_relation\":{\"active\":null,\"additional_information\":null,\"address\":null,\"city_id\":null,\"city_relation\":null,\"citys_relation\":null,\"created_by\":null,\"created_by_relation\":null,\"created_on\":null,\"district_id\":null,\"district_relation\":null,\"districts_relation\":null,\"email\":null,\"id\":\"\",\"modas_relation\":null,\"name\":null,\"package_types_relation\":null,\"postal_code\":null,\"postal_codes_relation\":null,\"province_id\":null,\"province_relation\":null,\"provinces_relation\":null,\"subdistrict_id\":null,\"subdistrict_relation\":null,\"subdistricts_relation\":null,\"telp\":null,\"updated_by\":null,\"updated_on\":null,\"users_roless_relation\":null,\"userss_relation\":null},\"name\":null,\"role_key\":\"\",\"updated_by\":null,\"updated_on\":null,\"userss_relation\":null}}"}
{"level":"info","ts":"2026-08-16T01:09:49.806+0700","caller":"handlers/handlers.go:64","msg":"Request Success","status":200,"method":"POST","path":"/logout","query":"","ip":"127.0.0.1","latency":0.040288125,"user-agent":"insomnia/13.1.0"}
{"level":"info","ts":"2026-08-16T01:09:56.587+0700","caller":"handlers/handlers.go:64","msg":"Request Success","status":200,"method":"POST","path":"/login","query":"","ip":"127.0.0.1","latency":0.1347395,"user-agent":"insomnia/13.1.0"}

3126
storage/logs/db.log Normal file

File diff suppressed because it is too large Load Diff