Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions .github/workflows/CI-checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: CI integrity checks

on:
pull_request:
branches:
- main
- dev
push:
branches:
- main

jobs:
backend-checks:
name: Backend integrity checks
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: '1.25'
cache: true

- name: Download Dependencies
run: go mod download

- name: Verify Backend Build
run: go build -v ./...

- name: Run Tests
run: go test -v ./...

frontend-checks:
name: Frontend integrity checks
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./lb-web
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: lb-web/package-lock.json

- name: Install Dependencies
run: npm ci

- name: Type check & lint
run: npm run lint
continue-on-error: true

- name: Run Tests
run: npm run test

- name: Build
run: npm run build
26 changes: 26 additions & 0 deletions Internal/adapters/repository/authRepo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package repository

import "github.com/Arjuna-Ragil/Localbase/Internal/core/domain"

type AuthRepo struct {
DB *DBContainer
}

func NewAuthRepo(db *DBContainer) *AuthRepo {
return &AuthRepo{DB: db}
}

func (ar *AuthRepo) RegisterRepo(user *domain.User) error {
if err := ar.DB.Gorm.Create(user).Error; err != nil {
return err
}
return nil
}

func (ar *AuthRepo) LoginRepo(email string) (*domain.User, error) {
var user domain.User
if err := ar.DB.Gorm.Where("email = ?", email).First(&user).Error; err != nil {
return nil, err
}
return &user, nil
}
49 changes: 49 additions & 0 deletions Internal/adapters/repository/postgres.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package repository

import (
"fmt"
"log"

"github.com/Arjuna-Ragil/Localbase/Internal/config"
"github.com/Arjuna-Ragil/Localbase/Internal/core/domain"
"github.com/jmoiron/sqlx"
_ "github.com/lib/pq"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)

type DBContainer struct {
Gorm *gorm.DB
Sqlx *sqlx.DB
}

func ConnectDB() (*DBContainer, error) {
cfg := config.LoadConfig()

gormDB, err := gorm.Open(postgres.Open(cfg.DBURL), &gorm.Config{})
if err != nil {
log.Fatalf("Failed to connect to gorm database: %v", err)
}
fmt.Println("Connected to gorm database")

sqlxDB, err := sqlx.Connect("postgres", cfg.DBURL)
if err != nil {
log.Fatalf("Failed to connect to sqlx database: %v", err)
}
fmt.Println("Connected to sqlx database")

return &DBContainer{
Gorm: gormDB,
Sqlx: sqlxDB,
}, nil
}
func (db *DBContainer) Migrate() error {
err := db.Gorm.AutoMigrate(
&domain.User{},
)
if err != nil {
log.Fatalf("Failed to migrate users: %v", err)
}
fmt.Println("Migrated users")
return nil
}
19 changes: 19 additions & 0 deletions Internal/adapters/repository/sysRepo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package repository

import "github.com/Arjuna-Ragil/Localbase/Internal/core/domain"

type SystemRepo struct {
DB *DBContainer
}

func NewSystemRepo(db *DBContainer) *SystemRepo {
return &SystemRepo{DB: db}
}

func (sr *SystemRepo) IsAppInitialized() (bool, error) {
var count int64
if err := sr.DB.Gorm.Model(&domain.User{}).Count(&count).Error; err != nil {
return false, err
}
return count > 0, nil
}
30 changes: 30 additions & 0 deletions Internal/adapters/repository/userRepo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package repository

import "github.com/Arjuna-Ragil/Localbase/Internal/core/domain"

type UserQuery interface {
Save(user *domain.User) error
FindById(id string) (*domain.User, error)
}
type UserRepository struct {
db *DBContainer
}

func NewUserRepository(db *DBContainer) *UserRepository {
return &UserRepository{db: db}
}

// Query

func (ur *UserRepository) save(user *domain.User) error {
return ur.db.Gorm.Create(user).Error
}

func (ur *UserRepository) FindById(id uint) (*domain.User, error) {
var user domain.User
err := ur.db.Gorm.First(&user, id).Error
if err != nil {
return nil, err
}
return &user, nil
}
45 changes: 45 additions & 0 deletions Internal/api/Routes/routev1.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package Routes

