26 Commits
Author SHA1 Message Date
ericprd c48a4b944b fix: update tags and categories not updated 2025-03-07 15:16:05 +08:00
ericprd c410e651ce fix: categories filter is multiple now 2025-03-07 14:30:31 +08:00
ericprd 51b50a3132 fix: get news by query category and tags filters 2025-03-07 00:55:50 +08:00
ericprd e3a9b67e28 feat: update news 2025-03-06 23:55:46 +08:00
ericprd 2046021e01 Merge branch 'main' of github.com:ardeman/project-legalgo-go 2025-03-06 13:30:39 +08:00
ericprd a2a6fe6374 fix: use redis config 2025-03-06 13:30:24 +08:00
Ardeman d6a680634c chore: remove deprecated jwt dependency and update to v5 2025-03-06 12:58:28 +08:00
ericprd cf410bdf05 feat: add redis config env 2025-03-06 12:56:35 +08:00
ericprd efc87ee63e Merge branch 'main' of github.com:ardeman/project-legalgo-go 2025-03-06 11:59:17 +08:00
ericprd c037da4879 feat: add vendor 2025-03-06 11:59:05 +08:00
0xCA7andGitHub 53fda59e3c Delete .DS_Store 2025-03-06 11:17:10 +08:00
ericprd 2525eed2ea Merge branch 'main' of github.com:ardeman/project-legalgo-go 2025-03-06 11:07:04 +08:00
ericprd 85247643e6 feat: get news by slug and and author detail 2025-03-06 11:06:06 +08:00
ericprd d217ec8a62 feat: delete news, tag, and category 2025-03-05 23:55:41 +08:00
ericprd 299fcf949b feat: update and delete tags and categories 2025-03-05 23:13:24 +08:00
ericprd 5c699dfa53 fix: create and get news 2025-03-05 22:15:39 +08:00
ericprd dd109c8aa0 chores: remove unused code 2025-03-05 21:27:37 +08:00
ericprd 567e0e32ca chores: refactor struct and structure project 2025-03-05 21:21:44 +08:00
altru 2b54a9343b Merge pull request 'Update Env' (#4) from feature-test into main
Reviewed-on: #4
2025-03-03 12:56:18 +00:00
altru 43d5608a51 Merge pull request 'update docker' (#3) from feature-test into main
Reviewed-on: #3
2025-03-03 12:39:25 +00:00
altru 883ff8b7d1 Merge pull request 'add docker file' (#2) from feature-test into main
Reviewed-on: #2
2025-03-03 12:30:23 +00:00
altru 750a4dc3db Merge pull request 'Add Infra' (#1) from feature-test into main
Reviewed-on: #1
2025-03-03 12:03:19 +00:00
ericprd 13a8481f22 fix: payload update staff 2025-03-03 13:23:04 +08:00
ericprd 642566c8b2 Merge branch 'main' of github.com:ardeman/project-legalgo-go 2025-03-03 10:39:38 +08:00
ericprd afe6f4e9a6 fix: change username to name and add profile pic to staff 2025-03-03 10:39:01 +08:00
Ardeman 20c9660c3a docs: add instruction to execute binary directly 2025-03-02 22:43:39 +08:00
141 changed files with 1981 additions and 888 deletions
+1
View File
@@ -14,3 +14,4 @@ REDIS_PORT=
REDIS_PASSWORD= REDIS_PASSWORD=
REDIS_TTL= REDIS_TTL=
REDIS_DB= REDIS_DB=
REDIS_TIMEOUT=
+2
View File
@@ -1,2 +1,4 @@
bin bin
.env .env
.DS_Store
/cmd/legalgo/env
+6
View File
@@ -148,6 +148,12 @@ make run
This command will compile and execute your Go application. This command will compile and execute your Go application.
Or you can execute the compiled binary at `bin/legalgo` by:
```bash
./bin/legalgo
```
--- ---
**Note:** Ensure that your `Makefile` is correctly set up to handle these commands. If not, you may need to create **Note:** Ensure that your `Makefile` is correctly set up to handle these commands. If not, you may need to create
+12 -1
View File
@@ -15,6 +15,7 @@ import (
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/joho/godotenv" "github.com/joho/godotenv"
"github.com/redis/go-redis/v9"
"go.uber.org/fx" "go.uber.org/fx"
) )
@@ -32,16 +33,26 @@ func init() {
config.InitEnv() config.InitEnv()
} }
func run(lc fx.Lifecycle, db *database.DB, apiRouter chi.Router) { func run(
lc fx.Lifecycle,
db *database.DB,
apiRouter chi.Router,
rdb *redis.Client,
) {
lc.Append(fx.Hook{ lc.Append(fx.Hook{
OnStart: func(ctx context.Context) error { OnStart: func(ctx context.Context) error {
fmt.Println("Application has started...") fmt.Println("Application has started...")
_, err := rdb.Ping(ctx).Result()
if err != nil {
log.Fatalf("Could not connect to Redis: %v", err)
}
pkgconfig.Router(apiRouter) pkgconfig.Router(apiRouter)
return nil return nil
}, },
OnStop: func(ctx context.Context) error { OnStop: func(ctx context.Context) error {
fmt.Println("Shutting down...") fmt.Println("Shutting down...")
rdb.Close()
return nil return nil
}, },
}) })
+14 -1
View File
@@ -1,7 +1,11 @@
package config package config
import "fmt"
var ( var (
APP_PORT int APP_PORT,
REDIS_DB,
REDIS_TIMEOUT,
GRACEFULL_TIMEOUT int GRACEFULL_TIMEOUT int
// DB // DB
@@ -10,6 +14,9 @@ var (
DB_PASSWORD, DB_PASSWORD,
DB_NAME, DB_NAME,
DB_PORT, DB_PORT,
REDIS_PASSWORD,
REDIS_ADDR,
REDIS_USERNAME,
SALT_SECURITY string SALT_SECURITY string
) )
@@ -24,4 +31,10 @@ func InitEnv() {
APP_PORT = GetOrDefault("APP_PORT", 3000) APP_PORT = GetOrDefault("APP_PORT", 3000)
GRACEFULL_TIMEOUT = GetOrDefault("GRACEFULL_TIMEOUT", 10) GRACEFULL_TIMEOUT = GetOrDefault("GRACEFULL_TIMEOUT", 10)
REDIS_DB = GetOrDefault("REDIS_DB", 0)
REDIS_PASSWORD = GetOrDefault("REDIS_PASSWORD", "")
REDIS_ADDR = fmt.Sprintf("%s:%s", GetOrDefault("REDIS_HOST", "localhost"), GetOrDefault("REDIS_PORT", "6379"))
REDIS_USERNAME = GetOrDefault("REDIS_USERNAME", "")
REDIS_TIMEOUT = GetOrDefault("REDIS_TIMEOUT", 60)
} }
+1
View File
@@ -28,6 +28,7 @@ type Config struct {
Database Database `mapstructure:"postgresql"` Database Database `mapstructure:"postgresql"`
Jwt Jwt `mapstructure:"jwt"` Jwt Jwt `mapstructure:"jwt"`
OSSConfig OSSConfig `mapstructure:"oss"` OSSConfig OSSConfig `mapstructure:"oss"`
Redis Redis `mapstructure:"redis"`
} }
var ( var (
+14
View File
@@ -0,0 +1,14 @@
package config
type Redis struct {
Host string `mapstructure:"host"`
Port string `mapstructure:"port"`
DB int `mapstructure:"db"`
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
SslMode string `mapstructure:"ssl-mode"`
Debug bool `mapstructure:"debug"`
MaxIdleConnectionsInSecond int `mapstructure:"max-idle-connections-in-second"`
MaxOpenConnectionsInSecond int `mapstructure:"max-open-connections-in-second"`
ConnectionMaxLifetimeInSecond int64 `mapstructure:"connection-max-life-time-in-second"`
}
+3 -8
View File
@@ -6,16 +6,11 @@ import (
type Category struct { type Category struct {
ID string `gorm:"primaryKey" json:"id"` ID string `gorm:"primaryKey" json:"id"`
Code string `gorm:"not null;unique" json:"code"`
Name string `gorm:"not null" json:"name"` Name string `gorm:"not null" json:"name"`
Code string `gorm:"not null;unique" json:"code"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"` CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"` UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"`
} DeletedAt time.Time `gorm:"default:null" json:"deleted_at"`
type CategoryModel struct { News []News `gorm:"many2many:news_categories" json:"news"`
ID string `gorm:"primaryKey" json:"id"`
Name string `gorm:"not null;unique" json:"name"`
Code string `gorm:"not null" json:"code"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"`
} }
+3 -3
View File
@@ -58,8 +58,8 @@ func (db *DB) Migrate() error {
// &Tag{}, // &Tag{},
// &Category{}, // &Category{},
// &News{}, // &News{},
&NewsModel{}, &News{},
&TagModel{}, &Tag{},
&CategoryModel{}, &Category{},
) )
} }
+5 -17
View File
@@ -5,26 +5,11 @@ import (
) )
type News struct { type News struct {
ID string `gorm:"primaryKey" json:"id"`
Title string `gorm:"default:null" json:"title"`
Tags []Tag `gorm:"many2many:news_tags" json:"tags"`
Categories []Category `gorm:"many2many:news_categories" json:"categories"`
Content string `gorm:"default:null" json:"content"`
LiveAt time.Time `gorm:"not null" json:"live_at"`
AuthorID string `gorm:"not null" json:"author_id"`
IsPremium bool `gorm:"default:false" json:"is_premium"`
Slug string `gorm:"default:null" json:"slug"`
FeaturedImage string `gorm:"default:null" json:"featured_image"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"`
}
type NewsModel struct {
ID string `gorm:"primaryKey" json:"id"` ID string `gorm:"primaryKey" json:"id"`
Title string `json:"title"` Title string `json:"title"`
Content string `json:"content"` Content string `json:"content"`
Categories []CategoryModel `gorm:"many2many:news_categories" json:"categories"` Categories []Category `gorm:"many2many:news_categories" json:"categories"`
Tags []TagModel `gorm:"many2many:news_tags" json:"tags"` Tags []Tag `gorm:"many2many:news_tags" json:"tags"`
IsPremium bool `gorm:"default:false" json:"is_premium"` IsPremium bool `gorm:"default:false" json:"is_premium"`
Slug string `gorm:"default:null" json:"slug"` Slug string `gorm:"default:null" json:"slug"`
FeaturedImage string `gorm:"default:null" json:"featured_image"` FeaturedImage string `gorm:"default:null" json:"featured_image"`
@@ -32,4 +17,7 @@ type NewsModel struct {
LiveAt time.Time `gorm:"not null" json:"live_at"` LiveAt time.Time `gorm:"not null" json:"live_at"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"` CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"` UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"`
DeletedAt time.Time `gorm:"default:null" json:"deleted_at"`
Author Staff `gorm:"foreignKey:AuthorID" json:"author"`
} }
+3 -1
View File
@@ -6,9 +6,11 @@ import (
type Staff struct { type Staff struct {
ID string `gorm:"primaryKey" json:"id"` ID string `gorm:"primaryKey" json:"id"`
Username string `gorm:"default:null;unique" json:"username"` Name string `gorm:"default:null" json:"name"`
ProfilePicture string `gorm:"default:null" json:"profile_picture"`
Email string `gorm:"unique;not null" json:"email"` Email string `gorm:"unique;not null" json:"email"`
Password string `gorm:"not null" json:"password"` Password string `gorm:"not null" json:"password"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"` CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"` UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"`
DeletedAt time.Time `gorm:"default:null" json:"deleted_at"`
} }
+2 -1
View File
@@ -13,8 +13,9 @@ type Subscribe struct {
AutoRenew bool `gorm:"default:true"` AutoRenew bool `gorm:"default:true"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP"` CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP"`
UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP"` UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP"`
DeletedAt time.Time `gorm:"default:null" json:"deleted_at"`
SubscribePlan SubscribePlan `gorm:"foreignKey:SubscribePlanID;constraint:OnDelete:CASCADE"` SubscribePlan SubscribePlan `gorm:"foreignKey:SubscribePlanID;constraint:OnDelete:CASCADE" json:"subscribe_plan"`
} }
type SubscribePlan struct { type SubscribePlan struct {
@@ -6,16 +6,11 @@ import (
type Tag struct { type Tag struct {
ID string `gorm:"primaryKey" json:"id"` ID string `gorm:"primaryKey" json:"id"`
Code string `gorm:"not null;unique" json:"code"`
Name string `gorm:"not null" json:"name"` Name string `gorm:"not null" json:"name"`
Code string `gorm:"not null;unique" json:"code"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"` CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"` UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"`
} DeletedAt time.Time `gorm:"default:null" json:"deleted_at"`
type TagModel struct { News []News `gorm:"many2many:news_tags" json:"news"`
ID string `gorm:"primaryKey" json:"id"`
Name string `gorm:"not null;unique" json:"name"`
Code string `gorm:"not null" json:"code"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"`
} }
+2 -1
View File
@@ -12,6 +12,7 @@ type User struct {
Phone string `gorm:"default:not null;unique" json:"phone"` Phone string `gorm:"default:not null;unique" json:"phone"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"` CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"` UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"`
DeletedAt time.Time `gorm:"default:null" json:"deleted_at"`
Subscribe Subscribe `gorm:"foreignKey:SubscribeID;constraint:OnDelete:CASCADE"` Subscribe Subscribe `gorm:"foreignKey:SubscribeID;constraint:OnDelete:CASCADE" json:"subscribe"`
} }
+12 -1
View File
@@ -14,7 +14,7 @@ postgresql:
driver: postgres driver: postgres
db: legalgonews-dev db: legalgonews-dev
username: legalgo_admin username: legalgo_admin
password: 'K4K!2Kg7c@KW6H&4A2aBy2dFCRY3Sh' password: "K4K!2Kg7c@KW6H&4A2aBy2dFCRY3Sh"
ssl-mode: disable ssl-mode: disable
max-idle-connections-in-second: 600 max-idle-connections-in-second: 600
max-open-connections-in-second: 600 max-open-connections-in-second: 600
@@ -29,3 +29,14 @@ oss:
log_level: Error log_level: Error
host_url: https://sin1.contabostorage.com host_url: https://sin1.contabostorage.com
public_url: https://sin1.contabostorage.com/fda98c2228f246f29a7e466b86b3b9e7 public_url: https://sin1.contabostorage.com/fda98c2228f246f29a7e466b86b3b9e7
redis:
host: 62.72.45.250
port: 26379
password: mDtpsyEW8W26vwLhglpO
db: 5
ssl: false
max-idle-connections-in-second: 600
max-open-connections-in-second: 600
connection-max-life-time-in-second: 600
debug: false
+4 -3
View File
@@ -1,19 +1,20 @@
package categoryrepository package categoryrepository
import ( import (
"legalgo-BE-go/database"
categorydomain "legalgo-BE-go/internal/domain/category" categorydomain "legalgo-BE-go/internal/domain/category"
"github.com/google/uuid" "github.com/google/uuid"
) )
func (a *accessor) Create(spec categorydomain.CategoryReq) error { func (a *accessor) CreateModel(spec categorydomain.CategoryReq) error {
data := categorydomain.Category{ data := database.Category{
ID: uuid.NewString(), ID: uuid.NewString(),
Name: spec.Name, Name: spec.Name,
Code: spec.Code, Code: spec.Code,
} }
if err := a.DB.Create(&data).Error; err != nil { if err := a.db.Create(&data).Error; err != nil {
return err return err
} }
@@ -1,22 +0,0 @@
package categoryrepository
import (
"legalgo-BE-go/database"
categorydomain "legalgo-BE-go/internal/domain/category"
"github.com/google/uuid"
)
func (a *accessor) CreateModel(spec categorydomain.CategoryReq) error {
data := database.CategoryModel{
ID: uuid.NewString(),
Name: spec.Name,
Code: spec.Code,
}
if err := a.DB.Create(&data).Error; err != nil {
return err
}
return nil
}
+24
View File
@@ -0,0 +1,24 @@
package categoryrepository
import (
"fmt"
"legalgo-BE-go/database"
)
func (a *accessor) Delete(id string) error {
var category database.Category
if err := a.db.First(&category, "id = ?", id).Error; err != nil {
return fmt.Errorf("failed to find category: %v", err)
}
if err := a.db.Model(&category).Association("News").Clear(); err != nil {
return fmt.Errorf("failed to remove categories association: %v", err)
}
if err := a.db.Delete(&category).Error; err != nil {
return fmt.Errorf("failed to delete category %s : %v", id, err)
}
return nil
}
+5 -3
View File
@@ -1,11 +1,13 @@
package categoryrepository package categoryrepository
import categorydomain "legalgo-BE-go/internal/domain/category" import (
categorydomain "legalgo-BE-go/internal/domain/category"
)
func (a *accessor) GetAll() ([]categorydomain.Category, error) { func (a *accessor) GetAllModel() ([]categorydomain.Category, error) {
var categories []categorydomain.Category var categories []categorydomain.Category
if err := a.DB.Find(&categories).Error; err != nil { if err := a.db.Find(&categories).Error; err != nil {
return nil, err return nil, err
} }
@@ -1,15 +0,0 @@
package categoryrepository
import (
"legalgo-BE-go/database"
)
func (a *accessor) GetAllModel() ([]database.CategoryModel, error) {
var categories []database.CategoryModel
if err := a.DB.Find(&categories).Error; err != nil {
return nil, err
}
return categories, nil
}
@@ -1,15 +0,0 @@
package categoryrepository
import (
"legalgo-BE-go/database"
)
func (a *accessor) GetBulks(ids []string) ([]database.CategoryModel, error) {
var categories []database.CategoryModel
if err := a.DB.Find(&categories, "id IN ?", ids).Error; err != nil {
return nil, err
}
return categories, nil
}
@@ -0,0 +1,19 @@
package categoryrepository
import (
categorydomain "legalgo-BE-go/internal/domain/category"
)
func (a *accessor) GetIDByCode(codes []string) ([]string, error) {
var categories []string
if err := a.db.
Model(&categorydomain.Category{}).
Select("id").Where("code IN ?", codes).
Pluck("id", &categories).
Error; err != nil {
return nil, err
}
return categories, nil
}
@@ -7,7 +7,7 @@ import (
func (a *accessor) GetByIDs(ids []string) ([]categorydomain.Category, error) { func (a *accessor) GetByIDs(ids []string) ([]categorydomain.Category, error) {
var categories []categorydomain.Category var categories []categorydomain.Category
if err := a.DB.Find(&categories, "id IN ?", ids).Error; err != nil { if err := a.db.Find(&categories, "id IN ?", ids).Error; err != nil {
return nil, err return nil, err
} }
+6 -8
View File
@@ -6,18 +6,16 @@ import (
) )
type accessor struct { type accessor struct {
DB *database.DB db *database.DB
} }
type Category interface { type Category interface {
Create(categorydomain.CategoryReq) error GetAllModel() ([]categorydomain.Category, error)
CreateModel(categorydomain.CategoryReq) error
GetAll() ([]categorydomain.Category, error)
GetAllModel() ([]database.CategoryModel, error)
GetByIDs([]string) ([]categorydomain.Category, error) GetByIDs([]string) ([]categorydomain.Category, error)
GetBulks([]string) ([]database.CategoryModel, error) GetIDByCode([]string) ([]string, error)
CreateModel(categorydomain.CategoryReq) error
Update(categorydomain.Category) error
Delete(string) error
} }
func New( func New(
+19
View File
@@ -0,0 +1,19 @@
package categoryrepository
import (
"fmt"
categorydomain "legalgo-BE-go/internal/domain/category"
"gorm.io/gorm/clause"
)
func (a *accessor) Update(spec categorydomain.Category) error {
if err := a.db.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
DoUpdates: clause.AssignmentColumns([]string{"name", "code", "updated_at"}),
}).Select("name", "code", "updated_at").Save(&spec).Error; err != nil {
return fmt.Errorf("failed to update category: %v", err)
}
return nil
}
+1 -1
View File
@@ -5,7 +5,7 @@ import (
newsdomain "legalgo-BE-go/internal/domain/news" newsdomain "legalgo-BE-go/internal/domain/news"
) )
func (a *accessor) Create(spec *newsdomain.News) error { func (a *accessor) Create(spec newsdomain.News) error {
if err := a.db.Create(&spec).Error; err != nil { if err := a.db.Create(&spec).Error; err != nil {
return fmt.Errorf("failed to create news: %w", err) return fmt.Errorf("failed to create news: %w", err)
} }
-13
View File
@@ -1,13 +0,0 @@
package newsrepository
import (
"fmt"
"legalgo-BE-go/database"
)
func (a *accessor) CreateModel(spec database.NewsModel) error {
if err := a.db.Create(&spec).Error; err != nil {
return fmt.Errorf("failed to create news: %w", err)
}
return nil
}
+28
View File
@@ -0,0 +1,28 @@
package newsrepository
import (
"fmt"
newsdomain "legalgo-BE-go/internal/domain/news"
)
func (a *accessor) Delete(id string) error {
var news newsdomain.News
if err := a.db.Preload("Categories").Preload("Tags").First(&news, "id = ?", id).Error; err != nil {
return fmt.Errorf("failed to find news: %v", err)
}
if err := a.db.Model(&news).Association("Categories").Clear(); err != nil {
return fmt.Errorf("failed to remove categories association: %v", err)
}
if err := a.db.Model(&news).Association("Tags").Clear(); err != nil {
return fmt.Errorf("failed to remove tags association: %v", err)
}
if err := a.db.Delete(&news, "id = ?", id).Error; err != nil {
return fmt.Errorf("failed to delete news %s : %v", id, err)
}
return nil
}
+17 -2
View File
@@ -2,10 +2,25 @@ package newsrepository
import newsdomain "legalgo-BE-go/internal/domain/news" import newsdomain "legalgo-BE-go/internal/domain/news"
func (a *accessor) GetAll() ([]newsdomain.News, error) { func (a *accessor) GetAll(filter newsdomain.NewsFilter) ([]newsdomain.News, error) {
var news []newsdomain.News var news []newsdomain.News
query := a.db.
Preload("Tags").
Preload("Categories").
Preload("Author")
if err := a.db.Preload("Tags").Preload("Categories").Find(&news).Error; err != nil { if len(filter.Category) > 0 {
query = query.Joins("JOIN news_categories nc ON nc.news_id = news.id").
Where("nc.category_id IN (?)", filter.Category)
}
if len(filter.Tags) > 0 {
query = query.Joins("JOIN news_tags nt ON nt.news_id = news.id").
Where("nt.tag_id IN (?)", filter.Tags)
}
if err := query.
Find(&news).Error; err != nil {
return nil, err return nil, err
} }
-13
View File
@@ -1,13 +0,0 @@
package newsrepository
import "legalgo-BE-go/database"
func (a *accessor) GetAllModel() ([]database.NewsModel, error) {
var news []database.NewsModel
if err := a.db.Preload("Tags").Preload("Categories").Find(&news).Error; err != nil {
return nil, err
}
return news, nil
}
+17
View File
@@ -0,0 +1,17 @@
package newsrepository
import newsdomain "legalgo-BE-go/internal/domain/news"
func (a *accessor) GetBySlug(slug string) (*newsdomain.News, error) {
var news newsdomain.News
if err := a.db.
Preload("Tags").
Preload("Categories").
Preload("Author").
First(&news, "slug = ?", slug).Error; err != nil {
return nil, err
}
return &news, nil
}
+5 -4
View File
@@ -10,10 +10,11 @@ type accessor struct {
} }
type News interface { type News interface {
GetAll() ([]newsdomain.News, error) GetAll(filter newsdomain.NewsFilter) ([]newsdomain.News, error)
GetAllModel() ([]database.NewsModel, error) GetBySlug(string) (*newsdomain.News, error)
Create(*newsdomain.News) error Create(newsdomain.News) error
CreateModel(database.NewsModel) error Update(newsdomain.News) error
Delete(string) error
} }
func New(db *database.DB) News { func New(db *database.DB) News {
+69
View File
@@ -0,0 +1,69 @@
package newsrepository
import (
"fmt"
categorydomain "legalgo-BE-go/internal/domain/category"
newsdomain "legalgo-BE-go/internal/domain/news"
tagdomain "legalgo-BE-go/internal/domain/tag"
"gorm.io/gorm/clause"
)
func (a *accessor) Update(spec newsdomain.News) error {
tx := a.db.Begin()
if err := tx.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
DoUpdates: clause.AssignmentColumns([]string{
"title",
"content",
"featured_image",
"is_premium",
"slug",
"author_id",
"live_at",
"updated_at",
}),
}).Select(
"title",
"content",
"featured_image",
"is_premium",
"slug",
"author_id",
"live_at",
"updated_at",
).Save(&spec).Error; err != nil {
tx.Rollback()
return fmt.Errorf("failed to update news: %v", err)
}
tagsDeleted := make([]tagdomain.Tag, len(spec.Tags))
copy(tagsDeleted, spec.Tags)
if err := tx.Model(&spec).Association("Tags").Clear(); err != nil {
tx.Rollback()
return fmt.Errorf("failed to remove previous tags: %v", err)
}
if err := tx.Model(&spec).Association("Tags").Append(tagsDeleted); err != nil {
tx.Rollback()
return fmt.Errorf("failed to add tags: %v", err)
}
categoriesDeleted := make([]categorydomain.Category, len(spec.Categories))
copy(categoriesDeleted, spec.Categories)
if err := tx.Model(&spec).Association("Categories").Clear(); err != nil {
tx.Rollback()
return fmt.Errorf("failed to remove previous categories: %v", err)
}
if err := tx.Model(&spec).Association("Categories").Append(categoriesDeleted); err != nil {
tx.Rollback()
return fmt.Errorf("failed to add categories: %v", err)
}
if err := tx.Commit().Error; err != nil {
return fmt.Errorf("failed to commit transaction: %v", err)
}
return nil
}
+5 -11
View File
@@ -2,7 +2,7 @@ package redisaccessor
import ( import (
"fmt" "fmt"
"os" "legalgo-BE-go/config"
"github.com/redis/go-redis/v9" "github.com/redis/go-redis/v9"
) )
@@ -13,19 +13,13 @@ func Get() *redis.Client {
return redisClient return redisClient
} }
func New() *redis.Client { func New(cfg *config.Config) *redis.Client {
var ( addr := fmt.Sprintf("%s:%s", cfg.Redis.Host, cfg.Redis.Port)
username = os.Getenv("REDIS_USERNAME")
addr = fmt.Sprintf("%s:%s", os.Getenv("REDIS_HOST"), os.Getenv("REDIS_PORT"))
password = os.Getenv("REDIS_PASSWORD")
db = 2 // TODO: change later
)
redisClient = redis.NewClient(&redis.Options{ redisClient = redis.NewClient(&redis.Options{
Username: username,
Addr: addr, Addr: addr,
Password: password, Password: cfg.Redis.Password,
DB: db, DB: cfg.Redis.DB,
}) })
return redisClient return redisClient
+7 -5
View File
@@ -1,11 +1,13 @@
package staffrepository package staffrepository
import authdomain "legalgo-BE-go/internal/domain/auth" import (
staffdomain "legalgo-BE-go/internal/domain/staff"
)
func (ur *StaffRepository) Create(spec *authdomain.Staff) (*authdomain.Staff, error) { func (ur *impl) Create(spec staffdomain.Staff) error {
if err := ur.DB.Create(&spec).Error; err != nil { if err := ur.db.Create(&spec).Error; err != nil {
return nil, err return err
} }
return spec, nil return nil
} }
+4 -4
View File
@@ -3,19 +3,19 @@ package staffrepository
import ( import (
"errors" "errors"
authdomain "legalgo-BE-go/internal/domain/auth" staffdomain "legalgo-BE-go/internal/domain/staff"
"gorm.io/gorm" "gorm.io/gorm"
) )
func (sr *StaffRepository) GetStaffByEmail(email string) (*authdomain.Staff, error) { func (sr *impl) GetStaffByEmail(email string) (*staffdomain.Staff, error) {
var staff authdomain.Staff var staff staffdomain.Staff
if email == "" { if email == "" {
return nil, errors.New("email is required") return nil, errors.New("email is required")
} }
if err := sr.DB.First(&staff, "email = ?", email).Error; err != nil { if err := sr.db.First(&staff, "email = ?", email).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("staff not found") return nil, errors.New("staff not found")
} }
+4 -4
View File
@@ -2,19 +2,19 @@ package staffrepository
import ( import (
"errors" "errors"
authdomain "legalgo-BE-go/internal/domain/auth" staffdomain "legalgo-BE-go/internal/domain/staff"
"gorm.io/gorm" "gorm.io/gorm"
) )
func (sr *StaffRepository) GetStaffByID(ID string) (*authdomain.Staff, error) { func (sr *impl) GetStaffByID(ID string) (*staffdomain.Staff, error) {
var staff authdomain.Staff var staff staffdomain.Staff
if ID == "" { if ID == "" {
return nil, errors.New("id is required") return nil, errors.New("id is required")
} }
if err := sr.DB.First(&staff, "id = ? ", ID).Error; err != nil { if err := sr.db.First(&staff, "id = ? ", ID).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("staff not found") return nil, errors.New("staff not found")
} }
+10 -10
View File
@@ -2,20 +2,20 @@ package staffrepository
import ( import (
"legalgo-BE-go/database" "legalgo-BE-go/database"
authdomain "legalgo-BE-go/internal/domain/auth" staffdomain "legalgo-BE-go/internal/domain/staff"
) )
type StaffRepository struct { type impl struct {
DB *database.DB db *database.DB
} }
type StaffIntf interface { type Staff interface {
GetStaffByEmail(string) (*authdomain.Staff, error) GetStaffByEmail(string) (*staffdomain.Staff, error)
GetStaffByID(string) (*authdomain.Staff, error) GetStaffByID(string) (*staffdomain.Staff, error)
Create(*authdomain.Staff) (*authdomain.Staff, error) Create(staffdomain.Staff) error
Update(authdomain.Staff) error Update(staffdomain.Staff) error
} }
func New(db *database.DB) StaffIntf { func New(db *database.DB) Staff {
return &StaffRepository{db} return &impl{db}
} }
+3 -3
View File
@@ -1,17 +1,17 @@
package staffrepository package staffrepository
import ( import (
authdomain "legalgo-BE-go/internal/domain/auth" staffdomain "legalgo-BE-go/internal/domain/staff"
"legalgo-BE-go/internal/utilities/utils" "legalgo-BE-go/internal/utilities/utils"
) )
func (ur *StaffRepository) Update(spec authdomain.Staff) error { func (ur *impl) Update(spec staffdomain.Staff) error {
val, err := utils.StructToMap(spec) val, err := utils.StructToMap(spec)
if err != nil { if err != nil {
return err return err
} }
if err := ur.DB.Model(&authdomain.Staff{}).Where("id = ?", spec.ID).Updates(val).Error; err != nil { if err := ur.db.Model(&staffdomain.Staff{}).Where("id = ?", spec.ID).Updates(val).Error; err != nil {
return err return err
} }
+23
View File
@@ -0,0 +1,23 @@
package subscriberepository
import (
"errors"
"fmt"
"legalgo-BE-go/database"
"gorm.io/gorm"
)
func (s *SubsAccs) GetByID(id string) (database.Subscribe, error) {
var subscribe database.Subscribe
if err := s.DB.First(&subscribe, "id = ?", id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return subscribe, fmt.Errorf("subscribe data not found: %v", err)
}
return subscribe, err
}
return subscribe, nil
}
+2
View File
@@ -8,6 +8,8 @@ type SubsAccs struct {
type SubsIntf interface { type SubsIntf interface {
Create(string) (string, error) Create(string) (string, error)
GetByID(string) (database.Subscribe, error)
UpdateSubscribeStatus(database.Subscribe) error
} }
func New(db *database.DB) SubsIntf { func New(db *database.DB) SubsIntf {
@@ -0,0 +1,19 @@
package subscriberepository
import (
"fmt"
"legalgo-BE-go/database"
"gorm.io/gorm/clause"
)
func (a *SubsAccs) UpdateSubscribeStatus(spec database.Subscribe) error {
if err := a.DB.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
DoUpdates: clause.AssignmentColumns([]string{"status"}),
}).Create(&spec).Error; err != nil {
return fmt.Errorf("failed to update status: %v", err)
}
return nil
}
+3 -3
View File
@@ -2,13 +2,13 @@ package subscribeplanrepository
import ( import (
"errors" "errors"
subscribeplandomain "legalgo-BE-go/internal/domain/subscribe_plan" "legalgo-BE-go/database"
"gorm.io/gorm" "gorm.io/gorm"
) )
func (s *SubsPlan) GetByID(id string) (subscribeplandomain.SubscribePlan, error) { func (s *SubsPlan) GetByID(id string) (*database.SubscribePlan, error) {
var subscribePlan subscribeplandomain.SubscribePlan var subscribePlan *database.SubscribePlan
if err := s.DB.First(&subscribePlan, "id = ? ", id).Error; err != nil { if err := s.DB.First(&subscribePlan, "id = ? ", id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
@@ -1,16 +1,16 @@
package subscribeplanrepository package subscribeplanrepository
import ( import (
subscribeplandomain "legalgo-BE-go/internal/domain/subscribe_plan" "legalgo-BE-go/database"
"github.com/google/uuid" "github.com/google/uuid"
) )
func (s *SubsPlan) GetDefault() (subscribeplandomain.SubscribePlan, error) { func (s *SubsPlan) GetDefault() (*database.SubscribePlan, error) {
var subscribePlan subscribeplandomain.SubscribePlan var subscribePlan *database.SubscribePlan
if err := s.DB.First(&subscribePlan, "code = ?", "basic").Error; err != nil { if err := s.DB.First(&subscribePlan, "code = ?", "basic").Error; err != nil {
s.DB.Create(&subscribeplandomain.SubscribePlan{ s.DB.Create(&database.SubscribePlan{
ID: uuid.NewString(), ID: uuid.NewString(),
Code: "basic", Code: "basic",
Name: "Basic", Name: "Basic",
+2 -2
View File
@@ -12,8 +12,8 @@ type SubsPlan struct {
type SubsPlanIntf interface { type SubsPlanIntf interface {
Create(subscribeplandomain.SubscribePlanReq) error Create(subscribeplandomain.SubscribePlanReq) error
GetAll() ([]subscribeplandomain.SubscribePlan, error) GetAll() ([]subscribeplandomain.SubscribePlan, error)
GetByID(string) (subscribeplandomain.SubscribePlan, error) GetByID(string) (*database.SubscribePlan, error)
GetDefault() (subscribeplandomain.SubscribePlan, error) GetDefault() (*database.SubscribePlan, error)
} }
func New( func New(
+1 -1
View File
@@ -13,7 +13,7 @@ func (acc *accessor) Create(spec tagdomain.TagReq) error {
Name: spec.Name, Name: spec.Name,
} }
if err := acc.DB.Create(&data).Error; err != nil { if err := acc.db.Create(&data).Error; err != nil {
return err return err
} }
-22
View File
@@ -1,22 +0,0 @@
package tagrepository
import (
"legalgo-BE-go/database"
tagdomain "legalgo-BE-go/internal/domain/tag"
"github.com/google/uuid"
)
func (acc *accessor) CreateModel(spec tagdomain.TagReq) error {
data := &database.TagModel{
ID: uuid.NewString(),
Code: spec.Code,
Name: spec.Name,
}
if err := acc.DB.Create(&data).Error; err != nil {
return err
}
return nil
}
+24
View File
@@ -0,0 +1,24 @@
package tagrepository
import (
"fmt"
"legalgo-BE-go/database"
)
func (a *accessor) Delete(id string) error {
var tag database.Tag
if err := a.db.First(&tag, "id = ?", id).Error; err != nil {
return fmt.Errorf("failed to find tag: %v", err)
}
if err := a.db.Model(&tag).Association("News").Clear(); err != nil {
return fmt.Errorf("failed to remove tag association: %v", err)
}
if err := a.db.Delete(&tag).Error; err != nil {
return fmt.Errorf("failed to delete tag %s : %v", id, err)
}
return nil
}
+1 -1
View File
@@ -7,7 +7,7 @@ import (
func (acc *accessor) GetAll() ([]tagdomain.Tag, error) { func (acc *accessor) GetAll() ([]tagdomain.Tag, error) {
var tags []tagdomain.Tag var tags []tagdomain.Tag
if err := acc.DB.Find(&tags).Error; err != nil { if err := acc.db.Find(&tags).Error; err != nil {
return nil, err return nil, err
} }
-13
View File
@@ -1,13 +0,0 @@
package tagrepository
import "legalgo-BE-go/database"
func (acc *accessor) GetAllModel() ([]database.TagModel, error) {
var tags []database.TagModel
if err := acc.DB.Find(&tags).Error; err != nil {
return nil, err
}
return tags, nil
}
-13
View File
@@ -1,13 +0,0 @@
package tagrepository
import "legalgo-BE-go/database"
func (a *accessor) GetBulks(ids []string) ([]database.TagModel, error) {
var tags []database.TagModel
if err := a.DB.Find(&tags, "id IN ?", ids).Error; err != nil {
return nil, err
}
return tags, nil
}
+17
View File
@@ -0,0 +1,17 @@
package tagrepository
import tagdomain "legalgo-BE-go/internal/domain/tag"
func (a *accessor) GetIDsByCodes(codes []string) ([]string, error) {
var tags []string
if err := a.db.
Model(&tagdomain.Tag{}).
Select("id").Where("code IN ?", codes).
Pluck("id", &tags).
Error; err != nil {
return nil, err
}
return tags, nil
}
@@ -5,7 +5,7 @@ import tagdomain "legalgo-BE-go/internal/domain/tag"
func (a *accessor) GetByIDs(ids []string) ([]tagdomain.Tag, error) { func (a *accessor) GetByIDs(ids []string) ([]tagdomain.Tag, error) {
var tags []tagdomain.Tag var tags []tagdomain.Tag
if err := a.DB.Find(&tags, "id IN ?", ids).Error; err != nil { if err := a.db.Find(&tags, "id IN ?", ids).Error; err != nil {
return nil, err return nil, err
} }
+4 -4
View File
@@ -6,16 +6,16 @@ import (
) )
type accessor struct { type accessor struct {
DB *database.DB db *database.DB
} }
type TagAccessor interface { type TagAccessor interface {
Create(tagdomain.TagReq) error Create(tagdomain.TagReq) error
CreateModel(tagdomain.TagReq) error
GetAll() ([]tagdomain.Tag, error) GetAll() ([]tagdomain.Tag, error)
GetAllModel() ([]database.TagModel, error)
GetByIDs([]string) ([]tagdomain.Tag, error) GetByIDs([]string) ([]tagdomain.Tag, error)
GetBulks(ids []string) ([]database.TagModel, error) GetIDsByCodes([]string) ([]string, error)
Update(tagdomain.Tag) error
Delete(string) error
} }
func New( func New(
+23
View File
@@ -0,0 +1,23 @@
package tagrepository
import (
"fmt"
tagdomain "legalgo-BE-go/internal/domain/tag"
"gorm.io/gorm/clause"
)
func (a *accessor) Update(spec tagdomain.Tag) error {
if err := a.db.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
DoUpdates: clause.AssignmentColumns([]string{
"name",
"code",
"updated_at",
}),
}).Select("name", "code", "updated_at").Save(&spec).Error; err != nil {
return fmt.Errorf("failed to update tag: %v", err)
}
return nil
}
@@ -1,13 +1,13 @@
package userrepository package userrepository
import ( import (
authdomain "legalgo-BE-go/internal/domain/auth" userdomain "legalgo-BE-go/internal/domain/user"
) )
func (ur *UserRepository) CreateUser(spec *authdomain.User) (*authdomain.User, error) { func (ur *accessor) CreateUser(spec userdomain.User) error {
if err := ur.DB.Create(&spec).Error; err != nil { if err := ur.db.Create(&spec).Error; err != nil {
return nil, err return err
} }
return spec, nil return nil
} }
@@ -2,25 +2,24 @@ package userrepository
import ( import (
"errors" "errors"
userdomain "legalgo-BE-go/internal/domain/user"
authdomain "legalgo-BE-go/internal/domain/auth"
"gorm.io/gorm" "gorm.io/gorm"
) )
func (ur *UserRepository) GetUserByEmail(email string) (*authdomain.User, error) { func (ur *accessor) GetUserByEmail(email string) (*userdomain.User, error) {
var user authdomain.User var user *userdomain.User
if email == "" { if email == "" {
return nil, errors.New("email is empty") return nil, errors.New("email is empty")
} }
if err := ur.DB.First(&user, "email = ?", email).Error; err != nil { if err := ur.db.First(&user, "email = ?", email).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("user not found") return nil, errors.New("user not found")
} }
return nil, err return nil, err
} }
return &user, nil return user, nil
} }
@@ -3,28 +3,22 @@ package userrepository
import ( import (
"errors" "errors"
authdomain "legalgo-BE-go/internal/domain/auth" userdomain "legalgo-BE-go/internal/domain/user"
"gorm.io/gorm"
) )
func (ur *UserRepository) GetUserByID(email string) (*authdomain.UserProfile, error) { func (ur *accessor) GetUserByID(id string) (*userdomain.User, error) {
var users []authdomain.UserProfile var user userdomain.User
if email == "" { if id == "" {
return nil, errors.New("email is empty") return nil, errors.New("id is empty")
} }
if err := ur.DB.Table("users u"). if err := ur.db.
Select("u.email, u.id, s.status as subscribe_status, sp.code as subscribe_plan_code, sp.name as subscribe_plan_name"). Preload("Subscribe").
Joins("join subscribes s on s.id = u.subscribe_id"). Preload("Subscribe.SubscribePlan").
Joins("join subscribe_plans sp on s.subscribe_plan_id = sp.id"). First(&user, "id = ?", id).Error; err != nil {
Scan(&users).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("user not found")
}
return nil, err return nil, err
} }
return &users[0], nil return &user, nil
} }
@@ -2,29 +2,30 @@ package userrepository
import ( import (
"errors" "errors"
authdomain "legalgo-BE-go/internal/domain/auth" userdomain "legalgo-BE-go/internal/domain/user"
"gorm.io/gorm"
) )
func (ur *UserRepository) GetUserProfile(email string) (*authdomain.UserProfile, error) { func (ur *accessor) GetUserProfile(email string) (*userdomain.UserProfile, error) {
var users []authdomain.UserProfile var user *userdomain.User
if email == "" { if email == "" {
return nil, errors.New("email is empty") return nil, errors.New("email is empty")
} }
if err := ur.DB.Table("users u"). if err := ur.db.
Where("email = ?", email). Preload("Subscribe").
Select("u.email, u.id, s.status as subscribe_status, sp.code as subscribe_plan_code, sp.name as subscribe_plan_name"). Preload("Subscribe.SubscribePlan").
Joins("join subscribes s on s.id = u.subscribe_id"). First(&user, "email = ?", email).
Joins("join subscribe_plans sp on s.subscribe_plan_id = sp.id"). Error; err != nil {
Scan(&users).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("user not found")
}
return nil, err return nil, err
} }
return &users[0], nil userProfile := &userdomain.UserProfile{
ID: user.ID,
Email: user.Email,
Phone: user.Phone,
Subscribe: user.Subscribe,
}
return userProfile, nil
} }
+10 -10
View File
@@ -2,22 +2,22 @@ package userrepository
import ( import (
"legalgo-BE-go/database" "legalgo-BE-go/database"
authdomain "legalgo-BE-go/internal/domain/auth" userdomain "legalgo-BE-go/internal/domain/user"
) )
type UserRepository struct { type accessor struct {
DB *database.DB db *database.DB
} }
type UserIntf interface { type User interface {
GetUserByEmail(string) (*authdomain.User, error) GetUserByEmail(string) (*userdomain.User, error)
GetUserByID(string) (*authdomain.UserProfile, error) GetUserByID(string) (*userdomain.User, error)
GetUserProfile(string) (*authdomain.UserProfile, error) GetUserProfile(string) (*userdomain.UserProfile, error)
CreateUser(*authdomain.User) (*authdomain.User, error) CreateUser(userdomain.User) error
} }
func New( func New(
db *database.DB, db *database.DB,
) UserIntf { ) User {
return &UserRepository{db} return &accessor{db}
} }
@@ -3,24 +3,27 @@ package authhttp
import ( import (
"net/http" "net/http"
authdomain "legalgo-BE-go/internal/domain/auth" responsedomain "legalgo-BE-go/internal/domain/reponse"
staffdomain "legalgo-BE-go/internal/domain/staff"
authsvc "legalgo-BE-go/internal/services/auth" authsvc "legalgo-BE-go/internal/services/auth"
"legalgo-BE-go/internal/utilities/response" "legalgo-BE-go/internal/utilities/response"
"legalgo-BE-go/internal/utilities/utils" "legalgo-BE-go/internal/utilities/utils"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
"github.com/redis/go-redis/v9"
) )
func LoginStaff( func LoginStaff(
router chi.Router, router chi.Router,
authSvc authsvc.AuthIntf, authSvc authsvc.Auth,
validate *validator.Validate, validate *validator.Validate,
rdb *redis.Client,
) { ) {
router.Post("/staff/login", func(w http.ResponseWriter, r *http.Request) { router.Post("/staff/login", func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
var spec authdomain.LoginReq var spec staffdomain.StaffLogin
if err := utils.UnmarshalBody(r, &spec); err != nil { if err := utils.UnmarshalBody(r, &spec); err != nil {
response.ResponseWithErrorCode( response.ResponseWithErrorCode(
@@ -59,50 +62,7 @@ func LoginStaff(
return return
} }
responsePayload := &authdomain.AuthResponse{ if err := utils.StoreTokenRedis(ctx, rdb, token, spec.Email); err != nil {
Token: token,
}
response.RespondJsonSuccess(ctx, w, responsePayload)
})
}
func LoginUser(
router chi.Router,
authSvc authsvc.AuthIntf,
validate *validator.Validate,
) {
router.Post("/user/login", func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var spec authdomain.LoginReq
if err := utils.UnmarshalBody(r, &spec); err != nil {
response.ResponseWithErrorCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
"failed to unmarshal request",
)
return
}
if err := validate.Struct(spec); err != nil {
response.ResponseWithErrorCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
err.(validator.ValidationErrors).Error(),
)
return
}
token, err := authSvc.LoginAsUser(spec)
if err != nil {
response.ResponseWithErrorCode( response.ResponseWithErrorCode(
ctx, ctx,
w, w,
@@ -114,7 +74,7 @@ func LoginUser(
return return
} }
responsePayload := &authdomain.AuthResponse{ responsePayload := &responsedomain.Auth{
Token: token, Token: token,
} }
+82
View File
@@ -0,0 +1,82 @@
package authhttp
import (
responsedomain "legalgo-BE-go/internal/domain/reponse"
userdomain "legalgo-BE-go/internal/domain/user"
authsvc "legalgo-BE-go/internal/services/auth"
"legalgo-BE-go/internal/utilities/response"
"legalgo-BE-go/internal/utilities/utils"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-playground/validator/v10"
"github.com/redis/go-redis/v9"
)
func LoginUser(
router chi.Router,
authSvc authsvc.Auth,
validate *validator.Validate,
rdb *redis.Client,
) {
router.Post("/user/login", func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var spec userdomain.UserLogin
if err := utils.UnmarshalBody(r, &spec); err != nil {
response.ResponseWithErrorCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
"failed to unmarshal request",
)
return
}
if err := validate.Struct(spec); err != nil {
response.ResponseWithErrorCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
err.(validator.ValidationErrors).Error(),
)
return
}
token, err := authSvc.LoginAsUser(spec)
if err != nil {
response.ResponseWithErrorCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
err.Error(),
)
return
}
if err := utils.StoreTokenRedis(ctx, rdb, token, spec.Email); err != nil {
response.ResponseWithErrorCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
err.Error(),
)
return
}
responsePayload := &responsedomain.Auth{
Token: token,
}
response.RespondJsonSuccess(ctx, w, responsePayload)
})
}
+2 -2
View File
@@ -11,7 +11,7 @@ import (
func GetStaffProfile( func GetStaffProfile(
router chi.Router, router chi.Router,
authSvc authsvc.AuthIntf, authSvc authsvc.Auth,
) { ) {
router.Get("/staff/profile", func(w http.ResponseWriter, r *http.Request) { router.Get("/staff/profile", func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
@@ -47,7 +47,7 @@ func GetStaffProfile(
func GetUserProfile( func GetUserProfile(
router chi.Router, router chi.Router,
authSvc authsvc.AuthIntf, authSvc authsvc.Auth,
) { ) {
router.Get("/user/profile", func(w http.ResponseWriter, r *http.Request) { router.Get("/user/profile", func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
@@ -3,79 +3,27 @@ package authhttp
import ( import (
"net/http" "net/http"
authdomain "legalgo-BE-go/internal/domain/auth" responsedomain "legalgo-BE-go/internal/domain/reponse"
staffdomain "legalgo-BE-go/internal/domain/staff"
authsvc "legalgo-BE-go/internal/services/auth" authsvc "legalgo-BE-go/internal/services/auth"
"legalgo-BE-go/internal/utilities/response" "legalgo-BE-go/internal/utilities/response"
"legalgo-BE-go/internal/utilities/utils" "legalgo-BE-go/internal/utilities/utils"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
"github.com/redis/go-redis/v9"
) )
func RegisterUser(
router chi.Router,
validate *validator.Validate,
authSvc authsvc.AuthIntf,
) {
router.Post("/user/register", func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var spec authdomain.RegisterUserReq
if err := utils.UnmarshalBody(r, &spec); err != nil {
response.ResponseWithErrorCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
"failed to unmarshal request",
)
return
}
if err := validate.Struct(spec); err != nil {
response.ResponseWithErrorCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
err.(validator.ValidationErrors).Error(),
)
return
}
token, err := authSvc.RegisterUser(spec)
if err != nil {
response.ResponseWithErrorCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
err.Error(),
)
return
}
responsePayload := &authdomain.AuthResponse{
Token: token,
}
response.RespondJsonSuccess(ctx, w, responsePayload)
})
}
func RegisterStaff( func RegisterStaff(
router chi.Router, router chi.Router,
validate *validator.Validate, validate *validator.Validate,
authSvc authsvc.AuthIntf, authSvc authsvc.Auth,
rdb *redis.Client,
) { ) {
router.Post("/staff/register", func(w http.ResponseWriter, r *http.Request) { router.Post("/staff/register", func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
var spec authdomain.RegisterStaffReq var spec staffdomain.StaffRegister
if err := utils.UnmarshalBody(r, &spec); err != nil { if err := utils.UnmarshalBody(r, &spec); err != nil {
response.ResponseWithErrorCode( response.ResponseWithErrorCode(
@@ -113,7 +61,20 @@ func RegisterStaff(
) )
return return
} }
responsePayload := &authdomain.AuthResponse{
if err := utils.StoreTokenRedis(ctx, rdb, token, spec.Email); err != nil {
response.ResponseWithErrorCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
err.Error(),
)
return
}
responsePayload := &responsedomain.Auth{
Token: token, Token: token,
} }
response.RespondJsonSuccess(ctx, w, responsePayload) response.RespondJsonSuccess(ctx, w, responsePayload)
+82
View File
@@ -0,0 +1,82 @@
package authhttp
import (
responsedomain "legalgo-BE-go/internal/domain/reponse"
userdomain "legalgo-BE-go/internal/domain/user"
authsvc "legalgo-BE-go/internal/services/auth"
"legalgo-BE-go/internal/utilities/response"
"legalgo-BE-go/internal/utilities/utils"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-playground/validator/v10"
"github.com/redis/go-redis/v9"
)
func RegisterUser(
router chi.Router,
validate *validator.Validate,
authSvc authsvc.Auth,
rdb *redis.Client,
) {
router.Post("/user/register", func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var spec userdomain.UserRegister
if err := utils.UnmarshalBody(r, &spec); err != nil {
response.ResponseWithErrorCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
"failed to unmarshal request",
)
return
}
if err := validate.Struct(spec); err != nil {
response.ResponseWithErrorCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
err.(validator.ValidationErrors).Error(),
)
return
}
token, err := authSvc.RegisterUser(spec)
if err != nil {
response.ResponseWithErrorCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
err.Error(),
)
return
}
if err := utils.StoreTokenRedis(ctx, rdb, token, spec.Email); err != nil {
response.ResponseWithErrorCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
err.Error(),
)
return
}
responsePayload := &responsedomain.Auth{
Token: token,
}
response.RespondJsonSuccess(ctx, w, responsePayload)
})
}
+7 -7
View File
@@ -2,7 +2,7 @@ package authhttp
import ( import (
"errors" "errors"
authdomain "legalgo-BE-go/internal/domain/auth" staffdomain "legalgo-BE-go/internal/domain/staff"
authsvc "legalgo-BE-go/internal/services/auth" authsvc "legalgo-BE-go/internal/services/auth"
"legalgo-BE-go/internal/utilities/response" "legalgo-BE-go/internal/utilities/response"
"legalgo-BE-go/internal/utilities/utils" "legalgo-BE-go/internal/utilities/utils"
@@ -13,7 +13,7 @@ import (
func UpdateStaff( func UpdateStaff(
router chi.Router, router chi.Router,
authSvc authsvc.AuthIntf, authSvc authsvc.Auth,
) { ) {
router.Patch("/staff/{id}/update", func(w http.ResponseWriter, r *http.Request) { router.Patch("/staff/{id}/update", func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
@@ -31,7 +31,7 @@ func UpdateStaff(
return return
} }
var spec authdomain.RegisterStaffReq var spec staffdomain.StaffRegister
if err := utils.UnmarshalBody(r, &spec); err != nil { if err := utils.UnmarshalBody(r, &spec); err != nil {
response.ResponseWithErrorCode( response.ResponseWithErrorCode(
@@ -45,11 +45,11 @@ func UpdateStaff(
return return
} }
staff := authdomain.Staff{ staff := staffdomain.Staff{
ID: id, ID: id,
Email: spec.Email, Email: spec.Email,
Password: spec.Password, Password: spec.Password,
Username: spec.Username, Name: spec.Name,
} }
if err := authSvc.UpdateStaff(staff); err != nil { if err := authSvc.UpdateStaff(staff); err != nil {
@@ -64,9 +64,9 @@ func UpdateStaff(
return return
} }
responsePayload := struct{ responsePayload := struct {
Message string Message string
} { }{
Message: "update staff success", Message: "update staff success",
} }
+4 -1
View File
@@ -1,6 +1,7 @@
package categoryhttp package categoryhttp
import ( import (
authmiddleware "legalgo-BE-go/internal/api/http/middleware/auth"
categorydomain "legalgo-BE-go/internal/domain/category" categorydomain "legalgo-BE-go/internal/domain/category"
categorysvc "legalgo-BE-go/internal/services/category" categorysvc "legalgo-BE-go/internal/services/category"
"legalgo-BE-go/internal/utilities/response" "legalgo-BE-go/internal/utilities/response"
@@ -16,7 +17,9 @@ func Create(
validate *validator.Validate, validate *validator.Validate,
categorySvc categorysvc.Category, categorySvc categorysvc.Category,
) { ) {
router.Post("/category/create", func(w http.ResponseWriter, r *http.Request) { router.
With(authmiddleware.Authorize()).
Post("/category/create", func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
var spec categorydomain.CategoryReq var spec categorydomain.CategoryReq
+53
View File
@@ -0,0 +1,53 @@
package categoryhttp
import (
"fmt"
authmiddleware "legalgo-BE-go/internal/api/http/middleware/auth"
categorysvc "legalgo-BE-go/internal/services/category"
"legalgo-BE-go/internal/utilities/response"
"net/http"
"github.com/go-chi/chi/v5"
)
func Delete(
router chi.Router,
categorySvc categorysvc.Category,
) {
router.
With(authmiddleware.Authorize()).
Delete("/category/{category_id}/delete", func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
categoryID := chi.URLParam(r, "category_id")
if categoryID == "" {
response.RespondJsonErrorWithCode(
ctx,
w,
fmt.Errorf("category id is not provided"),
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
"category id is not provided",
)
return
}
if err := categorySvc.Delete(categoryID); err != nil {
response.RespondJsonErrorWithCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
err.Error(),
)
return
}
response.RespondJsonSuccess(ctx, w, struct {
Message string
}{
Message: "category has been deleted",
})
})
}
+1 -2
View File
@@ -14,8 +14,7 @@ func GetAll(
) { ) {
router.Get("/category", func(w http.ResponseWriter, r *http.Request) { router.Get("/category", func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
subsPlan, err := categorySvc.GetAllModel() subsPlan, err := categorySvc.GetAll()
// subsPlan, err := categorySvc.GetAll()
if err != nil { if err != nil {
response.ResponseWithErrorCode( response.ResponseWithErrorCode(
ctx, ctx,
+2
View File
@@ -5,4 +5,6 @@ import "go.uber.org/fx"
var Module = fx.Module("categories", fx.Invoke( var Module = fx.Module("categories", fx.Invoke(
Create, Create,
GetAll, GetAll,
Update,
Delete,
)) ))
+82
View File
@@ -0,0 +1,82 @@
package categoryhttp
import (
"fmt"
authmiddleware "legalgo-BE-go/internal/api/http/middleware/auth"
categorydomain "legalgo-BE-go/internal/domain/category"
categorysvc "legalgo-BE-go/internal/services/category"
"legalgo-BE-go/internal/utilities/response"
"legalgo-BE-go/internal/utilities/utils"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-playground/validator/v10"
)
func Update(
router chi.Router,
validate *validator.Validate,
categorySvc categorysvc.Category,
) {
router.
With(authmiddleware.Authorize()).
Put("/category/{category_id}/update", func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
categoryID := chi.URLParam(r, "category_id")
if categoryID == "" {
response.RespondJsonErrorWithCode(
ctx,
w,
fmt.Errorf("category id is not provided"),
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
"category id is not provided",
)
return
}
var spec categorydomain.CategoryReq
if err := utils.UnmarshalBody(r, &spec); err != nil {
response.RespondJsonErrorWithCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
"failed to unmarshal body",
)
return
}
if err := validate.Struct(spec); err != nil {
response.RespondJsonErrorWithCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
err.(validator.ValidationErrors).Error(),
)
return
}
if err := categorySvc.Update(categoryID, spec); err != nil {
response.RespondJsonErrorWithCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
err.Error(),
)
return
}
response.RespondJsonSuccess(ctx, w, struct {
Message string
}{
Message: "update category success",
})
})
}
-5
View File
@@ -1,5 +0,0 @@
package contenthttp
import "go.uber.org/fx"
var Module = fx.Module("content-api", fx.Invoke())
+57 -119
View File
@@ -1,138 +1,76 @@
package authmiddleware package authmiddleware
// import ( import (
// "context" "context"
// "fmt" "fmt"
// "net/http" "net/http"
// "strings" "time"
// redisaccessor "legalgo-BE-go/internal/accessor/redis" redisaccessor "legalgo-BE-go/internal/accessor/redis"
// contextkeyenum "legalgo-BE-go/internal/enums/context_key" "legalgo-BE-go/internal/utilities/response"
// jwtclaimenum "legalgo-BE-go/internal/enums/jwt" "legalgo-BE-go/internal/utilities/utils"
// resourceenum "legalgo-BE-go/internal/enums/resource"
// "legalgo-BE-go/internal/services/auth"
// "github.com/golang-jwt/jwt/v5"
// )
// const SessionHeader = "Authorization" "github.com/golang-jwt/jwt/v5"
)
// func Authorization() func(next http.Handler) http.Handler { const SessionHeader = "Authorization"
// return func(next http.Handler) http.Handler {
// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// ctx := r.Context()
// tokenString, err := GetToken(r) func Authorize() func(next http.Handler) http.Handler {
// if err != nil { return func(next http.Handler) http.Handler {
// RespondWithError(w, r, err, "Invalid auth header") return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// return ctx := r.Context()
// }
// token, err := ValidateToken(ctx, tokenString) tokenString, err := utils.GetToken(r)
// if err != nil { if err != nil {
// RespondWithError(w, r, err, err.Error()) response.RespondWithError(w, r, err, "Invalid auth header")
// return return
// } }
// if isAuthorized, ctx := VerifyClaims(ctx, token, nil); !isAuthorized { spec, err := utils.GetTokenDetail(r)
// RespondWithError(w, r, errorcode.ErrCodeUnauthorized, errorcode.ErrCodeUnauthorized.Message)
// return
// } else {
// next.ServeHTTP(w, r.WithContext(ctx))
// return
// }
// })
// }
// }
// func GetToken(r *http.Request) (string, error) { token, err := ValidateToken(ctx, spec.Email)
// tokenString := GetTokenFromHeader(r) if err != nil {
// if tokenString == "" { response.RespondWithError(w, r, err, err.Error())
// tokenString = getTokenFromQuery(r) return
// } }
// if tokenString == "" { isValid := token == tokenString
// return "", fmt.Errorf("token not found")
// }
// return tokenString, nil if !isValid {
// } response.RespondWithError(w, r, err, "invalid token")
return
}
// func GetTokenFromHeader(r *http.Request) string { next.ServeHTTP(w, r.WithContext(ctx))
// session := r.Header.Get(SessionHeader) })
// arr := strings.Split(session, " ") }
}
// if len(arr) != 2 || strings.ToUpper(arr[0]) != "BEARER" { func ValidateToken(ctx context.Context, id string) (string, error) {
// return "" redisClient := redisaccessor.Get()
// } redisToken, err := utils.GetTokenRedis(ctx, redisClient, id)
// return arr[1] if err != nil {
// } return "", err
}
// func getTokenFromQuery(r *http.Request) string { token, err := utils.ParseToken(redisToken)
// token := r.URL.Query().Get("token")
// return token
// }
// func VerifyClaims(ctx context.Context, token *jwt.Token, if err != nil {
// requiredResources []resourceenum.Resource) (bool, context.Context) { return "", fmt.Errorf("invalid token: %w", err)
// if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { }
// rawResources := []interface{}{}
// if claimValue, exist := claims[string(jwtclaimenum.RESOURCES)]; exist {
// rawResources = claimValue.([]interface{})
// }
// resources := []resourceenum.Resource{} // Check if the token is valid
// resourceMap := map[string]bool{} if !token.Valid {
return "", fmt.Errorf("invalid token: token is not valid")
}
// for _, v := range rawResources { if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
// value := v.(string) expirationTime := claims["exp"].(float64) // Expiration time in Unix timestamp format
// resources = append(resources, resourceenum.Resource(value)) if time.Unix(int64(expirationTime), 0).Before(time.Now()) {
// resourceMap[value] = true return "", fmt.Errorf("token has expired")
// } }
}
// ctx = context.WithValue(ctx, contextkeyenum.Authorization, UserAuthorization{ return redisToken, nil
// Type: claims[string(jwtclaimenum.TYPE)].(string), }
// UserId: claims[string(jwtclaimenum.AUDIENCE)].(string),
// Username: claims[string(jwtclaimenum.USERNAME)].(string),
// Resources: resources,
// })
// isResourceFulfilled := false
// for _, v := range requiredResources {
// if _, ok := resourceMap[string(v)]; ok {
// isResourceFulfilled = true
// ctx = context.WithValue(ctx, contextkeyenum.Resource, v)
// break
// }
// }
// if isResourceFulfilled || len(requiredResources) == 0 {
// return true, ctx
// }
// }
// return false, nil
// }
// func ValidateToken(ctx context.Context, tokenString string) (*jwt.Token, error) {
// redisClient := redisaccessor.Get()
// redisToken, err := redisClient.Exists(ctx, fmt.Sprintf("%s:%s", auth.BLACKLISTED_TOKEN_KEY, tokenString)).Result()
// if err != nil || redisToken > 0 {
// return nil, fmt.Errorf("session already expired")
// }
// token, err := jwt.Parse(tokenString, authsvc.VerifyToken(conf.JWTAccessToken))
// if err != nil {
// if ve, ok := err.(*jwt.ValidationError); ok {
// if ve.Errors&jwt.ValidationErrorExpired != 0 {
// err = errorcode.ErrCodeExpiredToken
// }
// }
// return nil, err
// }
// return token, nil
// }
+2 -2
View File
@@ -15,7 +15,7 @@ import (
func Create( func Create(
validate *validator.Validate, validate *validator.Validate,
newsSvc newssvc.News, newsSvc newssvc.News,
staffRepo staffrepository.StaffIntf, staffRepo staffrepository.Staff,
router chi.Router, router chi.Router,
) { ) {
router.Post("/news/create", func(w http.ResponseWriter, r *http.Request) { router.Post("/news/create", func(w http.ResponseWriter, r *http.Request) {
@@ -72,7 +72,7 @@ func Create(
return return
} }
if err := newsSvc.CreateModel(spec, staffProfile.ID); err != nil { if err := newsSvc.Create(spec, staffProfile.ID); err != nil {
response.ResponseWithErrorCode( response.ResponseWithErrorCode(
ctx, ctx,
w, w,
+53
View File
@@ -0,0 +1,53 @@
package newshttp
import (
"fmt"
authmiddleware "legalgo-BE-go/internal/api/http/middleware/auth"
newssvc "legalgo-BE-go/internal/services/news"
"legalgo-BE-go/internal/utilities/response"
"net/http"
"github.com/go-chi/chi/v5"
)
func Delete(
router chi.Router,
newsSvc newssvc.News,
) {
router.
With(authmiddleware.Authorize()).
Delete("/news/{news_id}/delete", func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
newsID := chi.URLParam(r, "news_id")
if newsID == "" {
response.RespondJsonErrorWithCode(
ctx,
w,
fmt.Errorf("category id is not provided"),
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
"news id is not provided",
)
return
}
if err := newsSvc.Delete(newsID); err != nil {
response.RespondJsonErrorWithCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
err.Error(),
)
return
}
response.RespondJsonSuccess(ctx, w, struct {
Message string
}{
Message: "news has been deleted",
})
})
}
+12 -1
View File
@@ -1,6 +1,7 @@
package newshttp package newshttp
import ( import (
newsdomain "legalgo-BE-go/internal/domain/news"
newssvc "legalgo-BE-go/internal/services/news" newssvc "legalgo-BE-go/internal/services/news"
"legalgo-BE-go/internal/utilities/response" "legalgo-BE-go/internal/utilities/response"
"net/http" "net/http"
@@ -13,8 +14,18 @@ func GetAll(
newsSvc newssvc.News, newsSvc newssvc.News,
) { ) {
router.Get("/news", func(w http.ResponseWriter, r *http.Request) { router.Get("/news", func(w http.ResponseWriter, r *http.Request) {
var (
news []newsdomain.News
err error
)
ctx := r.Context() ctx := r.Context()
news, err := newsSvc.GetAllModel() query := r.URL.Query()
category := query.Get("categories")
tags := query.Get("tags")
news, err = newsSvc.GetAll(category, tags)
if err != nil { if err != nil {
response.ResponseWithErrorCode( response.ResponseWithErrorCode(
ctx, ctx,
+34
View File
@@ -0,0 +1,34 @@
package newshttp
import (
newssvc "legalgo-BE-go/internal/services/news"
"legalgo-BE-go/internal/utilities/response"
"net/http"
"github.com/go-chi/chi/v5"
)
func GetBySlug(
router chi.Router,
newsSvc newssvc.News,
) {
router.Get("/news/{slug}", func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
slug := chi.URLParam(r, "slug")
news, err := newsSvc.GetBySlug(slug)
if err != nil {
response.ResponseWithErrorCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
err.Error(),
)
return
}
response.RespondJsonSuccess(ctx, w, news)
})
}
+3
View File
@@ -4,5 +4,8 @@ import "go.uber.org/fx"
var Module = fx.Module("news", fx.Invoke( var Module = fx.Module("news", fx.Invoke(
GetAll, GetAll,
GetBySlug,
Create, Create,
Update,
Delete,
)) ))
+97
View File
@@ -0,0 +1,97 @@
package newshttp
import (
"fmt"
authmiddleware "legalgo-BE-go/internal/api/http/middleware/auth"
newsdomain "legalgo-BE-go/internal/domain/news"
authsvc "legalgo-BE-go/internal/services/auth"
newssvc "legalgo-BE-go/internal/services/news"
"legalgo-BE-go/internal/utilities/response"
"legalgo-BE-go/internal/utilities/utils"
"net/http"
"github.com/go-chi/chi/v5"
)
func Update(
router chi.Router,
newsSvc newssvc.News,
authSvc authsvc.Auth,
) {
router.With(authmiddleware.Authorize()).
Put("/news/{news_id}/update", func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
newsID := chi.URLParam(r, "news_id")
if newsID == "" {
response.ResponseWithErrorCode(
ctx,
w,
fmt.Errorf("news id is not provided"),
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
"news id is not provided",
)
return
}
destructedToken, err := utils.GetTokenDetail(r)
if err != nil {
response.ResponseWithErrorCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
err.Error(),
)
return
}
staff, err := authSvc.GetStaffProfile(destructedToken.Email)
if err != nil {
response.ResponseWithErrorCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
err.Error(),
)
return
}
var spec newsdomain.NewsUpdate
if err := utils.UnmarshalBody(r, &spec); err != nil {
response.ResponseWithErrorCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
err.Error(),
)
return
}
spec.ID = newsID
if err := newsSvc.Update(staff.ID, spec); err != nil {
response.ResponseWithErrorCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
err.Error(),
)
return
}
response.RespondJsonSuccess(ctx, w, struct {
Message string
}{
Message: "news updated successfully.",
})
})
}
+5
View File
@@ -0,0 +1,5 @@
package subscribehttp
import "go.uber.org/fx"
var Module = fx.Module("subscribe", fx.Invoke())
+77
View File
@@ -0,0 +1,77 @@
package subscribehttp
import (
authmiddleware "legalgo-BE-go/internal/api/http/middleware/auth"
userdomain "legalgo-BE-go/internal/domain/user"
authsvc "legalgo-BE-go/internal/services/auth"
subscribesvc "legalgo-BE-go/internal/services/subscribe"
"legalgo-BE-go/internal/utilities/response"
"legalgo-BE-go/internal/utilities/utils"
"net/http"
"github.com/go-chi/chi/v5"
)
func Update(
router chi.Router,
authSvc authsvc.Auth,
subSvc subscribesvc.Subscribe,
) {
router.
With(authmiddleware.Authorize()).
Patch("/subscribe/update", func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
detail, err := utils.GetTokenDetail(r)
if err != nil {
response.ResponseWithErrorCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
err.Error(),
)
return
}
if detail.Role != "user" {
response.ResponseWithErrorCode(
ctx,
w,
err,
response.ErrUnauthorized.Code,
response.ErrUnauthorized.HttpCode,
"unauthorized",
)
return
}
var body userdomain.UserSubsUpdate
err = utils.UnmarshalBody(r, &body)
if err != nil {
response.ResponseWithErrorCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
err.Error(),
)
return
}
user, err := authSvc.GetUserProfile(detail.Email)
if err := subSvc.Update(user.ID, body); err != nil {
response.ResponseWithErrorCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
err.Error(),
)
return
}
})
}
+5 -2
View File
@@ -3,6 +3,7 @@ package subscribeplanhttp
import ( import (
"net/http" "net/http"
authmiddleware "legalgo-BE-go/internal/api/http/middleware/auth"
subscribeplandomain "legalgo-BE-go/internal/domain/subscribe_plan" subscribeplandomain "legalgo-BE-go/internal/domain/subscribe_plan"
subscribeplansvc "legalgo-BE-go/internal/services/subscribe_plan" subscribeplansvc "legalgo-BE-go/internal/services/subscribe_plan"
"legalgo-BE-go/internal/utilities/response" "legalgo-BE-go/internal/utilities/response"
@@ -15,9 +16,11 @@ import (
func CreateSubscribePlan( func CreateSubscribePlan(
router chi.Router, router chi.Router,
validate *validator.Validate, validate *validator.Validate,
subsSvc subscribeplansvc.SubsPlanIntf, subsSvc subscribeplansvc.SubscribePlan,
) { ) {
router.Post("/subscribe-plan/create", func(w http.ResponseWriter, r *http.Request) { router.
With(authmiddleware.Authorize()).
Post("/subscribe-plan/create", func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
var spec subscribeplandomain.SubscribePlanReq var spec subscribeplandomain.SubscribePlanReq
+1 -1
View File
@@ -11,7 +11,7 @@ import (
func GetAllPlan( func GetAllPlan(
router chi.Router, router chi.Router,
subsPlanSvc subscribeplansvc.SubsPlanIntf, subsPlanSvc subscribeplansvc.SubscribePlan,
) { ) {
router.Get("/subscribe-plan", func(w http.ResponseWriter, r *http.Request) { router.Get("/subscribe-plan", func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
+5 -2
View File
@@ -3,6 +3,7 @@ package taghttp
import ( import (
"net/http" "net/http"
authmiddleware "legalgo-BE-go/internal/api/http/middleware/auth"
tagdomain "legalgo-BE-go/internal/domain/tag" tagdomain "legalgo-BE-go/internal/domain/tag"
tagsvc "legalgo-BE-go/internal/services/tag" tagsvc "legalgo-BE-go/internal/services/tag"
"legalgo-BE-go/internal/utilities/response" "legalgo-BE-go/internal/utilities/response"
@@ -15,9 +16,11 @@ import (
func Create( func Create(
router chi.Router, router chi.Router,
validate *validator.Validate, validate *validator.Validate,
tagSvc tagsvc.TagIntf, tagSvc tagsvc.Tag,
) { ) {
router.Post("/tag/create", func(w http.ResponseWriter, r *http.Request) { router.
With(authmiddleware.Authorize()).
Post("/tag/create", func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
var spec tagdomain.TagReq var spec tagdomain.TagReq
+53
View File
@@ -0,0 +1,53 @@
package taghttp
import (
"fmt"
authmiddleware "legalgo-BE-go/internal/api/http/middleware/auth"
tagsvc "legalgo-BE-go/internal/services/tag"
"legalgo-BE-go/internal/utilities/response"
"net/http"
"github.com/go-chi/chi/v5"
)
func Delete(
router chi.Router,
tagSvc tagsvc.Tag,
) {
router.
With(authmiddleware.Authorize()).
Delete("/tag/{category_id}/delete", func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
categoryID := chi.URLParam(r, "category_id")
if categoryID == "" {
response.RespondJsonErrorWithCode(
ctx,
w,
fmt.Errorf("category id is not provided"),
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
"category id is not provided",
)
return
}
if err := tagSvc.Delete(categoryID); err != nil {
response.RespondJsonErrorWithCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
err.Error(),
)
return
}
response.RespondJsonSuccess(ctx, w, struct {
Message string
}{
Message: "tag has been deleted",
})
})
}
+1 -1
View File
@@ -10,7 +10,7 @@ import (
func GetAll( func GetAll(
router chi.Router, router chi.Router,
tagSvc tagsvc.TagIntf, tagSvc tagsvc.Tag,
) { ) {
router.Get("/tag", func(w http.ResponseWriter, r *http.Request) { router.Get("/tag", func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
+2
View File
@@ -5,4 +5,6 @@ import "go.uber.org/fx"
var Module = fx.Module("tag", fx.Invoke( var Module = fx.Module("tag", fx.Invoke(
Create, Create,
GetAll, GetAll,
Update,
Delete,
)) ))
+82
View File
@@ -0,0 +1,82 @@
package taghttp
import (
"fmt"
authmiddleware "legalgo-BE-go/internal/api/http/middleware/auth"
tagdomain "legalgo-BE-go/internal/domain/tag"
tagsvc "legalgo-BE-go/internal/services/tag"
"legalgo-BE-go/internal/utilities/response"
"legalgo-BE-go/internal/utilities/utils"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-playground/validator/v10"
)
func Update(
router chi.Router,
validate *validator.Validate,
tagSvc tagsvc.Tag,
) {
router.
With(authmiddleware.Authorize()).
Put("/tag/{tag_id}/update", func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
tagID := chi.URLParam(r, "tag_id")
if tagID == "" {
response.RespondJsonErrorWithCode(
ctx,
w,
fmt.Errorf("tag id is not provided"),
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
"tag id is not provided",
)
return
}
var spec tagdomain.TagReq
if err := utils.UnmarshalBody(r, &spec); err != nil {
response.RespondJsonErrorWithCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
"failed to unmarshal body",
)
return
}
if err := validate.Struct(spec); err != nil {
response.RespondJsonErrorWithCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
err.(validator.ValidationErrors).Error(),
)
return
}
if err := tagSvc.Update(tagID, spec); err != nil {
response.RespondJsonErrorWithCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
err.Error(),
)
return
}
response.RespondJsonSuccess(ctx, w, struct {
Message string
}{
Message: "update tag success",
})
})
}
-31
View File
@@ -1,31 +0,0 @@
package authdomain
type LoginReq struct {
Email string `json:"email" validate:"required"`
Password string `json:"password" validate:"required"`
}
type AuthResponse struct {
Token string `json:"token"`
}
type LoginRepoResponse struct {
ID string `json:"id"`
Email string `json:"email"`
Password string `json:"password"`
Username string `json:"username"`
}
type StaffProfile struct {
ID string `json:"id"`
Email string `json:"email"`
Username string `json:"username"`
}
type UserProfile struct {
ID string `json:"id"`
Email string `json:"email"`
SubscribePlanCode string `json:"subscribe_plan_code"`
SubscribePlanName string `json:"subscribe_plan_name"`
SubscribeStatus string `json:"subscribe_status"`
}
+1
View File
@@ -3,4 +3,5 @@ package authdomain
type AuthToken struct { type AuthToken struct {
Email string Email string
SessionID string SessionID string
Role string
} }
+6
View File
@@ -1,9 +1,15 @@
package categorydomain package categorydomain
import (
"time"
)
type Category struct { type Category struct {
ID string `json:"id" gorm:"primaryKey"` ID string `json:"id" gorm:"primaryKey"`
Name string `json:"name"` Name string `json:"name"`
Code string `json:"code"` Code string `json:"code"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
} }
type CategoryReq struct { type CategoryReq struct {
+27 -3
View File
@@ -16,17 +16,41 @@ type NewsReq struct {
LiveAt string `json:"live_at" validate:"required"` LiveAt string `json:"live_at" validate:"required"`
} }
type News struct { type Staff struct {
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"`
ProfilePicture string `json:"profile_picture"`
}
type News struct {
ID string `json:"id" gorm:"primaryKey"`
Title string `json:"title"` Title string `json:"title"`
Content string `json:"content"` Content string `json:"content"`
FeaturedImage string `json:"featured_image"` FeaturedImage string `json:"featured_image"`
Tags []tagdomain.Tag `json:"tags"` Tags []tagdomain.Tag `gorm:"many2many:news_tags" json:"tags"`
Categories []categorydomain.Category `json:"categories"` Categories []categorydomain.Category `gorm:"many2many:news_categories" json:"categories"`
IsPremium bool `json:"is_premium"` IsPremium bool `json:"is_premium"`
Slug string `json:"slug"` Slug string `json:"slug"`
AuthorID string `json:"author_id"` AuthorID string `json:"author_id"`
LiveAt time.Time `json:"live_at"` LiveAt time.Time `json:"live_at"`
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"` UpdatedAt time.Time `json:"updated_at"`
Author Staff `json:"author"`
}
type NewsUpdate struct {
ID string `json:"id"`
Title string `json:"title"`
Content string `json:"content"`
FeaturedImage string `json:"featured_image"`
Tags []string `json:"tags"`
Categories []string `json:"categories"`
IsPremium bool `json:"is_premium"`
LiveAt time.Time `json:"live_at"`
}
type NewsFilter struct {
Tags []string
Category []string
} }
+5
View File
@@ -0,0 +1,5 @@
package responsedomain
type Auth struct {
Token string `json:"token"`
}
+28
View File
@@ -0,0 +1,28 @@
package staffdomain
type Staff struct {
ID string `json:"id"`
Name string `json:"name"`
ProfilePicture string `json:"profile_picture"`
Email string `json:"email"`
Password string `json:"password"`
}
type StaffRegister struct {
Email string `json:"email" validate:"required"`
Password string `json:"password" validate:"required"`
Name string `json:"name" validate:"required"`
ProfilePicture string `json:"profile_picture"`
}
type StaffProfile struct {
ID string `json:"id"`
Name string `json:"name"`
ProfilePicture string `json:"profile_picture"`
Email string `json:"email"`
}
type StaffLogin struct {
Email string `json:"email" validate:"required"`
Password string `json:"password" validate:"required"`
}
+23 -1
View File
@@ -1,6 +1,28 @@
package subscribedomain package subscribedomain
type Subscribe struct { import (
subscribeplandm "legalgo-BE-go/internal/domain/subscribe_plan_model"
"time"
)
type SubscribeDepecrate struct {
ID string `json:"id"` ID string `json:"id"`
SubscribePlanID string `json:"subscribe_plan_id"` SubscribePlanID string `json:"subscribe_plan_id"`
} }
type SubscribeUpdateReqDepecrate struct {
ID string `json:"id"`
Status string `json:"status"`
}
// refactored
type Subscribe struct {
ID string `gorm:"primaryKey" json:"id"`
SubscribePlanID string `gorm:"not null" json:"subscribe_plan_id"`
StartDate time.Time `gorm:"default:CURRENT_TIMESTAMP"`
EndDate time.Time `gorm:"default:null"`
Status string `gorm:"default:'inactive'"`
AutoRenew bool `gorm:"default:true"`
SubscribePlan subscribeplandm.SubscribePlan `gorm:"foreignKey:SubscribePlanID;constraint:OnDelete:CASCADE" json:"subscribe_plan"`
}
@@ -0,0 +1,17 @@
package subscribedm
import (
subscribeplandm "legalgo-BE-go/internal/domain/subscribe_plan_model"
"time"
)
type Subscribe struct {
ID string `json:"id"`
SubscribePlanID string `json:"subscribe_plan_id"`
StartDate time.Time `json:"start_date"`
EndDate *time.Time `json:"end_date"`
Status string `json:"status"`
AutoRenew bool `json:"auto_renew"`
SubscribePlan subscribeplandm.SubscribePlan `gorm:"foreignKey:SubscribePlanID" json:"subscribe_plan"`
}
@@ -0,0 +1,8 @@
package subscribeplandm
// refactored
type SubscribePlan struct {
ID string `json:"id"`
Code string `json:"code"`
Name string `json:"name"`
}
+4
View File
@@ -1,5 +1,7 @@
package tagdomain package tagdomain
import "time"
type TagReq struct { type TagReq struct {
Code string `json:"code" validate:"required"` Code string `json:"code" validate:"required"`
Name string `json:"name" validate:"required"` Name string `json:"name" validate:"required"`
@@ -9,4 +11,6 @@ type Tag struct {
ID string `json:"id" gorm:"primaryKey"` ID string `json:"id" gorm:"primaryKey"`
Code string `json:"code"` Code string `json:"code"`
Name string `json:"name"` Name string `json:"name"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
} }
@@ -1,35 +1,37 @@
package authdomain package userdomain
type RegisterUserReq struct { import subscribedm "legalgo-BE-go/internal/domain/subscribe_model"
type User struct {
ID string `json:"id"`
SubscribeID string `json:"subscribe_id"`
Password string `json:"password"`
Email string `json:"email"`
Phone string `json:"phone"`
Subscribe subscribedm.Subscribe `gorm:"foreignKey:SubscribeID" json:"subscribe"`
}
type UserRegister struct {
Email string `json:"email" validate:"required"` Email string `json:"email" validate:"required"`
Password string `json:"password" validate:"required"` Password string `json:"password" validate:"required"`
Phone string `json:"phone" validate:"required"` Phone string `json:"phone" validate:"required"`
SubscribePlanID string `json:"subscribe_plan_id"` SubscribePlanID string `json:"subscribe_plan_id"`
} }
type User struct { type UserProfile struct {
ID string `json:"id"` ID string `json:"id"`
Email string `json:"email"` Email string `json:"email"`
Password string `json:"password"`
Phone string `json:"phone"` Phone string `json:"phone"`
SubscribeID string `json:"subscribe_id"`
Subscribe subscribedm.Subscribe `gorm:"foreignKey:SubscribeID" json:"subscribe"`
} }
type RegisterStaffReq struct { type UserLogin struct {
Email string `json:"email" validate:"required"` Email string `json:"email" validate:"required"`
Password string `json:"password" validate:"required"` Password string `json:"password" validate:"required"`
Username string `json:"username" validate:"required"`
} }
type UpdateStaffReq struct { type UserSubsUpdate struct {
Email string `json:"email"` Status string `json:"status"`
Password string `json:"password"`
Username string `json:"username"`
}
type Staff struct {
ID string `json:"id"`
Email string `json:"email"`
Password string `json:"password"`
Username string `json:"username"`
} }
+7 -4
View File
@@ -1,17 +1,20 @@
package authsvc package authsvc
import authdomain "legalgo-BE-go/internal/domain/auth" import (
staffdomain "legalgo-BE-go/internal/domain/staff"
)
func (as *AuthSvc) GetStaffProfile(email string) (*authdomain.StaffProfile, error) { func (as *impl) GetStaffProfile(email string) (*staffdomain.StaffProfile, error) {
staff, err := as.staffRepo.GetStaffByEmail(email) staff, err := as.staffRepo.GetStaffByEmail(email)
if err != nil { if err != nil {
return nil, err return nil, err
} }
profile := &authdomain.StaffProfile{ profile := &staffdomain.StaffProfile{
ID: staff.ID, ID: staff.ID,
Username: staff.Username, Name: staff.Name,
Email: staff.Email, Email: staff.Email,
ProfilePicture: staff.ProfilePicture,
} }
return profile, nil return profile, nil

Some files were not shown because too many files have changed in this diff Show More