Update JWT
This commit is contained in:
parent
54aeb3568a
commit
1db200435e
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# Ignore environment config
|
||||||
|
.env
|
||||||
16
Dockerfile
Normal file
16
Dockerfile
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Dockerfile
|
||||||
|
FROM golang:1.24.4
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY go.mod ./
|
||||||
|
COPY go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN go build -o main ./cmd/main.go
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
CMD ["./main"]
|
||||||
@ -1,6 +1,7 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"BE-MiniERP/middlewares"
|
||||||
"BE-MiniERP/modules/auth"
|
"BE-MiniERP/modules/auth"
|
||||||
"BE-MiniERP/modules/inventory"
|
"BE-MiniERP/modules/inventory"
|
||||||
"BE-MiniERP/modules/sales"
|
"BE-MiniERP/modules/sales"
|
||||||
@ -10,6 +11,10 @@ import (
|
|||||||
|
|
||||||
func SetupRoutes(app *fiber.App) {
|
func SetupRoutes(app *fiber.App) {
|
||||||
auth.RegisterRoutes(app.Group("/auth"))
|
auth.RegisterRoutes(app.Group("/auth"))
|
||||||
|
|
||||||
|
app.Use("/inventory", middlewares.JWTProtected())
|
||||||
|
app.Use("/sales", middlewares.JWTProtected())
|
||||||
|
|
||||||
inventory.RegisterRoutes(app.Group("/inventory"))
|
inventory.RegisterRoutes(app.Group("/inventory"))
|
||||||
sales.RegisterRoutes(app.Group("/sales"))
|
sales.RegisterRoutes(app.Group("/sales"))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,12 +4,15 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
|
|
||||||
"BE-MiniERP/api"
|
"BE-MiniERP/api"
|
||||||
|
"BE-MiniERP/config"
|
||||||
"BE-MiniERP/database" // pastikan ini sesuai dengan module di go.mod
|
"BE-MiniERP/database" // pastikan ini sesuai dengan module di go.mod
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
// Inisialisasi konfigurasi
|
||||||
|
config.InitConfig()
|
||||||
// Koneksi database
|
// Koneksi database
|
||||||
database.Connect()
|
database.Connect()
|
||||||
|
|
||||||
|
|||||||
@ -1,15 +1,45 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import "os"
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/joho/godotenv"
|
||||||
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
DBUrl string
|
|
||||||
JWTSecret string
|
JWTSecret string
|
||||||
|
DBHost string
|
||||||
|
DBPort string
|
||||||
|
DBUser string
|
||||||
|
DBPassword string
|
||||||
|
DBName string
|
||||||
|
DBSSLMode string
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetConfig() Config {
|
var appConfig = &Config{}
|
||||||
return Config{
|
|
||||||
DBUrl: os.Getenv("DB_URL"),
|
func InitConfig() {
|
||||||
JWTSecret: os.Getenv("JWT_SECRET"),
|
// Load .env file
|
||||||
|
err := godotenv.Load()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Warning: .env file not found, using system environment variables.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load config from env
|
||||||
|
appConfig.JWTSecret = os.Getenv("JWT_SECRET")
|
||||||
|
appConfig.DBHost = os.Getenv("DB_HOST")
|
||||||
|
appConfig.DBPort = os.Getenv("DB_PORT")
|
||||||
|
appConfig.DBUser = os.Getenv("DB_USER")
|
||||||
|
appConfig.DBPassword = os.Getenv("DB_PASSWORD")
|
||||||
|
appConfig.DBName = os.Getenv("DB_NAME")
|
||||||
|
appConfig.DBSSLMode = os.Getenv("DB_SSLMODE")
|
||||||
|
|
||||||
|
if appConfig.JWTSecret == "" || appConfig.DBHost == "" {
|
||||||
|
log.Fatal("Required environment variables are missing")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetConfig() *Config {
|
||||||
|
return appConfig
|
||||||
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@ CREATE TABLE users (
|
|||||||
updated_at TIMESTAMP DEFAULT NOW()
|
updated_at TIMESTAMP DEFAULT NOW()
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE master_product_category (
|
CREATE TABLE product_categories (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
code VARCHAR(50),
|
code VARCHAR(50),
|
||||||
name VARCHAR(100),
|
name VARCHAR(100),
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
package database
|
package database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"BE-MiniERP/config"
|
||||||
auth_models "BE-MiniERP/modules/auth/models"
|
auth_models "BE-MiniERP/modules/auth/models"
|
||||||
inventory_models "BE-MiniERP/modules/inventory/models"
|
inventory_models "BE-MiniERP/modules/inventory/models"
|
||||||
sales_models "BE-MiniERP/modules/sales/models"
|
sales_models "BE-MiniERP/modules/sales/models"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"gorm.io/driver/postgres"
|
"gorm.io/driver/postgres"
|
||||||
@ -13,18 +15,22 @@ import (
|
|||||||
var DB *gorm.DB
|
var DB *gorm.DB
|
||||||
|
|
||||||
func Connect() {
|
func Connect() {
|
||||||
dsn := "host=10.5.50.9 user=probindo password=Pr08ind0 dbname=erp port=5432 sslmode=disable"
|
cfg := config.GetConfig()
|
||||||
|
|
||||||
|
dsn := fmt.Sprintf(
|
||||||
|
"host=%s user=%s password=%s dbname=%s port=%s sslmode=%s",
|
||||||
|
cfg.DBHost, cfg.DBUser, cfg.DBPassword, cfg.DBName, cfg.DBPort, cfg.DBSSLMode,
|
||||||
|
)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})
|
DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Failed to connect to DB:", err)
|
log.Fatal("Failed to connect to DB:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Auto migrate models
|
||||||
err = DB.AutoMigrate(
|
err = DB.AutoMigrate(
|
||||||
// Auth
|
|
||||||
&auth_models.User{},
|
&auth_models.User{},
|
||||||
|
|
||||||
// Inventory
|
|
||||||
&inventory_models.Product{},
|
&inventory_models.Product{},
|
||||||
&inventory_models.ProductCategory{},
|
&inventory_models.ProductCategory{},
|
||||||
&inventory_models.Collection{},
|
&inventory_models.Collection{},
|
||||||
@ -34,8 +40,6 @@ func Connect() {
|
|||||||
&inventory_models.ProductionOrder{},
|
&inventory_models.ProductionOrder{},
|
||||||
&inventory_models.Warehouse{},
|
&inventory_models.Warehouse{},
|
||||||
&inventory_models.StockMovement{},
|
&inventory_models.StockMovement{},
|
||||||
|
|
||||||
// Sales
|
|
||||||
&sales_models.Customer{},
|
&sales_models.Customer{},
|
||||||
&sales_models.SalesOrder{},
|
&sales_models.SalesOrder{},
|
||||||
&sales_models.SalesOrderItem{},
|
&sales_models.SalesOrderItem{},
|
||||||
|
|||||||
10
docker-compose.yml
Normal file
10
docker-compose.yml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
build: .
|
||||||
|
container_name: be-minierp-container
|
||||||
|
ports:
|
||||||
|
- "8080:3000"
|
||||||
|
environment:
|
||||||
|
- DATABASE_URL=host=10.5.50.9 user=probindo password=Pr08ind0 dbname=erp port=5432 sslmode=disable
|
||||||
1
go.mod
1
go.mod
@ -14,6 +14,7 @@ require (
|
|||||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/jinzhu/now v1.1.5 // indirect
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
|
github.com/joho/godotenv v1.5.1
|
||||||
github.com/klauspost/compress v1.17.9 // indirect
|
github.com/klauspost/compress v1.17.9 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
|||||||
2
go.sum
2
go.sum
@ -19,6 +19,8 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
|
|||||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
||||||
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
|
|||||||
@ -12,7 +12,9 @@ func JWTProtected() fiber.Handler {
|
|||||||
return func(c *fiber.Ctx) error {
|
return func(c *fiber.Ctx) error {
|
||||||
authHeader := c.Get("Authorization")
|
authHeader := c.Get("Authorization")
|
||||||
if authHeader == "" {
|
if authHeader == "" {
|
||||||
return c.SendStatus(fiber.StatusUnauthorized)
|
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
|
||||||
|
"message": "Missing Authorization header",
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenString := strings.TrimPrefix(authHeader, "Bearer ")
|
tokenString := strings.TrimPrefix(authHeader, "Bearer ")
|
||||||
@ -21,10 +23,19 @@ func JWTProtected() fiber.Handler {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if err != nil || !token.Valid {
|
if err != nil || !token.Valid {
|
||||||
return c.SendStatus(fiber.StatusUnauthorized)
|
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
|
||||||
|
"message": "Invalid or expired token",
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Locals("user", token.Claims)
|
claims, ok := token.Claims.(jwt.MapClaims)
|
||||||
|
if !ok {
|
||||||
|
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
|
||||||
|
"message": "Invalid token claims",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Locals("user", claims)
|
||||||
return c.Next()
|
return c.Next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,27 +1,21 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"BE-MiniERP/config"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang-jwt/jwt/v4"
|
"github.com/golang-jwt/jwt/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
var jwtKey = []byte("supersecret") // sebaiknya dari env
|
|
||||||
|
|
||||||
type Claims struct {
|
|
||||||
UserID uint `json:"user_id"`
|
|
||||||
Role string `json:"role"`
|
|
||||||
jwt.RegisteredClaims
|
|
||||||
}
|
|
||||||
|
|
||||||
func GenerateJWT(userID uint, role string) (string, error) {
|
func GenerateJWT(userID uint, role string) (string, error) {
|
||||||
claims := &Claims{
|
claims := jwt.MapClaims{
|
||||||
UserID: userID,
|
"user_id": userID,
|
||||||
Role: role,
|
"role": role,
|
||||||
RegisteredClaims: jwt.RegisteredClaims{
|
"exp": time.Now().Add(time.Hour * 1).Unix(), // expired dalam 1 jam
|
||||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||||
return token.SignedString(jwtKey)
|
|
||||||
|
// INI PALING PENTING ⬇️
|
||||||
|
return token.SignedString([]byte(config.GetConfig().JWTSecret))
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user