import (
"github.com/Arjuna-Ragil/Localbase/Internal/adapters/repository"
"github.com/Arjuna-Ragil/Localbase/Internal/api/handlers"
"github.com/Arjuna-Ragil/Localbase/Internal/api/middleware"
"github.com/Arjuna-Ragil/Localbase/Internal/config"
"github.com/gin-gonic/gin"
)

type Deps struct {
User *handlers.UserHandler
Auth *handlers.AuthHandler
System *handlers.SystemHandler
UserRepo *repository.UserRepository
Config *config.Config
}

func SetupRouterV1(r *gin.Engine, deps Deps) {
api := r.Group("/api/v1")

authMiddleware := middleware.AuthMiddleware(deps.UserRepo, deps.Config)
{
system := api.Group("/system")
{
system.GET("/status", deps.System.CheckAppInitStatus)
}

auth := api.Group("/auth")
{
auth.POST("/Register", deps.Auth.RegisterHandler)
auth.POST("/Login", deps.Auth.LoginHandler)
auth.POST("/logout", deps.Auth.LogoutHandler)
}

protected := api.Group("/protected")
protected.Use(authMiddleware)
{
user := protected.Group("/user")
{
user.GET("/me", deps.User.GetUserHandler)
}
}
}
}
71 changes: 71 additions & 0 deletions Internal/api/handlers/authHandler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package handlers

import (
"net/http"

"github.com/Arjuna-Ragil/Localbase/Internal/core/services"
"github.com/gin-gonic/gin"
)

type AuthHandler struct {
Service *services.AuthService
}

func NewAuthHandler(service *services.AuthService) *AuthHandler {
return &AuthHandler{Service: service}
}

func (ah *AuthHandler) RegisterHandler(c *gin.Context) {
var input services.RegisterInput
if err := c.ShouldBindJSON(&input); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"message": "Invalid Input",
"data": err.Error(),
})
return
}
if err := ah.Service.RegisterService(&input); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "Register service failed",
"data": err.Error(),
})
return
}
c.JSON(http.StatusOK, gin.H{
"message": "Register service success",
"data": nil,
})
}

func (ah *AuthHandler) LoginHandler(c *gin.Context) {
var input services.LoginInput
if err := c.ShouldBindJSON(&input); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"message": "Invalid Input",
"data": err.Error(),
})
return
}
tokenString, err := ah.Service.LoginService(&input)
if err != nil {
c.JSON(401, gin.H{
"message": "Login failed",
"data": err.Error(),
})
return
}
c.SetSameSite(http.SameSiteLaxMode)
c.SetCookie("authorization", tokenString, 3600*24, "/", "", false, true)
c.JSON(http.StatusOK, gin.H{
"message": "Login success",
"data": tokenString,
})
}

func (ah *AuthHandler) LogoutHandler(c *gin.Context) {
c.SetCookie("authorization", "", -1, "/", "", false, true)
c.JSON(http.StatusOK, gin.H{
"message": "Logout success",
"data": nil,
})
}
24 changes: 24 additions & 0 deletions Internal/api/handlers/sysHandler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package handlers

import (
"net/http"

"github.com/Arjuna-Ragil/Localbase/Internal/core/services"
"github.com/gin-gonic/gin"
)

type SystemHandler struct {
Service *services.SystemService
}

func NewSystemHandler(service *services.SystemService) *SystemHandler {
return &SystemHandler{Service: service}
}

func (sh *SystemHandler) CheckAppInitStatus(c *gin.Context) {
isInit, _ := sh.Service.CheckAppInitialized()
c.JSON(http.StatusOK, gin.H{
"message": "App init status",
"data": isInit,
})
}
37 changes: 37 additions & 0 deletions Internal/api/handlers/userHandler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package handlers

import (
"github.com/Arjuna-Ragil/Localbase/Internal/core/services"
"github.com/gin-gonic/gin"
)

type UserHandler struct {
Service *services.UserService
}

func NewUserHandler(s *services.UserService) *UserHandler {
return &UserHandler{Service: s}
}

func (uh *UserHandler) GetUserHandler(c *gin.Context) {
userID, exist := c.Get("userID")
if !exist {
c.JSON(400, gin.H{
"message": "Unable to find user",
"data": exist,
})
return
}
user, err := uh.Service.GetUser(userID.(uint))
if err != nil {
c.JSON(400, gin.H{
"message": "Unable to get user data",
"data": err,
})
return
}
c.JSON(200, gin.H{
"message": "Successfully get user data",
"data": user,
})
}
Loading
Loading