Spaces:
Runtime error
Runtime error
Commit ·
92bfa2a
1
Parent(s): a389a2e
Deploy files from GitHub repository
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .github/workflows/deploy-development.yml +32 -0
- .github/workflows/deploy-production.yml +32 -0
- .github/workflows/deploy-test.yml +49 -0
- .github/workflows/go-build-test.yml +28 -0
- .github/workflows/uptime-check.yml +44 -0
- .gitignore +8 -0
- Dockerfile +28 -0
- README.md +216 -10
- cmd/compile_swagger.ps1 +1 -0
- cmd/do_inject_config.ps1 +435 -0
- cmd/do_inject_controllers.ps1 +310 -0
- cmd/do_inject_middleware.ps1 +312 -0
- cmd/do_inject_repository.ps1 +327 -0
- cmd/do_inject_services.ps1 +383 -0
- config/config.go +1 -0
- config/database_config.go +55 -0
- config/env_config.go +112 -0
- config/jwt_config.go +24 -0
- config/supabase_config.go +25 -0
- config/upload_config.go +46 -0
- config/xendit_config.go +31 -0
- controllers/authentication_controller.go +59 -0
- controllers/controller.go +53 -0
- controllers/home_controller.go +113 -0
- controllers/otp_verification_controller.go +42 -0
- controllers/payment_callback_controller.go +79 -0
- docs/docs.go +705 -0
- docs/swagger.json +676 -0
- docs/swagger.yaml +440 -0
- err.txt +17 -0
- err2.txt +12 -0
- err3.txt +7 -0
- err4.txt +0 -0
- err_swag.txt +0 -0
- err_swag2.txt +3 -0
- go.mod +75 -0
- go.sum +215 -0
- go.work +3 -0
- go.work.sum +91 -0
- logs/error_log.txt +0 -0
- logs/security_log.txt +0 -0
- main.go +11 -0
- middleware/authentication_middleware.go +48 -0
- middleware/middleware.go +1 -0
- models/dto/authentication_dto.go +55 -0
- models/dto/email_verification_dto.go +5 -0
- models/dto/home_dto.go +92 -0
- models/dto/home_swagger_wrapper.go +18 -0
- models/dto/http_response_dto.go +15 -0
- models/dto/jwt_dto.go +15 -0
.github/workflows/deploy-development.yml
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
name: Deploy to Development
|
| 3 |
+
on:
|
| 4 |
+
push:
|
| 5 |
+
branches:
|
| 6 |
+
- development
|
| 7 |
+
|
| 8 |
+
jobs:
|
| 9 |
+
deploy:
|
| 10 |
+
runs-on: ubuntu-latest
|
| 11 |
+
|
| 12 |
+
steps:
|
| 13 |
+
- name: Checkout
|
| 14 |
+
uses: actions/checkout@v4
|
| 15 |
+
with:
|
| 16 |
+
fetch-depth: 0
|
| 17 |
+
|
| 18 |
+
- name: Setup SSH
|
| 19 |
+
uses: webfactory/ssh-agent@v0.9.0
|
| 20 |
+
with:
|
| 21 |
+
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
|
| 22 |
+
|
| 23 |
+
- name: Add VPS to known_hosts
|
| 24 |
+
run: |
|
| 25 |
+
mkdir -p ~/.ssh
|
| 26 |
+
ssh-keyscan -H ${{ secrets.VPS_HOST }} >> ~/.ssh/known_hosts
|
| 27 |
+
|
| 28 |
+
- name: Push to VPS
|
| 29 |
+
run: |
|
| 30 |
+
git remote remove vps || true
|
| 31 |
+
git remote add vps git@${{ secrets.VPS_HOST }}:/home/git/repos/backend-development.git
|
| 32 |
+
git push vps HEAD:development --force
|
.github/workflows/deploy-production.yml
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Deploy to Production
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
push:
|
| 5 |
+
branches:
|
| 6 |
+
- production
|
| 7 |
+
|
| 8 |
+
jobs:
|
| 9 |
+
deploy:
|
| 10 |
+
runs-on: ubuntu-latest
|
| 11 |
+
|
| 12 |
+
steps:
|
| 13 |
+
- name: Checkout
|
| 14 |
+
uses: actions/checkout@v4
|
| 15 |
+
with:
|
| 16 |
+
fetch-depth: 0
|
| 17 |
+
|
| 18 |
+
- name: Setup SSH
|
| 19 |
+
uses: webfactory/ssh-agent@v0.9.0
|
| 20 |
+
with:
|
| 21 |
+
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
|
| 22 |
+
|
| 23 |
+
- name: Add VPS to known_hosts
|
| 24 |
+
run: |
|
| 25 |
+
mkdir -p ~/.ssh
|
| 26 |
+
ssh-keyscan -H ${{ secrets.VPS_HOST }} >> ~/.ssh/known_hosts
|
| 27 |
+
|
| 28 |
+
- name: Push to VPS
|
| 29 |
+
run: |
|
| 30 |
+
git remote remove vps || true
|
| 31 |
+
git remote add vps git@${{ secrets.VPS_HOST }}:/home/git/repos/backend-production.git
|
| 32 |
+
git push vps HEAD:production --force
|
.github/workflows/deploy-test.yml
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Deploy to Huggingface - tests
|
| 2 |
+
on:
|
| 3 |
+
push:
|
| 4 |
+
branches:
|
| 5 |
+
- test
|
| 6 |
+
jobs:
|
| 7 |
+
deploy-to-huggingface:
|
| 8 |
+
runs-on: ubuntu-latest
|
| 9 |
+
steps:
|
| 10 |
+
# Checkout repository
|
| 11 |
+
- name: Checkout Repository
|
| 12 |
+
uses: actions/checkout@v3
|
| 13 |
+
# Setup Git
|
| 14 |
+
- name: Setup Git for Huggingface
|
| 15 |
+
run: |
|
| 16 |
+
git config --global user.email "abdan.hafidz@gmail.com"
|
| 17 |
+
git config --global user.name "abdanhafidz"
|
| 18 |
+
# Clone Huggingface Space Repository
|
| 19 |
+
- name: Clone Huggingface Space
|
| 20 |
+
env:
|
| 21 |
+
HF_TOKEN: ${{ secrets.HF_TOKEN }}
|
| 22 |
+
run: |
|
| 23 |
+
git clone https://huggingface.co/spaces/lifedebugger/DanzApp-BE-Test space
|
| 24 |
+
# Update Git Remote URL and Pull Latest Changes
|
| 25 |
+
- name: Update Remote and Pull Changes
|
| 26 |
+
env:
|
| 27 |
+
HF_TOKEN: ${{ secrets.HF_TOKEN }}
|
| 28 |
+
run: |
|
| 29 |
+
cd space
|
| 30 |
+
git remote set-url origin https://lifedebugger:$HF_TOKEN@huggingface.co/spaces/lifedebugger/DanzApp-BE-Test
|
| 31 |
+
git pull origin main || echo "No changes to pull"
|
| 32 |
+
# Clean Space Directory - Delete all files except .git
|
| 33 |
+
- name: Clean Space Directory
|
| 34 |
+
run: |
|
| 35 |
+
cd space
|
| 36 |
+
find . -mindepth 1 -not -path "./.git*" -delete
|
| 37 |
+
# Copy Files to Huggingface Space
|
| 38 |
+
- name: Copy Files to Space
|
| 39 |
+
run: |
|
| 40 |
+
rsync -av --exclude='.git' ./ space/
|
| 41 |
+
# Commit and Push to Huggingface Space
|
| 42 |
+
- name: Commit and Push to Huggingface
|
| 43 |
+
env:
|
| 44 |
+
HF_TOKEN: ${{ secrets.HF_TOKEN }}
|
| 45 |
+
run: |
|
| 46 |
+
cd space
|
| 47 |
+
git add .
|
| 48 |
+
git commit -m "Deploy files from GitHub repository" || echo "No changes to commit"
|
| 49 |
+
git push origin main || echo "No changes to push"
|
.github/workflows/go-build-test.yml
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This workflow will build a golang project
|
| 2 |
+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go
|
| 3 |
+
|
| 4 |
+
name: Go
|
| 5 |
+
|
| 6 |
+
on:
|
| 7 |
+
push:
|
| 8 |
+
branches: [ "test" ]
|
| 9 |
+
pull_request:
|
| 10 |
+
branches: [ "development" ]
|
| 11 |
+
|
| 12 |
+
jobs:
|
| 13 |
+
|
| 14 |
+
build:
|
| 15 |
+
runs-on: ubuntu-latest
|
| 16 |
+
steps:
|
| 17 |
+
- uses: actions/checkout@v4
|
| 18 |
+
|
| 19 |
+
- name: Set up Go
|
| 20 |
+
uses: actions/setup-go@v4
|
| 21 |
+
with:
|
| 22 |
+
go-version: '1.20'
|
| 23 |
+
|
| 24 |
+
- name: Build
|
| 25 |
+
run: go build -v ./...
|
| 26 |
+
|
| 27 |
+
- name: Test
|
| 28 |
+
run: go test -v ./...
|
.github/workflows/uptime-check.yml
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
name: Uptime Check Backend API
|
| 3 |
+
|
| 4 |
+
on:
|
| 5 |
+
push:
|
| 6 |
+
branches:
|
| 7 |
+
- tests
|
| 8 |
+
|
| 9 |
+
jobs:
|
| 10 |
+
check-uptime:
|
| 11 |
+
runs-on: ubuntu-latest
|
| 12 |
+
|
| 13 |
+
steps:
|
| 14 |
+
- name: Wait 5 Minutes Before Checking Uptime
|
| 15 |
+
run: |
|
| 16 |
+
echo "⏳ Waiting 5 minutes before running uptime checks..."
|
| 17 |
+
sleep 300
|
| 18 |
+
|
| 19 |
+
- name: Check Dev API Uptime
|
| 20 |
+
run: |
|
| 21 |
+
echo "🔍 Checking Development API..."
|
| 22 |
+
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" {YOUR_DEV_ENDPOINT})
|
| 23 |
+
|
| 24 |
+
echo "Development API HTTP Status: $RESPONSE"
|
| 25 |
+
|
| 26 |
+
if [ "$RESPONSE" -ne 200 ]; then
|
| 27 |
+
echo "❌ Development API DOWN! Expected 200 but got $RESPONSE"
|
| 28 |
+
exit 1
|
| 29 |
+
fi
|
| 30 |
+
|
| 31 |
+
- name: Check Test API Uptime
|
| 32 |
+
run: |
|
| 33 |
+
echo "🔍 Checking Test API..."
|
| 34 |
+
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" {YOUR_TEST_ENDPOINT})
|
| 35 |
+
|
| 36 |
+
echo "Test API HTTP Status: $RESPONSE"
|
| 37 |
+
|
| 38 |
+
if [ "$RESPONSE" -ne 200 ]; then
|
| 39 |
+
echo "❌ Test API DOWN! Expected 200 but got $RESPONSE"
|
| 40 |
+
exit 1
|
| 41 |
+
fi
|
| 42 |
+
|
| 43 |
+
- name: Success Message
|
| 44 |
+
run: echo "✔ All API endpoints are UP!"
|
.gitignore
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*.env
|
| 2 |
+
go-boilerplate.exe
|
| 3 |
+
vendor/
|
| 4 |
+
*.log
|
| 5 |
+
*.exe
|
| 6 |
+
*.dll
|
| 7 |
+
*.so
|
| 8 |
+
*.dylib
|
Dockerfile
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# =====================
|
| 2 |
+
# BUILD STAGE
|
| 3 |
+
# =====================
|
| 4 |
+
FROM golang:1.25.4 AS builder
|
| 5 |
+
|
| 6 |
+
WORKDIR /app
|
| 7 |
+
|
| 8 |
+
COPY go.mod go.sum ./
|
| 9 |
+
RUN go mod download
|
| 10 |
+
|
| 11 |
+
COPY . .
|
| 12 |
+
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o app
|
| 13 |
+
|
| 14 |
+
# =====================
|
| 15 |
+
# RUNTIME STAGE
|
| 16 |
+
# =====================
|
| 17 |
+
FROM gcr.io/distroless/base-debian12
|
| 18 |
+
|
| 19 |
+
WORKDIR /app
|
| 20 |
+
|
| 21 |
+
COPY --from=builder /app/app /app/app
|
| 22 |
+
|
| 23 |
+
# Non-root user
|
| 24 |
+
USER nonroot:nonroot
|
| 25 |
+
|
| 26 |
+
EXPOSE 8080
|
| 27 |
+
|
| 28 |
+
CMD ["/app/app"]
|
README.md
CHANGED
|
@@ -1,10 +1,216 @@
|
|
| 1 |
-
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
--
|
| 9 |
-
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 🚀 Go Starter Boilerplate - Clean Layered Architecture
|
| 2 |
+
|
| 3 |
+
[](https://golang.org/)
|
| 4 |
+
[](https://gin-gonic.com/)
|
| 5 |
+
[](https://gorm.io/)
|
| 6 |
+
[](LICENSE)
|
| 7 |
+
|
| 8 |
+
A high-performance, scalable, and professional Go boilerplate built with **Clean Layered Architecture**. This starter kit is designed to provide a solid foundation for enterprise-grade backend applications, featuring dependency injection, modularity, and robust CI/CD pipelines.
|
| 9 |
+
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
## 📑 Table of Contents
|
| 13 |
+
- [Core Architecture & Theory](#-core-architecture--theory)
|
| 14 |
+
- [Tech Stack](#-tech-stack)
|
| 15 |
+
- [Key Features](#-key-features)
|
| 16 |
+
- [Project Structure](#-project-structure)
|
| 17 |
+
- [CLI Tools & Automated DI](#-cli-tools--automated-di)
|
| 18 |
+
- [Setup & Installation](#-setup--installation)
|
| 19 |
+
- [Environment Variables](#-environment-variables)
|
| 20 |
+
- [Documentation (Swagger)](#-documentation-swagger)
|
| 21 |
+
- [CI/CD & Deployment](#-cicd--deployment)
|
| 22 |
+
- [SOLID Principles](#-solid-principles)
|
| 23 |
+
|
| 24 |
+
---
|
| 25 |
+
|
| 26 |
+
## 🏗️ Core Architecture & Theory
|
| 27 |
+
|
| 28 |
+
This project follows the **Clean Layered Architecture** pattern, ensuring high maintainability and testability by decoupling business logic from external dependencies (DB, API, Frameworks).
|
| 29 |
+
|
| 30 |
+
### The Four Layers:
|
| 31 |
+
1. **Transport / Router**: Defines API endpoints and handles HTTP protocol specifics using Gin.
|
| 32 |
+
2. **Controller**: Entry point for requests. Validates input, calls services, and formats JSON responses.
|
| 33 |
+
3. **Service (Usecase)**: The heart of the application. Contains business logic and orchestrates data flow between repositories.
|
| 34 |
+
4. **Repository (Data Access)**: Handles database interactions using GORM. Isolated from business logic.
|
| 35 |
+
|
| 36 |
+
### 🔌 Dependency Injection (Provider Pattern)
|
| 37 |
+
We use a **Provider Pattern** for Dependency Injection, managed through the `provider/` directory. While the wiring is handled in Go, this project features an **Automated DI Engine** that generates this wiring for you.
|
| 38 |
+
|
| 39 |
+
Benefits:
|
| 40 |
+
- **Singleton Management**: Ensures single instances of services/repositories.
|
| 41 |
+
- **Automated Wiring**: No more manual `NewService(repo1, repo2, ...)` maintenance.
|
| 42 |
+
- **Cycle Detection**: The induction engine automatically detects and prevents circular dependencies.
|
| 43 |
+
- **Decoupling**: Layers remain isolated through interfaces.
|
| 44 |
+
|
| 45 |
+
---
|
| 46 |
+
|
| 47 |
+
## 🛠️ Tech Stack
|
| 48 |
+
|
| 49 |
+
- **Language**: [Go (Golang)](https://golang.org/)
|
| 50 |
+
- **Web Framework**: [Gin Gonic](https://github.com/gin-gonic/gin)
|
| 51 |
+
- **ORM**: [GORM](https://gorm.io/) with PostgreSQL
|
| 52 |
+
- **Database**: [PostgreSQL](https://www.postgresql.org/)
|
| 53 |
+
- **Authentication**: [JWT (JSON Web Token)](https://jwt.io/)
|
| 54 |
+
- **Payment Gateway**: [Xendit](https://www.xendit.co/)
|
| 55 |
+
- **Object Storage**: [Supabase Storage](https://supabase.com/storage)
|
| 56 |
+
- **Documentation**: [Swagger (swaggo)](https://github.com/swaggo/swag)
|
| 57 |
+
- **Containerization**: [Docker](https://www.docker.com/)
|
| 58 |
+
|
| 59 |
+
---
|
| 60 |
+
|
| 61 |
+
## ✨ Key Features
|
| 62 |
+
|
| 63 |
+
- **Lock-tight Auth**: Complete Authentication flow (Login, Register, Email Verification, Forgot Password).
|
| 64 |
+
- **Role-Based Access**: Specialized routes for Admin and User roles.
|
| 65 |
+
- **Payment Ready**: Integrated with Xendit for seamless payment processing and webhooks.
|
| 66 |
+
- **Cloud Storage**: Native support for Supabase storage for file uploads.
|
| 67 |
+
- **Automated Migrations**: Database schema automatically synchronizes on startup.
|
| 68 |
+
- **Standardized Responses**: Unified JSON response structure for success and error handling.
|
| 69 |
+
- **Modular Routing**: Cleanly separated route definitions per module.
|
| 70 |
+
|
| 71 |
+
---
|
| 72 |
+
|
| 73 |
+
## 📁 Project Structure
|
| 74 |
+
|
| 75 |
+
```text
|
| 76 |
+
├── cmd/ # Custom CLI tools (e.g., Swagger compiler)
|
| 77 |
+
├── config/ # Configuration loaders & schema definitions
|
| 78 |
+
├── controllers/ # Request handlers (Input validation, Response formatting)
|
| 79 |
+
├── middleware/ # Gin middlewares (Auth, Logging, Gzip)
|
| 80 |
+
├── models/
|
| 81 |
+
│ ├── dto/ # Data Transfer Objects (Request/Response structs)
|
| 82 |
+
│ ├── entity/ # GORM database models
|
| 83 |
+
│ └── error/ # Custom error definitions
|
| 84 |
+
├── provider/ # Dependency Injection & Bootstrapping
|
| 85 |
+
├── repositories/ # Database access layer (SQL logic)
|
| 86 |
+
├── router/ # Route definitions & grouping
|
| 87 |
+
├── services/ # Business logic layer
|
| 88 |
+
├── swagger/ # Automatically generated Swagger UI files
|
| 89 |
+
├── utils/ # Cross-cutting helpers (Response helpers, string utils)
|
| 90 |
+
├── main.go # Application entry point
|
| 91 |
+
├── Dockerfile # Multi-stage production container
|
| 92 |
+
└── .github/workflows/ # CI/CD Pipelines
|
| 93 |
+
```
|
| 94 |
+
|
| 95 |
+
---
|
| 96 |
+
|
| 97 |
+
## 🛠️ CLI Tools & Automated DI
|
| 98 |
+
|
| 99 |
+
This boilerplate features a unique **PowerShell-based Dependency Injection Engine** located in the `cmd/` directory. These scripts scan your code, resolve dependencies, and automatically generate the necessary boilerplate code in the `provider/` package.
|
| 100 |
+
|
| 101 |
+
### 💉 Automated Injection Scripts
|
| 102 |
+
| Script | Purpose |
|
| 103 |
+
| :--- | :--- |
|
| 104 |
+
| `do_inject_config.ps1` | Scans configuration files and wires them into the Config Provider. |
|
| 105 |
+
| `do_inject_repository.ps1` | Discovers GORM repositories and updates `repositories_provider.go`. |
|
| 106 |
+
| `do_inject_services.ps1` | Performs **topological sorting** on services to resolve their dependencies in the correct order. |
|
| 107 |
+
| `do_inject_controllers.ps1` | Wires services into controllers and updates the Controller Provider. |
|
| 108 |
+
| `do_inject_middleware.ps1` | Manages registration of custom Gin middlewares. |
|
| 109 |
+
|
| 110 |
+
### 🧠 How the DI Engine Works:
|
| 111 |
+
1. **Scanning**: The scripts use regex to find constructor functions (e.g., `NewAccountService`).
|
| 112 |
+
2. **Resolution**: It identifies the required parameters (Repositories, other Services, or Configs).
|
| 113 |
+
3. **Topological Sort**: (For Services) It builds a dependency graph and sorts them so dependencies are initialized before they are needed.
|
| 114 |
+
4. **Codegen**: It writes a clean, standardized `provider/*.go` file with all the wiring logic.
|
| 115 |
+
|
| 116 |
+
### 📝 Swagger Compilation
|
| 117 |
+
- **`compile_swagger.ps1`**: This script wraps the `swag init` command with optimized flags (`--parseDependency`, `--parseInternal`) to ensure your Swagger documentation is always complete and up-to-date.
|
| 118 |
+
|
| 119 |
+
---
|
| 120 |
+
|
| 121 |
+
## 🚀 Setup & Installation
|
| 122 |
+
|
| 123 |
+
### Prerequisites
|
| 124 |
+
- Go 1.25.4 or higher
|
| 125 |
+
- PostgreSQL
|
| 126 |
+
- Docker (Optional, for containerization)
|
| 127 |
+
|
| 128 |
+
### Step 1: Clone the Repository
|
| 129 |
+
```bash
|
| 130 |
+
git clone <your-repo-url>
|
| 131 |
+
cd <your-directory-name>
|
| 132 |
+
```
|
| 133 |
+
|
| 134 |
+
### Step 2: Environment Setup
|
| 135 |
+
Copy the example environment file and fill in your credentials:
|
| 136 |
+
```bash
|
| 137 |
+
cp .env.example .env
|
| 138 |
+
```
|
| 139 |
+
|
| 140 |
+
### Step 3: Install Dependencies
|
| 141 |
+
```bash
|
| 142 |
+
go mod download
|
| 143 |
+
```
|
| 144 |
+
|
| 145 |
+
### Step 4: Run the Application
|
| 146 |
+
```bash
|
| 147 |
+
go run main.go
|
| 148 |
+
```
|
| 149 |
+
|
| 150 |
+
---
|
| 151 |
+
|
| 152 |
+
## � Environment Variables
|
| 153 |
+
|
| 154 |
+
| Variable | Description |
|
| 155 |
+
| :--- | :--- |
|
| 156 |
+
| `DB_HOST` | PostgreSQL Host address |
|
| 157 |
+
| `DB_USER` | Database username |
|
| 158 |
+
| `DB_PASSWORD` | Database password |
|
| 159 |
+
| `DB_PORT` | Database port (default 5432) |
|
| 160 |
+
| `DB_NAME` | Name of the database |
|
| 161 |
+
| `JWT_SECRET_KEY` | Secret key for signing JWT tokens |
|
| 162 |
+
| `XENDIT_API_KEY` | Your Xendit Secret Key |
|
| 163 |
+
| `HOST_PORT` | Port for the Go server to listen on |
|
| 164 |
+
|
| 165 |
+
---
|
| 166 |
+
|
| 167 |
+
## 📖 Documentation (Swagger)
|
| 168 |
+
|
| 169 |
+
This project uses `swaggo` to generate interactive API documentation.
|
| 170 |
+
|
| 171 |
+
### How to Compile Swagger:
|
| 172 |
+
Run the provided script to synchronize your code comments with the documentation:
|
| 173 |
+
```bash
|
| 174 |
+
./cmd/compile_swagger
|
| 175 |
+
```
|
| 176 |
+
|
| 177 |
+
### Accessing Swagger UI:
|
| 178 |
+
Once the app is running, visit:
|
| 179 |
+
`http://localhost:<PORT>/swagger/index.html`
|
| 180 |
+
|
| 181 |
+
---
|
| 182 |
+
|
| 183 |
+
## 🚢 CI/CD & Deployment
|
| 184 |
+
|
| 185 |
+
### GitHub Workflows
|
| 186 |
+
The project includes automated pipelines in `.github/workflows/`:
|
| 187 |
+
- **`go-build-test.yml`**: Automatically runs tests and builds the binary on every push.
|
| 188 |
+
- **`deploy-production.yml` / `deploy-development.yml`**: CD pipelines for automated shipping to target environments.
|
| 189 |
+
- **`uptime-check.yml`**: Specialized workflow to monitor service health.
|
| 190 |
+
|
| 191 |
+
### Docker
|
| 192 |
+
We use a **Multi-Stage Dockerfile** to ensure the production image is as small and secure as possible.
|
| 193 |
+
|
| 194 |
+
**Build the image:**
|
| 195 |
+
```bash
|
| 196 |
+
docker build -t go-starter .
|
| 197 |
+
```
|
| 198 |
+
|
| 199 |
+
**Run the container:**
|
| 200 |
+
```bash
|
| 201 |
+
docker run -p 8080:8080 --env-file .env go-starter
|
| 202 |
+
```
|
| 203 |
+
|
| 204 |
+
---
|
| 205 |
+
|
| 206 |
+
## 💎 SOLID Principles
|
| 207 |
+
|
| 208 |
+
- **S**: Single Responsibility. Each layer (Controller, Service, Repo) does exactly one thing.
|
| 209 |
+
- **O**: Open/Closed. Services are open for extension via interfaces but closed for modification.
|
| 210 |
+
- **L**: Liskov Substitution. Implementations are interchangeable via Provider interfaces.
|
| 211 |
+
- **I**: Interface Segregation. Slim, specific interfaces for each capability.
|
| 212 |
+
- **D**: Dependency Inversion. High-level modules don't depend on low-level modules; both depend on abstractions.
|
| 213 |
+
|
| 214 |
+
---
|
| 215 |
+
|
| 216 |
+
Developed with ❤️ by Abdan Hafidz.
|
cmd/compile_swagger.ps1
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
swag init -g swagger/swagger_info.go -o swagger/docs --parseDependency --parseInternal
|
cmd/do_inject_config.ps1
ADDED
|
@@ -0,0 +1,435 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#Requires -Version 5.1
|
| 2 |
+
<#
|
| 3 |
+
.SYNOPSIS
|
| 4 |
+
Automatic Dependency Injection Generator for Go Configuration
|
| 5 |
+
|
| 6 |
+
.DESCRIPTION
|
| 7 |
+
Scans ./config/ directory, discovers all config constructors, infers their dependencies,
|
| 8 |
+
and generates provider/config_provider.go with full DI wiring. Special handling for
|
| 9 |
+
chained dependencies where configs depend on other configs (e.g., DatabaseConfig depends on EnvConfig).
|
| 10 |
+
|
| 11 |
+
.EXAMPLE
|
| 12 |
+
.\config_injector.ps1
|
| 13 |
+
|
| 14 |
+
.NOTES
|
| 15 |
+
- Works with PowerShell 5.1+ and PowerShell 7+
|
| 16 |
+
- No external dependencies required
|
| 17 |
+
- Supports multi-line constructor signatures
|
| 18 |
+
- Handles config-to-config dependencies with topological sorting
|
| 19 |
+
- Special handling for method calls like envConfig.GetDatabaseHost()
|
| 20 |
+
#>
|
| 21 |
+
|
| 22 |
+
[CmdletBinding()]
|
| 23 |
+
param()
|
| 24 |
+
|
| 25 |
+
# Configuration
|
| 26 |
+
$ConfigDir = "./config"
|
| 27 |
+
$OutputFile = "provider/config_provider.go"
|
| 28 |
+
$ModulePath = "abdanhafidz.com/go-boilerplate/config"
|
| 29 |
+
|
| 30 |
+
# ANSI colors for better output
|
| 31 |
+
$script:UseColors = $Host.UI.SupportsVirtualTerminal
|
| 32 |
+
function Write-ColorOutput {
|
| 33 |
+
param([string]$Message, [string]$Color = "White")
|
| 34 |
+
if ($script:UseColors) {
|
| 35 |
+
$colors = @{
|
| 36 |
+
"Green" = "`e[32m"; "Yellow" = "`e[33m"; "Red" = "`e[31m"
|
| 37 |
+
"Cyan" = "`e[36m"; "Blue" = "`e[34m"; "Reset" = "`e[0m"
|
| 38 |
+
}
|
| 39 |
+
Write-Host "$($colors[$Color])$Message$($colors['Reset'])"
|
| 40 |
+
} else {
|
| 41 |
+
Write-Host $Message
|
| 42 |
+
}
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
# Data structures
|
| 46 |
+
class ConfigInfo {
|
| 47 |
+
[string]$ConstructorName # NewDatabaseConfig
|
| 48 |
+
[string]$Domain # DatabaseConfig
|
| 49 |
+
[string]$VarName # databaseConfig
|
| 50 |
+
[System.Collections.Generic.List[Parameter]]$Parameters
|
| 51 |
+
[System.Collections.Generic.List[string]]$ConfigDependencies
|
| 52 |
+
|
| 53 |
+
ConfigInfo() {
|
| 54 |
+
$this.Parameters = [System.Collections.Generic.List[Parameter]]::new()
|
| 55 |
+
$this.ConfigDependencies = [System.Collections.Generic.List[string]]::new()
|
| 56 |
+
}
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
class Parameter {
|
| 60 |
+
[string]$Name
|
| 61 |
+
[string]$RawType
|
| 62 |
+
[string]$NormalizedType
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
function Get-LowerCamelCase {
|
| 66 |
+
param([string]$Text)
|
| 67 |
+
if ($Text.Length -eq 0) { return $Text }
|
| 68 |
+
return $Text.Substring(0, 1).ToLower() + $Text.Substring(1)
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
function Normalize-TypeName {
|
| 72 |
+
param([string]$TypeStr)
|
| 73 |
+
|
| 74 |
+
# Remove leading pointer
|
| 75 |
+
$cleaned = $TypeStr -replace '^\*+', ''
|
| 76 |
+
|
| 77 |
+
# Remove package prefix (everything before last dot)
|
| 78 |
+
if ($cleaned -match '\.([^.]+)$') {
|
| 79 |
+
$cleaned = $matches[1]
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
return $cleaned.Trim()
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
function Parse-GoFiles {
|
| 86 |
+
param([string]$Directory)
|
| 87 |
+
|
| 88 |
+
Write-ColorOutput "Scanning for config constructors in $Directory..." "Cyan"
|
| 89 |
+
|
| 90 |
+
if (-not (Test-Path $Directory)) {
|
| 91 |
+
Write-ColorOutput "ERROR: Directory '$Directory' not found!" "Red"
|
| 92 |
+
exit 1
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
$goFiles = Get-ChildItem -Path $Directory -Filter "*.go" -Recurse -File
|
| 96 |
+
$configs = [System.Collections.Generic.List[ConfigInfo]]::new()
|
| 97 |
+
|
| 98 |
+
foreach ($file in $goFiles) {
|
| 99 |
+
$content = Get-Content $file.FullName -Raw
|
| 100 |
+
|
| 101 |
+
# Match function signatures (support multi-line)
|
| 102 |
+
# Pattern: func NewXxxConfig(...) XxxConfig
|
| 103 |
+
$pattern = '(?ms)func\s+(New[a-zA-Z0-9]+Config)\s*\(([^)]*)\)\s+([a-zA-Z0-9*_.]+Config)'
|
| 104 |
+
$matches = [regex]::Matches($content, $pattern)
|
| 105 |
+
|
| 106 |
+
foreach ($match in $matches) {
|
| 107 |
+
$constructorName = $match.Groups[1].Value
|
| 108 |
+
$paramsStr = $match.Groups[2].Value
|
| 109 |
+
$returnType = $match.Groups[3].Value
|
| 110 |
+
|
| 111 |
+
# Extract domain name (XxxConfig)
|
| 112 |
+
$domain = Normalize-TypeName $returnType
|
| 113 |
+
$varName = Get-LowerCamelCase $domain
|
| 114 |
+
|
| 115 |
+
$config = [ConfigInfo]::new()
|
| 116 |
+
$config.ConstructorName = $constructorName
|
| 117 |
+
$config.Domain = $domain
|
| 118 |
+
$config.VarName = $varName
|
| 119 |
+
|
| 120 |
+
# Parse parameters
|
| 121 |
+
if ($paramsStr.Trim() -ne "") {
|
| 122 |
+
# Split by comma, but be careful with nested types
|
| 123 |
+
$paramList = $paramsStr -split ',\s*(?![^<>]*>)'
|
| 124 |
+
|
| 125 |
+
foreach ($param in $paramList) {
|
| 126 |
+
$param = $param.Trim()
|
| 127 |
+
if ($param -eq "") { continue }
|
| 128 |
+
|
| 129 |
+
# Split into name and type
|
| 130 |
+
$parts = $param -split '\s+', 2
|
| 131 |
+
|
| 132 |
+
$p = [Parameter]::new()
|
| 133 |
+
if ($parts.Count -eq 2) {
|
| 134 |
+
$p.Name = $parts[0]
|
| 135 |
+
$p.RawType = $parts[1]
|
| 136 |
+
} elseif ($parts.Count -eq 1) {
|
| 137 |
+
# Anonymous parameter - synthesize name
|
| 138 |
+
$p.Name = "param$($config.Parameters.Count)"
|
| 139 |
+
$p.RawType = $parts[0]
|
| 140 |
+
} else {
|
| 141 |
+
continue
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
$p.NormalizedType = Normalize-TypeName $p.RawType
|
| 145 |
+
$config.Parameters.Add($p)
|
| 146 |
+
|
| 147 |
+
# Track config dependencies (configs that depend on other configs)
|
| 148 |
+
if ($p.NormalizedType -match 'Config$') {
|
| 149 |
+
$config.ConfigDependencies.Add($p.NormalizedType)
|
| 150 |
+
}
|
| 151 |
+
}
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
$configs.Add($config)
|
| 155 |
+
Write-ColorOutput " Found: $constructorName" "Green"
|
| 156 |
+
}
|
| 157 |
+
}
|
| 158 |
+
|
| 159 |
+
if ($configs.Count -eq 0) {
|
| 160 |
+
Write-ColorOutput "No config constructors found matching pattern 'NewXxxConfig'!" "Red"
|
| 161 |
+
exit 1
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
Write-ColorOutput "`nTotal configs discovered: $($configs.Count)" "Blue"
|
| 165 |
+
return $configs
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
function Get-TopologicalOrder {
|
| 169 |
+
param([System.Collections.Generic.List[ConfigInfo]]$Configs)
|
| 170 |
+
|
| 171 |
+
Write-ColorOutput "`nBuilding dependency graph..." "Cyan"
|
| 172 |
+
|
| 173 |
+
# Build adjacency list
|
| 174 |
+
$graph = @{}
|
| 175 |
+
$inDegree = @{}
|
| 176 |
+
$domainToConfig = @{}
|
| 177 |
+
|
| 178 |
+
foreach ($cfg in $Configs) {
|
| 179 |
+
$graph[$cfg.Domain] = [System.Collections.Generic.List[string]]::new()
|
| 180 |
+
$inDegree[$cfg.Domain] = 0
|
| 181 |
+
$domainToConfig[$cfg.Domain] = $cfg
|
| 182 |
+
}
|
| 183 |
+
|
| 184 |
+
# Build edges (dependencies)
|
| 185 |
+
foreach ($cfg in $Configs) {
|
| 186 |
+
foreach ($dep in $cfg.ConfigDependencies) {
|
| 187 |
+
if ($graph.ContainsKey($dep)) {
|
| 188 |
+
$graph[$dep].Add($cfg.Domain)
|
| 189 |
+
$inDegree[$cfg.Domain]++
|
| 190 |
+
}
|
| 191 |
+
}
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
# Kahn's algorithm for topological sort
|
| 195 |
+
$queue = [System.Collections.Generic.Queue[string]]::new()
|
| 196 |
+
foreach ($domain in $inDegree.Keys) {
|
| 197 |
+
if ($inDegree[$domain] -eq 0) {
|
| 198 |
+
$queue.Enqueue($domain)
|
| 199 |
+
}
|
| 200 |
+
}
|
| 201 |
+
|
| 202 |
+
$sorted = [System.Collections.Generic.List[string]]::new()
|
| 203 |
+
|
| 204 |
+
while ($queue.Count -gt 0) {
|
| 205 |
+
$current = $queue.Dequeue()
|
| 206 |
+
$sorted.Add($current)
|
| 207 |
+
|
| 208 |
+
foreach ($neighbor in $graph[$current]) {
|
| 209 |
+
$inDegree[$neighbor]--
|
| 210 |
+
if ($inDegree[$neighbor] -eq 0) {
|
| 211 |
+
$queue.Enqueue($neighbor)
|
| 212 |
+
}
|
| 213 |
+
}
|
| 214 |
+
}
|
| 215 |
+
|
| 216 |
+
# Check for cycles
|
| 217 |
+
if ($sorted.Count -ne $Configs.Count) {
|
| 218 |
+
$remaining = $inDegree.Keys | Where-Object { $inDegree[$_] -gt 0 }
|
| 219 |
+
Write-ColorOutput "`nERROR: Circular dependency detected in configs!" "Red"
|
| 220 |
+
Write-ColorOutput "Configs involved in cycle: $($remaining -join ', ')" "Yellow"
|
| 221 |
+
exit 1
|
| 222 |
+
}
|
| 223 |
+
|
| 224 |
+
Write-ColorOutput " Dependency graph validated (no cycles)" "Green"
|
| 225 |
+
Write-ColorOutput " Topological order: $($sorted -join ' -> ')" "Blue"
|
| 226 |
+
|
| 227 |
+
# Return configs in topological order
|
| 228 |
+
return $sorted | ForEach-Object { $domainToConfig[$_] }
|
| 229 |
+
}
|
| 230 |
+
|
| 231 |
+
function Resolve-ConfigArgument {
|
| 232 |
+
param(
|
| 233 |
+
[Parameter]$Param,
|
| 234 |
+
[string]$DependentConfigVar
|
| 235 |
+
)
|
| 236 |
+
|
| 237 |
+
$type = $Param.NormalizedType
|
| 238 |
+
$paramName = $Param.Name
|
| 239 |
+
|
| 240 |
+
# SPECIAL CASE MAPPINGS FOR CONFIG
|
| 241 |
+
# ============================================
|
| 242 |
+
|
| 243 |
+
# 1. Config pattern: XxxConfig -> already instantiated config variable
|
| 244 |
+
if ($type -match '^(.+)Config$') {
|
| 245 |
+
$configVarName = Get-LowerCamelCase $type
|
| 246 |
+
return $configVarName
|
| 247 |
+
}
|
| 248 |
+
|
| 249 |
+
# 2. String parameters - try to infer from parameter name and match with config getter methods
|
| 250 |
+
if ($type -eq "string") {
|
| 251 |
+
# Common patterns for EnvConfig getters
|
| 252 |
+
$getterMappings = @{
|
| 253 |
+
"host" = "GetDatabaseHost()"
|
| 254 |
+
"databaseHost" = "GetDatabaseHost()"
|
| 255 |
+
"user" = "GetDatabaseUser()"
|
| 256 |
+
"databaseUser" = "GetDatabaseUser()"
|
| 257 |
+
"password" = "GetDatabasePassword()"
|
| 258 |
+
"databasePassword" = "GetDatabasePassword()"
|
| 259 |
+
"name" = "GetDatabaseName()"
|
| 260 |
+
"databaseName" = "GetDatabaseName()"
|
| 261 |
+
"dbName" = "GetDatabaseName()"
|
| 262 |
+
"port" = "GetDatabasePort()"
|
| 263 |
+
"databasePort" = "GetDatabasePort()"
|
| 264 |
+
"salt" = "GetSalt()"
|
| 265 |
+
"secret" = "GetSecretKey()"
|
| 266 |
+
"secretKey" = "GetSecretKey()"
|
| 267 |
+
"jwtSecret" = "GetSecretKey()"
|
| 268 |
+
"apiKey" = "GetAPIKey()"
|
| 269 |
+
"timezone" = "GetTimezone()"
|
| 270 |
+
}
|
| 271 |
+
|
| 272 |
+
# Try to find matching getter
|
| 273 |
+
foreach ($key in $getterMappings.Keys) {
|
| 274 |
+
if ($paramName -like "*$key*") {
|
| 275 |
+
return "envConfig.$($getterMappings[$key])"
|
| 276 |
+
}
|
| 277 |
+
}
|
| 278 |
+
|
| 279 |
+
# If dependent on a config, try to construct getter name from param name
|
| 280 |
+
if ($DependentConfigVar) {
|
| 281 |
+
# Convert paramName to PascalCase for getter
|
| 282 |
+
$getterName = (Get-Culture).TextInfo.ToTitleCase($paramName)
|
| 283 |
+
$getterName = $getterName -replace '\s', ''
|
| 284 |
+
return "${DependentConfigVar}.Get${getterName}()"
|
| 285 |
+
}
|
| 286 |
+
}
|
| 287 |
+
|
| 288 |
+
# 3. Int/port parameters
|
| 289 |
+
if ($type -eq "int" -or $type -eq "int32" -or $type -eq "int64") {
|
| 290 |
+
if ($paramName -match "port") {
|
| 291 |
+
return "envConfig.GetDatabasePort()"
|
| 292 |
+
}
|
| 293 |
+
}
|
| 294 |
+
|
| 295 |
+
# ADD MORE SPECIAL CASES HERE:
|
| 296 |
+
# --------------------------------------------
|
| 297 |
+
# Example: Redis config
|
| 298 |
+
# if ($paramName -match "redis") {
|
| 299 |
+
# return "envConfig.GetRedisURL()"
|
| 300 |
+
# }
|
| 301 |
+
#
|
| 302 |
+
# Example: Mail config
|
| 303 |
+
# if ($paramName -match "smtp") {
|
| 304 |
+
# return "envConfig.GetSMTPHost()"
|
| 305 |
+
# }
|
| 306 |
+
# --------------------------------------------
|
| 307 |
+
|
| 308 |
+
# 4. Hardcoded constants (timezone example)
|
| 309 |
+
if ($type -eq "string" -and $paramName -match "timezone|location") {
|
| 310 |
+
return "`"Asia/Jakarta`""
|
| 311 |
+
}
|
| 312 |
+
|
| 313 |
+
# 5. Fallback: unresolved type
|
| 314 |
+
return "/* TODO: provide $($Param.RawType) for $paramName */"
|
| 315 |
+
}
|
| 316 |
+
|
| 317 |
+
function Generate-ProviderCode {
|
| 318 |
+
param([System.Collections.Generic.List[ConfigInfo]]$ConfigsInOrder)
|
| 319 |
+
|
| 320 |
+
Write-ColorOutput "`nGenerating config provider code..." "Cyan"
|
| 321 |
+
|
| 322 |
+
$sb = [System.Text.StringBuilder]::new()
|
| 323 |
+
[void]$sb.AppendLine("package provider")
|
| 324 |
+
[void]$sb.AppendLine()
|
| 325 |
+
[void]$sb.AppendLine("import `"$ModulePath`"")
|
| 326 |
+
[void]$sb.AppendLine()
|
| 327 |
+
|
| 328 |
+
# Interface
|
| 329 |
+
[void]$sb.AppendLine("type ConfigProvider interface {")
|
| 330 |
+
foreach ($cfg in $ConfigsInOrder) {
|
| 331 |
+
$line = "`tProvide$($cfg.Domain)() config.$($cfg.Domain)"
|
| 332 |
+
[void]$sb.AppendLine($line)
|
| 333 |
+
}
|
| 334 |
+
[void]$sb.AppendLine("}")
|
| 335 |
+
[void]$sb.AppendLine()
|
| 336 |
+
|
| 337 |
+
# Struct
|
| 338 |
+
[void]$sb.AppendLine("type configProvider struct {")
|
| 339 |
+
foreach ($cfg in $ConfigsInOrder) {
|
| 340 |
+
$line = "`t$($cfg.VarName) config.$($cfg.Domain)"
|
| 341 |
+
[void]$sb.AppendLine($line)
|
| 342 |
+
}
|
| 343 |
+
[void]$sb.AppendLine("}")
|
| 344 |
+
[void]$sb.AppendLine()
|
| 345 |
+
|
| 346 |
+
# Constructor
|
| 347 |
+
[void]$sb.AppendLine("func NewConfigProvider() ConfigProvider {")
|
| 348 |
+
|
| 349 |
+
# Initialize configs in topological order
|
| 350 |
+
foreach ($cfg in $ConfigsInOrder) {
|
| 351 |
+
# Check if this config depends on another config
|
| 352 |
+
$dependentConfigVar = $null
|
| 353 |
+
if ($cfg.ConfigDependencies.Count -gt 0) {
|
| 354 |
+
$dependentConfigVar = Get-LowerCamelCase $cfg.ConfigDependencies[0]
|
| 355 |
+
}
|
| 356 |
+
|
| 357 |
+
$args = @()
|
| 358 |
+
foreach ($param in $cfg.Parameters) {
|
| 359 |
+
$args += Resolve-ConfigArgument -Param $param -DependentConfigVar $dependentConfigVar
|
| 360 |
+
}
|
| 361 |
+
$argsStr = $args -join ", "
|
| 362 |
+
$line = "`t$($cfg.VarName) := config.$($cfg.ConstructorName)($argsStr)"
|
| 363 |
+
[void]$sb.AppendLine($line)
|
| 364 |
+
}
|
| 365 |
+
|
| 366 |
+
[void]$sb.AppendLine("`treturn &configProvider{")
|
| 367 |
+
foreach ($cfg in $ConfigsInOrder) {
|
| 368 |
+
$line = "`t`t$($cfg.VarName): $($cfg.VarName),"
|
| 369 |
+
[void]$sb.AppendLine($line)
|
| 370 |
+
}
|
| 371 |
+
[void]$sb.AppendLine("`t}")
|
| 372 |
+
[void]$sb.AppendLine("}")
|
| 373 |
+
[void]$sb.AppendLine()
|
| 374 |
+
|
| 375 |
+
# Getter methods
|
| 376 |
+
foreach ($cfg in $ConfigsInOrder) {
|
| 377 |
+
[void]$sb.AppendLine("func (c *configProvider) Provide$($cfg.Domain)() config.$($cfg.Domain) {")
|
| 378 |
+
[void]$sb.AppendLine("`treturn c.$($cfg.VarName)")
|
| 379 |
+
[void]$sb.AppendLine("}")
|
| 380 |
+
[void]$sb.AppendLine()
|
| 381 |
+
}
|
| 382 |
+
|
| 383 |
+
return $sb.ToString()
|
| 384 |
+
}
|
| 385 |
+
|
| 386 |
+
function Write-ProviderFile {
|
| 387 |
+
param([string]$Code, [string]$OutputPath)
|
| 388 |
+
|
| 389 |
+
Write-ColorOutput "Writing to $OutputPath..." "Cyan"
|
| 390 |
+
|
| 391 |
+
# Ensure directory exists
|
| 392 |
+
$dir = Split-Path $OutputPath -Parent
|
| 393 |
+
if ($dir -and -not (Test-Path $dir)) {
|
| 394 |
+
New-Item -ItemType Directory -Path $dir -Force | Out-Null
|
| 395 |
+
}
|
| 396 |
+
|
| 397 |
+
# Write file as UTF-8 without BOM
|
| 398 |
+
$utf8NoBom = [System.Text.UTF8Encoding]::new($false)
|
| 399 |
+
[System.IO.File]::WriteAllText($OutputPath, $Code, $utf8NoBom)
|
| 400 |
+
|
| 401 |
+
Write-ColorOutput " Successfully generated $OutputPath" "Green"
|
| 402 |
+
}
|
| 403 |
+
|
| 404 |
+
# ============================================
|
| 405 |
+
# MAIN EXECUTION
|
| 406 |
+
# ============================================
|
| 407 |
+
|
| 408 |
+
try {
|
| 409 |
+
Write-ColorOutput "`n=========================================" "Blue"
|
| 410 |
+
Write-ColorOutput " Go Config Provider Generator v1.0" "Blue"
|
| 411 |
+
Write-ColorOutput "=========================================`n" "Blue"
|
| 412 |
+
|
| 413 |
+
# Step 1: Parse all config constructors
|
| 414 |
+
$configs = Parse-GoFiles -Directory $ConfigDir
|
| 415 |
+
|
| 416 |
+
# Step 2: Perform topological sort (configs can depend on other configs)
|
| 417 |
+
$sortedConfigs = Get-TopologicalOrder -Configs $configs
|
| 418 |
+
|
| 419 |
+
# Step 3: Generate provider code
|
| 420 |
+
$code = Generate-ProviderCode -ConfigsInOrder $sortedConfigs
|
| 421 |
+
|
| 422 |
+
# Step 4: Write to file
|
| 423 |
+
Write-ProviderFile -Code $code -OutputPath $OutputFile
|
| 424 |
+
|
| 425 |
+
Write-ColorOutput "`nSUCCESS! Config provider generated successfully.`n" "Green"
|
| 426 |
+
Write-ColorOutput "Next steps:" "Cyan"
|
| 427 |
+
Write-ColorOutput " 1. Review $OutputFile" "White"
|
| 428 |
+
Write-ColorOutput " 2. Fill any /* TODO: provide ... */ placeholders" "White"
|
| 429 |
+
Write-ColorOutput " 3. Run: go build ./provider" "White"
|
| 430 |
+
|
| 431 |
+
} catch {
|
| 432 |
+
Write-ColorOutput "`nERROR: $($_.Exception.Message)" "Red"
|
| 433 |
+
Write-ColorOutput "Stack trace: $($_.ScriptStackTrace)" "Yellow"
|
| 434 |
+
exit 1
|
| 435 |
+
}
|
cmd/do_inject_controllers.ps1
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#Requires -Version 5.1
|
| 2 |
+
<#
|
| 3 |
+
.SYNOPSIS
|
| 4 |
+
Automatic Dependency Injection Generator for Go Controllers
|
| 5 |
+
|
| 6 |
+
.DESCRIPTION
|
| 7 |
+
Scans ./controllers/ directory, discovers all controller constructors, infers their dependencies,
|
| 8 |
+
and generates provider/controller_provider.go with full DI wiring.
|
| 9 |
+
|
| 10 |
+
.EXAMPLE
|
| 11 |
+
.\controller_injector.ps1
|
| 12 |
+
|
| 13 |
+
.NOTES
|
| 14 |
+
- Works with PowerShell 5.1+ and PowerShell 7+
|
| 15 |
+
- No external dependencies required
|
| 16 |
+
- Supports multi-line constructor signatures
|
| 17 |
+
- Controllers depend on services from ServicesProvider
|
| 18 |
+
#>
|
| 19 |
+
|
| 20 |
+
[CmdletBinding()]
|
| 21 |
+
param()
|
| 22 |
+
|
| 23 |
+
# Configuration
|
| 24 |
+
$ControllersDir = "./controllers"
|
| 25 |
+
$OutputFile = "provider/controller_provider.go"
|
| 26 |
+
$ModulePath = "abdanhafidz.com/go-boilerplate/controllers"
|
| 27 |
+
|
| 28 |
+
# ANSI colors for better output
|
| 29 |
+
$script:UseColors = $Host.UI.SupportsVirtualTerminal
|
| 30 |
+
function Write-ColorOutput {
|
| 31 |
+
param([string]$Message, [string]$Color = "White")
|
| 32 |
+
if ($script:UseColors) {
|
| 33 |
+
$colors = @{
|
| 34 |
+
"Green" = "`e[32m"; "Yellow" = "`e[33m"; "Red" = "`e[31m"
|
| 35 |
+
"Cyan" = "`e[36m"; "Blue" = "`e[34m"; "Reset" = "`e[0m"
|
| 36 |
+
}
|
| 37 |
+
Write-Host "$($colors[$Color])$Message$($colors['Reset'])"
|
| 38 |
+
} else {
|
| 39 |
+
Write-Host $Message
|
| 40 |
+
}
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
# Data structures
|
| 44 |
+
class ControllerInfo {
|
| 45 |
+
[string]$ConstructorName # NewAccountController
|
| 46 |
+
[string]$Domain # AccountController
|
| 47 |
+
[string]$VarName # accountController
|
| 48 |
+
[System.Collections.Generic.List[Parameter]]$Parameters
|
| 49 |
+
|
| 50 |
+
ControllerInfo() {
|
| 51 |
+
$this.Parameters = [System.Collections.Generic.List[Parameter]]::new()
|
| 52 |
+
}
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
class Parameter {
|
| 56 |
+
[string]$Name
|
| 57 |
+
[string]$RawType
|
| 58 |
+
[string]$NormalizedType
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
function Get-LowerCamelCase {
|
| 62 |
+
param([string]$Text)
|
| 63 |
+
if ($Text.Length -eq 0) { return $Text }
|
| 64 |
+
return $Text.Substring(0, 1).ToLower() + $Text.Substring(1)
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
function Normalize-TypeName {
|
| 68 |
+
param([string]$TypeStr)
|
| 69 |
+
|
| 70 |
+
# Remove leading pointer
|
| 71 |
+
$cleaned = $TypeStr -replace '^\*+', ''
|
| 72 |
+
|
| 73 |
+
# Remove package prefix (everything before last dot)
|
| 74 |
+
if ($cleaned -match '\.([^.]+)$') {
|
| 75 |
+
$cleaned = $matches[1]
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
return $cleaned.Trim()
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
function Parse-GoFiles {
|
| 82 |
+
param([string]$Directory)
|
| 83 |
+
|
| 84 |
+
Write-ColorOutput "Scanning for controller constructors in $Directory..." "Cyan"
|
| 85 |
+
|
| 86 |
+
if (-not (Test-Path $Directory)) {
|
| 87 |
+
Write-ColorOutput "ERROR: Directory '$Directory' not found!" "Red"
|
| 88 |
+
exit 1
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
$goFiles = Get-ChildItem -Path $Directory -Filter "*.go" -Recurse -File
|
| 92 |
+
$controllers = [System.Collections.Generic.List[ControllerInfo]]::new()
|
| 93 |
+
|
| 94 |
+
foreach ($file in $goFiles) {
|
| 95 |
+
$content = Get-Content $file.FullName -Raw
|
| 96 |
+
|
| 97 |
+
# Match function signatures (support multi-line)
|
| 98 |
+
# Pattern: func NewXxxController(...) XxxController
|
| 99 |
+
$pattern = '(?ms)func\s+(New[a-zA-Z0-9]+Controller)\s*\(([^)]*)\)\s+([a-zA-Z0-9*_.]+Controller)'
|
| 100 |
+
$matches = [regex]::Matches($content, $pattern)
|
| 101 |
+
|
| 102 |
+
foreach ($match in $matches) {
|
| 103 |
+
$constructorName = $match.Groups[1].Value
|
| 104 |
+
$paramsStr = $match.Groups[2].Value
|
| 105 |
+
$returnType = $match.Groups[3].Value
|
| 106 |
+
|
| 107 |
+
# Extract domain name (XxxController)
|
| 108 |
+
$domain = Normalize-TypeName $returnType
|
| 109 |
+
$varName = Get-LowerCamelCase $domain
|
| 110 |
+
|
| 111 |
+
$controller = [ControllerInfo]::new()
|
| 112 |
+
$controller.ConstructorName = $constructorName
|
| 113 |
+
$controller.Domain = $domain
|
| 114 |
+
$controller.VarName = $varName
|
| 115 |
+
|
| 116 |
+
# Parse parameters
|
| 117 |
+
if ($paramsStr.Trim() -ne "") {
|
| 118 |
+
# Split by comma, but be careful with nested types
|
| 119 |
+
$paramList = $paramsStr -split ',\s*(?![^<>]*>)'
|
| 120 |
+
|
| 121 |
+
foreach ($param in $paramList) {
|
| 122 |
+
$param = $param.Trim()
|
| 123 |
+
if ($param -eq "") { continue }
|
| 124 |
+
|
| 125 |
+
# Split into name and type
|
| 126 |
+
$parts = $param -split '\s+', 2
|
| 127 |
+
|
| 128 |
+
$p = [Parameter]::new()
|
| 129 |
+
if ($parts.Count -eq 2) {
|
| 130 |
+
$p.Name = $parts[0]
|
| 131 |
+
$p.RawType = $parts[1]
|
| 132 |
+
} elseif ($parts.Count -eq 1) {
|
| 133 |
+
# Anonymous parameter - synthesize name
|
| 134 |
+
$p.Name = "param$($controller.Parameters.Count)"
|
| 135 |
+
$p.RawType = $parts[0]
|
| 136 |
+
} else {
|
| 137 |
+
continue
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
$p.NormalizedType = Normalize-TypeName $p.RawType
|
| 141 |
+
$controller.Parameters.Add($p)
|
| 142 |
+
}
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
$controllers.Add($controller)
|
| 146 |
+
Write-ColorOutput " Found: $constructorName" "Green"
|
| 147 |
+
}
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
if ($controllers.Count -eq 0) {
|
| 151 |
+
Write-ColorOutput "No controller constructors found matching pattern 'NewXxxController'!" "Red"
|
| 152 |
+
exit 1
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
Write-ColorOutput "`nTotal controllers discovered: $($controllers.Count)" "Blue"
|
| 156 |
+
return $controllers
|
| 157 |
+
}
|
| 158 |
+
|
| 159 |
+
function Resolve-ControllerArgument {
|
| 160 |
+
param([Parameter]$Param)
|
| 161 |
+
|
| 162 |
+
$type = $Param.NormalizedType
|
| 163 |
+
|
| 164 |
+
# DEPENDENCY RESOLUTION RULES
|
| 165 |
+
# ============================================
|
| 166 |
+
|
| 167 |
+
# 1. Service pattern: XxxxService -> servicesProvider.ProvideXxxxService()
|
| 168 |
+
if ($type -match '^(.+)Service$') {
|
| 169 |
+
$serviceName = $type
|
| 170 |
+
return "servicesProvider.Provide${serviceName}()"
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
+
# ADD MORE SPECIAL CASES HERE:
|
| 174 |
+
# --------------------------------------------
|
| 175 |
+
# Example: Config dependency
|
| 176 |
+
# if ($type -eq "Config") {
|
| 177 |
+
# return "configProvider.ProvideConfig()"
|
| 178 |
+
# }
|
| 179 |
+
#
|
| 180 |
+
# Example: Logger
|
| 181 |
+
# if ($type -eq "Logger") {
|
| 182 |
+
# return "loggerProvider.ProvideLogger()"
|
| 183 |
+
# }
|
| 184 |
+
#
|
| 185 |
+
# Example: Validator
|
| 186 |
+
# if ($type -eq "Validator") {
|
| 187 |
+
# return "validatorProvider.ProvideValidator()"
|
| 188 |
+
# }
|
| 189 |
+
# --------------------------------------------
|
| 190 |
+
|
| 191 |
+
# 2. Fallback: unresolved type
|
| 192 |
+
return "/* TODO: provide $($Param.RawType) */"
|
| 193 |
+
}
|
| 194 |
+
|
| 195 |
+
function Generate-ProviderCode {
|
| 196 |
+
param([System.Collections.Generic.List[ControllerInfo]]$Controllers)
|
| 197 |
+
|
| 198 |
+
Write-ColorOutput "`nGenerating controller provider code..." "Cyan"
|
| 199 |
+
|
| 200 |
+
# Sort controllers alphabetically for consistent output
|
| 201 |
+
$sortedControllers = $Controllers | Sort-Object -Property Domain
|
| 202 |
+
|
| 203 |
+
$sb = [System.Text.StringBuilder]::new()
|
| 204 |
+
[void]$sb.AppendLine("package provider")
|
| 205 |
+
[void]$sb.AppendLine()
|
| 206 |
+
[void]$sb.AppendLine("import `"$ModulePath`"")
|
| 207 |
+
[void]$sb.AppendLine()
|
| 208 |
+
|
| 209 |
+
# Interface
|
| 210 |
+
[void]$sb.AppendLine("type ControllerProvider interface {")
|
| 211 |
+
foreach ($ctrl in $sortedControllers) {
|
| 212 |
+
$line = "`tProvide$($ctrl.Domain)() controllers.$($ctrl.Domain)"
|
| 213 |
+
[void]$sb.AppendLine($line)
|
| 214 |
+
}
|
| 215 |
+
[void]$sb.AppendLine("}")
|
| 216 |
+
[void]$sb.AppendLine()
|
| 217 |
+
|
| 218 |
+
# Struct
|
| 219 |
+
[void]$sb.AppendLine("type controllerProvider struct {")
|
| 220 |
+
foreach ($ctrl in $sortedControllers) {
|
| 221 |
+
$line = "`t$($ctrl.VarName) controllers.$($ctrl.Domain)"
|
| 222 |
+
[void]$sb.AppendLine($line)
|
| 223 |
+
}
|
| 224 |
+
[void]$sb.AppendLine("}")
|
| 225 |
+
[void]$sb.AppendLine()
|
| 226 |
+
|
| 227 |
+
# Constructor
|
| 228 |
+
[void]$sb.AppendLine("func NewControllerProvider(servicesProvider ServicesProvider) ControllerProvider {")
|
| 229 |
+
[void]$sb.AppendLine()
|
| 230 |
+
|
| 231 |
+
# Initialize controllers
|
| 232 |
+
foreach ($ctrl in $sortedControllers) {
|
| 233 |
+
$args = @()
|
| 234 |
+
foreach ($param in $ctrl.Parameters) {
|
| 235 |
+
$args += Resolve-ControllerArgument $param
|
| 236 |
+
}
|
| 237 |
+
$argsStr = $args -join ", "
|
| 238 |
+
$line = "`t$($ctrl.VarName) := controllers.$($ctrl.ConstructorName)($argsStr)"
|
| 239 |
+
[void]$sb.AppendLine($line)
|
| 240 |
+
}
|
| 241 |
+
|
| 242 |
+
[void]$sb.AppendLine("`treturn &controllerProvider{")
|
| 243 |
+
foreach ($ctrl in $sortedControllers) {
|
| 244 |
+
$line = "`t`t$($ctrl.VarName): $($ctrl.VarName),"
|
| 245 |
+
[void]$sb.AppendLine($line)
|
| 246 |
+
}
|
| 247 |
+
[void]$sb.AppendLine("`t}")
|
| 248 |
+
[void]$sb.AppendLine("}")
|
| 249 |
+
[void]$sb.AppendLine()
|
| 250 |
+
|
| 251 |
+
# Getter methods
|
| 252 |
+
[void]$sb.AppendLine("// --- Getter Methods ---")
|
| 253 |
+
[void]$sb.AppendLine()
|
| 254 |
+
foreach ($ctrl in $sortedControllers) {
|
| 255 |
+
[void]$sb.AppendLine("func (c *controllerProvider) Provide$($ctrl.Domain)() controllers.$($ctrl.Domain) {")
|
| 256 |
+
[void]$sb.AppendLine("`treturn c.$($ctrl.VarName)")
|
| 257 |
+
[void]$sb.AppendLine("}")
|
| 258 |
+
[void]$sb.AppendLine()
|
| 259 |
+
}
|
| 260 |
+
|
| 261 |
+
return $sb.ToString()
|
| 262 |
+
}
|
| 263 |
+
|
| 264 |
+
function Write-ProviderFile {
|
| 265 |
+
param([string]$Code, [string]$OutputPath)
|
| 266 |
+
|
| 267 |
+
Write-ColorOutput "Writing to $OutputPath..." "Cyan"
|
| 268 |
+
|
| 269 |
+
# Ensure directory exists
|
| 270 |
+
$dir = Split-Path $OutputPath -Parent
|
| 271 |
+
if ($dir -and -not (Test-Path $dir)) {
|
| 272 |
+
New-Item -ItemType Directory -Path $dir -Force | Out-Null
|
| 273 |
+
}
|
| 274 |
+
|
| 275 |
+
# Write file as UTF-8 without BOM
|
| 276 |
+
$utf8NoBom = [System.Text.UTF8Encoding]::new($false)
|
| 277 |
+
[System.IO.File]::WriteAllText($OutputPath, $Code, $utf8NoBom)
|
| 278 |
+
|
| 279 |
+
Write-ColorOutput " Successfully generated $OutputPath" "Green"
|
| 280 |
+
}
|
| 281 |
+
|
| 282 |
+
# ============================================
|
| 283 |
+
# MAIN EXECUTION
|
| 284 |
+
# ============================================
|
| 285 |
+
|
| 286 |
+
try {
|
| 287 |
+
Write-ColorOutput "`n=========================================" "Blue"
|
| 288 |
+
Write-ColorOutput " Go Controller Provider Generator v1.0" "Blue"
|
| 289 |
+
Write-ColorOutput "=========================================`n" "Blue"
|
| 290 |
+
|
| 291 |
+
# Step 1: Parse all controller constructors
|
| 292 |
+
$controllers = Parse-GoFiles -Directory $ControllersDir
|
| 293 |
+
|
| 294 |
+
# Step 2: Generate provider code
|
| 295 |
+
$code = Generate-ProviderCode -Controllers $controllers
|
| 296 |
+
|
| 297 |
+
# Step 3: Write to file
|
| 298 |
+
Write-ProviderFile -Code $code -OutputPath $OutputFile
|
| 299 |
+
|
| 300 |
+
Write-ColorOutput "`nSUCCESS! Controller provider generated successfully.`n" "Green"
|
| 301 |
+
Write-ColorOutput "Next steps:" "Cyan"
|
| 302 |
+
Write-ColorOutput " 1. Review $OutputFile" "White"
|
| 303 |
+
Write-ColorOutput " 2. Fill any /* TODO: provide ... */ placeholders" "White"
|
| 304 |
+
Write-ColorOutput " 3. Run: go build ./provider" "White"
|
| 305 |
+
|
| 306 |
+
} catch {
|
| 307 |
+
Write-ColorOutput "`nERROR: $($_.Exception.Message)" "Red"
|
| 308 |
+
Write-ColorOutput "Stack trace: $($_.ScriptStackTrace)" "Yellow"
|
| 309 |
+
exit 1
|
| 310 |
+
}
|
cmd/do_inject_middleware.ps1
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#Requires -Version 5.1
|
| 2 |
+
<#
|
| 3 |
+
.SYNOPSIS
|
| 4 |
+
Automatic Dependency Injection Generator for Go Middleware
|
| 5 |
+
|
| 6 |
+
.DESCRIPTION
|
| 7 |
+
Scans ./middleware/ directory, discovers all middleware constructors, infers their dependencies,
|
| 8 |
+
and generates provider/middleware_provider.go with full DI wiring.
|
| 9 |
+
|
| 10 |
+
.EXAMPLE
|
| 11 |
+
.\middleware_injector.ps1
|
| 12 |
+
|
| 13 |
+
.NOTES
|
| 14 |
+
- Works with PowerShell 5.1+ and PowerShell 7+
|
| 15 |
+
- No external dependencies required
|
| 16 |
+
- Supports multi-line constructor signatures
|
| 17 |
+
- Middleware depend on services from ServicesProvider
|
| 18 |
+
#>
|
| 19 |
+
|
| 20 |
+
[CmdletBinding()]
|
| 21 |
+
param()
|
| 22 |
+
|
| 23 |
+
# Configuration
|
| 24 |
+
$MiddlewareDir = "./middleware"
|
| 25 |
+
$OutputFile = "provider/middleware_provider.go"
|
| 26 |
+
$ModulePath = "abdanhafidz.com/go-boilerplate/middleware"
|
| 27 |
+
|
| 28 |
+
# ANSI colors for better output
|
| 29 |
+
$script:UseColors = $Host.UI.SupportsVirtualTerminal
|
| 30 |
+
function Write-ColorOutput {
|
| 31 |
+
param([string]$Message, [string]$Color = "White")
|
| 32 |
+
if ($script:UseColors) {
|
| 33 |
+
$colors = @{
|
| 34 |
+
"Green" = "`e[32m"; "Yellow" = "`e[33m"; "Red" = "`e[31m"
|
| 35 |
+
"Cyan" = "`e[36m"; "Blue" = "`e[34m"; "Reset" = "`e[0m"
|
| 36 |
+
}
|
| 37 |
+
Write-Host "$($colors[$Color])$Message$($colors['Reset'])"
|
| 38 |
+
} else {
|
| 39 |
+
Write-Host $Message
|
| 40 |
+
}
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
# Data structures
|
| 44 |
+
class MiddlewareInfo {
|
| 45 |
+
[string]$ConstructorName # NewAuthenticationMiddleware
|
| 46 |
+
[string]$Domain # AuthenticationMiddleware
|
| 47 |
+
[string]$VarName # authenticationMiddleware
|
| 48 |
+
[System.Collections.Generic.List[Parameter]]$Parameters
|
| 49 |
+
|
| 50 |
+
MiddlewareInfo() {
|
| 51 |
+
$this.Parameters = [System.Collections.Generic.List[Parameter]]::new()
|
| 52 |
+
}
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
class Parameter {
|
| 56 |
+
[string]$Name
|
| 57 |
+
[string]$RawType
|
| 58 |
+
[string]$NormalizedType
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
function Get-LowerCamelCase {
|
| 62 |
+
param([string]$Text)
|
| 63 |
+
if ($Text.Length -eq 0) { return $Text }
|
| 64 |
+
return $Text.Substring(0, 1).ToLower() + $Text.Substring(1)
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
function Normalize-TypeName {
|
| 68 |
+
param([string]$TypeStr)
|
| 69 |
+
|
| 70 |
+
# Remove leading pointer
|
| 71 |
+
$cleaned = $TypeStr -replace '^\*+', ''
|
| 72 |
+
|
| 73 |
+
# Remove package prefix (everything before last dot)
|
| 74 |
+
if ($cleaned -match '\.([^.]+)$') {
|
| 75 |
+
$cleaned = $matches[1]
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
return $cleaned.Trim()
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
function Parse-GoFiles {
|
| 82 |
+
param([string]$Directory)
|
| 83 |
+
|
| 84 |
+
Write-ColorOutput "Scanning for middleware constructors in $Directory..." "Cyan"
|
| 85 |
+
|
| 86 |
+
if (-not (Test-Path $Directory)) {
|
| 87 |
+
Write-ColorOutput "ERROR: Directory '$Directory' not found!" "Red"
|
| 88 |
+
exit 1
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
$goFiles = Get-ChildItem -Path $Directory -Filter "*.go" -Recurse -File
|
| 92 |
+
$middlewares = [System.Collections.Generic.List[MiddlewareInfo]]::new()
|
| 93 |
+
|
| 94 |
+
foreach ($file in $goFiles) {
|
| 95 |
+
$content = Get-Content $file.FullName -Raw
|
| 96 |
+
|
| 97 |
+
# Match function signatures (support multi-line)
|
| 98 |
+
# Pattern: func NewXxxMiddleware(...) XxxMiddleware
|
| 99 |
+
$pattern = '(?ms)func\s+(New[a-zA-Z0-9]+Middleware)\s*\(([^)]*)\)\s+([a-zA-Z0-9*_.]+Middleware)'
|
| 100 |
+
$matches = [regex]::Matches($content, $pattern)
|
| 101 |
+
|
| 102 |
+
foreach ($match in $matches) {
|
| 103 |
+
$constructorName = $match.Groups[1].Value
|
| 104 |
+
$paramsStr = $match.Groups[2].Value
|
| 105 |
+
$returnType = $match.Groups[3].Value
|
| 106 |
+
|
| 107 |
+
# Extract domain name (XxxMiddleware)
|
| 108 |
+
$domain = Normalize-TypeName $returnType
|
| 109 |
+
$varName = Get-LowerCamelCase $domain
|
| 110 |
+
|
| 111 |
+
$middleware = [MiddlewareInfo]::new()
|
| 112 |
+
$middleware.ConstructorName = $constructorName
|
| 113 |
+
$middleware.Domain = $domain
|
| 114 |
+
$middleware.VarName = $varName
|
| 115 |
+
|
| 116 |
+
# Parse parameters
|
| 117 |
+
if ($paramsStr.Trim() -ne "") {
|
| 118 |
+
# Split by comma, but be careful with nested types
|
| 119 |
+
$paramList = $paramsStr -split ',\s*(?![^<>]*>)'
|
| 120 |
+
|
| 121 |
+
foreach ($param in $paramList) {
|
| 122 |
+
$param = $param.Trim()
|
| 123 |
+
if ($param -eq "") { continue }
|
| 124 |
+
|
| 125 |
+
# Split into name and type
|
| 126 |
+
$parts = $param -split '\s+', 2
|
| 127 |
+
|
| 128 |
+
$p = [Parameter]::new()
|
| 129 |
+
if ($parts.Count -eq 2) {
|
| 130 |
+
$p.Name = $parts[0]
|
| 131 |
+
$p.RawType = $parts[1]
|
| 132 |
+
} elseif ($parts.Count -eq 1) {
|
| 133 |
+
# Anonymous parameter - synthesize name
|
| 134 |
+
$p.Name = "param$($middleware.Parameters.Count)"
|
| 135 |
+
$p.RawType = $parts[0]
|
| 136 |
+
} else {
|
| 137 |
+
continue
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
$p.NormalizedType = Normalize-TypeName $p.RawType
|
| 141 |
+
$middleware.Parameters.Add($p)
|
| 142 |
+
}
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
$middlewares.Add($middleware)
|
| 146 |
+
Write-ColorOutput " Found: $constructorName" "Green"
|
| 147 |
+
}
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
if ($middlewares.Count -eq 0) {
|
| 151 |
+
Write-ColorOutput "No middleware constructors found matching pattern 'NewXxxMiddleware'!" "Red"
|
| 152 |
+
exit 1
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
Write-ColorOutput "`nTotal middleware discovered: $($middlewares.Count)" "Blue"
|
| 156 |
+
return $middlewares
|
| 157 |
+
}
|
| 158 |
+
|
| 159 |
+
function Resolve-MiddlewareArgument {
|
| 160 |
+
param([Parameter]$Param)
|
| 161 |
+
|
| 162 |
+
$type = $Param.NormalizedType
|
| 163 |
+
|
| 164 |
+
# DEPENDENCY RESOLUTION RULES
|
| 165 |
+
# ============================================
|
| 166 |
+
|
| 167 |
+
# 1. Service pattern: XxxxService -> servicesProvider.ProvideXxxxService()
|
| 168 |
+
if ($type -match '^(.+)Service$') {
|
| 169 |
+
$serviceName = $type
|
| 170 |
+
return "servicesProvider.Provide${serviceName}()"
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
+
# ADD MORE SPECIAL CASES HERE:
|
| 174 |
+
# --------------------------------------------
|
| 175 |
+
# Example: Config dependency
|
| 176 |
+
# if ($type -eq "Config") {
|
| 177 |
+
# return "configProvider.ProvideConfig()"
|
| 178 |
+
# }
|
| 179 |
+
#
|
| 180 |
+
# Example: Logger
|
| 181 |
+
# if ($type -eq "Logger") {
|
| 182 |
+
# return "loggerProvider.ProvideLogger()"
|
| 183 |
+
# }
|
| 184 |
+
#
|
| 185 |
+
# Example: JWT Config
|
| 186 |
+
# if ($type -eq "JWTConfig") {
|
| 187 |
+
# return "configProvider.ProvideJWTConfig()"
|
| 188 |
+
# }
|
| 189 |
+
#
|
| 190 |
+
# Example: Database
|
| 191 |
+
# if ($type -eq "DB" -or $type -eq "Database") {
|
| 192 |
+
# return "dbProvider.ProvideDatabase()"
|
| 193 |
+
# }
|
| 194 |
+
# --------------------------------------------
|
| 195 |
+
|
| 196 |
+
# 2. Fallback: unresolved type
|
| 197 |
+
return "/* TODO: provide $($Param.RawType) */"
|
| 198 |
+
}
|
| 199 |
+
|
| 200 |
+
function Generate-ProviderCode {
|
| 201 |
+
param([System.Collections.Generic.List[MiddlewareInfo]]$Middlewares)
|
| 202 |
+
|
| 203 |
+
Write-ColorOutput "`nGenerating middleware provider code..." "Cyan"
|
| 204 |
+
|
| 205 |
+
# Sort middleware alphabetically for consistent output
|
| 206 |
+
$sortedMiddlewares = $Middlewares | Sort-Object -Property Domain
|
| 207 |
+
|
| 208 |
+
$sb = [System.Text.StringBuilder]::new()
|
| 209 |
+
[void]$sb.AppendLine("package provider")
|
| 210 |
+
[void]$sb.AppendLine()
|
| 211 |
+
[void]$sb.AppendLine("import `"$ModulePath`"")
|
| 212 |
+
[void]$sb.AppendLine()
|
| 213 |
+
|
| 214 |
+
# Interface
|
| 215 |
+
[void]$sb.AppendLine("type MiddlewareProvider interface {")
|
| 216 |
+
foreach ($mw in $sortedMiddlewares) {
|
| 217 |
+
$line = "`tProvide$($mw.Domain)() middleware.$($mw.Domain)"
|
| 218 |
+
[void]$sb.AppendLine($line)
|
| 219 |
+
}
|
| 220 |
+
[void]$sb.AppendLine("}")
|
| 221 |
+
[void]$sb.AppendLine()
|
| 222 |
+
|
| 223 |
+
# Struct
|
| 224 |
+
[void]$sb.AppendLine("type middlewareProvider struct {")
|
| 225 |
+
foreach ($mw in $sortedMiddlewares) {
|
| 226 |
+
$line = "`t$($mw.VarName) middleware.$($mw.Domain)"
|
| 227 |
+
[void]$sb.AppendLine($line)
|
| 228 |
+
}
|
| 229 |
+
[void]$sb.AppendLine("}")
|
| 230 |
+
[void]$sb.AppendLine()
|
| 231 |
+
|
| 232 |
+
# Constructor
|
| 233 |
+
[void]$sb.AppendLine("func NewMiddlewareProvider(servicesProvider ServicesProvider) MiddlewareProvider {")
|
| 234 |
+
|
| 235 |
+
# Initialize middleware
|
| 236 |
+
foreach ($mw in $sortedMiddlewares) {
|
| 237 |
+
$args = @()
|
| 238 |
+
foreach ($param in $mw.Parameters) {
|
| 239 |
+
$args += Resolve-MiddlewareArgument $param
|
| 240 |
+
}
|
| 241 |
+
$argsStr = $args -join ", "
|
| 242 |
+
$line = "`t$($mw.VarName) := middleware.$($mw.ConstructorName)($argsStr)"
|
| 243 |
+
[void]$sb.AppendLine($line)
|
| 244 |
+
}
|
| 245 |
+
|
| 246 |
+
[void]$sb.AppendLine("`treturn &middlewareProvider{")
|
| 247 |
+
foreach ($mw in $sortedMiddlewares) {
|
| 248 |
+
$line = "`t`t$($mw.VarName): $($mw.VarName),"
|
| 249 |
+
[void]$sb.AppendLine($line)
|
| 250 |
+
}
|
| 251 |
+
[void]$sb.AppendLine("`t}")
|
| 252 |
+
[void]$sb.AppendLine("}")
|
| 253 |
+
[void]$sb.AppendLine()
|
| 254 |
+
|
| 255 |
+
# Getter methods
|
| 256 |
+
foreach ($mw in $sortedMiddlewares) {
|
| 257 |
+
[void]$sb.AppendLine("func (p *middlewareProvider) Provide$($mw.Domain)() middleware.$($mw.Domain) {")
|
| 258 |
+
[void]$sb.AppendLine("`treturn p.$($mw.VarName)")
|
| 259 |
+
[void]$sb.AppendLine("}")
|
| 260 |
+
[void]$sb.AppendLine()
|
| 261 |
+
}
|
| 262 |
+
|
| 263 |
+
return $sb.ToString()
|
| 264 |
+
}
|
| 265 |
+
|
| 266 |
+
function Write-ProviderFile {
|
| 267 |
+
param([string]$Code, [string]$OutputPath)
|
| 268 |
+
|
| 269 |
+
Write-ColorOutput "Writing to $OutputPath..." "Cyan"
|
| 270 |
+
|
| 271 |
+
# Ensure directory exists
|
| 272 |
+
$dir = Split-Path $OutputPath -Parent
|
| 273 |
+
if ($dir -and -not (Test-Path $dir)) {
|
| 274 |
+
New-Item -ItemType Directory -Path $dir -Force | Out-Null
|
| 275 |
+
}
|
| 276 |
+
|
| 277 |
+
# Write file as UTF-8 without BOM
|
| 278 |
+
$utf8NoBom = [System.Text.UTF8Encoding]::new($false)
|
| 279 |
+
[System.IO.File]::WriteAllText($OutputPath, $Code, $utf8NoBom)
|
| 280 |
+
|
| 281 |
+
Write-ColorOutput " Successfully generated $OutputPath" "Green"
|
| 282 |
+
}
|
| 283 |
+
|
| 284 |
+
# ============================================
|
| 285 |
+
# MAIN EXECUTION
|
| 286 |
+
# ============================================
|
| 287 |
+
|
| 288 |
+
try {
|
| 289 |
+
Write-ColorOutput "`n=========================================" "Blue"
|
| 290 |
+
Write-ColorOutput " Go Middleware Provider Generator v1.0" "Blue"
|
| 291 |
+
Write-ColorOutput "=========================================`n" "Blue"
|
| 292 |
+
|
| 293 |
+
# Step 1: Parse all middleware constructors
|
| 294 |
+
$middlewares = Parse-GoFiles -Directory $MiddlewareDir
|
| 295 |
+
|
| 296 |
+
# Step 2: Generate provider code
|
| 297 |
+
$code = Generate-ProviderCode -Middlewares $middlewares
|
| 298 |
+
|
| 299 |
+
# Step 3: Write to file
|
| 300 |
+
Write-ProviderFile -Code $code -OutputPath $OutputFile
|
| 301 |
+
|
| 302 |
+
Write-ColorOutput "`nSUCCESS! Middleware provider generated successfully.`n" "Green"
|
| 303 |
+
Write-ColorOutput "Next steps:" "Cyan"
|
| 304 |
+
Write-ColorOutput " 1. Review $OutputFile" "White"
|
| 305 |
+
Write-ColorOutput " 2. Fill any /* TODO: provide ... */ placeholders" "White"
|
| 306 |
+
Write-ColorOutput " 3. Run: go build ./provider" "White"
|
| 307 |
+
|
| 308 |
+
} catch {
|
| 309 |
+
Write-ColorOutput "`nERROR: $($_.Exception.Message)" "Red"
|
| 310 |
+
Write-ColorOutput "Stack trace: $($_.ScriptStackTrace)" "Yellow"
|
| 311 |
+
exit 1
|
| 312 |
+
}
|
cmd/do_inject_repository.ps1
ADDED
|
@@ -0,0 +1,327 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#Requires -Version 5.1
|
| 2 |
+
<#
|
| 3 |
+
.SYNOPSIS
|
| 4 |
+
Automatic Dependency Injection Generator for Go Repositories
|
| 5 |
+
|
| 6 |
+
.DESCRIPTION
|
| 7 |
+
Scans ./repositories/ directory, discovers all repository constructors,
|
| 8 |
+
and generates provider/repositories_provider.go with full DI wiring.
|
| 9 |
+
Repositories typically depend on database connection from ConfigProvider.
|
| 10 |
+
|
| 11 |
+
.EXAMPLE
|
| 12 |
+
.\repository_injector.ps1
|
| 13 |
+
|
| 14 |
+
.NOTES
|
| 15 |
+
- Works with PowerShell 5.1+ and PowerShell 7+
|
| 16 |
+
- No external dependencies required
|
| 17 |
+
- Supports multi-line constructor signatures
|
| 18 |
+
- Repositories depend on database instance from ConfigProvider
|
| 19 |
+
#>
|
| 20 |
+
|
| 21 |
+
[CmdletBinding()]
|
| 22 |
+
param()
|
| 23 |
+
|
| 24 |
+
# Configuration
|
| 25 |
+
$RepositoriesDir = "./repositories"
|
| 26 |
+
$OutputFile = "provider/repositories_provider.go"
|
| 27 |
+
$ModulePath = "abdanhafidz.com/go-boilerplate/repositories"
|
| 28 |
+
|
| 29 |
+
# ANSI colors for better output
|
| 30 |
+
$script:UseColors = $Host.UI.SupportsVirtualTerminal
|
| 31 |
+
function Write-ColorOutput {
|
| 32 |
+
param([string]$Message, [string]$Color = "White")
|
| 33 |
+
if ($script:UseColors) {
|
| 34 |
+
$colors = @{
|
| 35 |
+
"Green" = "`e[32m"; "Yellow" = "`e[33m"; "Red" = "`e[31m"
|
| 36 |
+
"Cyan" = "`e[36m"; "Blue" = "`e[34m"; "Reset" = "`e[0m"
|
| 37 |
+
}
|
| 38 |
+
Write-Host "$($colors[$Color])$Message$($colors['Reset'])"
|
| 39 |
+
} else {
|
| 40 |
+
Write-Host $Message
|
| 41 |
+
}
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
# Data structures
|
| 45 |
+
class RepositoryInfo {
|
| 46 |
+
[string]$ConstructorName # NewAccountRepository
|
| 47 |
+
[string]$Domain # AccountRepository
|
| 48 |
+
[string]$VarName # accountRepository
|
| 49 |
+
[System.Collections.Generic.List[Parameter]]$Parameters
|
| 50 |
+
|
| 51 |
+
RepositoryInfo() {
|
| 52 |
+
$this.Parameters = [System.Collections.Generic.List[Parameter]]::new()
|
| 53 |
+
}
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
class Parameter {
|
| 57 |
+
[string]$Name
|
| 58 |
+
[string]$RawType
|
| 59 |
+
[string]$NormalizedType
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
function Get-LowerCamelCase {
|
| 63 |
+
param([string]$Text)
|
| 64 |
+
if ($Text.Length -eq 0) { return $Text }
|
| 65 |
+
return $Text.Substring(0, 1).ToLower() + $Text.Substring(1)
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
function Normalize-TypeName {
|
| 69 |
+
param([string]$TypeStr)
|
| 70 |
+
|
| 71 |
+
# Remove leading pointer
|
| 72 |
+
$cleaned = $TypeStr -replace '^\*+', ''
|
| 73 |
+
|
| 74 |
+
# Remove package prefix (everything before last dot)
|
| 75 |
+
if ($cleaned -match '\.([^.]+)$') {
|
| 76 |
+
$cleaned = $matches[1]
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
return $cleaned.Trim()
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
function Parse-GoFiles {
|
| 83 |
+
param([string]$Directory)
|
| 84 |
+
|
| 85 |
+
Write-ColorOutput "Scanning for repository constructors in $Directory..." "Cyan"
|
| 86 |
+
|
| 87 |
+
if (-not (Test-Path $Directory)) {
|
| 88 |
+
Write-ColorOutput "ERROR: Directory '$Directory' not found!" "Red"
|
| 89 |
+
exit 1
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
+
$goFiles = Get-ChildItem -Path $Directory -Filter "*.go" -Recurse -File
|
| 93 |
+
$repositories = [System.Collections.Generic.List[RepositoryInfo]]::new()
|
| 94 |
+
|
| 95 |
+
foreach ($file in $goFiles) {
|
| 96 |
+
$content = Get-Content $file.FullName -Raw
|
| 97 |
+
|
| 98 |
+
# Match function signatures (support multi-line)
|
| 99 |
+
# Pattern: func NewXxxRepository(...) XxxRepository
|
| 100 |
+
$pattern = '(?ms)func\s+(New[a-zA-Z0-9]+Repository)\s*\(([^)]*)\)\s+([a-zA-Z0-9*_.]+Repository)'
|
| 101 |
+
$matches = [regex]::Matches($content, $pattern)
|
| 102 |
+
|
| 103 |
+
foreach ($match in $matches) {
|
| 104 |
+
$constructorName = $match.Groups[1].Value
|
| 105 |
+
$paramsStr = $match.Groups[2].Value
|
| 106 |
+
$returnType = $match.Groups[3].Value
|
| 107 |
+
|
| 108 |
+
# Extract domain name (XxxRepository)
|
| 109 |
+
$domain = Normalize-TypeName $returnType
|
| 110 |
+
$varName = Get-LowerCamelCase $domain
|
| 111 |
+
|
| 112 |
+
$repo = [RepositoryInfo]::new()
|
| 113 |
+
$repo.ConstructorName = $constructorName
|
| 114 |
+
$repo.Domain = $domain
|
| 115 |
+
$repo.VarName = $varName
|
| 116 |
+
|
| 117 |
+
# Parse parameters
|
| 118 |
+
if ($paramsStr.Trim() -ne "") {
|
| 119 |
+
# Split by comma, but be careful with nested types
|
| 120 |
+
$paramList = $paramsStr -split ',\s*(?![^<>]*>)'
|
| 121 |
+
|
| 122 |
+
foreach ($param in $paramList) {
|
| 123 |
+
$param = $param.Trim()
|
| 124 |
+
if ($param -eq "") { continue }
|
| 125 |
+
|
| 126 |
+
# Split into name and type
|
| 127 |
+
$parts = $param -split '\s+', 2
|
| 128 |
+
|
| 129 |
+
$p = [Parameter]::new()
|
| 130 |
+
if ($parts.Count -eq 2) {
|
| 131 |
+
$p.Name = $parts[0]
|
| 132 |
+
$p.RawType = $parts[1]
|
| 133 |
+
} elseif ($parts.Count -eq 1) {
|
| 134 |
+
# Anonymous parameter - synthesize name
|
| 135 |
+
$p.Name = "param$($repo.Parameters.Count)"
|
| 136 |
+
$p.RawType = $parts[0]
|
| 137 |
+
} else {
|
| 138 |
+
continue
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
$p.NormalizedType = Normalize-TypeName $p.RawType
|
| 142 |
+
$repo.Parameters.Add($p)
|
| 143 |
+
}
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
$repositories.Add($repo)
|
| 147 |
+
Write-ColorOutput " Found: $constructorName" "Green"
|
| 148 |
+
}
|
| 149 |
+
}
|
| 150 |
+
|
| 151 |
+
if ($repositories.Count -eq 0) {
|
| 152 |
+
Write-ColorOutput "No repository constructors found matching pattern 'NewXxxRepository'!" "Red"
|
| 153 |
+
exit 1
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
Write-ColorOutput "`nTotal repositories discovered: $($repositories.Count)" "Blue"
|
| 157 |
+
return $repositories
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
function Resolve-RepositoryArgument {
|
| 161 |
+
param([Parameter]$Param)
|
| 162 |
+
|
| 163 |
+
$type = $Param.NormalizedType
|
| 164 |
+
$paramName = $Param.Name
|
| 165 |
+
|
| 166 |
+
# DEPENDENCY RESOLUTION RULES FOR REPOSITORIES
|
| 167 |
+
# ============================================
|
| 168 |
+
|
| 169 |
+
# 1. Database connection patterns
|
| 170 |
+
if ($type -match '^(DB|Database|Gorm|SqlDB|Connection)$' -or $paramName -match '^(db|database|conn|connection)$') {
|
| 171 |
+
return "db"
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
# 2. *gorm.DB (most common in Go GORM projects)
|
| 175 |
+
if ($Param.RawType -match 'gorm\.DB' -or $type -eq "DB") {
|
| 176 |
+
return "db"
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
# 3. *sql.DB (standard library)
|
| 180 |
+
if ($Param.RawType -match 'sql\.DB') {
|
| 181 |
+
return "db"
|
| 182 |
+
}
|
| 183 |
+
|
| 184 |
+
# ADD MORE SPECIAL CASES HERE:
|
| 185 |
+
# --------------------------------------------
|
| 186 |
+
# Example: Redis connection
|
| 187 |
+
# if ($type -eq "RedisClient" -or $paramName -match "redis") {
|
| 188 |
+
# return "redisClient"
|
| 189 |
+
# }
|
| 190 |
+
#
|
| 191 |
+
# Example: MongoDB connection
|
| 192 |
+
# if ($type -eq "MongoClient" -or $paramName -match "mongo") {
|
| 193 |
+
# return "mongoClient"
|
| 194 |
+
# }
|
| 195 |
+
#
|
| 196 |
+
# Example: Cache
|
| 197 |
+
# if ($type -eq "Cache" -or $paramName -match "cache") {
|
| 198 |
+
# return "cache"
|
| 199 |
+
# }
|
| 200 |
+
#
|
| 201 |
+
# Example: Logger
|
| 202 |
+
# if ($type -eq "Logger" -or $paramName -match "logger") {
|
| 203 |
+
# return "logger"
|
| 204 |
+
# }
|
| 205 |
+
# --------------------------------------------
|
| 206 |
+
|
| 207 |
+
# 4. Fallback: unresolved type
|
| 208 |
+
return "/* TODO: provide $($Param.RawType) */"
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
function Generate-ProviderCode {
|
| 212 |
+
param([System.Collections.Generic.List[RepositoryInfo]]$Repositories)
|
| 213 |
+
|
| 214 |
+
Write-ColorOutput "`nGenerating repositories provider code..." "Cyan"
|
| 215 |
+
|
| 216 |
+
# Sort repositories alphabetically for consistent output
|
| 217 |
+
$sortedRepos = $Repositories | Sort-Object -Property Domain
|
| 218 |
+
|
| 219 |
+
$sb = [System.Text.StringBuilder]::new()
|
| 220 |
+
[void]$sb.AppendLine("package provider")
|
| 221 |
+
[void]$sb.AppendLine()
|
| 222 |
+
[void]$sb.AppendLine("import `"$ModulePath`"")
|
| 223 |
+
[void]$sb.AppendLine()
|
| 224 |
+
|
| 225 |
+
# Interface
|
| 226 |
+
[void]$sb.AppendLine("type RepositoriesProvider interface {")
|
| 227 |
+
foreach ($repo in $sortedRepos) {
|
| 228 |
+
$line = "`tProvide$($repo.Domain)() repositories.$($repo.Domain)"
|
| 229 |
+
[void]$sb.AppendLine($line)
|
| 230 |
+
}
|
| 231 |
+
[void]$sb.AppendLine("}")
|
| 232 |
+
[void]$sb.AppendLine()
|
| 233 |
+
|
| 234 |
+
# Struct
|
| 235 |
+
[void]$sb.AppendLine("type repositoriesProvider struct {")
|
| 236 |
+
foreach ($repo in $sortedRepos) {
|
| 237 |
+
$line = "`t$($repo.VarName) repositories.$($repo.Domain)"
|
| 238 |
+
[void]$sb.AppendLine($line)
|
| 239 |
+
}
|
| 240 |
+
[void]$sb.AppendLine("}")
|
| 241 |
+
[void]$sb.AppendLine()
|
| 242 |
+
|
| 243 |
+
# Constructor
|
| 244 |
+
[void]$sb.AppendLine("func NewRepositoriesProvider(cfg ConfigProvider) RepositoriesProvider {")
|
| 245 |
+
[void]$sb.AppendLine("`tdbConfig := cfg.ProvideDatabaseConfig()")
|
| 246 |
+
[void]$sb.AppendLine("`tdb := dbConfig.GetInstance()")
|
| 247 |
+
[void]$sb.AppendLine()
|
| 248 |
+
|
| 249 |
+
# Initialize repositories
|
| 250 |
+
foreach ($repo in $sortedRepos) {
|
| 251 |
+
$args = @()
|
| 252 |
+
foreach ($param in $repo.Parameters) {
|
| 253 |
+
$args += Resolve-RepositoryArgument $param
|
| 254 |
+
}
|
| 255 |
+
$argsStr = $args -join ", "
|
| 256 |
+
$line = "`t$($repo.VarName) := repositories.$($repo.ConstructorName)($argsStr)"
|
| 257 |
+
[void]$sb.AppendLine($line)
|
| 258 |
+
}
|
| 259 |
+
|
| 260 |
+
[void]$sb.AppendLine()
|
| 261 |
+
[void]$sb.AppendLine("`treturn &repositoriesProvider{")
|
| 262 |
+
foreach ($repo in $sortedRepos) {
|
| 263 |
+
$line = "`t`t$($repo.VarName): $($repo.VarName),"
|
| 264 |
+
[void]$sb.AppendLine($line)
|
| 265 |
+
}
|
| 266 |
+
[void]$sb.AppendLine("`t}")
|
| 267 |
+
[void]$sb.AppendLine("}")
|
| 268 |
+
[void]$sb.AppendLine()
|
| 269 |
+
|
| 270 |
+
# Getter methods
|
| 271 |
+
foreach ($repo in $sortedRepos) {
|
| 272 |
+
[void]$sb.AppendLine("func (r *repositoriesProvider) Provide$($repo.Domain)() repositories.$($repo.Domain) {")
|
| 273 |
+
[void]$sb.AppendLine("`treturn r.$($repo.VarName)")
|
| 274 |
+
[void]$sb.AppendLine("}")
|
| 275 |
+
[void]$sb.AppendLine()
|
| 276 |
+
}
|
| 277 |
+
|
| 278 |
+
return $sb.ToString()
|
| 279 |
+
}
|
| 280 |
+
|
| 281 |
+
function Write-ProviderFile {
|
| 282 |
+
param([string]$Code, [string]$OutputPath)
|
| 283 |
+
|
| 284 |
+
Write-ColorOutput "Writing to $OutputPath..." "Cyan"
|
| 285 |
+
|
| 286 |
+
# Ensure directory exists
|
| 287 |
+
$dir = Split-Path $OutputPath -Parent
|
| 288 |
+
if ($dir -and -not (Test-Path $dir)) {
|
| 289 |
+
New-Item -ItemType Directory -Path $dir -Force | Out-Null
|
| 290 |
+
}
|
| 291 |
+
|
| 292 |
+
# Write file as UTF-8 without BOM
|
| 293 |
+
$utf8NoBom = [System.Text.UTF8Encoding]::new($false)
|
| 294 |
+
[System.IO.File]::WriteAllText($OutputPath, $Code, $utf8NoBom)
|
| 295 |
+
|
| 296 |
+
Write-ColorOutput " Successfully generated $OutputPath" "Green"
|
| 297 |
+
}
|
| 298 |
+
|
| 299 |
+
# ============================================
|
| 300 |
+
# MAIN EXECUTION
|
| 301 |
+
# ============================================
|
| 302 |
+
|
| 303 |
+
try {
|
| 304 |
+
Write-ColorOutput "`n=========================================" "Blue"
|
| 305 |
+
Write-ColorOutput " Go Repository Provider Generator v1.0" "Blue"
|
| 306 |
+
Write-ColorOutput "=========================================`n" "Blue"
|
| 307 |
+
|
| 308 |
+
# Step 1: Parse all repository constructors
|
| 309 |
+
$repositories = Parse-GoFiles -Directory $RepositoriesDir
|
| 310 |
+
|
| 311 |
+
# Step 2: Generate provider code
|
| 312 |
+
$code = Generate-ProviderCode -Repositories $repositories
|
| 313 |
+
|
| 314 |
+
# Step 3: Write to file
|
| 315 |
+
Write-ProviderFile -Code $code -OutputPath $OutputFile
|
| 316 |
+
|
| 317 |
+
Write-ColorOutput "`nSUCCESS! Repositories provider generated successfully.`n" "Green"
|
| 318 |
+
Write-ColorOutput "Next steps:" "Cyan"
|
| 319 |
+
Write-ColorOutput " 1. Review $OutputFile" "White"
|
| 320 |
+
Write-ColorOutput " 2. Fill any /* TODO: provide ... */ placeholders" "White"
|
| 321 |
+
Write-ColorOutput " 3. Run: go build ./provider" "White"
|
| 322 |
+
|
| 323 |
+
} catch {
|
| 324 |
+
Write-ColorOutput "`nERROR: $($_.Exception.Message)" "Red"
|
| 325 |
+
Write-ColorOutput "Stack trace: $($_.ScriptStackTrace)" "Yellow"
|
| 326 |
+
exit 1
|
| 327 |
+
}
|
cmd/do_inject_services.ps1
ADDED
|
@@ -0,0 +1,383 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#Requires -Version 5.1
|
| 2 |
+
<#
|
| 3 |
+
.SYNOPSIS
|
| 4 |
+
Automatic Dependency Injection Generator for Go Services
|
| 5 |
+
|
| 6 |
+
.DESCRIPTION
|
| 7 |
+
Scans ./services/ directory, discovers service constructors, resolves dependencies,
|
| 8 |
+
performs topological sorting, and generates provider/services_provider.go with full DI wiring.
|
| 9 |
+
|
| 10 |
+
.EXAMPLE
|
| 11 |
+
.\service_injector.ps1
|
| 12 |
+
|
| 13 |
+
.NOTES
|
| 14 |
+
- Works with PowerShell 5.1+ and PowerShell 7+
|
| 15 |
+
- No external dependencies required
|
| 16 |
+
- Supports multi-line constructor signatures
|
| 17 |
+
- Handles dependency cycles with clear error messages
|
| 18 |
+
#>
|
| 19 |
+
|
| 20 |
+
[CmdletBinding()]
|
| 21 |
+
param()
|
| 22 |
+
|
| 23 |
+
# Configuration
|
| 24 |
+
$ServicesDir = "./services"
|
| 25 |
+
$OutputFile = "provider/services_provider.go"
|
| 26 |
+
$ModulePath = "abdanhafidz.com/go-boilerplate/services"
|
| 27 |
+
|
| 28 |
+
# ANSI colors for better output (fallback to plain text if not supported)
|
| 29 |
+
$script:UseColors = $Host.UI.SupportsVirtualTerminal
|
| 30 |
+
function Write-ColorOutput {
|
| 31 |
+
param([string]$Message, [string]$Color = "White")
|
| 32 |
+
if ($script:UseColors) {
|
| 33 |
+
$colors = @{
|
| 34 |
+
"Green" = "`e[32m"; "Yellow" = "`e[33m"; "Red" = "`e[31m"
|
| 35 |
+
"Cyan" = "`e[36m"; "Blue" = "`e[34m"; "Reset" = "`e[0m"
|
| 36 |
+
}
|
| 37 |
+
Write-Host "$($colors[$Color])$Message$($colors['Reset'])"
|
| 38 |
+
} else {
|
| 39 |
+
Write-Host $Message
|
| 40 |
+
}
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
# Data structures
|
| 44 |
+
class ServiceInfo {
|
| 45 |
+
[string]$ConstructorName # NewAccountService
|
| 46 |
+
[string]$Domain # AccountService
|
| 47 |
+
[string]$VarName # accountService
|
| 48 |
+
[System.Collections.Generic.List[Parameter]]$Parameters
|
| 49 |
+
[System.Collections.Generic.List[string]]$ServiceDependencies
|
| 50 |
+
|
| 51 |
+
ServiceInfo() {
|
| 52 |
+
$this.Parameters = [System.Collections.Generic.List[Parameter]]::new()
|
| 53 |
+
$this.ServiceDependencies = [System.Collections.Generic.List[string]]::new()
|
| 54 |
+
}
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
class Parameter {
|
| 58 |
+
[string]$Name
|
| 59 |
+
[string]$RawType
|
| 60 |
+
[string]$NormalizedType
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
function Get-LowerCamelCase {
|
| 64 |
+
param([string]$Text)
|
| 65 |
+
if ($Text.Length -eq 0) { return $Text }
|
| 66 |
+
return $Text.Substring(0, 1).ToLower() + $Text.Substring(1)
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
function Normalize-TypeName {
|
| 70 |
+
param([string]$TypeStr)
|
| 71 |
+
|
| 72 |
+
# Remove leading pointer
|
| 73 |
+
$cleaned = $TypeStr -replace '^\*+', ''
|
| 74 |
+
|
| 75 |
+
# Remove package prefix (everything before last dot)
|
| 76 |
+
if ($cleaned -match '\.([^.]+)$') {
|
| 77 |
+
$cleaned = $matches[1]
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
return $cleaned.Trim()
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
function Parse-GoFiles {
|
| 84 |
+
param([string]$Directory)
|
| 85 |
+
|
| 86 |
+
Write-ColorOutput "Scanning for service constructors in $Directory..." "Cyan"
|
| 87 |
+
|
| 88 |
+
if (-not (Test-Path $Directory)) {
|
| 89 |
+
Write-ColorOutput "ERROR: Directory '$Directory' not found!" "Red"
|
| 90 |
+
exit 1
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
$goFiles = Get-ChildItem -Path $Directory -Filter "*.go" -Recurse -File
|
| 94 |
+
$services = [System.Collections.Generic.List[ServiceInfo]]::new()
|
| 95 |
+
|
| 96 |
+
foreach ($file in $goFiles) {
|
| 97 |
+
$content = Get-Content $file.FullName -Raw
|
| 98 |
+
|
| 99 |
+
# Match function signatures (support multi-line)
|
| 100 |
+
# Pattern: func NewXxxService(...) XxxService
|
| 101 |
+
$pattern = '(?ms)func\s+(New[a-zA-Z0-9]+Service)\s*\(([^)]*)\)\s+([a-zA-Z0-9*_.]+Service)'
|
| 102 |
+
$matches = [regex]::Matches($content, $pattern)
|
| 103 |
+
|
| 104 |
+
foreach ($match in $matches) {
|
| 105 |
+
$constructorName = $match.Groups[1].Value
|
| 106 |
+
$paramsStr = $match.Groups[2].Value
|
| 107 |
+
$returnType = $match.Groups[3].Value
|
| 108 |
+
|
| 109 |
+
# Extract domain name (XxxService)
|
| 110 |
+
$domain = Normalize-TypeName $returnType
|
| 111 |
+
$varName = Get-LowerCamelCase $domain
|
| 112 |
+
|
| 113 |
+
$service = [ServiceInfo]::new()
|
| 114 |
+
$service.ConstructorName = $constructorName
|
| 115 |
+
$service.Domain = $domain
|
| 116 |
+
$service.VarName = $varName
|
| 117 |
+
|
| 118 |
+
# Parse parameters
|
| 119 |
+
if ($paramsStr.Trim() -ne "") {
|
| 120 |
+
# Split by comma, but be careful with nested types
|
| 121 |
+
$paramList = $paramsStr -split ',\s*(?![^<>]*>)'
|
| 122 |
+
|
| 123 |
+
foreach ($param in $paramList) {
|
| 124 |
+
$param = $param.Trim()
|
| 125 |
+
if ($param -eq "") { continue }
|
| 126 |
+
|
| 127 |
+
# Split into name and type
|
| 128 |
+
$parts = $param -split '\s+', 2
|
| 129 |
+
|
| 130 |
+
$p = [Parameter]::new()
|
| 131 |
+
if ($parts.Count -eq 2) {
|
| 132 |
+
$p.Name = $parts[0]
|
| 133 |
+
$p.RawType = $parts[1]
|
| 134 |
+
} elseif ($parts.Count -eq 1) {
|
| 135 |
+
# Anonymous parameter - synthesize name
|
| 136 |
+
$p.Name = "param$($service.Parameters.Count)"
|
| 137 |
+
$p.RawType = $parts[0]
|
| 138 |
+
} else {
|
| 139 |
+
continue
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
$p.NormalizedType = Normalize-TypeName $p.RawType
|
| 143 |
+
$service.Parameters.Add($p)
|
| 144 |
+
|
| 145 |
+
# Track service dependencies
|
| 146 |
+
if ($p.NormalizedType -match 'Service$') {
|
| 147 |
+
$service.ServiceDependencies.Add($p.NormalizedType)
|
| 148 |
+
}
|
| 149 |
+
}
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
$services.Add($service)
|
| 153 |
+
Write-ColorOutput " Found: $constructorName" "Green"
|
| 154 |
+
}
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
if ($services.Count -eq 0) {
|
| 158 |
+
Write-ColorOutput "No service constructors found matching pattern 'NewXxxService'!" "Red"
|
| 159 |
+
exit 1
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
Write-ColorOutput "`nTotal services discovered: $($services.Count)" "Blue"
|
| 163 |
+
return $services
|
| 164 |
+
}
|
| 165 |
+
|
| 166 |
+
function Get-TopologicalOrder {
|
| 167 |
+
param([System.Collections.Generic.List[ServiceInfo]]$Services)
|
| 168 |
+
|
| 169 |
+
Write-ColorOutput "`nBuilding dependency graph..." "Cyan"
|
| 170 |
+
|
| 171 |
+
# Build adjacency list
|
| 172 |
+
$graph = @{}
|
| 173 |
+
$inDegree = @{}
|
| 174 |
+
$domainToService = @{}
|
| 175 |
+
|
| 176 |
+
foreach ($svc in $Services) {
|
| 177 |
+
$graph[$svc.Domain] = [System.Collections.Generic.List[string]]::new()
|
| 178 |
+
$inDegree[$svc.Domain] = 0
|
| 179 |
+
$domainToService[$svc.Domain] = $svc
|
| 180 |
+
}
|
| 181 |
+
|
| 182 |
+
# Build edges
|
| 183 |
+
foreach ($svc in $Services) {
|
| 184 |
+
foreach ($dep in $svc.ServiceDependencies) {
|
| 185 |
+
if ($graph.ContainsKey($dep)) {
|
| 186 |
+
$graph[$dep].Add($svc.Domain)
|
| 187 |
+
$inDegree[$svc.Domain]++
|
| 188 |
+
}
|
| 189 |
+
}
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
+
# Kahn's algorithm for topological sort
|
| 193 |
+
$queue = [System.Collections.Generic.Queue[string]]::new()
|
| 194 |
+
foreach ($domain in $inDegree.Keys) {
|
| 195 |
+
if ($inDegree[$domain] -eq 0) {
|
| 196 |
+
$queue.Enqueue($domain)
|
| 197 |
+
}
|
| 198 |
+
}
|
| 199 |
+
|
| 200 |
+
$sorted = [System.Collections.Generic.List[string]]::new()
|
| 201 |
+
|
| 202 |
+
while ($queue.Count -gt 0) {
|
| 203 |
+
$current = $queue.Dequeue()
|
| 204 |
+
$sorted.Add($current)
|
| 205 |
+
|
| 206 |
+
foreach ($neighbor in $graph[$current]) {
|
| 207 |
+
$inDegree[$neighbor]--
|
| 208 |
+
if ($inDegree[$neighbor] -eq 0) {
|
| 209 |
+
$queue.Enqueue($neighbor)
|
| 210 |
+
}
|
| 211 |
+
}
|
| 212 |
+
}
|
| 213 |
+
|
| 214 |
+
# Check for cycles
|
| 215 |
+
if ($sorted.Count -ne $Services.Count) {
|
| 216 |
+
$remaining = $inDegree.Keys | Where-Object { $inDegree[$_] -gt 0 }
|
| 217 |
+
Write-ColorOutput "`nERROR: Circular dependency detected!" "Red"
|
| 218 |
+
Write-ColorOutput "Services involved in cycle: $($remaining -join ', ')" "Yellow"
|
| 219 |
+
exit 1
|
| 220 |
+
}
|
| 221 |
+
|
| 222 |
+
Write-ColorOutput " Dependency graph validated (no cycles)" "Green"
|
| 223 |
+
Write-ColorOutput " Topological order: $($sorted -join ' -> ')" "Blue"
|
| 224 |
+
|
| 225 |
+
# Return services in topological order
|
| 226 |
+
return $sorted | ForEach-Object { $domainToService[$_] }
|
| 227 |
+
}
|
| 228 |
+
|
| 229 |
+
function Resolve-ConstructorArgument {
|
| 230 |
+
param([Parameter]$Param)
|
| 231 |
+
|
| 232 |
+
$type = $Param.NormalizedType
|
| 233 |
+
|
| 234 |
+
# SPECIAL CASE MAPPINGS - Add more here as needed
|
| 235 |
+
# ============================================
|
| 236 |
+
|
| 237 |
+
# 1. JWT secret string
|
| 238 |
+
if ($Param.RawType -eq "string" -and $Param.Name -match "secret|key") {
|
| 239 |
+
return "configProvider.ProvideJWTConfig().GetSecretKey()"
|
| 240 |
+
}
|
| 241 |
+
|
| 242 |
+
# 2. Repository pattern: XxxxRepository -> repoProvider.ProvideXxxxRepository()
|
| 243 |
+
if ($type -match '^(.+)Repository$') {
|
| 244 |
+
$repoName = $matches[1]
|
| 245 |
+
return "repoProvider.Provide${repoName}Repository()"
|
| 246 |
+
}
|
| 247 |
+
|
| 248 |
+
# 3. Service pattern: XxxxService -> use variable (will be constructed before this)
|
| 249 |
+
if ($type -match 'Service$') {
|
| 250 |
+
return (Get-LowerCamelCase $type)
|
| 251 |
+
}
|
| 252 |
+
|
| 253 |
+
# ADD MORE SPECIAL CASES HERE:
|
| 254 |
+
# --------------------------------------------
|
| 255 |
+
# Example: Mail config
|
| 256 |
+
# if ($type -eq "MailConfig") {
|
| 257 |
+
# return "configProvider.ProvideMailConfig()"
|
| 258 |
+
# }
|
| 259 |
+
#
|
| 260 |
+
# Example: Redis client
|
| 261 |
+
# if ($type -eq "RedisClient") {
|
| 262 |
+
# return "configProvider.ProvideRedisClient()"
|
| 263 |
+
# }
|
| 264 |
+
# --------------------------------------------
|
| 265 |
+
|
| 266 |
+
# 4. Fallback: unresolved type
|
| 267 |
+
return "/* TODO: provide $($Param.RawType) */"
|
| 268 |
+
}
|
| 269 |
+
|
| 270 |
+
function Generate-ProviderCode {
|
| 271 |
+
param([System.Collections.Generic.List[ServiceInfo]]$ServicesInOrder)
|
| 272 |
+
|
| 273 |
+
Write-ColorOutput "`nGenerating provider code..." "Cyan"
|
| 274 |
+
|
| 275 |
+
$sb = [System.Text.StringBuilder]::new()
|
| 276 |
+
[void]$sb.AppendLine("package provider")
|
| 277 |
+
[void]$sb.AppendLine()
|
| 278 |
+
[void]$sb.AppendLine("import `"$ModulePath`"")
|
| 279 |
+
[void]$sb.AppendLine()
|
| 280 |
+
|
| 281 |
+
# Interface
|
| 282 |
+
[void]$sb.AppendLine("type ServicesProvider interface {")
|
| 283 |
+
foreach ($svc in $ServicesInOrder) {
|
| 284 |
+
$line = "`tProvide$($svc.Domain)() services.$($svc.Domain)"
|
| 285 |
+
[void]$sb.AppendLine($line)
|
| 286 |
+
}
|
| 287 |
+
[void]$sb.AppendLine("}")
|
| 288 |
+
[void]$sb.AppendLine()
|
| 289 |
+
|
| 290 |
+
# Struct
|
| 291 |
+
[void]$sb.AppendLine("type servicesProvider struct {")
|
| 292 |
+
foreach ($svc in $ServicesInOrder) {
|
| 293 |
+
$line = "`t$($svc.VarName) services.$($svc.Domain)"
|
| 294 |
+
[void]$sb.AppendLine($line)
|
| 295 |
+
}
|
| 296 |
+
[void]$sb.AppendLine("}")
|
| 297 |
+
[void]$sb.AppendLine()
|
| 298 |
+
|
| 299 |
+
# Constructor
|
| 300 |
+
[void]$sb.AppendLine("func NewServicesProvider(repoProvider RepositoriesProvider, configProvider ConfigProvider) ServicesProvider {")
|
| 301 |
+
|
| 302 |
+
# Initialize services in topological order
|
| 303 |
+
foreach ($svc in $ServicesInOrder) {
|
| 304 |
+
$args = @()
|
| 305 |
+
foreach ($param in $svc.Parameters) {
|
| 306 |
+
$args += Resolve-ConstructorArgument $param
|
| 307 |
+
}
|
| 308 |
+
$argsStr = $args -join ", "
|
| 309 |
+
$line = "`t$($svc.VarName) := services.$($svc.ConstructorName)($argsStr)"
|
| 310 |
+
[void]$sb.AppendLine($line)
|
| 311 |
+
}
|
| 312 |
+
|
| 313 |
+
[void]$sb.AppendLine()
|
| 314 |
+
[void]$sb.AppendLine("`treturn &servicesProvider{")
|
| 315 |
+
foreach ($svc in $ServicesInOrder) {
|
| 316 |
+
$line = "`t`t$($svc.VarName): $($svc.VarName),"
|
| 317 |
+
[void]$sb.AppendLine($line)
|
| 318 |
+
}
|
| 319 |
+
[void]$sb.AppendLine("`t}")
|
| 320 |
+
[void]$sb.AppendLine("}")
|
| 321 |
+
[void]$sb.AppendLine()
|
| 322 |
+
|
| 323 |
+
# Getter methods
|
| 324 |
+
foreach ($svc in $ServicesInOrder) {
|
| 325 |
+
[void]$sb.AppendLine("func (s *servicesProvider) Provide$($svc.Domain)() services.$($svc.Domain) {")
|
| 326 |
+
[void]$sb.AppendLine("`treturn s.$($svc.VarName)")
|
| 327 |
+
[void]$sb.AppendLine("}")
|
| 328 |
+
[void]$sb.AppendLine()
|
| 329 |
+
}
|
| 330 |
+
|
| 331 |
+
return $sb.ToString()
|
| 332 |
+
}
|
| 333 |
+
|
| 334 |
+
function Write-ProviderFile {
|
| 335 |
+
param([string]$Code, [string]$OutputPath)
|
| 336 |
+
|
| 337 |
+
Write-ColorOutput "Writing to $OutputPath..." "Cyan"
|
| 338 |
+
|
| 339 |
+
# Ensure directory exists
|
| 340 |
+
$dir = Split-Path $OutputPath -Parent
|
| 341 |
+
if ($dir -and -not (Test-Path $dir)) {
|
| 342 |
+
New-Item -ItemType Directory -Path $dir -Force | Out-Null
|
| 343 |
+
}
|
| 344 |
+
|
| 345 |
+
# Write file as UTF-8 without BOM
|
| 346 |
+
$utf8NoBom = [System.Text.UTF8Encoding]::new($false)
|
| 347 |
+
[System.IO.File]::WriteAllText($OutputPath, $Code, $utf8NoBom)
|
| 348 |
+
|
| 349 |
+
Write-ColorOutput " Successfully generated $OutputPath" "Green"
|
| 350 |
+
}
|
| 351 |
+
|
| 352 |
+
# ============================================
|
| 353 |
+
# MAIN EXECUTION
|
| 354 |
+
# ============================================
|
| 355 |
+
|
| 356 |
+
try {
|
| 357 |
+
Write-ColorOutput "`n=========================================" "Blue"
|
| 358 |
+
Write-ColorOutput " Go Service Dependency Injector v1.0" "Blue"
|
| 359 |
+
Write-ColorOutput "=========================================`n" "Blue"
|
| 360 |
+
|
| 361 |
+
# Step 1: Parse all service constructors
|
| 362 |
+
$services = Parse-GoFiles -Directory $ServicesDir
|
| 363 |
+
|
| 364 |
+
# Step 2: Perform topological sort
|
| 365 |
+
$sortedServices = Get-TopologicalOrder -Services $services
|
| 366 |
+
|
| 367 |
+
# Step 3: Generate provider code
|
| 368 |
+
$code = Generate-ProviderCode -ServicesInOrder $sortedServices
|
| 369 |
+
|
| 370 |
+
# Step 4: Write to file
|
| 371 |
+
Write-ProviderFile -Code $code -OutputPath $OutputFile
|
| 372 |
+
|
| 373 |
+
Write-ColorOutput "`nSUCCESS! Provider generated successfully.`n" "Green"
|
| 374 |
+
Write-ColorOutput "Next steps:" "Cyan"
|
| 375 |
+
Write-ColorOutput " 1. Review $OutputFile" "White"
|
| 376 |
+
Write-ColorOutput " 2. Fill any /* TODO: provide ... */ placeholders" "White"
|
| 377 |
+
Write-ColorOutput " 3. Run: go build ./provider" "White"
|
| 378 |
+
|
| 379 |
+
} catch {
|
| 380 |
+
Write-ColorOutput "`nERROR: $($_.Exception.Message)" "Red"
|
| 381 |
+
Write-ColorOutput "Stack trace: $($_.ScriptStackTrace)" "Yellow"
|
| 382 |
+
exit 1
|
| 383 |
+
}
|
config/config.go
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
package config
|
config/database_config.go
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package config
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"fmt"
|
| 5 |
+
"log"
|
| 6 |
+
|
| 7 |
+
"gorm.io/driver/postgres"
|
| 8 |
+
"gorm.io/gorm"
|
| 9 |
+
)
|
| 10 |
+
|
| 11 |
+
type DatabaseConfig interface {
|
| 12 |
+
AutoMigrateAll(entities ...interface{}) error
|
| 13 |
+
GetInstance() *gorm.DB
|
| 14 |
+
}
|
| 15 |
+
type databaseConfig struct {
|
| 16 |
+
db *gorm.DB
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
func NewDatabaseConfig(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME, DB_PORT string) DatabaseConfig {
|
| 20 |
+
dsn := fmt.Sprintf(
|
| 21 |
+
"host=%s user=%s password=%s dbname=%s port=%s sslmode=disable TimeZone=Asia/Jakarta ",
|
| 22 |
+
DB_HOST, DB_USER, DB_PASSWORD, DB_NAME, DB_PORT,
|
| 23 |
+
)
|
| 24 |
+
|
| 25 |
+
db, err := gorm.Open(postgres.New(postgres.Config{
|
| 26 |
+
DSN: dsn,
|
| 27 |
+
PreferSimpleProtocol: true, // required for PgBouncer/Supabase pooled connections
|
| 28 |
+
}), &gorm.Config{
|
| 29 |
+
TranslateError: true,
|
| 30 |
+
})
|
| 31 |
+
|
| 32 |
+
db = db.Session(&gorm.Session{
|
| 33 |
+
PrepareStmt: false,
|
| 34 |
+
})
|
| 35 |
+
|
| 36 |
+
if err != nil {
|
| 37 |
+
log.Fatal("Failed to connect to database:", err)
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
return &databaseConfig{db: db}
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
func (cfg *databaseConfig) AutoMigrateAll(entities ...interface{}) error {
|
| 44 |
+
|
| 45 |
+
err := cfg.db.AutoMigrate(
|
| 46 |
+
entities...,
|
| 47 |
+
)
|
| 48 |
+
|
| 49 |
+
return err
|
| 50 |
+
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
func (cfg *databaseConfig) GetInstance() *gorm.DB {
|
| 54 |
+
return cfg.db
|
| 55 |
+
}
|
config/env_config.go
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package config
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"os"
|
| 5 |
+
"strconv"
|
| 6 |
+
"strings"
|
| 7 |
+
"abdanhafidz.com/go-boilerplate/utils"
|
| 8 |
+
"github.com/joho/godotenv"
|
| 9 |
+
)
|
| 10 |
+
|
| 11 |
+
type EnvConfig interface {
|
| 12 |
+
GetTCPAddress() string
|
| 13 |
+
GetLogPath() string
|
| 14 |
+
GetHostAddress() string
|
| 15 |
+
GetHostPort() string
|
| 16 |
+
GetEmailVerificationDuration() int
|
| 17 |
+
GetDatabaseHost() string
|
| 18 |
+
GetDatabasePort() string
|
| 19 |
+
GetDatabaseUser() string
|
| 20 |
+
GetDatabasePassword() string
|
| 21 |
+
GetDatabaseName() string
|
| 22 |
+
GetSalt() string
|
| 23 |
+
GetSupabaseURL() string
|
| 24 |
+
GetSupabaseKey() string
|
| 25 |
+
GetSupabaseBucket() string
|
| 26 |
+
GetXenditAPIKey() string
|
| 27 |
+
GetXenditCallbackToken() string
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
type envConfig struct {
|
| 31 |
+
timezone string
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
func NewEnvConfig(timezone string) EnvConfig {
|
| 35 |
+
godotenv.Load()
|
| 36 |
+
os.Setenv("TZ", timezone)
|
| 37 |
+
return &envConfig{
|
| 38 |
+
timezone: timezone,
|
| 39 |
+
}
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
func (e *envConfig) GetTCPAddress() string {
|
| 43 |
+
return utils.GetEnv("HOST_ADDRESS") + ":" + utils.GetEnv("HOST_PORT")
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
func (e *envConfig) GetLogPath() string {
|
| 47 |
+
return utils.GetEnv("LOG_PATH")
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
func (e *envConfig) GetHostAddress() string {
|
| 51 |
+
return utils.GetEnv("HOST_ADDRESS")
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
func (e *envConfig) GetHostPort() string {
|
| 55 |
+
return utils.GetEnv("HOST_PORT")
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
func (e *envConfig) GetEmailVerificationDuration() int {
|
| 59 |
+
duration, err := strconv.Atoi(utils.GetEnv("EMAIL_VERIFICATION_DURATION"))
|
| 60 |
+
if err != nil {
|
| 61 |
+
return 0 // Default value if parsing fails
|
| 62 |
+
}
|
| 63 |
+
return duration
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
func (e *envConfig) GetDatabaseHost() string {
|
| 67 |
+
return utils.GetEnv("DB_HOST")
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
func (e *envConfig) GetDatabasePort() string {
|
| 71 |
+
return utils.GetEnv("DB_PORT")
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
func (e *envConfig) GetDatabaseUser() string {
|
| 75 |
+
return utils.GetEnv("DB_USER")
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
func (e *envConfig) GetDatabasePassword() string {
|
| 79 |
+
return utils.GetEnv("DB_PASSWORD")
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
func (e *envConfig) GetDatabaseName() string {
|
| 83 |
+
return utils.GetEnv("DB_NAME")
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
func (e *envConfig) GetSalt() string {
|
| 87 |
+
salt := utils.GetEnv("SALT")
|
| 88 |
+
if salt == "" {
|
| 89 |
+
return "Def4u|7" // Default salt value
|
| 90 |
+
}
|
| 91 |
+
return salt
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
func (e *envConfig) GetSupabaseURL() string {
|
| 95 |
+
return strings.TrimSpace(utils.GetEnv("SUPABASE_URL"))
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
func (e *envConfig) GetSupabaseKey() string {
|
| 99 |
+
return strings.TrimSpace(utils.GetEnv("SUPABASE_SERVICE_KEY"))
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
func (e *envConfig) GetSupabaseBucket() string {
|
| 103 |
+
return strings.TrimSpace(utils.GetEnv("SUPABASE_BUCKET_NAME"))
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
func (e *envConfig) GetXenditAPIKey() string {
|
| 107 |
+
return strings.TrimSpace(utils.GetEnv("XENDIT_API_KEY"))
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
func (e *envConfig) GetXenditCallbackToken() string {
|
| 111 |
+
return strings.TrimSpace(utils.GetEnv("XENDIT_CALLBACK_TOKEN"))
|
| 112 |
+
}
|
config/jwt_config.go
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package config
|
| 2 |
+
|
| 3 |
+
type JWTConfig interface {
|
| 4 |
+
SetSecretKey(key string)
|
| 5 |
+
GetSecretKey() string
|
| 6 |
+
}
|
| 7 |
+
|
| 8 |
+
type jwtConfig struct {
|
| 9 |
+
secretKey string
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
func NewJWTConfig(secretKey string) JWTConfig {
|
| 13 |
+
return &jwtConfig{
|
| 14 |
+
secretKey: secretKey,
|
| 15 |
+
}
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
func (cfg *jwtConfig) SetSecretKey(key string) {
|
| 19 |
+
cfg.secretKey = key
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
func (cfg *jwtConfig) GetSecretKey() string {
|
| 23 |
+
return cfg.secretKey
|
| 24 |
+
}
|
config/supabase_config.go
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package config
|
| 2 |
+
|
| 3 |
+
type SupabaseConfig interface {
|
| 4 |
+
GetURL() string
|
| 5 |
+
GetServiceKey() string
|
| 6 |
+
GetBucketName() string
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
+
type supabaseConfig struct {
|
| 10 |
+
url string
|
| 11 |
+
serviceKey string
|
| 12 |
+
bucketName string
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
func NewSupabaseConfig(url string, key string, bucket string) SupabaseConfig {
|
| 16 |
+
return &supabaseConfig{
|
| 17 |
+
url: url,
|
| 18 |
+
serviceKey: key,
|
| 19 |
+
bucketName: bucket,
|
| 20 |
+
}
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
func (c *supabaseConfig) GetURL() string { return c.url }
|
| 24 |
+
func (c *supabaseConfig) GetServiceKey() string { return c.serviceKey }
|
| 25 |
+
func (c *supabaseConfig) GetBucketName() string { return c.bucketName }
|
config/upload_config.go
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package config
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
models "abdanhafidz.com/go-boilerplate/models/entity"
|
| 5 |
+
http_error "abdanhafidz.com/go-boilerplate/models/error"
|
| 6 |
+
)
|
| 7 |
+
|
| 8 |
+
type UploadRule struct {
|
| 9 |
+
MaxBytes int64
|
| 10 |
+
AllowedExts map[string]bool
|
| 11 |
+
PathPrefix string
|
| 12 |
+
MaxCount int
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
type UploadConfig interface {
|
| 16 |
+
Get(contextType string) (UploadRule, error)
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
type uploadConfig struct{}
|
| 20 |
+
|
| 21 |
+
func NewUploadConfig() UploadConfig { return &uploadConfig{} }
|
| 22 |
+
|
| 23 |
+
func (c *uploadConfig) Get(contextType string) (UploadRule, error) {
|
| 24 |
+
codeExts := map[string]bool{".cpp": true, ".c": true, ".py": true, ".java": true, ".go": true, ".js": true, ".txt": true}
|
| 25 |
+
imgExts := map[string]bool{".jpg": true, ".jpeg": true, ".png": true, ".webp": true, ".gif": true}
|
| 26 |
+
docExts := map[string]bool{".pdf": true, ".doc": true, ".docx": true}
|
| 27 |
+
|
| 28 |
+
allExts := make(map[string]bool)
|
| 29 |
+
for k, v := range codeExts { allExts[k] = v }
|
| 30 |
+
for k, v := range imgExts { allExts[k] = v }
|
| 31 |
+
for k, v := range docExts { allExts[k] = v }
|
| 32 |
+
|
| 33 |
+
switch contextType {
|
| 34 |
+
case "image":
|
| 35 |
+
return UploadRule{ MaxBytes: 10 * models.MB, AllowedExts: imgExts, PathPrefix: "images", MaxCount: 5 }, nil
|
| 36 |
+
case "material":
|
| 37 |
+
return UploadRule{ MaxBytes: 10 * models.MB, AllowedExts: docExts, PathPrefix: "materials", MaxCount: 1 }, nil
|
| 38 |
+
case "submission":
|
| 39 |
+
return UploadRule{ MaxBytes: 1 * models.MB, AllowedExts: codeExts, PathPrefix: "submissions", MaxCount: 1 }, nil
|
| 40 |
+
case "general":
|
| 41 |
+
return UploadRule{ MaxBytes: 5 * models.MB, AllowedExts: allExts, PathPrefix: "temp", MaxCount: 5 }, nil
|
| 42 |
+
default:
|
| 43 |
+
return UploadRule{}, http_error.INVALID_UPLOAD_CONTEXT_ERROR
|
| 44 |
+
}
|
| 45 |
+
}
|
| 46 |
+
|
config/xendit_config.go
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package config
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"sync"
|
| 5 |
+
|
| 6 |
+
xendit "github.com/xendit/xendit-go/v7"
|
| 7 |
+
)
|
| 8 |
+
|
| 9 |
+
var (
|
| 10 |
+
xenditOnce sync.Once
|
| 11 |
+
)
|
| 12 |
+
|
| 13 |
+
type XenditConfig interface {
|
| 14 |
+
GetClient() *xendit.APIClient
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
type xenditConfig struct {
|
| 18 |
+
envConfig EnvConfig
|
| 19 |
+
client *xendit.APIClient
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
func NewXenditConfig(envConfig EnvConfig) XenditConfig {
|
| 23 |
+
return &xenditConfig{
|
| 24 |
+
envConfig: envConfig,
|
| 25 |
+
client: xendit.NewClient(envConfig.GetXenditAPIKey()),
|
| 26 |
+
}
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
func (c *xenditConfig) GetClient() *xendit.APIClient {
|
| 30 |
+
return c.client
|
| 31 |
+
}
|
controllers/authentication_controller.go
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package controllers
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"abdanhafidz.com/go-boilerplate/models/dto"
|
| 5 |
+
entity "abdanhafidz.com/go-boilerplate/models/entity"
|
| 6 |
+
"abdanhafidz.com/go-boilerplate/services"
|
| 7 |
+
"github.com/gin-gonic/gin"
|
| 8 |
+
)
|
| 9 |
+
|
| 10 |
+
type AuthenticationController interface {
|
| 11 |
+
SignUp(ctx *gin.Context)
|
| 12 |
+
SignIn(ctx *gin.Context)
|
| 13 |
+
ChangePassword(ctx *gin.Context)
|
| 14 |
+
UpdateUserRole(ctx *gin.Context)
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
type authenticationController struct {
|
| 18 |
+
userService services.UserService
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
func NewAuthenticationController(userService services.UserService) AuthenticationController {
|
| 22 |
+
return &authenticationController{
|
| 23 |
+
userService: userService,
|
| 24 |
+
}
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
func (c *authenticationController) SignUp(ctx *gin.Context) {
|
| 28 |
+
req := RequestJSON[dto.SignUpRequest](ctx)
|
| 29 |
+
res, err := c.userService.Create(ctx.Request.Context(), req.Name, req.Email, req.Username, req.Password)
|
| 30 |
+
ResponseJSON(ctx, req, res, err)
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
func (c *authenticationController) SignIn(ctx *gin.Context) {
|
| 34 |
+
req := RequestJSON[dto.SignInRequest](ctx)
|
| 35 |
+
res, err := c.userService.Validate(ctx, req.EmailorUsername, req.Password)
|
| 36 |
+
ResponseJSON(ctx, req, res, err)
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
func (c *authenticationController) ChangePassword(ctx *gin.Context) {
|
| 40 |
+
req := RequestJSON[dto.ChangePasswordRequest](ctx)
|
| 41 |
+
userId := ParseUserId(ctx)
|
| 42 |
+
res, err := c.userService.ChangePassword(ctx.Request.Context(), userId, req.OldPassword, req.NewPassword)
|
| 43 |
+
ResponseJSON(ctx, req, res, err)
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
func (c *authenticationController) UpdateUserRole(ctx *gin.Context) {
|
| 47 |
+
req := RequestJSON[dto.UpdateUserRoleRequest](ctx)
|
| 48 |
+
userId := ctx.Param("userId")
|
| 49 |
+
user, err := c.userService.GetById(ctx.Request.Context(), userId)
|
| 50 |
+
if err != nil {
|
| 51 |
+
ResponseJSON(ctx, req, entity.User{}, err)
|
| 52 |
+
return
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
// For now, don't implement full Role update logic (requires roles association in schema)
|
| 56 |
+
// Just stub it
|
| 57 |
+
res, err := c.userService.Update(ctx.Request.Context(), user)
|
| 58 |
+
ResponseJSON(ctx, req, res, err)
|
| 59 |
+
}
|
controllers/controller.go
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package controllers
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
http_error "abdanhafidz.com/go-boilerplate/models/error"
|
| 5 |
+
"abdanhafidz.com/go-boilerplate/utils"
|
| 6 |
+
"github.com/gin-gonic/gin"
|
| 7 |
+
uuid "github.com/google/uuid"
|
| 8 |
+
)
|
| 9 |
+
|
| 10 |
+
func ParseUserId(ctx *gin.Context) string {
|
| 11 |
+
guserId, _ := ctx.Get("user_id")
|
| 12 |
+
userId, ok := guserId.(string)
|
| 13 |
+
if !ok {
|
| 14 |
+
ResponseJSON(ctx, gin.H{"user_id": userId}, "", http_error.INVALID_TOKEN)
|
| 15 |
+
return ""
|
| 16 |
+
}
|
| 17 |
+
return userId
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
func ParseUUID(ctx *gin.Context, attrName string) uuid.UUID {
|
| 21 |
+
uuidRaw, _ := ctx.Get(attrName)
|
| 22 |
+
uuidParsed, err := utils.ToUUID(uuidRaw)
|
| 23 |
+
|
| 24 |
+
if err != nil {
|
| 25 |
+
ResponseJSON(ctx, gin.H{"id": uuidParsed}, uuid.UUID{}, http_error.INVALID_TOKEN)
|
| 26 |
+
return uuid.UUID{}
|
| 27 |
+
}
|
| 28 |
+
return uuidParsed
|
| 29 |
+
}
|
| 30 |
+
func RequestJSON[TRequest any](ctx *gin.Context) TRequest {
|
| 31 |
+
var request TRequest
|
| 32 |
+
if err := ctx.ShouldBindJSON(&request); err != nil {
|
| 33 |
+
utils.ResponseFAILED(ctx, request, http_error.BAD_REQUEST_ERROR)
|
| 34 |
+
ctx.Abort()
|
| 35 |
+
return request
|
| 36 |
+
} else {
|
| 37 |
+
return request
|
| 38 |
+
}
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
func RequestForm[TRequest any](ctx *gin.Context) TRequest {
|
| 42 |
+
var request TRequest
|
| 43 |
+
if err := ctx.ShouldBind(&request); err != nil {
|
| 44 |
+
utils.ResponseFAILED(ctx, request, http_error.BAD_REQUEST_ERROR)
|
| 45 |
+
ctx.Abort()
|
| 46 |
+
return request
|
| 47 |
+
}
|
| 48 |
+
return request
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
func ResponseJSON[TResponse any, TMetaData any](ctx *gin.Context, metaData TMetaData, res TResponse, err error) {
|
| 52 |
+
utils.SendResponse(ctx, metaData, res, err)
|
| 53 |
+
}
|
controllers/home_controller.go
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package controllers
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"abdanhafidz.com/go-boilerplate/services"
|
| 5 |
+
"github.com/gin-gonic/gin"
|
| 6 |
+
)
|
| 7 |
+
|
| 8 |
+
type HomeController interface {
|
| 9 |
+
GetHomeData(ctx *gin.Context)
|
| 10 |
+
GetSchedules(ctx *gin.Context)
|
| 11 |
+
GetEventCategories(ctx *gin.Context)
|
| 12 |
+
GetClassCategories(ctx *gin.Context)
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
type homeController struct {
|
| 16 |
+
homeService services.HomeService
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
func NewHomeController(homeService services.HomeService) HomeController {
|
| 20 |
+
return &homeController{homeService: homeService}
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
// GetHomeData godoc
|
| 24 |
+
// @Summary Get Home Page Data
|
| 25 |
+
// @Description Return all sections needed by the home page in one aggregated response.
|
| 26 |
+
// @Tags Home Dashboard
|
| 27 |
+
// @Accept json
|
| 28 |
+
// @Produce json
|
| 29 |
+
// @Param date query string false "Selected date for schedule card section (YYYY-MM-DD)"
|
| 30 |
+
// @Param latitude query number false "User latitude"
|
| 31 |
+
// @Param longitude query number false "User longitude"
|
| 32 |
+
// @Param city query string false "User city"
|
| 33 |
+
// @Success 200 {object} dto.HomeResponseWrapper
|
| 34 |
+
// @Failure 400 {object} dto.ErrorResponse
|
| 35 |
+
// @Security BearerAuth
|
| 36 |
+
// @Router /api/v1/home [get]
|
| 37 |
+
func (c *homeController) GetHomeData(ctx *gin.Context) {
|
| 38 |
+
userId := ParseUserId(ctx)
|
| 39 |
+
|
| 40 |
+
params := map[string]interface{}{
|
| 41 |
+
"date": ctx.Query("date"),
|
| 42 |
+
"latitude": ctx.Query("latitude"),
|
| 43 |
+
"longitude": ctx.Query("longitude"),
|
| 44 |
+
"city": ctx.Query("city"),
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
res, err := c.homeService.GetHomeData(ctx.Request.Context(), userId, params)
|
| 48 |
+
ResponseJSON(ctx, "", res, err)
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
// GetSchedules godoc
|
| 52 |
+
// @Summary Get Schedule List
|
| 53 |
+
// @Description Get a list of class or event schedules based on date range, city, filters, etc.
|
| 54 |
+
// @Tags Home Dashboard
|
| 55 |
+
// @Accept json
|
| 56 |
+
// @Produce json
|
| 57 |
+
// @Param date query string false "Exact date (YYYY-MM-DD)"
|
| 58 |
+
// @Param start_date query string false "Start date range (YYYY-MM-DD)"
|
| 59 |
+
// @Param end_date query string false "End date range (YYYY-MM-DD)"
|
| 60 |
+
// @Param city query string false "Filter by city"
|
| 61 |
+
// @Param level query string false "Filter by class level (e.g., beginner)"
|
| 62 |
+
// @Param category query string false "Filter by class category (e.g., salsa)"
|
| 63 |
+
// @Param page query int false "Page number (default: 1)"
|
| 64 |
+
// @Param limit query int false "Items per page (default: 10)"
|
| 65 |
+
// @Success 200 {object} dto.ScheduleListResponseWrapper
|
| 66 |
+
// @Failure 400 {object} dto.ErrorResponse
|
| 67 |
+
// @Security BearerAuth
|
| 68 |
+
// @Router /api/v1/schedules [get]
|
| 69 |
+
func (c *homeController) GetSchedules(ctx *gin.Context) {
|
| 70 |
+
userId := ParseUserId(ctx)
|
| 71 |
+
|
| 72 |
+
params := map[string]interface{}{
|
| 73 |
+
"date": ctx.Query("date"),
|
| 74 |
+
"start_date": ctx.Query("start_date"),
|
| 75 |
+
"end_date": ctx.Query("end_date"),
|
| 76 |
+
"city": ctx.Query("city"),
|
| 77 |
+
"level": ctx.Query("level"),
|
| 78 |
+
"category": ctx.Query("category"),
|
| 79 |
+
"page": ctx.Query("page"),
|
| 80 |
+
"limit": ctx.Query("limit"),
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
res, err := c.homeService.GetSchedules(ctx.Request.Context(), userId, params)
|
| 84 |
+
ResponseJSON(ctx, "", res, err)
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
// GetEventCategories godoc
|
| 88 |
+
// @Summary Get Event Categories
|
| 89 |
+
// @Description Get master list of event categories
|
| 90 |
+
// @Tags Master Data
|
| 91 |
+
// @Accept json
|
| 92 |
+
// @Produce json
|
| 93 |
+
// @Success 200 {object} dto.CategoryResponseWrapper
|
| 94 |
+
// @Failure 400 {object} dto.ErrorResponse
|
| 95 |
+
// @Router /api/v1/event-categories [get]
|
| 96 |
+
func (c *homeController) GetEventCategories(ctx *gin.Context) {
|
| 97 |
+
res, err := c.homeService.GetEventCategories(ctx.Request.Context())
|
| 98 |
+
ResponseJSON(ctx, "", res, err)
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
// GetClassCategories godoc
|
| 102 |
+
// @Summary Get Class Categories
|
| 103 |
+
// @Description Get master list of class categories
|
| 104 |
+
// @Tags Master Data
|
| 105 |
+
// @Accept json
|
| 106 |
+
// @Produce json
|
| 107 |
+
// @Success 200 {object} dto.CategoryResponseWrapper
|
| 108 |
+
// @Failure 400 {object} dto.ErrorResponse
|
| 109 |
+
// @Router /api/v1/class-categories [get]
|
| 110 |
+
func (c *homeController) GetClassCategories(ctx *gin.Context) {
|
| 111 |
+
res, err := c.homeService.GetClassCategories(ctx.Request.Context())
|
| 112 |
+
ResponseJSON(ctx, "", res, err)
|
| 113 |
+
}
|
controllers/otp_verification_controller.go
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package controllers
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"abdanhafidz.com/go-boilerplate/services"
|
| 5 |
+
"github.com/gin-gonic/gin"
|
| 6 |
+
)
|
| 7 |
+
|
| 8 |
+
type OTPVerificationController interface {
|
| 9 |
+
RequestOTP(ctx *gin.Context)
|
| 10 |
+
VerifyOTP(ctx *gin.Context)
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
type otpVerificationController struct {
|
| 14 |
+
otpService services.OTPVerificationService
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
func NewOTPVerificationController(otpService services.OTPVerificationService) OTPVerificationController {
|
| 18 |
+
return &otpVerificationController{
|
| 19 |
+
otpService: otpService,
|
| 20 |
+
}
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
type RequestOTPPayload struct {
|
| 24 |
+
PhoneNumber string `json:"phone_number" binding:"required"`
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
type VerifyOTPPayload struct {
|
| 28 |
+
PhoneNumber string `json:"phone_number" binding:"required"`
|
| 29 |
+
Code string `json:"code" binding:"required"`
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
func (c *otpVerificationController) RequestOTP(ctx *gin.Context) {
|
| 33 |
+
req := RequestJSON[RequestOTPPayload](ctx)
|
| 34 |
+
err := c.otpService.RequestOTP(ctx.Request.Context(), req.PhoneNumber)
|
| 35 |
+
ResponseJSON(ctx, req, "", err)
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
func (c *otpVerificationController) VerifyOTP(ctx *gin.Context) {
|
| 39 |
+
req := RequestJSON[VerifyOTPPayload](ctx)
|
| 40 |
+
err := c.otpService.VerifyOTP(ctx.Request.Context(), req.PhoneNumber, req.Code)
|
| 41 |
+
ResponseJSON(ctx, req, "", err)
|
| 42 |
+
}
|
controllers/payment_callback_controller.go
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package controllers
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"log"
|
| 5 |
+
|
| 6 |
+
"abdanhafidz.com/go-boilerplate/models/dto"
|
| 7 |
+
http_error "abdanhafidz.com/go-boilerplate/models/error"
|
| 8 |
+
"abdanhafidz.com/go-boilerplate/services"
|
| 9 |
+
"abdanhafidz.com/go-boilerplate/utils"
|
| 10 |
+
"github.com/gin-gonic/gin"
|
| 11 |
+
)
|
| 12 |
+
|
| 13 |
+
type PaymentCallbackController interface {
|
| 14 |
+
HandleCallback(ctx *gin.Context)
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
type paymentCallbackController struct {
|
| 18 |
+
paymentService services.PaymentService
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
func NewPaymentCallbackController(
|
| 22 |
+
paymentService services.PaymentService,
|
| 23 |
+
) PaymentCallbackController {
|
| 24 |
+
return &paymentCallbackController{
|
| 25 |
+
paymentService: paymentService,
|
| 26 |
+
}
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
// Handle Payment Callback godoc
|
| 30 |
+
// @Summary Handle Xendit Payment Callback
|
| 31 |
+
// @Description Receive and process payment status updates from Xendit
|
| 32 |
+
// @Tags Payment
|
| 33 |
+
// @Accept json
|
| 34 |
+
// @Produce json
|
| 35 |
+
// @Param request body map[string]interface{} true "Xendit Callback Payload"
|
| 36 |
+
// @Success 200 {object} dto.SuccessResponse[any]
|
| 37 |
+
// @Failure 400 {object} dto.ErrorResponse
|
| 38 |
+
// @Router /api/v1/payment/callback [post]
|
| 39 |
+
func (c *paymentCallbackController) HandleCallback(ctx *gin.Context) {
|
| 40 |
+
// Xendit sends JSON payload
|
| 41 |
+
// Basic structure for Invoice Callback:
|
| 42 |
+
// { "id": "...", "external_id": "...", "status": "PAID", ... }
|
| 43 |
+
var callbackData map[string]interface{}
|
| 44 |
+
if err := ctx.ShouldBindJSON(&callbackData); err != nil {
|
| 45 |
+
utils.ResponseFAILED(ctx, gin.H(nil), http_error.BAD_REQUEST_ERROR)
|
| 46 |
+
return
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
log.Printf("Payment Callback Received: %+v", callbackData)
|
| 50 |
+
|
| 51 |
+
status, ok := callbackData["status"].(string)
|
| 52 |
+
if !ok {
|
| 53 |
+
// Not a status update or unknown format
|
| 54 |
+
var _ dto.SuccessResponse[any]
|
| 55 |
+
|
| 56 |
+
ResponseJSON(ctx, gin.H(nil), "Ignored: No status", nil)
|
| 57 |
+
return
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
invoiceId, _ := callbackData["id"].(string)
|
| 61 |
+
|
| 62 |
+
if status == "PAID" || status == "SETTLED" {
|
| 63 |
+
// Handle Event Payment
|
| 64 |
+
// We need a method in Service to handle "ConfirmPayment" by InvoiceID
|
| 65 |
+
// But existing services don't have it.
|
| 66 |
+
// Let's add it to PaymentService? Yes.
|
| 67 |
+
err := c.paymentService.ConfirmPayment(ctx.Request.Context(), invoiceId)
|
| 68 |
+
if err != nil {
|
| 69 |
+
log.Printf("Payment Confirmation Failed: %v", err)
|
| 70 |
+
// Don't return error to Xendit if logic failed, but maybe we should?
|
| 71 |
+
// Xendit expects 200 OK.
|
| 72 |
+
}
|
| 73 |
+
} else if status == "EXPIRED" {
|
| 74 |
+
c.paymentService.ExpirePayment(ctx.Request.Context(), invoiceId)
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
// Always return 200 to Xendit
|
| 78 |
+
ResponseJSON(ctx, gin.H(nil), gin.H{"callback": "Callback Received"}, nil)
|
| 79 |
+
}
|
docs/docs.go
ADDED
|
@@ -0,0 +1,705 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Package docs Code generated by swaggo/swag. DO NOT EDIT
|
| 2 |
+
package docs
|
| 3 |
+
|
| 4 |
+
import "github.com/swaggo/swag"
|
| 5 |
+
|
| 6 |
+
const docTemplate = `{
|
| 7 |
+
"schemes": {{ marshal .Schemes }},
|
| 8 |
+
"swagger": "2.0",
|
| 9 |
+
"info": {
|
| 10 |
+
"description": "{{escape .Description}}",
|
| 11 |
+
"title": "{{.Title}}",
|
| 12 |
+
"contact": {},
|
| 13 |
+
"version": "{{.Version}}"
|
| 14 |
+
},
|
| 15 |
+
"host": "{{.Host}}",
|
| 16 |
+
"basePath": "{{.BasePath}}",
|
| 17 |
+
"paths": {
|
| 18 |
+
"/api/v1/class-categories": {
|
| 19 |
+
"get": {
|
| 20 |
+
"description": "Get master list of class categories",
|
| 21 |
+
"consumes": [
|
| 22 |
+
"application/json"
|
| 23 |
+
],
|
| 24 |
+
"produces": [
|
| 25 |
+
"application/json"
|
| 26 |
+
],
|
| 27 |
+
"tags": [
|
| 28 |
+
"Master Data"
|
| 29 |
+
],
|
| 30 |
+
"summary": "Get Class Categories",
|
| 31 |
+
"responses": {
|
| 32 |
+
"200": {
|
| 33 |
+
"description": "OK",
|
| 34 |
+
"schema": {
|
| 35 |
+
"$ref": "#/definitions/dto.CategoryResponseWrapper"
|
| 36 |
+
}
|
| 37 |
+
},
|
| 38 |
+
"400": {
|
| 39 |
+
"description": "Bad Request",
|
| 40 |
+
"schema": {
|
| 41 |
+
"$ref": "#/definitions/dto.ErrorResponse"
|
| 42 |
+
}
|
| 43 |
+
}
|
| 44 |
+
}
|
| 45 |
+
}
|
| 46 |
+
},
|
| 47 |
+
"/api/v1/event-categories": {
|
| 48 |
+
"get": {
|
| 49 |
+
"description": "Get master list of event categories",
|
| 50 |
+
"consumes": [
|
| 51 |
+
"application/json"
|
| 52 |
+
],
|
| 53 |
+
"produces": [
|
| 54 |
+
"application/json"
|
| 55 |
+
],
|
| 56 |
+
"tags": [
|
| 57 |
+
"Master Data"
|
| 58 |
+
],
|
| 59 |
+
"summary": "Get Event Categories",
|
| 60 |
+
"responses": {
|
| 61 |
+
"200": {
|
| 62 |
+
"description": "OK",
|
| 63 |
+
"schema": {
|
| 64 |
+
"$ref": "#/definitions/dto.CategoryResponseWrapper"
|
| 65 |
+
}
|
| 66 |
+
},
|
| 67 |
+
"400": {
|
| 68 |
+
"description": "Bad Request",
|
| 69 |
+
"schema": {
|
| 70 |
+
"$ref": "#/definitions/dto.ErrorResponse"
|
| 71 |
+
}
|
| 72 |
+
}
|
| 73 |
+
}
|
| 74 |
+
}
|
| 75 |
+
},
|
| 76 |
+
"/api/v1/home": {
|
| 77 |
+
"get": {
|
| 78 |
+
"security": [
|
| 79 |
+
{
|
| 80 |
+
"BearerAuth": []
|
| 81 |
+
}
|
| 82 |
+
],
|
| 83 |
+
"description": "Return all sections needed by the home page in one aggregated response.",
|
| 84 |
+
"consumes": [
|
| 85 |
+
"application/json"
|
| 86 |
+
],
|
| 87 |
+
"produces": [
|
| 88 |
+
"application/json"
|
| 89 |
+
],
|
| 90 |
+
"tags": [
|
| 91 |
+
"Home Dashboard"
|
| 92 |
+
],
|
| 93 |
+
"summary": "Get Home Page Data",
|
| 94 |
+
"parameters": [
|
| 95 |
+
{
|
| 96 |
+
"type": "string",
|
| 97 |
+
"description": "Selected date for schedule card section (YYYY-MM-DD)",
|
| 98 |
+
"name": "date",
|
| 99 |
+
"in": "query"
|
| 100 |
+
},
|
| 101 |
+
{
|
| 102 |
+
"type": "number",
|
| 103 |
+
"description": "User latitude",
|
| 104 |
+
"name": "latitude",
|
| 105 |
+
"in": "query"
|
| 106 |
+
},
|
| 107 |
+
{
|
| 108 |
+
"type": "number",
|
| 109 |
+
"description": "User longitude",
|
| 110 |
+
"name": "longitude",
|
| 111 |
+
"in": "query"
|
| 112 |
+
},
|
| 113 |
+
{
|
| 114 |
+
"type": "string",
|
| 115 |
+
"description": "User city",
|
| 116 |
+
"name": "city",
|
| 117 |
+
"in": "query"
|
| 118 |
+
}
|
| 119 |
+
],
|
| 120 |
+
"responses": {
|
| 121 |
+
"200": {
|
| 122 |
+
"description": "OK",
|
| 123 |
+
"schema": {
|
| 124 |
+
"$ref": "#/definitions/dto.HomeResponseWrapper"
|
| 125 |
+
}
|
| 126 |
+
},
|
| 127 |
+
"400": {
|
| 128 |
+
"description": "Bad Request",
|
| 129 |
+
"schema": {
|
| 130 |
+
"$ref": "#/definitions/dto.ErrorResponse"
|
| 131 |
+
}
|
| 132 |
+
}
|
| 133 |
+
}
|
| 134 |
+
}
|
| 135 |
+
},
|
| 136 |
+
"/api/v1/payment/callback": {
|
| 137 |
+
"post": {
|
| 138 |
+
"description": "Receive and process payment status updates from Xendit",
|
| 139 |
+
"consumes": [
|
| 140 |
+
"application/json"
|
| 141 |
+
],
|
| 142 |
+
"produces": [
|
| 143 |
+
"application/json"
|
| 144 |
+
],
|
| 145 |
+
"tags": [
|
| 146 |
+
"Payment"
|
| 147 |
+
],
|
| 148 |
+
"summary": "Handle Xendit Payment Callback",
|
| 149 |
+
"parameters": [
|
| 150 |
+
{
|
| 151 |
+
"description": "Xendit Callback Payload",
|
| 152 |
+
"name": "request",
|
| 153 |
+
"in": "body",
|
| 154 |
+
"required": true,
|
| 155 |
+
"schema": {
|
| 156 |
+
"type": "object",
|
| 157 |
+
"additionalProperties": true
|
| 158 |
+
}
|
| 159 |
+
}
|
| 160 |
+
],
|
| 161 |
+
"responses": {
|
| 162 |
+
"200": {
|
| 163 |
+
"description": "OK",
|
| 164 |
+
"schema": {
|
| 165 |
+
"$ref": "#/definitions/dto.SuccessResponse-any"
|
| 166 |
+
}
|
| 167 |
+
},
|
| 168 |
+
"400": {
|
| 169 |
+
"description": "Bad Request",
|
| 170 |
+
"schema": {
|
| 171 |
+
"$ref": "#/definitions/dto.ErrorResponse"
|
| 172 |
+
}
|
| 173 |
+
}
|
| 174 |
+
}
|
| 175 |
+
}
|
| 176 |
+
},
|
| 177 |
+
"/api/v1/schedules": {
|
| 178 |
+
"get": {
|
| 179 |
+
"security": [
|
| 180 |
+
{
|
| 181 |
+
"BearerAuth": []
|
| 182 |
+
}
|
| 183 |
+
],
|
| 184 |
+
"description": "Get a list of class or event schedules based on date range, city, filters, etc.",
|
| 185 |
+
"consumes": [
|
| 186 |
+
"application/json"
|
| 187 |
+
],
|
| 188 |
+
"produces": [
|
| 189 |
+
"application/json"
|
| 190 |
+
],
|
| 191 |
+
"tags": [
|
| 192 |
+
"Home Dashboard"
|
| 193 |
+
],
|
| 194 |
+
"summary": "Get Schedule List",
|
| 195 |
+
"parameters": [
|
| 196 |
+
{
|
| 197 |
+
"type": "string",
|
| 198 |
+
"description": "Exact date (YYYY-MM-DD)",
|
| 199 |
+
"name": "date",
|
| 200 |
+
"in": "query"
|
| 201 |
+
},
|
| 202 |
+
{
|
| 203 |
+
"type": "string",
|
| 204 |
+
"description": "Start date range (YYYY-MM-DD)",
|
| 205 |
+
"name": "start_date",
|
| 206 |
+
"in": "query"
|
| 207 |
+
},
|
| 208 |
+
{
|
| 209 |
+
"type": "string",
|
| 210 |
+
"description": "End date range (YYYY-MM-DD)",
|
| 211 |
+
"name": "end_date",
|
| 212 |
+
"in": "query"
|
| 213 |
+
},
|
| 214 |
+
{
|
| 215 |
+
"type": "string",
|
| 216 |
+
"description": "Filter by city",
|
| 217 |
+
"name": "city",
|
| 218 |
+
"in": "query"
|
| 219 |
+
},
|
| 220 |
+
{
|
| 221 |
+
"type": "string",
|
| 222 |
+
"description": "Filter by class level (e.g., beginner)",
|
| 223 |
+
"name": "level",
|
| 224 |
+
"in": "query"
|
| 225 |
+
},
|
| 226 |
+
{
|
| 227 |
+
"type": "string",
|
| 228 |
+
"description": "Filter by class category (e.g., salsa)",
|
| 229 |
+
"name": "category",
|
| 230 |
+
"in": "query"
|
| 231 |
+
},
|
| 232 |
+
{
|
| 233 |
+
"type": "integer",
|
| 234 |
+
"description": "Page number (default: 1)",
|
| 235 |
+
"name": "page",
|
| 236 |
+
"in": "query"
|
| 237 |
+
},
|
| 238 |
+
{
|
| 239 |
+
"type": "integer",
|
| 240 |
+
"description": "Items per page (default: 10)",
|
| 241 |
+
"name": "limit",
|
| 242 |
+
"in": "query"
|
| 243 |
+
}
|
| 244 |
+
],
|
| 245 |
+
"responses": {
|
| 246 |
+
"200": {
|
| 247 |
+
"description": "OK",
|
| 248 |
+
"schema": {
|
| 249 |
+
"$ref": "#/definitions/dto.ScheduleListResponseWrapper"
|
| 250 |
+
}
|
| 251 |
+
},
|
| 252 |
+
"400": {
|
| 253 |
+
"description": "Bad Request",
|
| 254 |
+
"schema": {
|
| 255 |
+
"$ref": "#/definitions/dto.ErrorResponse"
|
| 256 |
+
}
|
| 257 |
+
}
|
| 258 |
+
}
|
| 259 |
+
}
|
| 260 |
+
}
|
| 261 |
+
},
|
| 262 |
+
"definitions": {
|
| 263 |
+
"dto.CategoryResponse": {
|
| 264 |
+
"type": "object",
|
| 265 |
+
"properties": {
|
| 266 |
+
"id": {
|
| 267 |
+
"type": "string"
|
| 268 |
+
},
|
| 269 |
+
"image_url": {
|
| 270 |
+
"type": "string"
|
| 271 |
+
},
|
| 272 |
+
"name": {
|
| 273 |
+
"type": "string"
|
| 274 |
+
}
|
| 275 |
+
}
|
| 276 |
+
},
|
| 277 |
+
"dto.CategoryResponseWrapper": {
|
| 278 |
+
"type": "object",
|
| 279 |
+
"properties": {
|
| 280 |
+
"data": {
|
| 281 |
+
"type": "array",
|
| 282 |
+
"items": {
|
| 283 |
+
"$ref": "#/definitions/dto.CategoryResponse"
|
| 284 |
+
}
|
| 285 |
+
},
|
| 286 |
+
"success": {
|
| 287 |
+
"type": "boolean"
|
| 288 |
+
}
|
| 289 |
+
}
|
| 290 |
+
},
|
| 291 |
+
"dto.ErrorResponse": {
|
| 292 |
+
"type": "object",
|
| 293 |
+
"properties": {
|
| 294 |
+
"errors": {},
|
| 295 |
+
"message": {},
|
| 296 |
+
"meta_data": {},
|
| 297 |
+
"status": {
|
| 298 |
+
"type": "string"
|
| 299 |
+
}
|
| 300 |
+
}
|
| 301 |
+
},
|
| 302 |
+
"dto.HomeBeginnerClassSection": {
|
| 303 |
+
"type": "object",
|
| 304 |
+
"properties": {
|
| 305 |
+
"id": {
|
| 306 |
+
"type": "string"
|
| 307 |
+
},
|
| 308 |
+
"image_url": {
|
| 309 |
+
"type": "string"
|
| 310 |
+
},
|
| 311 |
+
"name": {
|
| 312 |
+
"type": "string"
|
| 313 |
+
},
|
| 314 |
+
"type": {
|
| 315 |
+
"type": "string"
|
| 316 |
+
}
|
| 317 |
+
}
|
| 318 |
+
},
|
| 319 |
+
"dto.HomeDay": {
|
| 320 |
+
"type": "object",
|
| 321 |
+
"properties": {
|
| 322 |
+
"date": {
|
| 323 |
+
"type": "string"
|
| 324 |
+
},
|
| 325 |
+
"day_name_short": {
|
| 326 |
+
"type": "string"
|
| 327 |
+
},
|
| 328 |
+
"day_number": {
|
| 329 |
+
"type": "integer"
|
| 330 |
+
},
|
| 331 |
+
"has_schedule": {
|
| 332 |
+
"type": "boolean"
|
| 333 |
+
},
|
| 334 |
+
"is_selected": {
|
| 335 |
+
"type": "boolean"
|
| 336 |
+
}
|
| 337 |
+
}
|
| 338 |
+
},
|
| 339 |
+
"dto.HomeEventSection": {
|
| 340 |
+
"type": "object",
|
| 341 |
+
"properties": {
|
| 342 |
+
"id": {
|
| 343 |
+
"type": "string"
|
| 344 |
+
},
|
| 345 |
+
"image_url": {
|
| 346 |
+
"type": "string"
|
| 347 |
+
},
|
| 348 |
+
"name": {
|
| 349 |
+
"type": "string"
|
| 350 |
+
},
|
| 351 |
+
"type": {
|
| 352 |
+
"type": "string"
|
| 353 |
+
}
|
| 354 |
+
}
|
| 355 |
+
},
|
| 356 |
+
"dto.HomeHeroBanner": {
|
| 357 |
+
"type": "object",
|
| 358 |
+
"properties": {
|
| 359 |
+
"badge_text": {
|
| 360 |
+
"type": "string"
|
| 361 |
+
},
|
| 362 |
+
"cta_target_id": {
|
| 363 |
+
"type": "string"
|
| 364 |
+
},
|
| 365 |
+
"cta_text": {
|
| 366 |
+
"type": "string"
|
| 367 |
+
},
|
| 368 |
+
"cta_type": {
|
| 369 |
+
"type": "string"
|
| 370 |
+
},
|
| 371 |
+
"id": {
|
| 372 |
+
"type": "string"
|
| 373 |
+
},
|
| 374 |
+
"image_url": {
|
| 375 |
+
"type": "string"
|
| 376 |
+
},
|
| 377 |
+
"subtitle": {
|
| 378 |
+
"type": "string"
|
| 379 |
+
},
|
| 380 |
+
"title": {
|
| 381 |
+
"type": "string"
|
| 382 |
+
}
|
| 383 |
+
}
|
| 384 |
+
},
|
| 385 |
+
"dto.HomeNotifications": {
|
| 386 |
+
"type": "object",
|
| 387 |
+
"properties": {
|
| 388 |
+
"unread_count": {
|
| 389 |
+
"type": "integer"
|
| 390 |
+
}
|
| 391 |
+
}
|
| 392 |
+
},
|
| 393 |
+
"dto.HomePromoStrip": {
|
| 394 |
+
"type": "object",
|
| 395 |
+
"properties": {
|
| 396 |
+
"cta_target_id": {
|
| 397 |
+
"type": "string"
|
| 398 |
+
},
|
| 399 |
+
"cta_text": {
|
| 400 |
+
"type": "string"
|
| 401 |
+
},
|
| 402 |
+
"cta_type": {
|
| 403 |
+
"type": "string"
|
| 404 |
+
},
|
| 405 |
+
"id": {
|
| 406 |
+
"type": "string"
|
| 407 |
+
},
|
| 408 |
+
"text": {
|
| 409 |
+
"type": "string"
|
| 410 |
+
}
|
| 411 |
+
}
|
| 412 |
+
},
|
| 413 |
+
"dto.HomeResponse": {
|
| 414 |
+
"type": "object",
|
| 415 |
+
"properties": {
|
| 416 |
+
"beginner_class_sections": {
|
| 417 |
+
"type": "array",
|
| 418 |
+
"items": {
|
| 419 |
+
"$ref": "#/definitions/dto.HomeBeginnerClassSection"
|
| 420 |
+
}
|
| 421 |
+
},
|
| 422 |
+
"event_sections": {
|
| 423 |
+
"type": "array",
|
| 424 |
+
"items": {
|
| 425 |
+
"$ref": "#/definitions/dto.HomeEventSection"
|
| 426 |
+
}
|
| 427 |
+
},
|
| 428 |
+
"hero_banners": {
|
| 429 |
+
"type": "array",
|
| 430 |
+
"items": {
|
| 431 |
+
"$ref": "#/definitions/dto.HomeHeroBanner"
|
| 432 |
+
}
|
| 433 |
+
},
|
| 434 |
+
"notifications": {
|
| 435 |
+
"$ref": "#/definitions/dto.HomeNotifications"
|
| 436 |
+
},
|
| 437 |
+
"promo_strip": {
|
| 438 |
+
"$ref": "#/definitions/dto.HomePromoStrip"
|
| 439 |
+
},
|
| 440 |
+
"search": {
|
| 441 |
+
"$ref": "#/definitions/dto.HomeSearch"
|
| 442 |
+
},
|
| 443 |
+
"user": {
|
| 444 |
+
"$ref": "#/definitions/dto.HomeUser"
|
| 445 |
+
},
|
| 446 |
+
"week_schedule": {
|
| 447 |
+
"$ref": "#/definitions/dto.HomeWeekSchedule"
|
| 448 |
+
}
|
| 449 |
+
}
|
| 450 |
+
},
|
| 451 |
+
"dto.HomeResponseWrapper": {
|
| 452 |
+
"type": "object",
|
| 453 |
+
"properties": {
|
| 454 |
+
"data": {
|
| 455 |
+
"$ref": "#/definitions/dto.HomeResponse"
|
| 456 |
+
},
|
| 457 |
+
"message": {
|
| 458 |
+
"type": "string"
|
| 459 |
+
},
|
| 460 |
+
"success": {
|
| 461 |
+
"type": "boolean"
|
| 462 |
+
}
|
| 463 |
+
}
|
| 464 |
+
},
|
| 465 |
+
"dto.HomeSearch": {
|
| 466 |
+
"type": "object",
|
| 467 |
+
"properties": {
|
| 468 |
+
"placeholder": {
|
| 469 |
+
"type": "string"
|
| 470 |
+
}
|
| 471 |
+
}
|
| 472 |
+
},
|
| 473 |
+
"dto.HomeUser": {
|
| 474 |
+
"type": "object",
|
| 475 |
+
"properties": {
|
| 476 |
+
"avatar_url": {
|
| 477 |
+
"type": "string"
|
| 478 |
+
},
|
| 479 |
+
"first_name": {
|
| 480 |
+
"type": "string"
|
| 481 |
+
},
|
| 482 |
+
"full_name": {
|
| 483 |
+
"type": "string"
|
| 484 |
+
},
|
| 485 |
+
"id": {
|
| 486 |
+
"type": "string"
|
| 487 |
+
}
|
| 488 |
+
}
|
| 489 |
+
},
|
| 490 |
+
"dto.HomeWeekSchedule": {
|
| 491 |
+
"type": "object",
|
| 492 |
+
"properties": {
|
| 493 |
+
"days": {
|
| 494 |
+
"type": "array",
|
| 495 |
+
"items": {
|
| 496 |
+
"$ref": "#/definitions/dto.HomeDay"
|
| 497 |
+
}
|
| 498 |
+
},
|
| 499 |
+
"end_date": {
|
| 500 |
+
"type": "string"
|
| 501 |
+
},
|
| 502 |
+
"items": {
|
| 503 |
+
"type": "array",
|
| 504 |
+
"items": {
|
| 505 |
+
"$ref": "#/definitions/dto.ScheduleItem"
|
| 506 |
+
}
|
| 507 |
+
},
|
| 508 |
+
"selected_date": {
|
| 509 |
+
"type": "string"
|
| 510 |
+
},
|
| 511 |
+
"start_date": {
|
| 512 |
+
"type": "string"
|
| 513 |
+
},
|
| 514 |
+
"view_all_url": {
|
| 515 |
+
"type": "string"
|
| 516 |
+
},
|
| 517 |
+
"week_label": {
|
| 518 |
+
"type": "string"
|
| 519 |
+
}
|
| 520 |
+
}
|
| 521 |
+
},
|
| 522 |
+
"dto.PaginationMeta": {
|
| 523 |
+
"type": "object",
|
| 524 |
+
"properties": {
|
| 525 |
+
"limit": {
|
| 526 |
+
"type": "integer"
|
| 527 |
+
},
|
| 528 |
+
"page": {
|
| 529 |
+
"type": "integer"
|
| 530 |
+
},
|
| 531 |
+
"total_items": {
|
| 532 |
+
"type": "integer"
|
| 533 |
+
},
|
| 534 |
+
"total_pages": {
|
| 535 |
+
"type": "integer"
|
| 536 |
+
}
|
| 537 |
+
}
|
| 538 |
+
},
|
| 539 |
+
"dto.PriceData": {
|
| 540 |
+
"type": "object",
|
| 541 |
+
"properties": {
|
| 542 |
+
"amount": {
|
| 543 |
+
"type": "integer"
|
| 544 |
+
},
|
| 545 |
+
"currency": {
|
| 546 |
+
"type": "string"
|
| 547 |
+
},
|
| 548 |
+
"display": {
|
| 549 |
+
"type": "string"
|
| 550 |
+
}
|
| 551 |
+
}
|
| 552 |
+
},
|
| 553 |
+
"dto.ScheduleItem": {
|
| 554 |
+
"type": "object",
|
| 555 |
+
"properties": {
|
| 556 |
+
"city": {
|
| 557 |
+
"type": "string"
|
| 558 |
+
},
|
| 559 |
+
"class_id": {
|
| 560 |
+
"type": "string"
|
| 561 |
+
},
|
| 562 |
+
"date": {
|
| 563 |
+
"type": "string"
|
| 564 |
+
},
|
| 565 |
+
"display_time": {
|
| 566 |
+
"type": "string"
|
| 567 |
+
},
|
| 568 |
+
"end_time": {
|
| 569 |
+
"type": "string"
|
| 570 |
+
},
|
| 571 |
+
"is_booked": {
|
| 572 |
+
"type": "boolean"
|
| 573 |
+
},
|
| 574 |
+
"schedule_id": {
|
| 575 |
+
"type": "string"
|
| 576 |
+
},
|
| 577 |
+
"start_time": {
|
| 578 |
+
"type": "string"
|
| 579 |
+
},
|
| 580 |
+
"studio_name": {
|
| 581 |
+
"type": "string"
|
| 582 |
+
},
|
| 583 |
+
"teacher_name": {
|
| 584 |
+
"type": "string"
|
| 585 |
+
},
|
| 586 |
+
"title": {
|
| 587 |
+
"type": "string"
|
| 588 |
+
}
|
| 589 |
+
}
|
| 590 |
+
},
|
| 591 |
+
"dto.ScheduleListItem": {
|
| 592 |
+
"type": "object",
|
| 593 |
+
"properties": {
|
| 594 |
+
"address": {
|
| 595 |
+
"type": "string"
|
| 596 |
+
},
|
| 597 |
+
"available_slots": {
|
| 598 |
+
"type": "integer"
|
| 599 |
+
},
|
| 600 |
+
"category": {
|
| 601 |
+
"type": "string"
|
| 602 |
+
},
|
| 603 |
+
"city": {
|
| 604 |
+
"type": "string"
|
| 605 |
+
},
|
| 606 |
+
"class_id": {
|
| 607 |
+
"type": "string"
|
| 608 |
+
},
|
| 609 |
+
"date": {
|
| 610 |
+
"type": "string"
|
| 611 |
+
},
|
| 612 |
+
"display_time": {
|
| 613 |
+
"type": "string"
|
| 614 |
+
},
|
| 615 |
+
"end_time": {
|
| 616 |
+
"type": "string"
|
| 617 |
+
},
|
| 618 |
+
"is_booked": {
|
| 619 |
+
"type": "boolean"
|
| 620 |
+
},
|
| 621 |
+
"level": {
|
| 622 |
+
"type": "string"
|
| 623 |
+
},
|
| 624 |
+
"price": {
|
| 625 |
+
"$ref": "#/definitions/dto.PriceData"
|
| 626 |
+
},
|
| 627 |
+
"schedule_id": {
|
| 628 |
+
"type": "string"
|
| 629 |
+
},
|
| 630 |
+
"start_time": {
|
| 631 |
+
"type": "string"
|
| 632 |
+
},
|
| 633 |
+
"studio_name": {
|
| 634 |
+
"type": "string"
|
| 635 |
+
},
|
| 636 |
+
"teacher_name": {
|
| 637 |
+
"type": "string"
|
| 638 |
+
},
|
| 639 |
+
"thumbnail_url": {
|
| 640 |
+
"type": "string"
|
| 641 |
+
},
|
| 642 |
+
"title": {
|
| 643 |
+
"type": "string"
|
| 644 |
+
}
|
| 645 |
+
}
|
| 646 |
+
},
|
| 647 |
+
"dto.ScheduleListResponse": {
|
| 648 |
+
"type": "object",
|
| 649 |
+
"properties": {
|
| 650 |
+
"items": {
|
| 651 |
+
"type": "array",
|
| 652 |
+
"items": {
|
| 653 |
+
"$ref": "#/definitions/dto.ScheduleListItem"
|
| 654 |
+
}
|
| 655 |
+
},
|
| 656 |
+
"pagination": {
|
| 657 |
+
"$ref": "#/definitions/dto.PaginationMeta"
|
| 658 |
+
}
|
| 659 |
+
}
|
| 660 |
+
},
|
| 661 |
+
"dto.ScheduleListResponseWrapper": {
|
| 662 |
+
"type": "object",
|
| 663 |
+
"properties": {
|
| 664 |
+
"data": {
|
| 665 |
+
"$ref": "#/definitions/dto.ScheduleListResponse"
|
| 666 |
+
},
|
| 667 |
+
"message": {
|
| 668 |
+
"type": "string"
|
| 669 |
+
},
|
| 670 |
+
"success": {
|
| 671 |
+
"type": "boolean"
|
| 672 |
+
}
|
| 673 |
+
}
|
| 674 |
+
},
|
| 675 |
+
"dto.SuccessResponse-any": {
|
| 676 |
+
"type": "object",
|
| 677 |
+
"properties": {
|
| 678 |
+
"data": {},
|
| 679 |
+
"message": {},
|
| 680 |
+
"meta_data": {},
|
| 681 |
+
"status": {
|
| 682 |
+
"type": "string"
|
| 683 |
+
}
|
| 684 |
+
}
|
| 685 |
+
}
|
| 686 |
+
}
|
| 687 |
+
}`
|
| 688 |
+
|
| 689 |
+
// SwaggerInfo holds exported Swagger Info so clients can modify it
|
| 690 |
+
var SwaggerInfo = &swag.Spec{
|
| 691 |
+
Version: "",
|
| 692 |
+
Host: "",
|
| 693 |
+
BasePath: "",
|
| 694 |
+
Schemes: []string{},
|
| 695 |
+
Title: "",
|
| 696 |
+
Description: "",
|
| 697 |
+
InfoInstanceName: "swagger",
|
| 698 |
+
SwaggerTemplate: docTemplate,
|
| 699 |
+
LeftDelim: "{{",
|
| 700 |
+
RightDelim: "}}",
|
| 701 |
+
}
|
| 702 |
+
|
| 703 |
+
func init() {
|
| 704 |
+
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
|
| 705 |
+
}
|
docs/swagger.json
ADDED
|
@@ -0,0 +1,676 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"swagger": "2.0",
|
| 3 |
+
"info": {
|
| 4 |
+
"contact": {}
|
| 5 |
+
},
|
| 6 |
+
"paths": {
|
| 7 |
+
"/api/v1/class-categories": {
|
| 8 |
+
"get": {
|
| 9 |
+
"description": "Get master list of class categories",
|
| 10 |
+
"consumes": [
|
| 11 |
+
"application/json"
|
| 12 |
+
],
|
| 13 |
+
"produces": [
|
| 14 |
+
"application/json"
|
| 15 |
+
],
|
| 16 |
+
"tags": [
|
| 17 |
+
"Master Data"
|
| 18 |
+
],
|
| 19 |
+
"summary": "Get Class Categories",
|
| 20 |
+
"responses": {
|
| 21 |
+
"200": {
|
| 22 |
+
"description": "OK",
|
| 23 |
+
"schema": {
|
| 24 |
+
"$ref": "#/definitions/dto.CategoryResponseWrapper"
|
| 25 |
+
}
|
| 26 |
+
},
|
| 27 |
+
"400": {
|
| 28 |
+
"description": "Bad Request",
|
| 29 |
+
"schema": {
|
| 30 |
+
"$ref": "#/definitions/dto.ErrorResponse"
|
| 31 |
+
}
|
| 32 |
+
}
|
| 33 |
+
}
|
| 34 |
+
}
|
| 35 |
+
},
|
| 36 |
+
"/api/v1/event-categories": {
|
| 37 |
+
"get": {
|
| 38 |
+
"description": "Get master list of event categories",
|
| 39 |
+
"consumes": [
|
| 40 |
+
"application/json"
|
| 41 |
+
],
|
| 42 |
+
"produces": [
|
| 43 |
+
"application/json"
|
| 44 |
+
],
|
| 45 |
+
"tags": [
|
| 46 |
+
"Master Data"
|
| 47 |
+
],
|
| 48 |
+
"summary": "Get Event Categories",
|
| 49 |
+
"responses": {
|
| 50 |
+
"200": {
|
| 51 |
+
"description": "OK",
|
| 52 |
+
"schema": {
|
| 53 |
+
"$ref": "#/definitions/dto.CategoryResponseWrapper"
|
| 54 |
+
}
|
| 55 |
+
},
|
| 56 |
+
"400": {
|
| 57 |
+
"description": "Bad Request",
|
| 58 |
+
"schema": {
|
| 59 |
+
"$ref": "#/definitions/dto.ErrorResponse"
|
| 60 |
+
}
|
| 61 |
+
}
|
| 62 |
+
}
|
| 63 |
+
}
|
| 64 |
+
},
|
| 65 |
+
"/api/v1/home": {
|
| 66 |
+
"get": {
|
| 67 |
+
"security": [
|
| 68 |
+
{
|
| 69 |
+
"BearerAuth": []
|
| 70 |
+
}
|
| 71 |
+
],
|
| 72 |
+
"description": "Return all sections needed by the home page in one aggregated response.",
|
| 73 |
+
"consumes": [
|
| 74 |
+
"application/json"
|
| 75 |
+
],
|
| 76 |
+
"produces": [
|
| 77 |
+
"application/json"
|
| 78 |
+
],
|
| 79 |
+
"tags": [
|
| 80 |
+
"Home Dashboard"
|
| 81 |
+
],
|
| 82 |
+
"summary": "Get Home Page Data",
|
| 83 |
+
"parameters": [
|
| 84 |
+
{
|
| 85 |
+
"type": "string",
|
| 86 |
+
"description": "Selected date for schedule card section (YYYY-MM-DD)",
|
| 87 |
+
"name": "date",
|
| 88 |
+
"in": "query"
|
| 89 |
+
},
|
| 90 |
+
{
|
| 91 |
+
"type": "number",
|
| 92 |
+
"description": "User latitude",
|
| 93 |
+
"name": "latitude",
|
| 94 |
+
"in": "query"
|
| 95 |
+
},
|
| 96 |
+
{
|
| 97 |
+
"type": "number",
|
| 98 |
+
"description": "User longitude",
|
| 99 |
+
"name": "longitude",
|
| 100 |
+
"in": "query"
|
| 101 |
+
},
|
| 102 |
+
{
|
| 103 |
+
"type": "string",
|
| 104 |
+
"description": "User city",
|
| 105 |
+
"name": "city",
|
| 106 |
+
"in": "query"
|
| 107 |
+
}
|
| 108 |
+
],
|
| 109 |
+
"responses": {
|
| 110 |
+
"200": {
|
| 111 |
+
"description": "OK",
|
| 112 |
+
"schema": {
|
| 113 |
+
"$ref": "#/definitions/dto.HomeResponseWrapper"
|
| 114 |
+
}
|
| 115 |
+
},
|
| 116 |
+
"400": {
|
| 117 |
+
"description": "Bad Request",
|
| 118 |
+
"schema": {
|
| 119 |
+
"$ref": "#/definitions/dto.ErrorResponse"
|
| 120 |
+
}
|
| 121 |
+
}
|
| 122 |
+
}
|
| 123 |
+
}
|
| 124 |
+
},
|
| 125 |
+
"/api/v1/payment/callback": {
|
| 126 |
+
"post": {
|
| 127 |
+
"description": "Receive and process payment status updates from Xendit",
|
| 128 |
+
"consumes": [
|
| 129 |
+
"application/json"
|
| 130 |
+
],
|
| 131 |
+
"produces": [
|
| 132 |
+
"application/json"
|
| 133 |
+
],
|
| 134 |
+
"tags": [
|
| 135 |
+
"Payment"
|
| 136 |
+
],
|
| 137 |
+
"summary": "Handle Xendit Payment Callback",
|
| 138 |
+
"parameters": [
|
| 139 |
+
{
|
| 140 |
+
"description": "Xendit Callback Payload",
|
| 141 |
+
"name": "request",
|
| 142 |
+
"in": "body",
|
| 143 |
+
"required": true,
|
| 144 |
+
"schema": {
|
| 145 |
+
"type": "object",
|
| 146 |
+
"additionalProperties": true
|
| 147 |
+
}
|
| 148 |
+
}
|
| 149 |
+
],
|
| 150 |
+
"responses": {
|
| 151 |
+
"200": {
|
| 152 |
+
"description": "OK",
|
| 153 |
+
"schema": {
|
| 154 |
+
"$ref": "#/definitions/dto.SuccessResponse-any"
|
| 155 |
+
}
|
| 156 |
+
},
|
| 157 |
+
"400": {
|
| 158 |
+
"description": "Bad Request",
|
| 159 |
+
"schema": {
|
| 160 |
+
"$ref": "#/definitions/dto.ErrorResponse"
|
| 161 |
+
}
|
| 162 |
+
}
|
| 163 |
+
}
|
| 164 |
+
}
|
| 165 |
+
},
|
| 166 |
+
"/api/v1/schedules": {
|
| 167 |
+
"get": {
|
| 168 |
+
"security": [
|
| 169 |
+
{
|
| 170 |
+
"BearerAuth": []
|
| 171 |
+
}
|
| 172 |
+
],
|
| 173 |
+
"description": "Get a list of class or event schedules based on date range, city, filters, etc.",
|
| 174 |
+
"consumes": [
|
| 175 |
+
"application/json"
|
| 176 |
+
],
|
| 177 |
+
"produces": [
|
| 178 |
+
"application/json"
|
| 179 |
+
],
|
| 180 |
+
"tags": [
|
| 181 |
+
"Home Dashboard"
|
| 182 |
+
],
|
| 183 |
+
"summary": "Get Schedule List",
|
| 184 |
+
"parameters": [
|
| 185 |
+
{
|
| 186 |
+
"type": "string",
|
| 187 |
+
"description": "Exact date (YYYY-MM-DD)",
|
| 188 |
+
"name": "date",
|
| 189 |
+
"in": "query"
|
| 190 |
+
},
|
| 191 |
+
{
|
| 192 |
+
"type": "string",
|
| 193 |
+
"description": "Start date range (YYYY-MM-DD)",
|
| 194 |
+
"name": "start_date",
|
| 195 |
+
"in": "query"
|
| 196 |
+
},
|
| 197 |
+
{
|
| 198 |
+
"type": "string",
|
| 199 |
+
"description": "End date range (YYYY-MM-DD)",
|
| 200 |
+
"name": "end_date",
|
| 201 |
+
"in": "query"
|
| 202 |
+
},
|
| 203 |
+
{
|
| 204 |
+
"type": "string",
|
| 205 |
+
"description": "Filter by city",
|
| 206 |
+
"name": "city",
|
| 207 |
+
"in": "query"
|
| 208 |
+
},
|
| 209 |
+
{
|
| 210 |
+
"type": "string",
|
| 211 |
+
"description": "Filter by class level (e.g., beginner)",
|
| 212 |
+
"name": "level",
|
| 213 |
+
"in": "query"
|
| 214 |
+
},
|
| 215 |
+
{
|
| 216 |
+
"type": "string",
|
| 217 |
+
"description": "Filter by class category (e.g., salsa)",
|
| 218 |
+
"name": "category",
|
| 219 |
+
"in": "query"
|
| 220 |
+
},
|
| 221 |
+
{
|
| 222 |
+
"type": "integer",
|
| 223 |
+
"description": "Page number (default: 1)",
|
| 224 |
+
"name": "page",
|
| 225 |
+
"in": "query"
|
| 226 |
+
},
|
| 227 |
+
{
|
| 228 |
+
"type": "integer",
|
| 229 |
+
"description": "Items per page (default: 10)",
|
| 230 |
+
"name": "limit",
|
| 231 |
+
"in": "query"
|
| 232 |
+
}
|
| 233 |
+
],
|
| 234 |
+
"responses": {
|
| 235 |
+
"200": {
|
| 236 |
+
"description": "OK",
|
| 237 |
+
"schema": {
|
| 238 |
+
"$ref": "#/definitions/dto.ScheduleListResponseWrapper"
|
| 239 |
+
}
|
| 240 |
+
},
|
| 241 |
+
"400": {
|
| 242 |
+
"description": "Bad Request",
|
| 243 |
+
"schema": {
|
| 244 |
+
"$ref": "#/definitions/dto.ErrorResponse"
|
| 245 |
+
}
|
| 246 |
+
}
|
| 247 |
+
}
|
| 248 |
+
}
|
| 249 |
+
}
|
| 250 |
+
},
|
| 251 |
+
"definitions": {
|
| 252 |
+
"dto.CategoryResponse": {
|
| 253 |
+
"type": "object",
|
| 254 |
+
"properties": {
|
| 255 |
+
"id": {
|
| 256 |
+
"type": "string"
|
| 257 |
+
},
|
| 258 |
+
"image_url": {
|
| 259 |
+
"type": "string"
|
| 260 |
+
},
|
| 261 |
+
"name": {
|
| 262 |
+
"type": "string"
|
| 263 |
+
}
|
| 264 |
+
}
|
| 265 |
+
},
|
| 266 |
+
"dto.CategoryResponseWrapper": {
|
| 267 |
+
"type": "object",
|
| 268 |
+
"properties": {
|
| 269 |
+
"data": {
|
| 270 |
+
"type": "array",
|
| 271 |
+
"items": {
|
| 272 |
+
"$ref": "#/definitions/dto.CategoryResponse"
|
| 273 |
+
}
|
| 274 |
+
},
|
| 275 |
+
"success": {
|
| 276 |
+
"type": "boolean"
|
| 277 |
+
}
|
| 278 |
+
}
|
| 279 |
+
},
|
| 280 |
+
"dto.ErrorResponse": {
|
| 281 |
+
"type": "object",
|
| 282 |
+
"properties": {
|
| 283 |
+
"errors": {},
|
| 284 |
+
"message": {},
|
| 285 |
+
"meta_data": {},
|
| 286 |
+
"status": {
|
| 287 |
+
"type": "string"
|
| 288 |
+
}
|
| 289 |
+
}
|
| 290 |
+
},
|
| 291 |
+
"dto.HomeBeginnerClassSection": {
|
| 292 |
+
"type": "object",
|
| 293 |
+
"properties": {
|
| 294 |
+
"id": {
|
| 295 |
+
"type": "string"
|
| 296 |
+
},
|
| 297 |
+
"image_url": {
|
| 298 |
+
"type": "string"
|
| 299 |
+
},
|
| 300 |
+
"name": {
|
| 301 |
+
"type": "string"
|
| 302 |
+
},
|
| 303 |
+
"type": {
|
| 304 |
+
"type": "string"
|
| 305 |
+
}
|
| 306 |
+
}
|
| 307 |
+
},
|
| 308 |
+
"dto.HomeDay": {
|
| 309 |
+
"type": "object",
|
| 310 |
+
"properties": {
|
| 311 |
+
"date": {
|
| 312 |
+
"type": "string"
|
| 313 |
+
},
|
| 314 |
+
"day_name_short": {
|
| 315 |
+
"type": "string"
|
| 316 |
+
},
|
| 317 |
+
"day_number": {
|
| 318 |
+
"type": "integer"
|
| 319 |
+
},
|
| 320 |
+
"has_schedule": {
|
| 321 |
+
"type": "boolean"
|
| 322 |
+
},
|
| 323 |
+
"is_selected": {
|
| 324 |
+
"type": "boolean"
|
| 325 |
+
}
|
| 326 |
+
}
|
| 327 |
+
},
|
| 328 |
+
"dto.HomeEventSection": {
|
| 329 |
+
"type": "object",
|
| 330 |
+
"properties": {
|
| 331 |
+
"id": {
|
| 332 |
+
"type": "string"
|
| 333 |
+
},
|
| 334 |
+
"image_url": {
|
| 335 |
+
"type": "string"
|
| 336 |
+
},
|
| 337 |
+
"name": {
|
| 338 |
+
"type": "string"
|
| 339 |
+
},
|
| 340 |
+
"type": {
|
| 341 |
+
"type": "string"
|
| 342 |
+
}
|
| 343 |
+
}
|
| 344 |
+
},
|
| 345 |
+
"dto.HomeHeroBanner": {
|
| 346 |
+
"type": "object",
|
| 347 |
+
"properties": {
|
| 348 |
+
"badge_text": {
|
| 349 |
+
"type": "string"
|
| 350 |
+
},
|
| 351 |
+
"cta_target_id": {
|
| 352 |
+
"type": "string"
|
| 353 |
+
},
|
| 354 |
+
"cta_text": {
|
| 355 |
+
"type": "string"
|
| 356 |
+
},
|
| 357 |
+
"cta_type": {
|
| 358 |
+
"type": "string"
|
| 359 |
+
},
|
| 360 |
+
"id": {
|
| 361 |
+
"type": "string"
|
| 362 |
+
},
|
| 363 |
+
"image_url": {
|
| 364 |
+
"type": "string"
|
| 365 |
+
},
|
| 366 |
+
"subtitle": {
|
| 367 |
+
"type": "string"
|
| 368 |
+
},
|
| 369 |
+
"title": {
|
| 370 |
+
"type": "string"
|
| 371 |
+
}
|
| 372 |
+
}
|
| 373 |
+
},
|
| 374 |
+
"dto.HomeNotifications": {
|
| 375 |
+
"type": "object",
|
| 376 |
+
"properties": {
|
| 377 |
+
"unread_count": {
|
| 378 |
+
"type": "integer"
|
| 379 |
+
}
|
| 380 |
+
}
|
| 381 |
+
},
|
| 382 |
+
"dto.HomePromoStrip": {
|
| 383 |
+
"type": "object",
|
| 384 |
+
"properties": {
|
| 385 |
+
"cta_target_id": {
|
| 386 |
+
"type": "string"
|
| 387 |
+
},
|
| 388 |
+
"cta_text": {
|
| 389 |
+
"type": "string"
|
| 390 |
+
},
|
| 391 |
+
"cta_type": {
|
| 392 |
+
"type": "string"
|
| 393 |
+
},
|
| 394 |
+
"id": {
|
| 395 |
+
"type": "string"
|
| 396 |
+
},
|
| 397 |
+
"text": {
|
| 398 |
+
"type": "string"
|
| 399 |
+
}
|
| 400 |
+
}
|
| 401 |
+
},
|
| 402 |
+
"dto.HomeResponse": {
|
| 403 |
+
"type": "object",
|
| 404 |
+
"properties": {
|
| 405 |
+
"beginner_class_sections": {
|
| 406 |
+
"type": "array",
|
| 407 |
+
"items": {
|
| 408 |
+
"$ref": "#/definitions/dto.HomeBeginnerClassSection"
|
| 409 |
+
}
|
| 410 |
+
},
|
| 411 |
+
"event_sections": {
|
| 412 |
+
"type": "array",
|
| 413 |
+
"items": {
|
| 414 |
+
"$ref": "#/definitions/dto.HomeEventSection"
|
| 415 |
+
}
|
| 416 |
+
},
|
| 417 |
+
"hero_banners": {
|
| 418 |
+
"type": "array",
|
| 419 |
+
"items": {
|
| 420 |
+
"$ref": "#/definitions/dto.HomeHeroBanner"
|
| 421 |
+
}
|
| 422 |
+
},
|
| 423 |
+
"notifications": {
|
| 424 |
+
"$ref": "#/definitions/dto.HomeNotifications"
|
| 425 |
+
},
|
| 426 |
+
"promo_strip": {
|
| 427 |
+
"$ref": "#/definitions/dto.HomePromoStrip"
|
| 428 |
+
},
|
| 429 |
+
"search": {
|
| 430 |
+
"$ref": "#/definitions/dto.HomeSearch"
|
| 431 |
+
},
|
| 432 |
+
"user": {
|
| 433 |
+
"$ref": "#/definitions/dto.HomeUser"
|
| 434 |
+
},
|
| 435 |
+
"week_schedule": {
|
| 436 |
+
"$ref": "#/definitions/dto.HomeWeekSchedule"
|
| 437 |
+
}
|
| 438 |
+
}
|
| 439 |
+
},
|
| 440 |
+
"dto.HomeResponseWrapper": {
|
| 441 |
+
"type": "object",
|
| 442 |
+
"properties": {
|
| 443 |
+
"data": {
|
| 444 |
+
"$ref": "#/definitions/dto.HomeResponse"
|
| 445 |
+
},
|
| 446 |
+
"message": {
|
| 447 |
+
"type": "string"
|
| 448 |
+
},
|
| 449 |
+
"success": {
|
| 450 |
+
"type": "boolean"
|
| 451 |
+
}
|
| 452 |
+
}
|
| 453 |
+
},
|
| 454 |
+
"dto.HomeSearch": {
|
| 455 |
+
"type": "object",
|
| 456 |
+
"properties": {
|
| 457 |
+
"placeholder": {
|
| 458 |
+
"type": "string"
|
| 459 |
+
}
|
| 460 |
+
}
|
| 461 |
+
},
|
| 462 |
+
"dto.HomeUser": {
|
| 463 |
+
"type": "object",
|
| 464 |
+
"properties": {
|
| 465 |
+
"avatar_url": {
|
| 466 |
+
"type": "string"
|
| 467 |
+
},
|
| 468 |
+
"first_name": {
|
| 469 |
+
"type": "string"
|
| 470 |
+
},
|
| 471 |
+
"full_name": {
|
| 472 |
+
"type": "string"
|
| 473 |
+
},
|
| 474 |
+
"id": {
|
| 475 |
+
"type": "string"
|
| 476 |
+
}
|
| 477 |
+
}
|
| 478 |
+
},
|
| 479 |
+
"dto.HomeWeekSchedule": {
|
| 480 |
+
"type": "object",
|
| 481 |
+
"properties": {
|
| 482 |
+
"days": {
|
| 483 |
+
"type": "array",
|
| 484 |
+
"items": {
|
| 485 |
+
"$ref": "#/definitions/dto.HomeDay"
|
| 486 |
+
}
|
| 487 |
+
},
|
| 488 |
+
"end_date": {
|
| 489 |
+
"type": "string"
|
| 490 |
+
},
|
| 491 |
+
"items": {
|
| 492 |
+
"type": "array",
|
| 493 |
+
"items": {
|
| 494 |
+
"$ref": "#/definitions/dto.ScheduleItem"
|
| 495 |
+
}
|
| 496 |
+
},
|
| 497 |
+
"selected_date": {
|
| 498 |
+
"type": "string"
|
| 499 |
+
},
|
| 500 |
+
"start_date": {
|
| 501 |
+
"type": "string"
|
| 502 |
+
},
|
| 503 |
+
"view_all_url": {
|
| 504 |
+
"type": "string"
|
| 505 |
+
},
|
| 506 |
+
"week_label": {
|
| 507 |
+
"type": "string"
|
| 508 |
+
}
|
| 509 |
+
}
|
| 510 |
+
},
|
| 511 |
+
"dto.PaginationMeta": {
|
| 512 |
+
"type": "object",
|
| 513 |
+
"properties": {
|
| 514 |
+
"limit": {
|
| 515 |
+
"type": "integer"
|
| 516 |
+
},
|
| 517 |
+
"page": {
|
| 518 |
+
"type": "integer"
|
| 519 |
+
},
|
| 520 |
+
"total_items": {
|
| 521 |
+
"type": "integer"
|
| 522 |
+
},
|
| 523 |
+
"total_pages": {
|
| 524 |
+
"type": "integer"
|
| 525 |
+
}
|
| 526 |
+
}
|
| 527 |
+
},
|
| 528 |
+
"dto.PriceData": {
|
| 529 |
+
"type": "object",
|
| 530 |
+
"properties": {
|
| 531 |
+
"amount": {
|
| 532 |
+
"type": "integer"
|
| 533 |
+
},
|
| 534 |
+
"currency": {
|
| 535 |
+
"type": "string"
|
| 536 |
+
},
|
| 537 |
+
"display": {
|
| 538 |
+
"type": "string"
|
| 539 |
+
}
|
| 540 |
+
}
|
| 541 |
+
},
|
| 542 |
+
"dto.ScheduleItem": {
|
| 543 |
+
"type": "object",
|
| 544 |
+
"properties": {
|
| 545 |
+
"city": {
|
| 546 |
+
"type": "string"
|
| 547 |
+
},
|
| 548 |
+
"class_id": {
|
| 549 |
+
"type": "string"
|
| 550 |
+
},
|
| 551 |
+
"date": {
|
| 552 |
+
"type": "string"
|
| 553 |
+
},
|
| 554 |
+
"display_time": {
|
| 555 |
+
"type": "string"
|
| 556 |
+
},
|
| 557 |
+
"end_time": {
|
| 558 |
+
"type": "string"
|
| 559 |
+
},
|
| 560 |
+
"is_booked": {
|
| 561 |
+
"type": "boolean"
|
| 562 |
+
},
|
| 563 |
+
"schedule_id": {
|
| 564 |
+
"type": "string"
|
| 565 |
+
},
|
| 566 |
+
"start_time": {
|
| 567 |
+
"type": "string"
|
| 568 |
+
},
|
| 569 |
+
"studio_name": {
|
| 570 |
+
"type": "string"
|
| 571 |
+
},
|
| 572 |
+
"teacher_name": {
|
| 573 |
+
"type": "string"
|
| 574 |
+
},
|
| 575 |
+
"title": {
|
| 576 |
+
"type": "string"
|
| 577 |
+
}
|
| 578 |
+
}
|
| 579 |
+
},
|
| 580 |
+
"dto.ScheduleListItem": {
|
| 581 |
+
"type": "object",
|
| 582 |
+
"properties": {
|
| 583 |
+
"address": {
|
| 584 |
+
"type": "string"
|
| 585 |
+
},
|
| 586 |
+
"available_slots": {
|
| 587 |
+
"type": "integer"
|
| 588 |
+
},
|
| 589 |
+
"category": {
|
| 590 |
+
"type": "string"
|
| 591 |
+
},
|
| 592 |
+
"city": {
|
| 593 |
+
"type": "string"
|
| 594 |
+
},
|
| 595 |
+
"class_id": {
|
| 596 |
+
"type": "string"
|
| 597 |
+
},
|
| 598 |
+
"date": {
|
| 599 |
+
"type": "string"
|
| 600 |
+
},
|
| 601 |
+
"display_time": {
|
| 602 |
+
"type": "string"
|
| 603 |
+
},
|
| 604 |
+
"end_time": {
|
| 605 |
+
"type": "string"
|
| 606 |
+
},
|
| 607 |
+
"is_booked": {
|
| 608 |
+
"type": "boolean"
|
| 609 |
+
},
|
| 610 |
+
"level": {
|
| 611 |
+
"type": "string"
|
| 612 |
+
},
|
| 613 |
+
"price": {
|
| 614 |
+
"$ref": "#/definitions/dto.PriceData"
|
| 615 |
+
},
|
| 616 |
+
"schedule_id": {
|
| 617 |
+
"type": "string"
|
| 618 |
+
},
|
| 619 |
+
"start_time": {
|
| 620 |
+
"type": "string"
|
| 621 |
+
},
|
| 622 |
+
"studio_name": {
|
| 623 |
+
"type": "string"
|
| 624 |
+
},
|
| 625 |
+
"teacher_name": {
|
| 626 |
+
"type": "string"
|
| 627 |
+
},
|
| 628 |
+
"thumbnail_url": {
|
| 629 |
+
"type": "string"
|
| 630 |
+
},
|
| 631 |
+
"title": {
|
| 632 |
+
"type": "string"
|
| 633 |
+
}
|
| 634 |
+
}
|
| 635 |
+
},
|
| 636 |
+
"dto.ScheduleListResponse": {
|
| 637 |
+
"type": "object",
|
| 638 |
+
"properties": {
|
| 639 |
+
"items": {
|
| 640 |
+
"type": "array",
|
| 641 |
+
"items": {
|
| 642 |
+
"$ref": "#/definitions/dto.ScheduleListItem"
|
| 643 |
+
}
|
| 644 |
+
},
|
| 645 |
+
"pagination": {
|
| 646 |
+
"$ref": "#/definitions/dto.PaginationMeta"
|
| 647 |
+
}
|
| 648 |
+
}
|
| 649 |
+
},
|
| 650 |
+
"dto.ScheduleListResponseWrapper": {
|
| 651 |
+
"type": "object",
|
| 652 |
+
"properties": {
|
| 653 |
+
"data": {
|
| 654 |
+
"$ref": "#/definitions/dto.ScheduleListResponse"
|
| 655 |
+
},
|
| 656 |
+
"message": {
|
| 657 |
+
"type": "string"
|
| 658 |
+
},
|
| 659 |
+
"success": {
|
| 660 |
+
"type": "boolean"
|
| 661 |
+
}
|
| 662 |
+
}
|
| 663 |
+
},
|
| 664 |
+
"dto.SuccessResponse-any": {
|
| 665 |
+
"type": "object",
|
| 666 |
+
"properties": {
|
| 667 |
+
"data": {},
|
| 668 |
+
"message": {},
|
| 669 |
+
"meta_data": {},
|
| 670 |
+
"status": {
|
| 671 |
+
"type": "string"
|
| 672 |
+
}
|
| 673 |
+
}
|
| 674 |
+
}
|
| 675 |
+
}
|
| 676 |
+
}
|
docs/swagger.yaml
ADDED
|
@@ -0,0 +1,440 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
definitions:
|
| 2 |
+
dto.CategoryResponse:
|
| 3 |
+
properties:
|
| 4 |
+
id:
|
| 5 |
+
type: string
|
| 6 |
+
image_url:
|
| 7 |
+
type: string
|
| 8 |
+
name:
|
| 9 |
+
type: string
|
| 10 |
+
type: object
|
| 11 |
+
dto.CategoryResponseWrapper:
|
| 12 |
+
properties:
|
| 13 |
+
data:
|
| 14 |
+
items:
|
| 15 |
+
$ref: '#/definitions/dto.CategoryResponse'
|
| 16 |
+
type: array
|
| 17 |
+
success:
|
| 18 |
+
type: boolean
|
| 19 |
+
type: object
|
| 20 |
+
dto.ErrorResponse:
|
| 21 |
+
properties:
|
| 22 |
+
errors: {}
|
| 23 |
+
message: {}
|
| 24 |
+
meta_data: {}
|
| 25 |
+
status:
|
| 26 |
+
type: string
|
| 27 |
+
type: object
|
| 28 |
+
dto.HomeBeginnerClassSection:
|
| 29 |
+
properties:
|
| 30 |
+
id:
|
| 31 |
+
type: string
|
| 32 |
+
image_url:
|
| 33 |
+
type: string
|
| 34 |
+
name:
|
| 35 |
+
type: string
|
| 36 |
+
type:
|
| 37 |
+
type: string
|
| 38 |
+
type: object
|
| 39 |
+
dto.HomeDay:
|
| 40 |
+
properties:
|
| 41 |
+
date:
|
| 42 |
+
type: string
|
| 43 |
+
day_name_short:
|
| 44 |
+
type: string
|
| 45 |
+
day_number:
|
| 46 |
+
type: integer
|
| 47 |
+
has_schedule:
|
| 48 |
+
type: boolean
|
| 49 |
+
is_selected:
|
| 50 |
+
type: boolean
|
| 51 |
+
type: object
|
| 52 |
+
dto.HomeEventSection:
|
| 53 |
+
properties:
|
| 54 |
+
id:
|
| 55 |
+
type: string
|
| 56 |
+
image_url:
|
| 57 |
+
type: string
|
| 58 |
+
name:
|
| 59 |
+
type: string
|
| 60 |
+
type:
|
| 61 |
+
type: string
|
| 62 |
+
type: object
|
| 63 |
+
dto.HomeHeroBanner:
|
| 64 |
+
properties:
|
| 65 |
+
badge_text:
|
| 66 |
+
type: string
|
| 67 |
+
cta_target_id:
|
| 68 |
+
type: string
|
| 69 |
+
cta_text:
|
| 70 |
+
type: string
|
| 71 |
+
cta_type:
|
| 72 |
+
type: string
|
| 73 |
+
id:
|
| 74 |
+
type: string
|
| 75 |
+
image_url:
|
| 76 |
+
type: string
|
| 77 |
+
subtitle:
|
| 78 |
+
type: string
|
| 79 |
+
title:
|
| 80 |
+
type: string
|
| 81 |
+
type: object
|
| 82 |
+
dto.HomeNotifications:
|
| 83 |
+
properties:
|
| 84 |
+
unread_count:
|
| 85 |
+
type: integer
|
| 86 |
+
type: object
|
| 87 |
+
dto.HomePromoStrip:
|
| 88 |
+
properties:
|
| 89 |
+
cta_target_id:
|
| 90 |
+
type: string
|
| 91 |
+
cta_text:
|
| 92 |
+
type: string
|
| 93 |
+
cta_type:
|
| 94 |
+
type: string
|
| 95 |
+
id:
|
| 96 |
+
type: string
|
| 97 |
+
text:
|
| 98 |
+
type: string
|
| 99 |
+
type: object
|
| 100 |
+
dto.HomeResponse:
|
| 101 |
+
properties:
|
| 102 |
+
beginner_class_sections:
|
| 103 |
+
items:
|
| 104 |
+
$ref: '#/definitions/dto.HomeBeginnerClassSection'
|
| 105 |
+
type: array
|
| 106 |
+
event_sections:
|
| 107 |
+
items:
|
| 108 |
+
$ref: '#/definitions/dto.HomeEventSection'
|
| 109 |
+
type: array
|
| 110 |
+
hero_banners:
|
| 111 |
+
items:
|
| 112 |
+
$ref: '#/definitions/dto.HomeHeroBanner'
|
| 113 |
+
type: array
|
| 114 |
+
notifications:
|
| 115 |
+
$ref: '#/definitions/dto.HomeNotifications'
|
| 116 |
+
promo_strip:
|
| 117 |
+
$ref: '#/definitions/dto.HomePromoStrip'
|
| 118 |
+
search:
|
| 119 |
+
$ref: '#/definitions/dto.HomeSearch'
|
| 120 |
+
user:
|
| 121 |
+
$ref: '#/definitions/dto.HomeUser'
|
| 122 |
+
week_schedule:
|
| 123 |
+
$ref: '#/definitions/dto.HomeWeekSchedule'
|
| 124 |
+
type: object
|
| 125 |
+
dto.HomeResponseWrapper:
|
| 126 |
+
properties:
|
| 127 |
+
data:
|
| 128 |
+
$ref: '#/definitions/dto.HomeResponse'
|
| 129 |
+
message:
|
| 130 |
+
type: string
|
| 131 |
+
success:
|
| 132 |
+
type: boolean
|
| 133 |
+
type: object
|
| 134 |
+
dto.HomeSearch:
|
| 135 |
+
properties:
|
| 136 |
+
placeholder:
|
| 137 |
+
type: string
|
| 138 |
+
type: object
|
| 139 |
+
dto.HomeUser:
|
| 140 |
+
properties:
|
| 141 |
+
avatar_url:
|
| 142 |
+
type: string
|
| 143 |
+
first_name:
|
| 144 |
+
type: string
|
| 145 |
+
full_name:
|
| 146 |
+
type: string
|
| 147 |
+
id:
|
| 148 |
+
type: string
|
| 149 |
+
type: object
|
| 150 |
+
dto.HomeWeekSchedule:
|
| 151 |
+
properties:
|
| 152 |
+
days:
|
| 153 |
+
items:
|
| 154 |
+
$ref: '#/definitions/dto.HomeDay'
|
| 155 |
+
type: array
|
| 156 |
+
end_date:
|
| 157 |
+
type: string
|
| 158 |
+
items:
|
| 159 |
+
items:
|
| 160 |
+
$ref: '#/definitions/dto.ScheduleItem'
|
| 161 |
+
type: array
|
| 162 |
+
selected_date:
|
| 163 |
+
type: string
|
| 164 |
+
start_date:
|
| 165 |
+
type: string
|
| 166 |
+
view_all_url:
|
| 167 |
+
type: string
|
| 168 |
+
week_label:
|
| 169 |
+
type: string
|
| 170 |
+
type: object
|
| 171 |
+
dto.PaginationMeta:
|
| 172 |
+
properties:
|
| 173 |
+
limit:
|
| 174 |
+
type: integer
|
| 175 |
+
page:
|
| 176 |
+
type: integer
|
| 177 |
+
total_items:
|
| 178 |
+
type: integer
|
| 179 |
+
total_pages:
|
| 180 |
+
type: integer
|
| 181 |
+
type: object
|
| 182 |
+
dto.PriceData:
|
| 183 |
+
properties:
|
| 184 |
+
amount:
|
| 185 |
+
type: integer
|
| 186 |
+
currency:
|
| 187 |
+
type: string
|
| 188 |
+
display:
|
| 189 |
+
type: string
|
| 190 |
+
type: object
|
| 191 |
+
dto.ScheduleItem:
|
| 192 |
+
properties:
|
| 193 |
+
city:
|
| 194 |
+
type: string
|
| 195 |
+
class_id:
|
| 196 |
+
type: string
|
| 197 |
+
date:
|
| 198 |
+
type: string
|
| 199 |
+
display_time:
|
| 200 |
+
type: string
|
| 201 |
+
end_time:
|
| 202 |
+
type: string
|
| 203 |
+
is_booked:
|
| 204 |
+
type: boolean
|
| 205 |
+
schedule_id:
|
| 206 |
+
type: string
|
| 207 |
+
start_time:
|
| 208 |
+
type: string
|
| 209 |
+
studio_name:
|
| 210 |
+
type: string
|
| 211 |
+
teacher_name:
|
| 212 |
+
type: string
|
| 213 |
+
title:
|
| 214 |
+
type: string
|
| 215 |
+
type: object
|
| 216 |
+
dto.ScheduleListItem:
|
| 217 |
+
properties:
|
| 218 |
+
address:
|
| 219 |
+
type: string
|
| 220 |
+
available_slots:
|
| 221 |
+
type: integer
|
| 222 |
+
category:
|
| 223 |
+
type: string
|
| 224 |
+
city:
|
| 225 |
+
type: string
|
| 226 |
+
class_id:
|
| 227 |
+
type: string
|
| 228 |
+
date:
|
| 229 |
+
type: string
|
| 230 |
+
display_time:
|
| 231 |
+
type: string
|
| 232 |
+
end_time:
|
| 233 |
+
type: string
|
| 234 |
+
is_booked:
|
| 235 |
+
type: boolean
|
| 236 |
+
level:
|
| 237 |
+
type: string
|
| 238 |
+
price:
|
| 239 |
+
$ref: '#/definitions/dto.PriceData'
|
| 240 |
+
schedule_id:
|
| 241 |
+
type: string
|
| 242 |
+
start_time:
|
| 243 |
+
type: string
|
| 244 |
+
studio_name:
|
| 245 |
+
type: string
|
| 246 |
+
teacher_name:
|
| 247 |
+
type: string
|
| 248 |
+
thumbnail_url:
|
| 249 |
+
type: string
|
| 250 |
+
title:
|
| 251 |
+
type: string
|
| 252 |
+
type: object
|
| 253 |
+
dto.ScheduleListResponse:
|
| 254 |
+
properties:
|
| 255 |
+
items:
|
| 256 |
+
items:
|
| 257 |
+
$ref: '#/definitions/dto.ScheduleListItem'
|
| 258 |
+
type: array
|
| 259 |
+
pagination:
|
| 260 |
+
$ref: '#/definitions/dto.PaginationMeta'
|
| 261 |
+
type: object
|
| 262 |
+
dto.ScheduleListResponseWrapper:
|
| 263 |
+
properties:
|
| 264 |
+
data:
|
| 265 |
+
$ref: '#/definitions/dto.ScheduleListResponse'
|
| 266 |
+
message:
|
| 267 |
+
type: string
|
| 268 |
+
success:
|
| 269 |
+
type: boolean
|
| 270 |
+
type: object
|
| 271 |
+
dto.SuccessResponse-any:
|
| 272 |
+
properties:
|
| 273 |
+
data: {}
|
| 274 |
+
message: {}
|
| 275 |
+
meta_data: {}
|
| 276 |
+
status:
|
| 277 |
+
type: string
|
| 278 |
+
type: object
|
| 279 |
+
info:
|
| 280 |
+
contact: {}
|
| 281 |
+
paths:
|
| 282 |
+
/api/v1/class-categories:
|
| 283 |
+
get:
|
| 284 |
+
consumes:
|
| 285 |
+
- application/json
|
| 286 |
+
description: Get master list of class categories
|
| 287 |
+
produces:
|
| 288 |
+
- application/json
|
| 289 |
+
responses:
|
| 290 |
+
"200":
|
| 291 |
+
description: OK
|
| 292 |
+
schema:
|
| 293 |
+
$ref: '#/definitions/dto.CategoryResponseWrapper'
|
| 294 |
+
"400":
|
| 295 |
+
description: Bad Request
|
| 296 |
+
schema:
|
| 297 |
+
$ref: '#/definitions/dto.ErrorResponse'
|
| 298 |
+
summary: Get Class Categories
|
| 299 |
+
tags:
|
| 300 |
+
- Master Data
|
| 301 |
+
/api/v1/event-categories:
|
| 302 |
+
get:
|
| 303 |
+
consumes:
|
| 304 |
+
- application/json
|
| 305 |
+
description: Get master list of event categories
|
| 306 |
+
produces:
|
| 307 |
+
- application/json
|
| 308 |
+
responses:
|
| 309 |
+
"200":
|
| 310 |
+
description: OK
|
| 311 |
+
schema:
|
| 312 |
+
$ref: '#/definitions/dto.CategoryResponseWrapper'
|
| 313 |
+
"400":
|
| 314 |
+
description: Bad Request
|
| 315 |
+
schema:
|
| 316 |
+
$ref: '#/definitions/dto.ErrorResponse'
|
| 317 |
+
summary: Get Event Categories
|
| 318 |
+
tags:
|
| 319 |
+
- Master Data
|
| 320 |
+
/api/v1/home:
|
| 321 |
+
get:
|
| 322 |
+
consumes:
|
| 323 |
+
- application/json
|
| 324 |
+
description: Return all sections needed by the home page in one aggregated response.
|
| 325 |
+
parameters:
|
| 326 |
+
- description: Selected date for schedule card section (YYYY-MM-DD)
|
| 327 |
+
in: query
|
| 328 |
+
name: date
|
| 329 |
+
type: string
|
| 330 |
+
- description: User latitude
|
| 331 |
+
in: query
|
| 332 |
+
name: latitude
|
| 333 |
+
type: number
|
| 334 |
+
- description: User longitude
|
| 335 |
+
in: query
|
| 336 |
+
name: longitude
|
| 337 |
+
type: number
|
| 338 |
+
- description: User city
|
| 339 |
+
in: query
|
| 340 |
+
name: city
|
| 341 |
+
type: string
|
| 342 |
+
produces:
|
| 343 |
+
- application/json
|
| 344 |
+
responses:
|
| 345 |
+
"200":
|
| 346 |
+
description: OK
|
| 347 |
+
schema:
|
| 348 |
+
$ref: '#/definitions/dto.HomeResponseWrapper'
|
| 349 |
+
"400":
|
| 350 |
+
description: Bad Request
|
| 351 |
+
schema:
|
| 352 |
+
$ref: '#/definitions/dto.ErrorResponse'
|
| 353 |
+
security:
|
| 354 |
+
- BearerAuth: []
|
| 355 |
+
summary: Get Home Page Data
|
| 356 |
+
tags:
|
| 357 |
+
- Home Dashboard
|
| 358 |
+
/api/v1/payment/callback:
|
| 359 |
+
post:
|
| 360 |
+
consumes:
|
| 361 |
+
- application/json
|
| 362 |
+
description: Receive and process payment status updates from Xendit
|
| 363 |
+
parameters:
|
| 364 |
+
- description: Xendit Callback Payload
|
| 365 |
+
in: body
|
| 366 |
+
name: request
|
| 367 |
+
required: true
|
| 368 |
+
schema:
|
| 369 |
+
additionalProperties: true
|
| 370 |
+
type: object
|
| 371 |
+
produces:
|
| 372 |
+
- application/json
|
| 373 |
+
responses:
|
| 374 |
+
"200":
|
| 375 |
+
description: OK
|
| 376 |
+
schema:
|
| 377 |
+
$ref: '#/definitions/dto.SuccessResponse-any'
|
| 378 |
+
"400":
|
| 379 |
+
description: Bad Request
|
| 380 |
+
schema:
|
| 381 |
+
$ref: '#/definitions/dto.ErrorResponse'
|
| 382 |
+
summary: Handle Xendit Payment Callback
|
| 383 |
+
tags:
|
| 384 |
+
- Payment
|
| 385 |
+
/api/v1/schedules:
|
| 386 |
+
get:
|
| 387 |
+
consumes:
|
| 388 |
+
- application/json
|
| 389 |
+
description: Get a list of class or event schedules based on date range, city,
|
| 390 |
+
filters, etc.
|
| 391 |
+
parameters:
|
| 392 |
+
- description: Exact date (YYYY-MM-DD)
|
| 393 |
+
in: query
|
| 394 |
+
name: date
|
| 395 |
+
type: string
|
| 396 |
+
- description: Start date range (YYYY-MM-DD)
|
| 397 |
+
in: query
|
| 398 |
+
name: start_date
|
| 399 |
+
type: string
|
| 400 |
+
- description: End date range (YYYY-MM-DD)
|
| 401 |
+
in: query
|
| 402 |
+
name: end_date
|
| 403 |
+
type: string
|
| 404 |
+
- description: Filter by city
|
| 405 |
+
in: query
|
| 406 |
+
name: city
|
| 407 |
+
type: string
|
| 408 |
+
- description: Filter by class level (e.g., beginner)
|
| 409 |
+
in: query
|
| 410 |
+
name: level
|
| 411 |
+
type: string
|
| 412 |
+
- description: Filter by class category (e.g., salsa)
|
| 413 |
+
in: query
|
| 414 |
+
name: category
|
| 415 |
+
type: string
|
| 416 |
+
- description: 'Page number (default: 1)'
|
| 417 |
+
in: query
|
| 418 |
+
name: page
|
| 419 |
+
type: integer
|
| 420 |
+
- description: 'Items per page (default: 10)'
|
| 421 |
+
in: query
|
| 422 |
+
name: limit
|
| 423 |
+
type: integer
|
| 424 |
+
produces:
|
| 425 |
+
- application/json
|
| 426 |
+
responses:
|
| 427 |
+
"200":
|
| 428 |
+
description: OK
|
| 429 |
+
schema:
|
| 430 |
+
$ref: '#/definitions/dto.ScheduleListResponseWrapper'
|
| 431 |
+
"400":
|
| 432 |
+
description: Bad Request
|
| 433 |
+
schema:
|
| 434 |
+
$ref: '#/definitions/dto.ErrorResponse'
|
| 435 |
+
security:
|
| 436 |
+
- BearerAuth: []
|
| 437 |
+
summary: Get Schedule List
|
| 438 |
+
tags:
|
| 439 |
+
- Home Dashboard
|
| 440 |
+
swagger: "2.0"
|
err.txt
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# abdanhafidz.com/go-boilerplate/repositories
|
| 2 |
+
repositories\account_details_repository.go:12:58: undefined: entity.AccountDetail
|
| 3 |
+
repositories\account_details_repository.go:13:66: undefined: entity.AccountDetail
|
| 4 |
+
repositories\account_details_repository.go:14:80: undefined: entity.AccountDetail
|
| 5 |
+
repositories\account_details_repository.go:15:53: undefined: entity.AccountDetail
|
| 6 |
+
repositories\account_details_repository.go:16:58: undefined: entity.AccountDetail
|
| 7 |
+
repositories\account_repository.go:12:52: undefined: entity.Account
|
| 8 |
+
repositories\account_repository.go:13:67: undefined: entity.Account
|
| 9 |
+
repositories\account_repository.go:14:63: undefined: entity.Account
|
| 10 |
+
repositories\account_repository.go:15:69: undefined: entity.Account
|
| 11 |
+
repositories\account_repository.go:16:47: undefined: entity.Account
|
| 12 |
+
repositories\account_repository.go:16:47: too many errors
|
| 13 |
+
# abdanhafidz.com/go-boilerplate/models/dto
|
| 14 |
+
models\dto\account_details_dto.go:8:17: undefined: entity.Account
|
| 15 |
+
models\dto\account_details_dto.go:9:17: undefined: entity.AccountDetail
|
| 16 |
+
models\dto\authentication_dto.go:53:17: undefined: entity.Account
|
| 17 |
+
models\dto\option_dto.go:11:19: undefined: entity.Options
|
err2.txt
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# abdanhafidz.com/go-boilerplate/repositories
|
| 2 |
+
repositories\account_repository.go:12:52: undefined: entity.Account
|
| 3 |
+
repositories\account_repository.go:13:67: undefined: entity.Account
|
| 4 |
+
repositories\account_repository.go:14:63: undefined: entity.Account
|
| 5 |
+
repositories\account_repository.go:15:69: undefined: entity.Account
|
| 6 |
+
repositories\account_repository.go:16:47: undefined: entity.Account
|
| 7 |
+
repositories\account_repository.go:17:52: undefined: entity.Account
|
| 8 |
+
repositories\account_repository.go:30:79: undefined: entity.Account
|
| 9 |
+
repositories\email_verification_repository.go:13:50: undefined: entity.EmailVerification
|
| 10 |
+
repositories\email_verification_repository.go:14:85: undefined: entity.EmailVerification
|
| 11 |
+
repositories\email_verification_repository.go:17:73: undefined: entity.EmailVerification
|
| 12 |
+
repositories\account_repository.go:30:79: too many errors
|
err3.txt
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# abdanhafidz.com/go-boilerplate/services
|
| 2 |
+
services\user_service.go:70:11: assignment mismatch: 1 variable but s.jwtService.GenerateToken returns 2 values
|
| 3 |
+
services\user_service.go:70:38: cannot use user.ID (variable of type string) as context.Context value in argument to s.jwtService.GenerateToken: string does not implement context.Context (missing method Deadline)
|
| 4 |
+
services\user_service.go:70:47: cannot use "" (untyped string constant) as dto.JWTCustomClaims value in argument to s.jwtService.GenerateToken
|
| 5 |
+
services\user_service.go:100:11: assignment mismatch: 1 variable but s.jwtService.GenerateToken returns 2 values
|
| 6 |
+
services\user_service.go:100:38: cannot use user.ID (variable of type string) as context.Context value in argument to s.jwtService.GenerateToken: string does not implement context.Context (missing method Deadline)
|
| 7 |
+
services\user_service.go:100:47: cannot use "" (untyped string constant) as dto.JWTCustomClaims value in argument to s.jwtService.GenerateToken
|
err4.txt
ADDED
|
File without changes
|
err_swag.txt
ADDED
|
Binary file (1.33 kB). View file
|
|
|
err_swag2.txt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
2026/03/10 20:32:28 Generate swagger docs....
|
| 2 |
+
2026/03/10 20:32:28 Generate general API Info, search dir:./
|
| 3 |
+
2026/03/10 20:32:30 ParseComment error in file C:\Users\asus\projects\danzapp-be\controllers\home_controller.go for comment: '// @Success 200 {object} dto.SuccessResponse[dto.HomeResponse]': cannot find type definition: dto.SuccessResponse[dto.HomeResponse]
|
go.mod
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
module abdanhafidz.com/go-boilerplate
|
| 2 |
+
|
| 3 |
+
go 1.25.4
|
| 4 |
+
|
| 5 |
+
require (
|
| 6 |
+
github.com/gin-contrib/gzip v1.2.5
|
| 7 |
+
github.com/gin-gonic/gin v1.11.0
|
| 8 |
+
github.com/golang-jwt/jwt/v4 v4.5.2
|
| 9 |
+
github.com/google/uuid v1.6.0
|
| 10 |
+
github.com/joho/godotenv v1.5.1
|
| 11 |
+
github.com/swaggo/files v1.0.1
|
| 12 |
+
github.com/swaggo/gin-swagger v1.6.1
|
| 13 |
+
github.com/swaggo/swag v1.16.6
|
| 14 |
+
github.com/xendit/xendit-go/v7 v7.0.0
|
| 15 |
+
golang.org/x/crypto v0.46.0
|
| 16 |
+
gorm.io/datatypes v1.2.7
|
| 17 |
+
gorm.io/driver/postgres v1.6.0
|
| 18 |
+
gorm.io/gorm v1.31.0
|
| 19 |
+
)
|
| 20 |
+
|
| 21 |
+
require (
|
| 22 |
+
filippo.io/edwards25519 v1.1.0 // indirect
|
| 23 |
+
github.com/KyleBanks/depth v1.2.1 // indirect
|
| 24 |
+
github.com/bytedance/gopkg v0.1.3 // indirect
|
| 25 |
+
github.com/bytedance/sonic v1.14.2 // indirect
|
| 26 |
+
github.com/bytedance/sonic/loader v0.4.0 // indirect
|
| 27 |
+
github.com/cloudwego/base64x v0.1.6 // indirect
|
| 28 |
+
github.com/gabriel-vasile/mimetype v1.4.12 // indirect
|
| 29 |
+
github.com/gin-contrib/sse v1.1.0 // indirect
|
| 30 |
+
github.com/go-openapi/jsonpointer v0.22.4 // indirect
|
| 31 |
+
github.com/go-openapi/jsonreference v0.21.4 // indirect
|
| 32 |
+
github.com/go-openapi/spec v0.22.2 // indirect
|
| 33 |
+
github.com/go-openapi/swag/conv v0.25.4 // indirect
|
| 34 |
+
github.com/go-openapi/swag/jsonname v0.25.4 // indirect
|
| 35 |
+
github.com/go-openapi/swag/jsonutils v0.25.4 // indirect
|
| 36 |
+
github.com/go-openapi/swag/loading v0.25.4 // indirect
|
| 37 |
+
github.com/go-openapi/swag/stringutils v0.25.4 // indirect
|
| 38 |
+
github.com/go-openapi/swag/typeutils v0.25.4 // indirect
|
| 39 |
+
github.com/go-openapi/swag/yamlutils v0.25.4 // indirect
|
| 40 |
+
github.com/go-playground/locales v0.14.1 // indirect
|
| 41 |
+
github.com/go-playground/universal-translator v0.18.1 // indirect
|
| 42 |
+
github.com/go-playground/validator/v10 v10.30.0 // indirect
|
| 43 |
+
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
| 44 |
+
github.com/goccy/go-json v0.10.5 // indirect
|
| 45 |
+
github.com/goccy/go-yaml v1.19.1 // indirect
|
| 46 |
+
github.com/jackc/pgpassfile v1.0.0 // indirect
|
| 47 |
+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
| 48 |
+
github.com/jackc/pgx/v5 v5.7.5 // indirect
|
| 49 |
+
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
| 50 |
+
github.com/jinzhu/inflection v1.0.0 // indirect
|
| 51 |
+
github.com/jinzhu/now v1.1.5 // indirect
|
| 52 |
+
github.com/json-iterator/go v1.1.12 // indirect
|
| 53 |
+
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
| 54 |
+
github.com/kr/text v0.2.0 // indirect
|
| 55 |
+
github.com/leodido/go-urn v1.4.0 // indirect
|
| 56 |
+
github.com/mattn/go-isatty v0.0.20 // indirect
|
| 57 |
+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
| 58 |
+
github.com/modern-go/reflect2 v1.0.2 // indirect
|
| 59 |
+
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
| 60 |
+
github.com/quic-go/qpack v0.6.0 // indirect
|
| 61 |
+
github.com/quic-go/quic-go v0.58.0 // indirect
|
| 62 |
+
github.com/rogpeppe/go-internal v1.13.1 // indirect
|
| 63 |
+
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
| 64 |
+
github.com/ugorji/go/codec v1.3.1 // indirect
|
| 65 |
+
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
| 66 |
+
golang.org/x/arch v0.23.0 // indirect
|
| 67 |
+
golang.org/x/mod v0.31.0 // indirect
|
| 68 |
+
golang.org/x/net v0.48.0 // indirect
|
| 69 |
+
golang.org/x/sync v0.19.0 // indirect
|
| 70 |
+
golang.org/x/sys v0.39.0 // indirect
|
| 71 |
+
golang.org/x/text v0.32.0 // indirect
|
| 72 |
+
golang.org/x/tools v0.40.0 // indirect
|
| 73 |
+
google.golang.org/protobuf v1.36.11 // indirect
|
| 74 |
+
gorm.io/driver/mysql v1.5.6 // indirect
|
| 75 |
+
)
|
go.sum
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
| 2 |
+
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
| 3 |
+
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
| 4 |
+
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
| 5 |
+
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
|
| 6 |
+
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
|
| 7 |
+
github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE=
|
| 8 |
+
github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980=
|
| 9 |
+
github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o=
|
| 10 |
+
github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
|
| 11 |
+
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
| 12 |
+
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
| 13 |
+
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
| 14 |
+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
| 15 |
+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
| 16 |
+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
| 17 |
+
github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
|
| 18 |
+
github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
| 19 |
+
github.com/gin-contrib/gzip v1.2.5 h1:fIZs0S+l17pIu1P5XRJOo/YNqfIuPCrZZ3TWB7pjckI=
|
| 20 |
+
github.com/gin-contrib/gzip v1.2.5/go.mod h1:aomRgR7ftdZV3uWY0gW/m8rChfxau0n8YVvwlOHONzw=
|
| 21 |
+
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
| 22 |
+
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
| 23 |
+
github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
|
| 24 |
+
github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
|
| 25 |
+
github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4=
|
| 26 |
+
github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80=
|
| 27 |
+
github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8=
|
| 28 |
+
github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4=
|
| 29 |
+
github.com/go-openapi/spec v0.22.2 h1:KEU4Fb+Lp1qg0V4MxrSCPv403ZjBl8Lx1a83gIPU8Qc=
|
| 30 |
+
github.com/go-openapi/spec v0.22.2/go.mod h1:iIImLODL2loCh3Vnox8TY2YWYJZjMAKYyLH2Mu8lOZs=
|
| 31 |
+
github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
|
| 32 |
+
github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4=
|
| 33 |
+
github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU=
|
| 34 |
+
github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI=
|
| 35 |
+
github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag=
|
| 36 |
+
github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA=
|
| 37 |
+
github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY=
|
| 38 |
+
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo=
|
| 39 |
+
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM=
|
| 40 |
+
github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s=
|
| 41 |
+
github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE=
|
| 42 |
+
github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8=
|
| 43 |
+
github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0=
|
| 44 |
+
github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw=
|
| 45 |
+
github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE=
|
| 46 |
+
github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw=
|
| 47 |
+
github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc=
|
| 48 |
+
github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4=
|
| 49 |
+
github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg=
|
| 50 |
+
github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls=
|
| 51 |
+
github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
|
| 52 |
+
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
| 53 |
+
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
| 54 |
+
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
| 55 |
+
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
| 56 |
+
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
| 57 |
+
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
| 58 |
+
github.com/go-playground/validator/v10 v10.30.0 h1:5YBPNs273uzsZJD1I8uiB4Aqg9sN6sMDVX3s6LxmhWU=
|
| 59 |
+
github.com/go-playground/validator/v10 v10.30.0/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
|
| 60 |
+
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
| 61 |
+
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
| 62 |
+
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
| 63 |
+
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
| 64 |
+
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
| 65 |
+
github.com/goccy/go-yaml v1.19.1 h1:3rG3+v8pkhRqoQ/88NYNMHYVGYztCOCIZ7UQhu7H+NE=
|
| 66 |
+
github.com/goccy/go-yaml v1.19.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
| 67 |
+
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
|
| 68 |
+
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
| 69 |
+
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
|
| 70 |
+
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
| 71 |
+
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
| 72 |
+
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
| 73 |
+
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
| 74 |
+
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
| 75 |
+
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
| 76 |
+
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
| 77 |
+
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
| 78 |
+
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
| 79 |
+
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
| 80 |
+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
| 81 |
+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
| 82 |
+
github.com/jackc/pgx/v5 v5.7.5 h1:JHGfMnQY+IEtGM63d+NGMjoRpysB2JBwDr5fsngwmJs=
|
| 83 |
+
github.com/jackc/pgx/v5 v5.7.5/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M=
|
| 84 |
+
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
| 85 |
+
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
| 86 |
+
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
| 87 |
+
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
| 88 |
+
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
| 89 |
+
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
| 90 |
+
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
| 91 |
+
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
| 92 |
+
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
| 93 |
+
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
| 94 |
+
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
| 95 |
+
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
| 96 |
+
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
| 97 |
+
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
| 98 |
+
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
| 99 |
+
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
| 100 |
+
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
| 101 |
+
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
| 102 |
+
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
| 103 |
+
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
| 104 |
+
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
| 105 |
+
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
| 106 |
+
github.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA=
|
| 107 |
+
github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA=
|
| 108 |
+
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
| 109 |
+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
| 110 |
+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
| 111 |
+
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
| 112 |
+
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
| 113 |
+
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
| 114 |
+
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
| 115 |
+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
| 116 |
+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
| 117 |
+
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
| 118 |
+
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
| 119 |
+
github.com/quic-go/quic-go v0.58.0 h1:ggY2pvZaVdB9EyojxL1p+5mptkuHyX5MOSv4dgWF4Ug=
|
| 120 |
+
github.com/quic-go/quic-go v0.58.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
|
| 121 |
+
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
| 122 |
+
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
| 123 |
+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
| 124 |
+
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
| 125 |
+
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
| 126 |
+
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
| 127 |
+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
| 128 |
+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
| 129 |
+
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
| 130 |
+
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
| 131 |
+
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
| 132 |
+
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
| 133 |
+
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
| 134 |
+
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
| 135 |
+
github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
|
| 136 |
+
github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
|
| 137 |
+
github.com/swaggo/gin-swagger v1.6.1 h1:Ri06G4gc9N4t4k8hekMigJ9zKTFSlqj/9paAQCQs7cY=
|
| 138 |
+
github.com/swaggo/gin-swagger v1.6.1/go.mod h1:LQ+hJStHakCWRiK/YNYtJOu4mR2FP+pxLnILT/qNiTw=
|
| 139 |
+
github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI=
|
| 140 |
+
github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg=
|
| 141 |
+
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
| 142 |
+
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
| 143 |
+
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
|
| 144 |
+
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
| 145 |
+
github.com/xendit/xendit-go/v7 v7.0.0 h1:A7Nhaulk1a+mOI/KgRcvb5VSQEB6nhsUGkAhi+RkrEM=
|
| 146 |
+
github.com/xendit/xendit-go/v7 v7.0.0/go.mod h1:W562aw0zhjzF/OUhZLc77q2iFQc9INa5tBy5xl6OLbo=
|
| 147 |
+
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
| 148 |
+
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
| 149 |
+
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
| 150 |
+
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
| 151 |
+
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
| 152 |
+
golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg=
|
| 153 |
+
golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
|
| 154 |
+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
| 155 |
+
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
| 156 |
+
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
| 157 |
+
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
| 158 |
+
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
| 159 |
+
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
|
| 160 |
+
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
|
| 161 |
+
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
| 162 |
+
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
| 163 |
+
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
| 164 |
+
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
| 165 |
+
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
| 166 |
+
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
| 167 |
+
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
| 168 |
+
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
| 169 |
+
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
| 170 |
+
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
| 171 |
+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
| 172 |
+
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
| 173 |
+
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
| 174 |
+
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
| 175 |
+
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
| 176 |
+
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
| 177 |
+
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
| 178 |
+
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
| 179 |
+
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
| 180 |
+
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
| 181 |
+
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
| 182 |
+
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
| 183 |
+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
| 184 |
+
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
| 185 |
+
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
| 186 |
+
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
| 187 |
+
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
| 188 |
+
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
| 189 |
+
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
| 190 |
+
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
| 191 |
+
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
| 192 |
+
golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
|
| 193 |
+
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
|
| 194 |
+
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
| 195 |
+
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
| 196 |
+
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
| 197 |
+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
| 198 |
+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
| 199 |
+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
| 200 |
+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
| 201 |
+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
| 202 |
+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
| 203 |
+
gorm.io/datatypes v1.2.7 h1:ww9GAhF1aGXZY3EB3cJPJ7//JiuQo7DlQA7NNlVaTdk=
|
| 204 |
+
gorm.io/datatypes v1.2.7/go.mod h1:M2iO+6S3hhi4nAyYe444Pcb0dcIiOMJ7QHaUXxyiNZY=
|
| 205 |
+
gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8=
|
| 206 |
+
gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
|
| 207 |
+
gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
|
| 208 |
+
gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
|
| 209 |
+
gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
|
| 210 |
+
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
|
| 211 |
+
gorm.io/driver/sqlserver v1.6.0 h1:VZOBQVsVhkHU/NzNhRJKoANt5pZGQAS1Bwc6m6dgfnc=
|
| 212 |
+
gorm.io/driver/sqlserver v1.6.0/go.mod h1:WQzt4IJo/WHKnckU9jXBLMJIVNMVeTu25dnOzehntWw=
|
| 213 |
+
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
| 214 |
+
gorm.io/gorm v1.31.0 h1:0VlycGreVhK7RF/Bwt51Fk8v0xLiiiFdbGDPIZQ7mJY=
|
| 215 |
+
gorm.io/gorm v1.31.0/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
|
go.work
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
go 1.25.4
|
| 2 |
+
|
| 3 |
+
use .
|
go.work.sum
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY=
|
| 2 |
+
cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
|
| 3 |
+
cloud.google.com/go v0.112.2 h1:ZaGT6LiG7dBzi6zNOvVZwacaXlmf3lRqnC4DQzqyRQw=
|
| 4 |
+
cloud.google.com/go v0.112.2/go.mod h1:iEqjp//KquGIJV/m+Pk3xecgKNhV+ry+vVTsy4TbDms=
|
| 5 |
+
cloud.google.com/go/longrunning v0.5.6 h1:xAe8+0YaWoCKr9t1+aWe+OeQgN/iJK1fEgZSXmjuEaE=
|
| 6 |
+
cloud.google.com/go/longrunning v0.5.6/go.mod h1:vUaDrWYOMKRuhiv6JBnn49YxCPz2Ayn9GqyjaBT8/mA=
|
| 7 |
+
cloud.google.com/go/translate v1.10.3 h1:g+B29z4gtRGsiKDoTF+bNeH25bLRokAaElygX2FcZkE=
|
| 8 |
+
cloud.google.com/go/translate v1.10.3/go.mod h1:GW0vC1qvPtd3pgtypCv4k4U8B7EdgK9/QEF2aJEUovs=
|
| 9 |
+
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
| 10 |
+
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0 h1:UQUsRi8WTzhZntp5313l+CHIAT95ojUI2lpP/ExlZa4=
|
| 11 |
+
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0/go.mod h1:Cz6ft6Dkn3Et6l2v2a9/RpN7epQ1GtDlO6lj8bEcOvw=
|
| 12 |
+
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
| 13 |
+
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
| 14 |
+
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
| 15 |
+
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
| 16 |
+
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
| 17 |
+
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
| 18 |
+
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
| 19 |
+
github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 h1:aQ3y1lwWyqYPiWZThqv1aFbZMiM9vblcSArJRf2Irls=
|
| 20 |
+
github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
| 21 |
+
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
| 22 |
+
github.com/envoyproxy/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M=
|
| 23 |
+
github.com/envoyproxy/go-control-plane v0.13.4/go.mod h1:kDfuBlDVsSj2MjrLEtRWtHlsWIFcGyB2RMO44Dc5GZA=
|
| 24 |
+
github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A=
|
| 25 |
+
github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw=
|
| 26 |
+
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI=
|
| 27 |
+
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4=
|
| 28 |
+
github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8=
|
| 29 |
+
github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU=
|
| 30 |
+
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
|
| 31 |
+
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
|
| 32 |
+
github.com/go-jose/go-jose/v4 v4.1.2 h1:TK/7NqRQZfgAh+Td8AlsrvtPoUyiHh0LqVvokh+1vHI=
|
| 33 |
+
github.com/go-jose/go-jose/v4 v4.1.2/go.mod h1:22cg9HWM1pOlnRiY+9cQYJ9XHmya1bYW8OeDM6Ku6Oo=
|
| 34 |
+
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
| 35 |
+
github.com/go-openapi/swag/cmdutils v0.25.4/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0=
|
| 36 |
+
github.com/go-openapi/swag/fileutils v0.25.4/go.mod h1:cdOT/PKbwcysVQ9Tpr0q20lQKH7MGhOEb6EwmHOirUk=
|
| 37 |
+
github.com/go-openapi/swag/mangling v0.25.4/go.mod h1:6dxwu6QyORHpIIApsdZgb6wBk/DPU15MdyYj/ikn0Hg=
|
| 38 |
+
github.com/go-openapi/swag/netutils v0.25.4/go.mod h1:m2W8dtdaoX7oj9rEttLyTeEFFEBvnAx9qHd5nJEBzYg=
|
| 39 |
+
github.com/golang/glog v1.2.5 h1:DrW6hGnjIhtvhOIiAKT6Psh/Kd/ldepEa81DKeiRJ5I=
|
| 40 |
+
github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
| 41 |
+
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
| 42 |
+
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
| 43 |
+
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
| 44 |
+
github.com/google/go-pkcs11 v0.3.0 h1:PVRnTgtArZ3QQqTGtbtjtnIkzl2iY2kt24yqbrf7td8=
|
| 45 |
+
github.com/google/go-pkcs11 v0.3.0/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY=
|
| 46 |
+
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
|
| 47 |
+
github.com/jordanlewis/gcassert v0.0.0-20250430164644-389ef753e22e/go.mod h1:ZybsQk6DWyN5t7An1MuPm1gtSZ1xDaTXS9ZjIOxvQrk=
|
| 48 |
+
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
| 49 |
+
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
| 50 |
+
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
|
| 51 |
+
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
|
| 52 |
+
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
|
| 53 |
+
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
|
| 54 |
+
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
| 55 |
+
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
| 56 |
+
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
|
| 57 |
+
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
|
| 58 |
+
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
| 59 |
+
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
| 60 |
+
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
| 61 |
+
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
| 62 |
+
github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE=
|
| 63 |
+
github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g=
|
| 64 |
+
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
| 65 |
+
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
| 66 |
+
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
|
| 67 |
+
github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM=
|
| 68 |
+
github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
|
| 69 |
+
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
| 70 |
+
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
| 71 |
+
go.opentelemetry.io/contrib/detectors/gcp v1.36.0 h1:F7q2tNlCaHY9nMKHR6XH9/qkp8FktLnIcy6jJNyOCQw=
|
| 72 |
+
go.opentelemetry.io/contrib/detectors/gcp v1.36.0/go.mod h1:IbBN8uAIIx734PTonTPxAxnjc2pQTxWNkwfstZ+6H2k=
|
| 73 |
+
golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8 h1:LvzTn0GQhWuvKH/kVRS3R3bVAsdQWI7hvfLHGgh9+lU=
|
| 74 |
+
golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8/go.mod h1:Pi4ztBfryZoJEkyFTI5/Ocsu2jXyDr6iSdgJiYE/uwE=
|
| 75 |
+
golang.org/x/telemetry v0.0.0-20251203150158-8fff8a5912fc/go.mod h1:hKdjCMrbv9skySur+Nek8Hd0uJ0GuxJIoIX2payrIdQ=
|
| 76 |
+
golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q=
|
| 77 |
+
golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
|
| 78 |
+
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
|
| 79 |
+
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
| 80 |
+
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
| 81 |
+
google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4=
|
| 82 |
+
google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s=
|
| 83 |
+
google.golang.org/genproto/googleapis/api v0.0.0-20250804133106-a7a43d27e69b h1:ULiyYQ0FdsJhwwZUwbaXpZF5yUE3h+RA+gxvBu37ucc=
|
| 84 |
+
google.golang.org/genproto/googleapis/api v0.0.0-20250804133106-a7a43d27e69b/go.mod h1:oDOGiMSXHL4sDTJvFvIB9nRQCGdLP1o/iVaqQK8zB+M=
|
| 85 |
+
google.golang.org/genproto/googleapis/bytestream v0.0.0-20251014184007-4626949a642f h1:T/BJL1nqPyWStq45hQyss5sEketltFJ/eWERyJ98U5M=
|
| 86 |
+
google.golang.org/genproto/googleapis/bytestream v0.0.0-20251014184007-4626949a642f/go.mod h1:ejCb7yLmK6GCVHp5qpeKbm4KZew/ldg+9b8kq5MONgk=
|
| 87 |
+
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
| 88 |
+
rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4=
|
| 89 |
+
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
| 90 |
+
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
| 91 |
+
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
logs/error_log.txt
ADDED
|
File without changes
|
logs/security_log.txt
ADDED
|
File without changes
|
main.go
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package main
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"abdanhafidz.com/go-boilerplate/provider"
|
| 5 |
+
"abdanhafidz.com/go-boilerplate/router"
|
| 6 |
+
)
|
| 7 |
+
|
| 8 |
+
func main() {
|
| 9 |
+
appProvider := provider.NewAppProvider()
|
| 10 |
+
router.RunRouter(appProvider)
|
| 11 |
+
}
|
middleware/authentication_middleware.go
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package middleware
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"errors"
|
| 5 |
+
"fmt"
|
| 6 |
+
"strings"
|
| 7 |
+
|
| 8 |
+
http_error "abdanhafidz.com/go-boilerplate/models/error"
|
| 9 |
+
"abdanhafidz.com/go-boilerplate/services"
|
| 10 |
+
utils "abdanhafidz.com/go-boilerplate/utils"
|
| 11 |
+
"github.com/gin-gonic/gin"
|
| 12 |
+
)
|
| 13 |
+
|
| 14 |
+
type AuthenticationMiddleware interface {
|
| 15 |
+
VerifyAccount(ctx *gin.Context)
|
| 16 |
+
}
|
| 17 |
+
type authenticationMiddleware struct {
|
| 18 |
+
jwtService services.JWTService
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
func NewAuthenticationMiddleware(jwtService services.JWTService) AuthenticationMiddleware {
|
| 22 |
+
return &authenticationMiddleware{
|
| 23 |
+
jwtService: jwtService,
|
| 24 |
+
}
|
| 25 |
+
}
|
| 26 |
+
func (m *authenticationMiddleware) VerifyAccount(c *gin.Context) {
|
| 27 |
+
|
| 28 |
+
authorizationBearer := c.Request.Header["Authorization"]
|
| 29 |
+
|
| 30 |
+
if authorizationBearer != nil {
|
| 31 |
+
token := strings.Split(authorizationBearer[0], " ")[1]
|
| 32 |
+
claim, err := m.jwtService.ValidateToken(c.Request.Context(), token)
|
| 33 |
+
|
| 34 |
+
if err != nil && errors.Is(err, http_error.INVALID_TOKEN) {
|
| 35 |
+
utils.ResponseFAILED(c, claim, http_error.INVALID_TOKEN)
|
| 36 |
+
c.Abort()
|
| 37 |
+
return
|
| 38 |
+
}
|
| 39 |
+
fmt.Println("Claims:", claim)
|
| 40 |
+
c.Set("user_id", claim.UserId)
|
| 41 |
+
c.Next()
|
| 42 |
+
|
| 43 |
+
} else {
|
| 44 |
+
utils.ResponseFAILED(c, "Empty Token", http_error.UNAUTHORIZED)
|
| 45 |
+
return
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
}
|
middleware/middleware.go
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
package middleware
|
models/dto/authentication_dto.go
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package dto
|
| 2 |
+
|
| 3 |
+
import entity "abdanhafidz.com/go-boilerplate/models/entity"
|
| 4 |
+
|
| 5 |
+
type SignInRequest struct {
|
| 6 |
+
EmailorUsername string `json:"email_or_username" binding:"required"`
|
| 7 |
+
Password string `json:"password" binding:"required"`
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
type SignUpRequest struct {
|
| 11 |
+
Name string `json:"name"`
|
| 12 |
+
Email string `json:"email" binding:"required,email"`
|
| 13 |
+
Username string `json:"username" binding:"required"`
|
| 14 |
+
Password string `json:"password" binding:"required"`
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
type CreateEmailVerificationRequest struct {
|
| 18 |
+
Email string `json:"email" binding:"required,email"`
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
type ChangePasswordRequest struct {
|
| 22 |
+
OldPassword string `json:"old_password" binding:"required" `
|
| 23 |
+
NewPassword string `json:"new_password" binding:"required"`
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
type UpdateUserRoleRequest struct {
|
| 27 |
+
Role string `json:"role" binding:"required"`
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
type ValidateVerifyEmailRequest struct {
|
| 31 |
+
Email string `json:"email" binding:"required,email"`
|
| 32 |
+
Token uint `json:"token" binding:"required"`
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
type ExternalAuthRequest struct {
|
| 36 |
+
OauthID string `json:"oauth_id" binding:"required"`
|
| 37 |
+
OauthProvider string `json:"oauth_provider" binding:"required"`
|
| 38 |
+
}
|
| 39 |
+
type ResetPasswordRequest struct {
|
| 40 |
+
Token uint `json:"token" binding:"required"`
|
| 41 |
+
NewPassword string `json:"new_password" binding:"required"`
|
| 42 |
+
}
|
| 43 |
+
type ForgotPasswordRequest struct {
|
| 44 |
+
Email string `json:"email" binding:"required,email"`
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
type ValidateForgotPasswordRequest struct {
|
| 48 |
+
Token uint `json:"token" binding:"required"`
|
| 49 |
+
NewPassword string `json:"new_password"`
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
type AuthenticatedUser struct {
|
| 53 |
+
User entity.User `json:"user"`
|
| 54 |
+
Token string `json:"token"`
|
| 55 |
+
}
|
models/dto/email_verification_dto.go
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package dto
|
| 2 |
+
|
| 3 |
+
type DeleteEmailVerificationRequest struct {
|
| 4 |
+
Token uint `json:"token" binding:"required"`
|
| 5 |
+
}
|
models/dto/home_dto.go
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package dto
|
| 2 |
+
|
| 3 |
+
type HomeResponse struct {
|
| 4 |
+
User HomeUser `json:"user"`
|
| 5 |
+
Notifications HomeNotifications `json:"notifications"`
|
| 6 |
+
Search HomeSearch `json:"search"`
|
| 7 |
+
HeroBanners []HomeHeroBanner `json:"hero_banners"`
|
| 8 |
+
PromoStrip HomePromoStrip `json:"promo_strip"`
|
| 9 |
+
WeekSchedule HomeWeekSchedule `json:"week_schedule"`
|
| 10 |
+
EventSections []HomeEventSection `json:"event_sections"`
|
| 11 |
+
BeginnerClassSections []HomeBeginnerClassSection `json:"beginner_class_sections"`
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
type HomeUser struct {
|
| 15 |
+
ID string `json:"id"`
|
| 16 |
+
FirstName string `json:"first_name"`
|
| 17 |
+
FullName string `json:"full_name"`
|
| 18 |
+
AvatarUrl string `json:"avatar_url"`
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
type HomeNotifications struct {
|
| 22 |
+
UnreadCount int `json:"unread_count"`
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
type HomeSearch struct {
|
| 26 |
+
Placeholder string `json:"placeholder"`
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
type HomeHeroBanner struct {
|
| 30 |
+
ID string `json:"id"`
|
| 31 |
+
Title string `json:"title"`
|
| 32 |
+
Subtitle string `json:"subtitle"`
|
| 33 |
+
ImageUrl string `json:"image_url"`
|
| 34 |
+
BadgeText string `json:"badge_text"`
|
| 35 |
+
CtaText string `json:"cta_text"`
|
| 36 |
+
CtaType string `json:"cta_type"`
|
| 37 |
+
CtaTargetID string `json:"cta_target_id"`
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
type HomePromoStrip struct {
|
| 41 |
+
ID string `json:"id"`
|
| 42 |
+
Text string `json:"text"`
|
| 43 |
+
CtaText string `json:"cta_text"`
|
| 44 |
+
CtaType string `json:"cta_type"`
|
| 45 |
+
CtaTargetID string `json:"cta_target_id"`
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
type HomeWeekSchedule struct {
|
| 49 |
+
WeekLabel string `json:"week_label"`
|
| 50 |
+
StartDate string `json:"start_date"`
|
| 51 |
+
EndDate string `json:"end_date"`
|
| 52 |
+
SelectedDate string `json:"selected_date"`
|
| 53 |
+
Days []HomeDay `json:"days"`
|
| 54 |
+
Items []ScheduleItem `json:"items"`
|
| 55 |
+
ViewAllUrl string `json:"view_all_url"`
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
type HomeDay struct {
|
| 59 |
+
Date string `json:"date"`
|
| 60 |
+
DayNameShort string `json:"day_name_short"`
|
| 61 |
+
DayNumber int `json:"day_number"`
|
| 62 |
+
IsSelected bool `json:"is_selected"`
|
| 63 |
+
HasSchedule bool `json:"has_schedule"`
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
type ScheduleItem struct {
|
| 67 |
+
ScheduleID string `json:"schedule_id"`
|
| 68 |
+
ClassID string `json:"class_id"`
|
| 69 |
+
Title string `json:"title"`
|
| 70 |
+
Date string `json:"date"`
|
| 71 |
+
StartTime string `json:"start_time"`
|
| 72 |
+
EndTime string `json:"end_time"`
|
| 73 |
+
DisplayTime string `json:"display_time"`
|
| 74 |
+
TeacherName string `json:"teacher_name"`
|
| 75 |
+
StudioName string `json:"studio_name"`
|
| 76 |
+
City string `json:"city"`
|
| 77 |
+
IsBooked bool `json:"is_booked"`
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
type HomeEventSection struct {
|
| 81 |
+
ID string `json:"id"`
|
| 82 |
+
Name string `json:"name"`
|
| 83 |
+
ImageUrl string `json:"image_url"`
|
| 84 |
+
Type string `json:"type"`
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
type HomeBeginnerClassSection struct {
|
| 88 |
+
ID string `json:"id"`
|
| 89 |
+
Name string `json:"name"`
|
| 90 |
+
ImageUrl string `json:"image_url"`
|
| 91 |
+
Type string `json:"type"`
|
| 92 |
+
}
|
models/dto/home_swagger_wrapper.go
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package dto
|
| 2 |
+
|
| 3 |
+
type HomeResponseWrapper struct {
|
| 4 |
+
Success bool `json:"success"`
|
| 5 |
+
Message string `json:"message"`
|
| 6 |
+
Data HomeResponse `json:"data"`
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
+
type ScheduleListResponseWrapper struct {
|
| 10 |
+
Success bool `json:"success"`
|
| 11 |
+
Message string `json:"message"`
|
| 12 |
+
Data ScheduleListResponse `json:"data"`
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
type CategoryResponseWrapper struct {
|
| 16 |
+
Success bool `json:"success"`
|
| 17 |
+
Data []CategoryResponse `json:"data"`
|
| 18 |
+
}
|
models/dto/http_response_dto.go
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package dto
|
| 2 |
+
|
| 3 |
+
type SuccessResponse[TResponse any] struct {
|
| 4 |
+
Status string `json:"status"`
|
| 5 |
+
Data TResponse `json:"data"`
|
| 6 |
+
Message any `json:"message"`
|
| 7 |
+
MetaData any `json:"meta_data"`
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
type ErrorResponse struct {
|
| 11 |
+
Status string `json:"status"`
|
| 12 |
+
Error error `json:"errors"`
|
| 13 |
+
Message any `json:"message"`
|
| 14 |
+
MetaData any `json:"meta_data"`
|
| 15 |
+
}
|
models/dto/jwt_dto.go
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package dto
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"github.com/golang-jwt/jwt/v4"
|
| 5 |
+
)
|
| 6 |
+
|
| 7 |
+
type JWTCustomClaims struct {
|
| 8 |
+
UserId string `json:"user_id" binding:"required"`
|
| 9 |
+
Role string `json:"role" binding:"required"`
|
| 10 |
+
jwt.RegisteredClaims
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
type AccountData struct {
|
| 14 |
+
UserId string `json:"user_id" binding:"required"`
|
| 15 |
+
}
|