Spaces:
Runtime error
Runtime error
RyZ
commited on
Commit
·
d834a80
1
Parent(s):
bf51ade
setting up the go clean architecture
Browse files- .env.example +0 -0
- .github/workflows/main.yml +49 -0
- .gitignore +1 -32
- DEPLOY.md +98 -0
- Dockerfile +34 -0
- README.md +0 -1
- build.bat +5 -0
- config/config.go +1 -0
- config/database_config.go +52 -0
- config/env_config.go +75 -0
- config/jwt_config.go +24 -0
- controllers/controller.go +23 -0
- do_inject_config.ps1 +435 -0
- do_inject_controllers.ps1 +310 -0
- do_inject_middleware.ps1 +312 -0
- do_inject_repository.ps1 +327 -0
- do_inject_services.ps1 +383 -0
- go.mod +50 -0
- go.sum +110 -0
- logs/error_log.txt +0 -0
- logs/security_log.txt +0 -0
- main.go +11 -0
- middleware/middleware.go +1 -0
- models/entity/entity.go +1 -0
- models/error/error.go +27 -0
- provider/config_provider.go +43 -0
- provider/controller_provider.go +11 -0
- provider/middleware_provider.go +11 -0
- provider/provider.go +63 -0
- provider/repositories_provider.go +11 -0
- provider/services_provider.go +11 -0
- router/router.go +10 -0
- services/service.go +1 -0
- utils/logger_util.go +31 -0
- utils/response_util.go +86 -0
- utils/util.go +38 -0
.env.example
ADDED
|
File without changes
|
.github/workflows/main.yml
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Deploy to Huggingface
|
| 2 |
+
on:
|
| 3 |
+
push:
|
| 4 |
+
branches:
|
| 5 |
+
- main
|
| 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 "everyrelativity@gmail.com"
|
| 17 |
+
git config --global user.name "Ryu2804"
|
| 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/${{secrets.HF_REPO_NAME}} 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/${{secrets.HF_REPO_NAME}}
|
| 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"
|
.gitignore
CHANGED
|
@@ -1,32 +1 @@
|
|
| 1 |
-
|
| 2 |
-
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
| 3 |
-
#
|
| 4 |
-
# Binaries for programs and plugins
|
| 5 |
-
*.exe
|
| 6 |
-
*.exe~
|
| 7 |
-
*.dll
|
| 8 |
-
*.so
|
| 9 |
-
*.dylib
|
| 10 |
-
|
| 11 |
-
# Test binary, built with `go test -c`
|
| 12 |
-
*.test
|
| 13 |
-
|
| 14 |
-
# Code coverage profiles and other test artifacts
|
| 15 |
-
*.out
|
| 16 |
-
coverage.*
|
| 17 |
-
*.coverprofile
|
| 18 |
-
profile.cov
|
| 19 |
-
|
| 20 |
-
# Dependency directories (remove the comment below to include it)
|
| 21 |
-
# vendor/
|
| 22 |
-
|
| 23 |
-
# Go workspace file
|
| 24 |
-
go.work
|
| 25 |
-
go.work.sum
|
| 26 |
-
|
| 27 |
-
# env file
|
| 28 |
-
.env
|
| 29 |
-
|
| 30 |
-
# Editor/IDE
|
| 31 |
-
# .idea/
|
| 32 |
-
# .vscode/
|
|
|
|
| 1 |
+
/.env
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DEPLOY.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Deploying to Hugging Face Spaces
|
| 2 |
+
|
| 3 |
+
This guide addresses how to deploy the WhatsApp backend to Hugging Face Spaces using the Docker SDK.
|
| 4 |
+
|
| 5 |
+
## Prerequisites
|
| 6 |
+
|
| 7 |
+
- A [Hugging Face](https://huggingface.co/) account.
|
| 8 |
+
- A Supabase database (or any PostgreSQL database).
|
| 9 |
+
|
| 10 |
+
## Steps
|
| 11 |
+
|
| 12 |
+
1. **Create a New Space**
|
| 13 |
+
|
| 14 |
+
- Go to [Hugging Face Spaces](https://huggingface.co/spaces).
|
| 15 |
+
- Click **Create new Space**.
|
| 16 |
+
- **Space Name**: `whatsapp-backend` (or your preferred name).
|
| 17 |
+
- **SDK**: Select `Docker`.
|
| 18 |
+
- **Hardware**: `CPU basic` (Free) is usually sufficient for testing.
|
| 19 |
+
|
| 20 |
+
2. **Upload Code**
|
| 21 |
+
|
| 22 |
+
- You can push your code to the Space's repository using `git`.
|
| 23 |
+
- Or, you can upload files manually via the "Files" tab on Hugging Face.
|
| 24 |
+
- **Essential Files**:
|
| 25 |
+
- `Dockerfile`
|
| 26 |
+
- `go.mod`, `go.sum`
|
| 27 |
+
- `main.go`
|
| 28 |
+
- All source directories (`controllers`, `services`, `repositories`, `models`, `config`, `provider`, `router`, `middleware`, `utils`, `logs`).
|
| 29 |
+
|
| 30 |
+
3. **Configure Environment Variables (Secrets)**
|
| 31 |
+
|
| 32 |
+
- Go to your Space's **Settings** tab.
|
| 33 |
+
- Scroll down to the **Variables and secrets** section.
|
| 34 |
+
- Click **New Secret** (for sensitive data like passwords) or **New Variable** (for public configs).
|
| 35 |
+
|
| 36 |
+
**Required Secrets/Variables:**
|
| 37 |
+
|
| 38 |
+
| Name | Value Example | Type |
|
| 39 |
+
| :---------------------------- | :----------------------------------------- | :--------- |
|
| 40 |
+
| `DB_HOST` | `aws-1-ap-southeast-1.pooler.supabase.com` | Variable |
|
| 41 |
+
| `DB_USER` | `postgres.vsozcjtygglvggyfjzfw` | Variable |
|
| 42 |
+
| `DB_PASSWORD` | `YOUR_ACTUAL_DB_PASSWORD` | **Secret** |
|
| 43 |
+
| `DB_PORT` | `5432` | Variable |
|
| 44 |
+
| `DB_NAME` | `postgres` | Variable |
|
| 45 |
+
| `SALT` | `YOUR_RANDOM_SALT_STRING` | **Secret** |
|
| 46 |
+
| `JWT_SECRET_KEY` | `YOUR_SECURE_JWT_KEY` | **Secret** |
|
| 47 |
+
| `HOST_ADDRESS` | `0.0.0.0` | Variable |
|
| 48 |
+
| `HOST_PORT` | `7860` | Variable |
|
| 49 |
+
| `LOG_PATH` | `logs` | Variable |
|
| 50 |
+
| `EMAIL_VERIFICATION_DURATION` | `2` | Variable |
|
| 51 |
+
|
| 52 |
+
> **Note**: Hugging Face Spaces exposes port `7860` by default. Your application MUST listen on this port (set `HOST_PORT` to `7860`).
|
| 53 |
+
|
| 54 |
+
4. **Build and Run**
|
| 55 |
+
- Once the files are uploaded and secrets are set, Hugging Face will automatically try to build your Docker image.
|
| 56 |
+
- You can monitor the progress in the **Logs** tab.
|
| 57 |
+
- If successful, your API will be live at `https://huggingface.co/spaces/<username>/<space-name>`.
|
| 58 |
+
|
| 59 |
+
## Troubleshooting
|
| 60 |
+
|
| 61 |
+
- **Application crashes at startup**: Check the **Logs**. It often indicates missing environment variables or database connection failures.
|
| 62 |
+
- **Port issues**: Ensure `HOST_ADDRESS` is `0.0.0.0` and `HOST_PORT` is `7860`.
|
| 63 |
+
|
| 64 |
+
## Merging with Existing Organization Repository
|
| 65 |
+
|
| 66 |
+
If you already have a repository created in your organization on Hugging Face:
|
| 67 |
+
|
| 68 |
+
1. **Initialize Git (if not already done)**:
|
| 69 |
+
|
| 70 |
+
```bash
|
| 71 |
+
git init
|
| 72 |
+
```
|
| 73 |
+
|
| 74 |
+
2. **Add the Hugging Face Remote**:
|
| 75 |
+
Replace `<username>` and `<space-name>` with your actual details.
|
| 76 |
+
|
| 77 |
+
```bash
|
| 78 |
+
git remote add huggingface https://huggingface.co/spaces/<username>/<space-name>
|
| 79 |
+
```
|
| 80 |
+
|
| 81 |
+
3. **Pull Existing Changes (to avoid conflicts)**:
|
| 82 |
+
|
| 83 |
+
```bash
|
| 84 |
+
git pull huggingface main --allow-unrelated-histories
|
| 85 |
+
```
|
| 86 |
+
|
| 87 |
+
4. **Add and Commit Your Files**:
|
| 88 |
+
|
| 89 |
+
```bash
|
| 90 |
+
git add .
|
| 91 |
+
git commit -m "feat: secure docker setup and docs"
|
| 92 |
+
```
|
| 93 |
+
|
| 94 |
+
5. **Push to Hugging Face**:
|
| 95 |
+
```bash
|
| 96 |
+
git push huggingface main
|
| 97 |
+
```
|
| 98 |
+
(You may be asked for your Hugging Face username and an Access Token as the password).
|
Dockerfile
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Gunakan image dasar Golang versi 1.24.5
|
| 2 |
+
FROM golang:1.24.5
|
| 3 |
+
|
| 4 |
+
# Tambahkan user non-root untuk keamanan
|
| 5 |
+
RUN useradd -m -u 1001 appuser
|
| 6 |
+
|
| 7 |
+
# Set working directory
|
| 8 |
+
WORKDIR /app
|
| 9 |
+
|
| 10 |
+
# Copy go.mod dan go.sum
|
| 11 |
+
COPY go.mod go.sum ./
|
| 12 |
+
|
| 13 |
+
# Download dependencies
|
| 14 |
+
RUN go mod download
|
| 15 |
+
|
| 16 |
+
# Copy seluruh kode
|
| 17 |
+
COPY . .
|
| 18 |
+
|
| 19 |
+
# Buat direktori yang dibutuhkan dan setup permission
|
| 20 |
+
RUN mkdir -p /app/images /app/logs /app/audio && \
|
| 21 |
+
chmod -R 777 /app/images /app/logs /app/audio && \
|
| 22 |
+
chown -R appuser:appuser /app/images /app/logs /app/audio
|
| 23 |
+
|
| 24 |
+
# Build aplikasi
|
| 25 |
+
RUN go build -o main .
|
| 26 |
+
|
| 27 |
+
# Beralih ke user non-root
|
| 28 |
+
USER appuser
|
| 29 |
+
|
| 30 |
+
# Expose port untuk Hugging Face Spaces
|
| 31 |
+
EXPOSE 7860
|
| 32 |
+
|
| 33 |
+
# Jalankan aplikasi
|
| 34 |
+
CMD ["./main"]
|
README.md
CHANGED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
# backend
|
|
|
|
|
|
build.bat
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
set GO111MODULE=on
|
| 2 |
+
go version > version.txt
|
| 3 |
+
go mod tidy
|
| 4 |
+
go build -o app.exe main.go
|
| 5 |
+
echo Done > done.txt
|
config/config.go
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
package config
|
config/database_config.go
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 TimeZone=Asia/Jakarta ",
|
| 22 |
+
DB_HOST, DB_USER, DB_PASSWORD, DB_NAME, DB_PORT,
|
| 23 |
+
)
|
| 24 |
+
|
| 25 |
+
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
|
| 26 |
+
TranslateError: true,
|
| 27 |
+
})
|
| 28 |
+
|
| 29 |
+
db = db.Session(&gorm.Session{
|
| 30 |
+
PrepareStmt: false,
|
| 31 |
+
})
|
| 32 |
+
|
| 33 |
+
if err != nil {
|
| 34 |
+
log.Fatal("Failed to connect to database:", err)
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
return &databaseConfig{db: db}
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
func (cfg *databaseConfig) AutoMigrateAll(entities ...interface{}) error {
|
| 41 |
+
|
| 42 |
+
err := cfg.db.AutoMigrate(
|
| 43 |
+
entities...,
|
| 44 |
+
)
|
| 45 |
+
|
| 46 |
+
return err
|
| 47 |
+
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
func (cfg *databaseConfig) GetInstance() *gorm.DB {
|
| 51 |
+
return cfg.db
|
| 52 |
+
}
|
config/env_config.go
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package config
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"os"
|
| 5 |
+
"github.com/joho/godotenv"
|
| 6 |
+
)
|
| 7 |
+
|
| 8 |
+
type EnvConfig interface {
|
| 9 |
+
GetTCPAddress() string
|
| 10 |
+
GetLogPath() string
|
| 11 |
+
GetHostAddress() string
|
| 12 |
+
GetHostPort() string
|
| 13 |
+
GetDatabaseHost() string
|
| 14 |
+
GetDatabasePort() string
|
| 15 |
+
GetDatabaseUser() string
|
| 16 |
+
GetDatabasePassword() string
|
| 17 |
+
GetDatabaseName() string
|
| 18 |
+
GetSalt() string
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
type envConfig struct {
|
| 22 |
+
timezone string
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
func NewEnvConfig(timezone string) EnvConfig {
|
| 26 |
+
godotenv.Load()
|
| 27 |
+
os.Setenv("TZ", timezone)
|
| 28 |
+
return &envConfig{
|
| 29 |
+
timezone: timezone,
|
| 30 |
+
}
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
func (e *envConfig) GetTCPAddress() string {
|
| 34 |
+
return os.Getenv("HOST_ADDRESS") + ":" + os.Getenv("HOST_PORT")
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
func (e *envConfig) GetLogPath() string {
|
| 38 |
+
return os.Getenv("LOG_PATH")
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
func (e *envConfig) GetHostAddress() string {
|
| 42 |
+
return os.Getenv("HOST_ADDRESS")
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
func (e *envConfig) GetHostPort() string {
|
| 46 |
+
return os.Getenv("HOST_PORT")
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
func (e *envConfig) GetDatabaseHost() string {
|
| 50 |
+
return os.Getenv("DB_HOST")
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
func (e *envConfig) GetDatabasePort() string {
|
| 54 |
+
return os.Getenv("DB_PORT")
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
func (e *envConfig) GetDatabaseUser() string {
|
| 58 |
+
return os.Getenv("DB_USER")
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
func (e *envConfig) GetDatabasePassword() string {
|
| 62 |
+
return os.Getenv("DB_PASSWORD")
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
func (e *envConfig) GetDatabaseName() string {
|
| 66 |
+
return os.Getenv("DB_NAME")
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
func (e *envConfig) GetSalt() string {
|
| 70 |
+
salt := os.Getenv("SALT")
|
| 71 |
+
if salt == "" {
|
| 72 |
+
return "Def4u|7" // Default salt value
|
| 73 |
+
}
|
| 74 |
+
return salt
|
| 75 |
+
}
|
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 |
+
}
|
controllers/controller.go
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package controllers
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
http_error "whatsapp-backend/models/error"
|
| 5 |
+
"whatsapp-backend/utils"
|
| 6 |
+
|
| 7 |
+
"github.com/gin-gonic/gin"
|
| 8 |
+
)
|
| 9 |
+
|
| 10 |
+
func RequestJSON[TRequest any](ctx *gin.Context) TRequest {
|
| 11 |
+
var request TRequest
|
| 12 |
+
if err := ctx.ShouldBindJSON(&request); err != nil {
|
| 13 |
+
utils.ResponseFAILED(ctx, request, http_error.BAD_REQUEST_ERROR)
|
| 14 |
+
ctx.Abort()
|
| 15 |
+
return request
|
| 16 |
+
} else {
|
| 17 |
+
return request
|
| 18 |
+
}
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
func ResponseJSON[TResponse any, TMetaData any](ctx *gin.Context, metaData TMetaData, res TResponse, err error) {
|
| 22 |
+
utils.SendResponse(ctx, metaData, res, err)
|
| 23 |
+
}
|
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 = "whatsapp-backend/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 |
+
}
|
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 = "whatsapp-backend/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 |
+
}
|
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 = "whatsapp-backend/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 |
+
}
|
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 = "whatsapp-backend/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 |
+
}
|
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 = "whatsapp-backend/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 |
+
}
|
go.mod
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
module whatsapp-backend
|
| 2 |
+
|
| 3 |
+
go 1.24.5
|
| 4 |
+
|
| 5 |
+
require (
|
| 6 |
+
github.com/gin-gonic/gin v1.11.0
|
| 7 |
+
github.com/google/uuid v1.6.0
|
| 8 |
+
github.com/joho/godotenv v1.5.1
|
| 9 |
+
gorm.io/driver/postgres v1.6.0
|
| 10 |
+
gorm.io/gorm v1.31.1
|
| 11 |
+
)
|
| 12 |
+
|
| 13 |
+
require (
|
| 14 |
+
github.com/bytedance/gopkg v0.1.3 // indirect
|
| 15 |
+
github.com/bytedance/sonic v1.14.2 // indirect
|
| 16 |
+
github.com/bytedance/sonic/loader v0.4.0 // indirect
|
| 17 |
+
github.com/cloudwego/base64x v0.1.6 // indirect
|
| 18 |
+
github.com/gabriel-vasile/mimetype v1.4.12 // indirect
|
| 19 |
+
github.com/gin-contrib/sse v1.1.0 // indirect
|
| 20 |
+
github.com/go-playground/locales v0.14.1 // indirect
|
| 21 |
+
github.com/go-playground/universal-translator v0.18.1 // indirect
|
| 22 |
+
github.com/go-playground/validator/v10 v10.29.0 // indirect
|
| 23 |
+
github.com/goccy/go-json v0.10.5 // indirect
|
| 24 |
+
github.com/goccy/go-yaml v1.19.1 // indirect
|
| 25 |
+
github.com/jackc/pgpassfile v1.0.0 // indirect
|
| 26 |
+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
| 27 |
+
github.com/jackc/pgx/v5 v5.7.6 // indirect
|
| 28 |
+
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
| 29 |
+
github.com/jinzhu/inflection v1.0.0 // indirect
|
| 30 |
+
github.com/jinzhu/now v1.1.5 // indirect
|
| 31 |
+
github.com/json-iterator/go v1.1.12 // indirect
|
| 32 |
+
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
| 33 |
+
github.com/leodido/go-urn v1.4.0 // indirect
|
| 34 |
+
github.com/mattn/go-isatty v0.0.20 // indirect
|
| 35 |
+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
| 36 |
+
github.com/modern-go/reflect2 v1.0.2 // indirect
|
| 37 |
+
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
| 38 |
+
github.com/quic-go/qpack v0.6.0 // indirect
|
| 39 |
+
github.com/quic-go/quic-go v0.58.0 // indirect
|
| 40 |
+
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
| 41 |
+
github.com/ugorji/go/codec v1.3.1 // indirect
|
| 42 |
+
go.uber.org/mock v0.6.0 // indirect
|
| 43 |
+
golang.org/x/arch v0.23.0 // indirect
|
| 44 |
+
golang.org/x/crypto v0.46.0 // indirect
|
| 45 |
+
golang.org/x/net v0.48.0 // indirect
|
| 46 |
+
golang.org/x/sync v0.19.0 // indirect
|
| 47 |
+
golang.org/x/sys v0.39.0 // indirect
|
| 48 |
+
golang.org/x/text v0.32.0 // indirect
|
| 49 |
+
google.golang.org/protobuf v1.36.11 // indirect
|
| 50 |
+
)
|
go.sum
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
|
| 2 |
+
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
|
| 3 |
+
github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE=
|
| 4 |
+
github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980=
|
| 5 |
+
github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o=
|
| 6 |
+
github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
|
| 7 |
+
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
| 8 |
+
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
| 9 |
+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
| 10 |
+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
| 11 |
+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
| 12 |
+
github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
|
| 13 |
+
github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
| 14 |
+
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
| 15 |
+
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
| 16 |
+
github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
|
| 17 |
+
github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
|
| 18 |
+
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
| 19 |
+
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
| 20 |
+
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
| 21 |
+
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
| 22 |
+
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
| 23 |
+
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
| 24 |
+
github.com/go-playground/validator/v10 v10.29.0 h1:lQlF5VNJWNlRbRZNeOIkWElR+1LL/OuHcc0Kp14w1xk=
|
| 25 |
+
github.com/go-playground/validator/v10 v10.29.0/go.mod h1:D6QxqeMlgIPuT02L66f2ccrZ7AGgHkzKmmTMZhk/Kc4=
|
| 26 |
+
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
| 27 |
+
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
| 28 |
+
github.com/goccy/go-yaml v1.19.1 h1:3rG3+v8pkhRqoQ/88NYNMHYVGYztCOCIZ7UQhu7H+NE=
|
| 29 |
+
github.com/goccy/go-yaml v1.19.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
| 30 |
+
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
| 31 |
+
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
| 32 |
+
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
| 33 |
+
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
| 34 |
+
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
| 35 |
+
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
| 36 |
+
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
| 37 |
+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
| 38 |
+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
| 39 |
+
github.com/jackc/pgx/v5 v5.7.6 h1:rWQc5FwZSPX58r1OQmkuaNicxdmExaEz5A2DO2hUuTk=
|
| 40 |
+
github.com/jackc/pgx/v5 v5.7.6/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M=
|
| 41 |
+
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
| 42 |
+
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
| 43 |
+
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
| 44 |
+
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
| 45 |
+
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
| 46 |
+
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
| 47 |
+
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
| 48 |
+
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
| 49 |
+
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
| 50 |
+
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
| 51 |
+
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
| 52 |
+
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
| 53 |
+
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
| 54 |
+
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
| 55 |
+
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
| 56 |
+
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
| 57 |
+
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
| 58 |
+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
| 59 |
+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
| 60 |
+
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
| 61 |
+
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
| 62 |
+
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
| 63 |
+
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
| 64 |
+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
| 65 |
+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
| 66 |
+
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
| 67 |
+
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
| 68 |
+
github.com/quic-go/quic-go v0.58.0 h1:ggY2pvZaVdB9EyojxL1p+5mptkuHyX5MOSv4dgWF4Ug=
|
| 69 |
+
github.com/quic-go/quic-go v0.58.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
|
| 70 |
+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
| 71 |
+
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
| 72 |
+
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
| 73 |
+
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
| 74 |
+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
| 75 |
+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
| 76 |
+
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
| 77 |
+
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
| 78 |
+
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
| 79 |
+
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
| 80 |
+
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
| 81 |
+
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
| 82 |
+
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
| 83 |
+
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
| 84 |
+
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
|
| 85 |
+
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
| 86 |
+
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
| 87 |
+
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
| 88 |
+
golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg=
|
| 89 |
+
golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
|
| 90 |
+
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
| 91 |
+
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
| 92 |
+
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
| 93 |
+
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
| 94 |
+
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
| 95 |
+
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
| 96 |
+
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
| 97 |
+
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
| 98 |
+
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
| 99 |
+
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
| 100 |
+
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
| 101 |
+
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
| 102 |
+
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
| 103 |
+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
| 104 |
+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
| 105 |
+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
| 106 |
+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
| 107 |
+
gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
|
| 108 |
+
gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
|
| 109 |
+
gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
|
| 110 |
+
gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
|
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 |
+
"whatsapp-backend/provider"
|
| 5 |
+
"whatsapp-backend/router"
|
| 6 |
+
)
|
| 7 |
+
|
| 8 |
+
func main() {
|
| 9 |
+
appProvider := provider.NewAppProvider()
|
| 10 |
+
router.RunRouter(appProvider)
|
| 11 |
+
}
|
middleware/middleware.go
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
package middleware
|
models/entity/entity.go
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
package models
|
models/error/error.go
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package http_error
|
| 2 |
+
|
| 3 |
+
import "errors"
|
| 4 |
+
|
| 5 |
+
var (
|
| 6 |
+
BAD_REQUEST_ERROR = errors.New("Invalid Request Format !")
|
| 7 |
+
INTERNAL_SERVER_ERROR = errors.New("Internal Server Error!")
|
| 8 |
+
UNAUTHORIZED = errors.New("Unauthorized, you don't have permission to access this service!")
|
| 9 |
+
DATA_NOT_FOUND = errors.New("There is not data with given credential / given parameter!")
|
| 10 |
+
TIMEOUT = errors.New("Server took to long respond!")
|
| 11 |
+
EXISTING_ACCOUNT = errors.New("There is existing account!")
|
| 12 |
+
INVALID_TOKEN = errors.New("Invalid Authentication Payload!")
|
| 13 |
+
DUPLICATE_DATA = errors.New("Duplicate data !")
|
| 14 |
+
ACCOUNT_NOT_FOUND = errors.New("There is no account with given credential!")
|
| 15 |
+
WRONG_PASSWORD = errors.New("Your password is wrong for given account credential, please recheck!")
|
| 16 |
+
INVALID_ACCOUNT_DIGITS = errors.New("Your account 3 digits is not found in account number data")
|
| 17 |
+
EXPIRED_TOKEN = errors.New("Token expired")
|
| 18 |
+
ALREADY_REGISTERED_TO_EVENT = errors.New("Account already registered to this event")
|
| 19 |
+
EMAIL_ALREADY_EXISTS = errors.New("Email already registered")
|
| 20 |
+
NOT_REGISTERED_TO_EVENT = errors.New("Account is not registered to this event")
|
| 21 |
+
INVALID_OTP = errors.New("Invalid OTP Code")
|
| 22 |
+
ERR_PROBLEM_SET_NOT_FOUND = errors.New("problem set not found")
|
| 23 |
+
ERR_QUESTION_NOT_FOUND = errors.New("question not found")
|
| 24 |
+
EVENT_FINISHED = errors.New("The event has ended, you were disallowed to do the exam!")
|
| 25 |
+
EVENT_NOT_STARTED = errors.New("Take it easy, event hasn't starting yet! you cannot do the exam!")
|
| 26 |
+
EXAMS_SUBMITTED = errors.New("You've submitted the exam, you were diasallowed to answer the question!")
|
| 27 |
+
)
|
provider/config_provider.go
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package provider
|
| 2 |
+
|
| 3 |
+
import "whatsapp-backend/config"
|
| 4 |
+
|
| 5 |
+
type ConfigProvider interface {
|
| 6 |
+
ProvideJWTConfig() config.JWTConfig
|
| 7 |
+
ProvideEnvConfig() config.EnvConfig
|
| 8 |
+
ProvideDatabaseConfig() config.DatabaseConfig
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
type configProvider struct {
|
| 12 |
+
jWTConfig config.JWTConfig
|
| 13 |
+
envConfig config.EnvConfig
|
| 14 |
+
databaseConfig config.DatabaseConfig
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
func NewConfigProvider() ConfigProvider {
|
| 18 |
+
envConfig := config.NewEnvConfig("Asia/Jakarta")
|
| 19 |
+
jWTConfig := config.NewJWTConfig(envConfig.GetSalt())
|
| 20 |
+
databaseConfig := config.NewDatabaseConfig(
|
| 21 |
+
envConfig.GetDatabaseHost(),
|
| 22 |
+
envConfig.GetDatabaseUser(),
|
| 23 |
+
envConfig.GetDatabasePassword(),
|
| 24 |
+
envConfig.GetDatabaseName(),
|
| 25 |
+
envConfig.GetDatabasePort())
|
| 26 |
+
return &configProvider{
|
| 27 |
+
jWTConfig: jWTConfig,
|
| 28 |
+
envConfig: envConfig,
|
| 29 |
+
databaseConfig: databaseConfig,
|
| 30 |
+
}
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
func (c *configProvider) ProvideJWTConfig() config.JWTConfig {
|
| 34 |
+
return c.jWTConfig
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
func (c *configProvider) ProvideEnvConfig() config.EnvConfig {
|
| 38 |
+
return c.envConfig
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
func (c *configProvider) ProvideDatabaseConfig() config.DatabaseConfig {
|
| 42 |
+
return c.databaseConfig
|
| 43 |
+
}
|
provider/controller_provider.go
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package provider
|
| 2 |
+
|
| 3 |
+
type ControllerProvider interface {
|
| 4 |
+
}
|
| 5 |
+
|
| 6 |
+
type controllerProvider struct {
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
+
func NewControllerProvider(servicesProvider ServicesProvider) ControllerProvider {
|
| 10 |
+
return &controllerProvider{}
|
| 11 |
+
}
|
provider/middleware_provider.go
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package provider
|
| 2 |
+
|
| 3 |
+
type MiddlewareProvider interface {
|
| 4 |
+
}
|
| 5 |
+
|
| 6 |
+
type middlewareProvider struct {
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
+
func NewMiddlewareProvider(servicesProvider ServicesProvider) MiddlewareProvider {
|
| 10 |
+
return &middlewareProvider{}
|
| 11 |
+
}
|
provider/provider.go
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package provider
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"github.com/gin-gonic/gin"
|
| 5 |
+
)
|
| 6 |
+
|
| 7 |
+
type AppProvider interface {
|
| 8 |
+
ProvideRouter() *gin.Engine
|
| 9 |
+
ProvideConfig() ConfigProvider
|
| 10 |
+
ProvideRepositories() RepositoriesProvider
|
| 11 |
+
ProvideServices() ServicesProvider
|
| 12 |
+
ProvideControllers() ControllerProvider
|
| 13 |
+
ProvideMiddlewares() MiddlewareProvider
|
| 14 |
+
}
|
| 15 |
+
type appProvider struct {
|
| 16 |
+
ginRouter *gin.Engine
|
| 17 |
+
configProvider ConfigProvider
|
| 18 |
+
repositoriesProvider RepositoriesProvider
|
| 19 |
+
servicesProvider ServicesProvider
|
| 20 |
+
controllerProvider ControllerProvider
|
| 21 |
+
middlewareProvider MiddlewareProvider
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
func NewAppProvider() AppProvider {
|
| 25 |
+
ginRouter := gin.Default()
|
| 26 |
+
configProvider := NewConfigProvider()
|
| 27 |
+
repositoriesProvider := NewRepositoriesProvider(configProvider)
|
| 28 |
+
servicesProvider := NewServicesProvider(repositoriesProvider, configProvider)
|
| 29 |
+
controllerProvider := NewControllerProvider(servicesProvider)
|
| 30 |
+
middlewareProvider := NewMiddlewareProvider(servicesProvider)
|
| 31 |
+
// configProvider.ProvideDatabaseConfig().AutoMigrateAll()
|
| 32 |
+
|
| 33 |
+
return &appProvider{
|
| 34 |
+
ginRouter: ginRouter,
|
| 35 |
+
configProvider: configProvider,
|
| 36 |
+
repositoriesProvider: repositoriesProvider,
|
| 37 |
+
servicesProvider: servicesProvider,
|
| 38 |
+
controllerProvider: controllerProvider,
|
| 39 |
+
middlewareProvider: middlewareProvider,
|
| 40 |
+
}
|
| 41 |
+
}
|
| 42 |
+
func (a *appProvider) ProvideRouter() *gin.Engine {
|
| 43 |
+
return a.ginRouter
|
| 44 |
+
}
|
| 45 |
+
func (a *appProvider) ProvideConfig() ConfigProvider {
|
| 46 |
+
return a.configProvider
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
func (a *appProvider) ProvideRepositories() RepositoriesProvider {
|
| 50 |
+
return a.repositoriesProvider
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
func (a *appProvider) ProvideServices() ServicesProvider {
|
| 54 |
+
return a.servicesProvider
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
func (a *appProvider) ProvideControllers() ControllerProvider {
|
| 58 |
+
return a.controllerProvider
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
func (a *appProvider) ProvideMiddlewares() MiddlewareProvider {
|
| 62 |
+
return a.middlewareProvider
|
| 63 |
+
}
|
provider/repositories_provider.go
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package provider
|
| 2 |
+
|
| 3 |
+
type RepositoriesProvider interface {
|
| 4 |
+
}
|
| 5 |
+
|
| 6 |
+
type repositoriesProvider struct {
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
+
func NewRepositoriesProvider(cfg ConfigProvider) RepositoriesProvider {
|
| 10 |
+
return &repositoriesProvider{}
|
| 11 |
+
}
|
provider/services_provider.go
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package provider
|
| 2 |
+
|
| 3 |
+
type ServicesProvider interface {
|
| 4 |
+
}
|
| 5 |
+
|
| 6 |
+
type servicesProvider struct {
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
+
func NewServicesProvider(repoProvider RepositoriesProvider, configProvider ConfigProvider) ServicesProvider {
|
| 10 |
+
return &servicesProvider{}
|
| 11 |
+
}
|
router/router.go
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package router
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"whatsapp-backend/provider"
|
| 5 |
+
)
|
| 6 |
+
|
| 7 |
+
func RunRouter(appProvider provider.AppProvider) {
|
| 8 |
+
router, config := appProvider.ProvideRouter(), appProvider.ProvideConfig()
|
| 9 |
+
router.Run(config.ProvideEnvConfig().GetTCPAddress())
|
| 10 |
+
}
|
services/service.go
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
package services
|
utils/logger_util.go
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package utils
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"fmt"
|
| 5 |
+
"log"
|
| 6 |
+
"os"
|
| 7 |
+
)
|
| 8 |
+
|
| 9 |
+
func InternalErrorLog(err_log error) {
|
| 10 |
+
fmt.Println("There is an error!")
|
| 11 |
+
|
| 12 |
+
file, err := os.OpenFile("logs/error_log.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
|
| 13 |
+
|
| 14 |
+
if err != nil {
|
| 15 |
+
log.Fatal(err)
|
| 16 |
+
}
|
| 17 |
+
log.Println("Error Log :", err_log)
|
| 18 |
+
log.SetOutput(file)
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
func SecurityLog(security_log string) {
|
| 22 |
+
fmt.Println("There is an error!")
|
| 23 |
+
|
| 24 |
+
file, err := os.OpenFile("logs/security_log.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
|
| 25 |
+
|
| 26 |
+
if err != nil {
|
| 27 |
+
log.Fatal(err)
|
| 28 |
+
}
|
| 29 |
+
log.Println("Security Log :", security_log)
|
| 30 |
+
log.SetOutput(file)
|
| 31 |
+
}
|
utils/response_util.go
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package utils
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"errors"
|
| 5 |
+
http_error "whatsapp-backend/models/error"
|
| 6 |
+
|
| 7 |
+
"github.com/gin-gonic/gin"
|
| 8 |
+
"gorm.io/gorm"
|
| 9 |
+
)
|
| 10 |
+
|
| 11 |
+
func ResponseOK[Tdata any, TMetaData any](c *gin.Context, metaData TMetaData, data Tdata) {
|
| 12 |
+
c.JSON(200, dto.SuccessResponse[Tdata]{
|
| 13 |
+
Status: "success",
|
| 14 |
+
Data: data,
|
| 15 |
+
Message: "Data retrieved Successfully!",
|
| 16 |
+
MetaData: metaData,
|
| 17 |
+
})
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
func ResponseFAILED[TMetaData any](c *gin.Context, metaData TMetaData, err error) {
|
| 21 |
+
if errors.Is(err, http_error.BAD_REQUEST_ERROR) {
|
| 22 |
+
c.JSON(400, dto.ErrorResponse{
|
| 23 |
+
Status: "error",
|
| 24 |
+
Error: err,
|
| 25 |
+
Message: "Invalid request format!",
|
| 26 |
+
MetaData: metaData,
|
| 27 |
+
})
|
| 28 |
+
return
|
| 29 |
+
} else if errors.Is(err, http_error.INTERNAL_SERVER_ERROR) {
|
| 30 |
+
c.JSON(500, dto.ErrorResponse{
|
| 31 |
+
Status: "error",
|
| 32 |
+
Error: err,
|
| 33 |
+
Message: "Internal Server Error!",
|
| 34 |
+
MetaData: metaData,
|
| 35 |
+
})
|
| 36 |
+
return
|
| 37 |
+
} else if errors.Is(err, http_error.UNAUTHORIZED) {
|
| 38 |
+
c.JSON(401, dto.ErrorResponse{
|
| 39 |
+
Status: "error",
|
| 40 |
+
Error: err,
|
| 41 |
+
Message: "Unauthorized, you don't have permission to access this service!",
|
| 42 |
+
MetaData: metaData,
|
| 43 |
+
})
|
| 44 |
+
return
|
| 45 |
+
} else if errors.Is(err, http_error.DATA_NOT_FOUND) || errors.Is(err, gorm.ErrRecordNotFound) {
|
| 46 |
+
c.JSON(404, dto.ErrorResponse{
|
| 47 |
+
Status: "error",
|
| 48 |
+
Error: err,
|
| 49 |
+
Message: "There is not data with given credential / given parameter!",
|
| 50 |
+
MetaData: metaData,
|
| 51 |
+
})
|
| 52 |
+
return
|
| 53 |
+
} else if errors.Is(err, http_error.TIMEOUT) {
|
| 54 |
+
c.JSON(504, dto.ErrorResponse{
|
| 55 |
+
Status: "error",
|
| 56 |
+
Error: err,
|
| 57 |
+
Message: "Server took to long to respond!",
|
| 58 |
+
MetaData: metaData,
|
| 59 |
+
})
|
| 60 |
+
return
|
| 61 |
+
} else {
|
| 62 |
+
c.JSON(405, dto.ErrorResponse{
|
| 63 |
+
Status: "error",
|
| 64 |
+
Error: err,
|
| 65 |
+
Message: err.Error(),
|
| 66 |
+
MetaData: metaData,
|
| 67 |
+
})
|
| 68 |
+
return
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
func SendResponse[Tdata any, TMetaData any](c *gin.Context, metaData TMetaData, data Tdata, err error) {
|
| 74 |
+
if !c.IsAborted() {
|
| 75 |
+
if err != nil {
|
| 76 |
+
ResponseFAILED(c, metaData, err)
|
| 77 |
+
c.Abort()
|
| 78 |
+
return
|
| 79 |
+
} else {
|
| 80 |
+
ResponseOK(c, metaData, data)
|
| 81 |
+
c.Abort()
|
| 82 |
+
return
|
| 83 |
+
}
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
}
|
utils/util.go
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package utils
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"time"
|
| 5 |
+
|
| 6 |
+
http_error "whatsapp-backend/models/error"
|
| 7 |
+
|
| 8 |
+
"github.com/google/uuid"
|
| 9 |
+
)
|
| 10 |
+
|
| 11 |
+
func ToUUID(s any) (uuid.UUID, error) {
|
| 12 |
+
sStr, ok := s.(string)
|
| 13 |
+
if !ok {
|
| 14 |
+
return uuid.UUID{}, http_error.INTERNAL_SERVER_ERROR
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
res, err := uuid.Parse(sStr)
|
| 18 |
+
if err != nil {
|
| 19 |
+
return uuid.UUID{}, http_error.INTERNAL_SERVER_ERROR
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
return res, nil
|
| 23 |
+
}
|
| 24 |
+
func CalculateRemainingTime(startTime, dueTime time.Time) int {
|
| 25 |
+
now := time.Now()
|
| 26 |
+
|
| 27 |
+
// kalau belum mulai (startTime > now), remaining = full duration
|
| 28 |
+
if startTime.After(now) {
|
| 29 |
+
return int(dueTime.Sub(startTime).Seconds())
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
remaining := int(dueTime.Sub(now).Seconds())
|
| 33 |
+
|
| 34 |
+
if remaining < 0 {
|
| 35 |
+
return 0
|
| 36 |
+
}
|
| 37 |
+
return remaining / 60
|
| 38 |
+
}
|