RyZ commited on
Commit
d834a80
·
1 Parent(s): bf51ade

setting up the go clean architecture

Browse files
.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
- # If you prefer the allow list template instead of the deny list, see community template:
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
+ }