RyZ commited on
Commit
c099d87
·
1 Parent(s): fddd3d4

feat: adding contact fetching and sending message feature; qa: adding swagger UI

Browse files
controllers/auth_controller.go CHANGED
@@ -28,6 +28,16 @@ func NewAuthController(authService services.AuthService) AuthController {
28
  return &authController{authService: authService}
29
  }
30
 
 
 
 
 
 
 
 
 
 
 
31
  func (c *authController) Register(ctx *gin.Context) {
32
  var req dto.RegisterRequest
33
  if err := ctx.ShouldBindJSON(&req); err != nil {
@@ -48,7 +58,7 @@ func (c *authController) Register(ctx *gin.Context) {
48
  }
49
 
50
  ctx.SetSameSite(http.SameSiteNoneMode)
51
- ctx.SetSameSite(http.SameSiteNoneMode)
52
  ctx.SetCookie("Authorization", resp.Token, 3600*24, "/", "", true, true)
53
  ctx.SetCookie("RefreshToken", resp.RefreshToken, 3600*24*365, "/api/auth/refresh", "", true, true)
54
  ctx.SetCookie("XSRF-TOKEN", csrfToken, 3600*24, "/", "", true, false)
@@ -56,6 +66,16 @@ func (c *authController) Register(ctx *gin.Context) {
56
  utils.SendResponse[dto.AuthResponse, any](ctx, nil, *resp, nil)
57
  }
58
 
 
 
 
 
 
 
 
 
 
 
59
  func (c *authController) Login(ctx *gin.Context) {
60
  var req dto.LoginRequest
61
  if err := ctx.ShouldBindJSON(&req); err != nil {
@@ -75,7 +95,6 @@ func (c *authController) Login(ctx *gin.Context) {
75
  return
76
  }
77
 
78
- ctx.SetSameSite(http.SameSiteNoneMode)
79
  ctx.SetSameSite(http.SameSiteNoneMode)
80
  ctx.SetCookie("Authorization", resp.Token, 3600*24, "/", "", true, true)
81
  ctx.SetCookie("RefreshToken", resp.RefreshToken, 3600*24*365, "/api/auth/refresh", "", true, true)
@@ -84,6 +103,13 @@ func (c *authController) Login(ctx *gin.Context) {
84
  utils.SendResponse[dto.AuthResponse, any](ctx, nil, *resp, nil)
85
  }
86
 
 
 
 
 
 
 
 
87
  func (c *authController) Logout(ctx *gin.Context) {
88
  tokenString := ""
89
  authHeader := ctx.GetHeader("Authorization")
@@ -109,6 +135,14 @@ func (c *authController) Logout(ctx *gin.Context) {
109
  utils.SendResponse[any, any](ctx, nil, nil, nil)
110
  }
111
 
 
 
 
 
 
 
 
 
112
  func (c *authController) RefreshToken(ctx *gin.Context) {
113
  refreshToken, err := ctx.Cookie("RefreshToken")
114
  if err != nil || refreshToken == "" {
@@ -133,6 +167,14 @@ func (c *authController) RefreshToken(ctx *gin.Context) {
133
  }, nil)
134
  }
135
 
 
 
 
 
 
 
 
 
136
  func (c *authController) Me(ctx *gin.Context) {
137
  userId, exists := ctx.Get("user_id")
138
  if !exists {
 
28
  return &authController{authService: authService}
29
  }
30
 
31
+ // Register godoc
32
+ // @Summary Register a new user
33
+ // @Description Register a new user with the provided details
34
+ // @Tags auth
35
+ // @Accept json
36
+ // @Produce json
37
+ // @Param request body dto.RegisterRequest true "Register Request"
38
+ // @Success 200 {object} dto.AuthResponse
39
+ // @Failure 400 {object} dto.ErrorResponse
40
+ // @Router /auth/register [post]
41
  func (c *authController) Register(ctx *gin.Context) {
42
  var req dto.RegisterRequest
43
  if err := ctx.ShouldBindJSON(&req); err != nil {
 
58
  }
59
 
60
  ctx.SetSameSite(http.SameSiteNoneMode)
61
+ ctx.Header("X-CSRF-Token", csrfToken)
62
  ctx.SetCookie("Authorization", resp.Token, 3600*24, "/", "", true, true)
63
  ctx.SetCookie("RefreshToken", resp.RefreshToken, 3600*24*365, "/api/auth/refresh", "", true, true)
64
  ctx.SetCookie("XSRF-TOKEN", csrfToken, 3600*24, "/", "", true, false)
 
66
  utils.SendResponse[dto.AuthResponse, any](ctx, nil, *resp, nil)
67
  }
68
 
69
+ // Login godoc
70
+ // @Summary Login a user
71
+ // @Description Login a user with credentials
72
+ // @Tags auth
73
+ // @Accept json
74
+ // @Produce json
75
+ // @Param request body dto.LoginRequest true "Login Request"
76
+ // @Success 200 {object} dto.AuthResponse
77
+ // @Failure 400 {object} dto.ErrorResponse
78
+ // @Router /auth/login [post]
79
  func (c *authController) Login(ctx *gin.Context) {
80
  var req dto.LoginRequest
81
  if err := ctx.ShouldBindJSON(&req); err != nil {
 
95
  return
96
  }
97
 
 
98
  ctx.SetSameSite(http.SameSiteNoneMode)
99
  ctx.SetCookie("Authorization", resp.Token, 3600*24, "/", "", true, true)
100
  ctx.SetCookie("RefreshToken", resp.RefreshToken, 3600*24*365, "/api/auth/refresh", "", true, true)
 
103
  utils.SendResponse[dto.AuthResponse, any](ctx, nil, *resp, nil)
104
  }
105
 
106
+ // Logout godoc
107
+ // @Summary Logout a user
108
+ // @Description Logout the current user
109
+ // @Tags auth
110
+ // @Param X-CSRF-Token header string true "CSRF Protection Token"
111
+ // @Success 200
112
+ // @Router /auth/logout [post]
113
  func (c *authController) Logout(ctx *gin.Context) {
114
  tokenString := ""
115
  authHeader := ctx.GetHeader("Authorization")
 
135
  utils.SendResponse[any, any](ctx, nil, nil, nil)
136
  }
137
 
138
+ // RefreshToken godoc
139
+ // @Summary Refresh access token
140
+ // @Description Refresh the access token using the refresh token cookie
141
+ // @Tags auth
142
+ // @Param X-CSRF-Token header string true "CSRF Protection Token"
143
+ // @Success 200 {object} dto.AuthResponse
144
+ // @Failure 401 {object} dto.ErrorResponse
145
+ // @Router /auth/refresh [post]
146
  func (c *authController) RefreshToken(ctx *gin.Context) {
147
  refreshToken, err := ctx.Cookie("RefreshToken")
148
  if err != nil || refreshToken == "" {
 
167
  }, nil)
168
  }
169
 
170
+ // Me godoc
171
+ // @Summary Get current user profile
172
+ // @Description Get the profile of the currently logged-in user
173
+ // @Tags auth
174
+ // @Security BearerAuth
175
+ // @Success 200 {object} dto.MeResponse
176
+ // @Failure 401 {object} dto.ErrorResponse
177
+ // @Router /auth/me [get]
178
  func (c *authController) Me(ctx *gin.Context) {
179
  userId, exists := ctx.Get("user_id")
180
  if !exists {
controllers/chat_controller.go ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package controllers
2
+
3
+ import (
4
+ "whatsapp-backend/models/dto"
5
+ http_error "whatsapp-backend/models/error"
6
+ "whatsapp-backend/services"
7
+ "whatsapp-backend/utils"
8
+
9
+ "github.com/gin-gonic/gin"
10
+ "github.com/google/uuid"
11
+ )
12
+
13
+ type ChatController interface {
14
+ FetchContacts(ctx *gin.Context)
15
+ SendMessage(ctx *gin.Context)
16
+ }
17
+
18
+ type chatController struct {
19
+ chatService services.ChatService
20
+ }
21
+
22
+ func NewChatController(chatService services.ChatService) ChatController {
23
+ return &chatController{chatService}
24
+ }
25
+
26
+ // FetchContacts godoc
27
+ // @Summary Fetch WhatsApp contacts
28
+ // @Description Fetch list of contacts from the connected WhatsApp account
29
+ // @Tags chat
30
+ // @Security BearerAuth
31
+ // @Accept json
32
+ // @Produce json
33
+ // @Success 200 {object} dto.FetchContactsResponse
34
+ // @Failure 401 {object} dto.ErrorResponse
35
+ // @Failure 400 {object} dto.ErrorResponse
36
+ // @Router /chat/fetch [get]
37
+ func (c *chatController) FetchContacts(ctx *gin.Context) {
38
+ userID, exists := ctx.Get("user_id")
39
+ if !exists {
40
+ utils.SendResponse[any, any](ctx, nil, nil, http_error.UNAUTHORIZED)
41
+ return
42
+ }
43
+ accountID := userID.(uuid.UUID)
44
+
45
+ contacts, err := c.chatService.FetchContacts(ctx, accountID)
46
+ if err != nil {
47
+ utils.SendResponse[any, any](ctx, nil, nil, err)
48
+ return
49
+ }
50
+
51
+ response := dto.FetchContactsResponse{
52
+ AccountID: accountID,
53
+ Contacts: contacts,
54
+ }
55
+
56
+ utils.SendResponse[dto.FetchContactsResponse, any](ctx, nil, response, nil)
57
+ }
58
+
59
+ // SendMessage godoc
60
+ // @Summary Send WhatsApp message
61
+ // @Description Send a text message to a WhatsApp number
62
+ // @Tags chat
63
+ // @Security BearerAuth
64
+ // @Accept json
65
+ // @Produce json
66
+ // @Param request body dto.SendMessageRequest true "Send Message Request"
67
+ // @Param X-CSRF-Token header string true "CSRF Protection Token"
68
+ // @Success 200 {object} dto.SendMessageResponse
69
+ // @Failure 401 {object} dto.ErrorResponse
70
+ // @Failure 400 {object} dto.ErrorResponse
71
+ // @Router /chat/send [post]
72
+ func (c *chatController) SendMessage(ctx *gin.Context) {
73
+ userID, exists := ctx.Get("user_id")
74
+ if !exists {
75
+ utils.SendResponse[any, any](ctx, nil, nil, http_error.UNAUTHORIZED)
76
+ return
77
+ }
78
+ accountID := userID.(uuid.UUID)
79
+
80
+ var req dto.SendMessageRequest
81
+ if err := ctx.ShouldBindJSON(&req); err != nil {
82
+ utils.SendResponse[any, any](ctx, nil, nil, http_error.ERR_BAD_REQUEST)
83
+ return
84
+ }
85
+
86
+ msgID, err := c.chatService.SendMessage(ctx, accountID, req.Recipient, req.Message)
87
+ if err != nil {
88
+ utils.SendResponse[any, any](ctx, nil, nil, err)
89
+ return
90
+ }
91
+
92
+ response := dto.SendMessageResponse{
93
+ MessageID: msgID,
94
+ Status: "SENT",
95
+ }
96
+
97
+ utils.SendResponse[dto.SendMessageResponse, any](ctx, nil, response, nil)
98
+ }
controllers/connection_controller.go CHANGED
@@ -2,15 +2,14 @@ package controllers
2
 
3
  import (
4
  "context"
5
-
6
- "github.com/gin-gonic/gin"
7
- "github.com/google/uuid"
8
-
9
  "whatsapp-backend/models/dto"
10
  entity "whatsapp-backend/models/entity"
11
  http_error "whatsapp-backend/models/error"
12
  "whatsapp-backend/services"
13
  "whatsapp-backend/utils"
 
 
 
14
  )
15
 
16
  type ConnectionController interface {
@@ -26,10 +25,23 @@ func NewConnectionController(connectionService services.ConnectionService) Conne
26
  return &connectionController{connectionService}
27
  }
28
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  func (cc *connectionController) Connect(ctx *gin.Context) {
30
  userID, exists := ctx.Get("user_id")
31
  if !exists {
32
- utils.SendResponse[any, any](ctx, dto.AuthResponse{}, nil, http_error.UNAUTHORIZED)
33
  return
34
  }
35
  accountID := userID.(uuid.UUID)
@@ -39,12 +51,12 @@ func (cc *connectionController) Connect(ctx *gin.Context) {
39
 
40
  qrCode, err := cc.connectionService.Connect(context.Background(), accountID)
41
  if err != nil {
42
- utils.SendResponse[any, any](ctx, dto.AuthResponse{}, nil, err)
43
  return
44
  }
45
 
46
  if qrCode == "" {
47
- utils.SendResponse[any, any](ctx, dto.AuthResponse{}, dto.ConnectResponse{
48
  Message: entity.CONNECTION_ALREADY_CONNECTED,
49
  AccountID: accountID,
50
  Details: entity.CONNECTION_ALREADY_ACTIVE,
@@ -59,13 +71,24 @@ func (cc *connectionController) Connect(ctx *gin.Context) {
59
  QRCode: qrCode,
60
  }
61
 
62
- utils.SendResponse[dto.ConnectResponse, any](ctx, dto.AuthResponse{}, response, nil)
63
  }
64
 
 
 
 
 
 
 
 
 
 
 
 
65
  func (cc *connectionController) GetStatus(ctx *gin.Context) {
66
  userID, exists := ctx.Get("user_id")
67
  if !exists {
68
- utils.SendResponse[any, any](ctx, dto.AuthResponse{}, nil, http_error.UNAUTHORIZED)
69
  return
70
  }
71
  accountID := userID.(uuid.UUID)
@@ -76,7 +99,7 @@ func (cc *connectionController) GetStatus(ctx *gin.Context) {
76
  AccountID: accountID,
77
  Status: entity.WHATSAPP_STATUS_DISCONNECTED,
78
  }
79
- utils.SendResponse[dto.ConnectionStatusResponse, any](ctx, dto.AuthResponse{}, response, nil)
80
  return
81
  }
82
 
@@ -93,5 +116,5 @@ func (cc *connectionController) GetStatus(ctx *gin.Context) {
93
  JID: client.Store.ID.String(),
94
  }
95
 
96
- utils.SendResponse[dto.ConnectionStatusResponse, any](ctx, dto.AuthResponse{}, response, nil)
97
  }
 
2
 
3
  import (
4
  "context"
 
 
 
 
5
  "whatsapp-backend/models/dto"
6
  entity "whatsapp-backend/models/entity"
7
  http_error "whatsapp-backend/models/error"
8
  "whatsapp-backend/services"
9
  "whatsapp-backend/utils"
10
+
11
+ "github.com/gin-gonic/gin"
12
+ "github.com/google/uuid"
13
  )
14
 
15
  type ConnectionController interface {
 
25
  return &connectionController{connectionService}
26
  }
27
 
28
+ // Connect godoc
29
+ // @Summary Connect new WhatsApp account
30
+ // @Description Initiate a connection request for a new WhatsApp account. Returns a QR code if not connected.
31
+ // @Tags whatsapp
32
+ // @Security BearerAuth
33
+ // @Accept json
34
+ // @Produce json
35
+ // @Param request body dto.ConnectRequest true "Connect Request"
36
+ // @Param X-CSRF-Token header string true "CSRF Protection Token"
37
+ // @Success 200 {object} dto.ConnectResponse
38
+ // @Failure 401 {object} dto.ErrorResponse
39
+ // @Failure 500 {object} dto.ErrorResponse
40
+ // @Router /whatsapp/connect [post]
41
  func (cc *connectionController) Connect(ctx *gin.Context) {
42
  userID, exists := ctx.Get("user_id")
43
  if !exists {
44
+ utils.SendResponse[any, any](ctx, nil, nil, http_error.UNAUTHORIZED)
45
  return
46
  }
47
  accountID := userID.(uuid.UUID)
 
51
 
52
  qrCode, err := cc.connectionService.Connect(context.Background(), accountID)
53
  if err != nil {
54
+ utils.SendResponse[any, any](ctx, nil, nil, err)
55
  return
56
  }
57
 
58
  if qrCode == "" {
59
+ utils.SendResponse[any, any](ctx, nil, dto.ConnectResponse{
60
  Message: entity.CONNECTION_ALREADY_CONNECTED,
61
  AccountID: accountID,
62
  Details: entity.CONNECTION_ALREADY_ACTIVE,
 
71
  QRCode: qrCode,
72
  }
73
 
74
+ utils.SendResponse[dto.ConnectResponse, any](ctx, nil, response, nil)
75
  }
76
 
77
+ // GetStatus godoc
78
+ // @Summary Get WhatsApp connection status
79
+ // @Description Get the current status of the WhatsApp connection
80
+ // @Tags whatsapp
81
+ // @Security BearerAuth
82
+ // @Accept json
83
+ // @Produce json
84
+ // @Success 200 {object} dto.ConnectionStatusResponse
85
+ // @Failure 401 {object} dto.ErrorResponse
86
+ // @Failure 500 {object} dto.ErrorResponse
87
+ // @Router /whatsapp/status [get]
88
  func (cc *connectionController) GetStatus(ctx *gin.Context) {
89
  userID, exists := ctx.Get("user_id")
90
  if !exists {
91
+ utils.SendResponse[any, any](ctx, nil, nil, http_error.UNAUTHORIZED)
92
  return
93
  }
94
  accountID := userID.(uuid.UUID)
 
99
  AccountID: accountID,
100
  Status: entity.WHATSAPP_STATUS_DISCONNECTED,
101
  }
102
+ utils.SendResponse[dto.ConnectionStatusResponse, any](ctx, nil, response, nil)
103
  return
104
  }
105
 
 
116
  JID: client.Store.ID.String(),
117
  }
118
 
119
+ utils.SendResponse[dto.ConnectionStatusResponse, any](ctx, nil, response, nil)
120
  }
docs/docs.go ADDED
@@ -0,0 +1,590 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Package docs Code generated by swaggo/swag. DO NOT EDIT
2
+ package docs
3
+
4
+ import "github.com/swaggo/swag"
5
+
6
+ const docTemplate = `{
7
+ "schemes": {{ marshal .Schemes }},
8
+ "swagger": "2.0",
9
+ "info": {
10
+ "description": "{{escape .Description}}",
11
+ "title": "{{.Title}}",
12
+ "termsOfService": "http://swagger.io/terms/",
13
+ "contact": {
14
+ "name": "API Support",
15
+ "url": "http://www.swagger.io/support",
16
+ "email": "support@swagger.io"
17
+ },
18
+ "license": {
19
+ "name": "Apache 2.0",
20
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
21
+ },
22
+ "version": "{{.Version}}"
23
+ },
24
+ "host": "{{.Host}}",
25
+ "basePath": "{{.BasePath}}",
26
+ "paths": {
27
+ "/auth/login": {
28
+ "post": {
29
+ "description": "Login a user with credentials",
30
+ "consumes": [
31
+ "application/json"
32
+ ],
33
+ "produces": [
34
+ "application/json"
35
+ ],
36
+ "tags": [
37
+ "auth"
38
+ ],
39
+ "summary": "Login a user",
40
+ "parameters": [
41
+ {
42
+ "description": "Login Request",
43
+ "name": "request",
44
+ "in": "body",
45
+ "required": true,
46
+ "schema": {
47
+ "$ref": "#/definitions/dto.LoginRequest"
48
+ }
49
+ }
50
+ ],
51
+ "responses": {
52
+ "200": {
53
+ "description": "OK",
54
+ "schema": {
55
+ "$ref": "#/definitions/dto.AuthResponse"
56
+ }
57
+ },
58
+ "400": {
59
+ "description": "Bad Request",
60
+ "schema": {
61
+ "$ref": "#/definitions/dto.ErrorResponse"
62
+ }
63
+ }
64
+ }
65
+ }
66
+ },
67
+ "/auth/logout": {
68
+ "post": {
69
+ "description": "Logout the current user",
70
+ "tags": [
71
+ "auth"
72
+ ],
73
+ "summary": "Logout a user",
74
+ "parameters": [
75
+ {
76
+ "type": "string",
77
+ "description": "CSRF Protection Token",
78
+ "name": "X-CSRF-Token",
79
+ "in": "header",
80
+ "required": true
81
+ }
82
+ ],
83
+ "responses": {
84
+ "200": {
85
+ "description": "OK"
86
+ }
87
+ }
88
+ }
89
+ },
90
+ "/auth/me": {
91
+ "get": {
92
+ "security": [
93
+ {
94
+ "BearerAuth": []
95
+ }
96
+ ],
97
+ "description": "Get the profile of the currently logged-in user",
98
+ "tags": [
99
+ "auth"
100
+ ],
101
+ "summary": "Get current user profile",
102
+ "responses": {
103
+ "200": {
104
+ "description": "OK",
105
+ "schema": {
106
+ "$ref": "#/definitions/dto.MeResponse"
107
+ }
108
+ },
109
+ "401": {
110
+ "description": "Unauthorized",
111
+ "schema": {
112
+ "$ref": "#/definitions/dto.ErrorResponse"
113
+ }
114
+ }
115
+ }
116
+ }
117
+ },
118
+ "/auth/refresh": {
119
+ "post": {
120
+ "description": "Refresh the access token using the refresh token cookie",
121
+ "tags": [
122
+ "auth"
123
+ ],
124
+ "summary": "Refresh access token",
125
+ "parameters": [
126
+ {
127
+ "type": "string",
128
+ "description": "CSRF Protection Token",
129
+ "name": "X-CSRF-Token",
130
+ "in": "header",
131
+ "required": true
132
+ }
133
+ ],
134
+ "responses": {
135
+ "200": {
136
+ "description": "OK",
137
+ "schema": {
138
+ "$ref": "#/definitions/dto.AuthResponse"
139
+ }
140
+ },
141
+ "401": {
142
+ "description": "Unauthorized",
143
+ "schema": {
144
+ "$ref": "#/definitions/dto.ErrorResponse"
145
+ }
146
+ }
147
+ }
148
+ }
149
+ },
150
+ "/auth/register": {
151
+ "post": {
152
+ "description": "Register a new user with the provided details",
153
+ "consumes": [
154
+ "application/json"
155
+ ],
156
+ "produces": [
157
+ "application/json"
158
+ ],
159
+ "tags": [
160
+ "auth"
161
+ ],
162
+ "summary": "Register a new user",
163
+ "parameters": [
164
+ {
165
+ "description": "Register Request",
166
+ "name": "request",
167
+ "in": "body",
168
+ "required": true,
169
+ "schema": {
170
+ "$ref": "#/definitions/dto.RegisterRequest"
171
+ }
172
+ }
173
+ ],
174
+ "responses": {
175
+ "200": {
176
+ "description": "OK",
177
+ "schema": {
178
+ "$ref": "#/definitions/dto.AuthResponse"
179
+ }
180
+ },
181
+ "400": {
182
+ "description": "Bad Request",
183
+ "schema": {
184
+ "$ref": "#/definitions/dto.ErrorResponse"
185
+ }
186
+ }
187
+ }
188
+ }
189
+ },
190
+ "/chat/fetch": {
191
+ "get": {
192
+ "security": [
193
+ {
194
+ "BearerAuth": []
195
+ }
196
+ ],
197
+ "description": "Fetch list of contacts from the connected WhatsApp account",
198
+ "consumes": [
199
+ "application/json"
200
+ ],
201
+ "produces": [
202
+ "application/json"
203
+ ],
204
+ "tags": [
205
+ "chat"
206
+ ],
207
+ "summary": "Fetch WhatsApp contacts",
208
+ "responses": {
209
+ "200": {
210
+ "description": "OK",
211
+ "schema": {
212
+ "$ref": "#/definitions/dto.FetchContactsResponse"
213
+ }
214
+ },
215
+ "400": {
216
+ "description": "Bad Request",
217
+ "schema": {
218
+ "$ref": "#/definitions/dto.ErrorResponse"
219
+ }
220
+ },
221
+ "401": {
222
+ "description": "Unauthorized",
223
+ "schema": {
224
+ "$ref": "#/definitions/dto.ErrorResponse"
225
+ }
226
+ }
227
+ }
228
+ }
229
+ },
230
+ "/chat/send": {
231
+ "post": {
232
+ "security": [
233
+ {
234
+ "BearerAuth": []
235
+ }
236
+ ],
237
+ "description": "Send a text message to a WhatsApp number",
238
+ "consumes": [
239
+ "application/json"
240
+ ],
241
+ "produces": [
242
+ "application/json"
243
+ ],
244
+ "tags": [
245
+ "chat"
246
+ ],
247
+ "summary": "Send WhatsApp message",
248
+ "parameters": [
249
+ {
250
+ "description": "Send Message Request",
251
+ "name": "request",
252
+ "in": "body",
253
+ "required": true,
254
+ "schema": {
255
+ "$ref": "#/definitions/dto.SendMessageRequest"
256
+ }
257
+ },
258
+ {
259
+ "type": "string",
260
+ "description": "CSRF Protection Token",
261
+ "name": "X-CSRF-Token",
262
+ "in": "header",
263
+ "required": true
264
+ }
265
+ ],
266
+ "responses": {
267
+ "200": {
268
+ "description": "OK",
269
+ "schema": {
270
+ "$ref": "#/definitions/dto.SendMessageResponse"
271
+ }
272
+ },
273
+ "400": {
274
+ "description": "Bad Request",
275
+ "schema": {
276
+ "$ref": "#/definitions/dto.ErrorResponse"
277
+ }
278
+ },
279
+ "401": {
280
+ "description": "Unauthorized",
281
+ "schema": {
282
+ "$ref": "#/definitions/dto.ErrorResponse"
283
+ }
284
+ }
285
+ }
286
+ }
287
+ },
288
+ "/whatsapp/connect": {
289
+ "post": {
290
+ "security": [
291
+ {
292
+ "BearerAuth": []
293
+ }
294
+ ],
295
+ "description": "Initiate a connection request for a new WhatsApp account. Returns a QR code if not connected.",
296
+ "consumes": [
297
+ "application/json"
298
+ ],
299
+ "produces": [
300
+ "application/json"
301
+ ],
302
+ "tags": [
303
+ "whatsapp"
304
+ ],
305
+ "summary": "Connect new WhatsApp account",
306
+ "parameters": [
307
+ {
308
+ "description": "Connect Request",
309
+ "name": "request",
310
+ "in": "body",
311
+ "required": true,
312
+ "schema": {
313
+ "$ref": "#/definitions/dto.ConnectRequest"
314
+ }
315
+ },
316
+ {
317
+ "type": "string",
318
+ "description": "CSRF Protection Token",
319
+ "name": "X-CSRF-Token",
320
+ "in": "header",
321
+ "required": true
322
+ }
323
+ ],
324
+ "responses": {
325
+ "200": {
326
+ "description": "OK",
327
+ "schema": {
328
+ "$ref": "#/definitions/dto.ConnectResponse"
329
+ }
330
+ },
331
+ "401": {
332
+ "description": "Unauthorized",
333
+ "schema": {
334
+ "$ref": "#/definitions/dto.ErrorResponse"
335
+ }
336
+ },
337
+ "500": {
338
+ "description": "Internal Server Error",
339
+ "schema": {
340
+ "$ref": "#/definitions/dto.ErrorResponse"
341
+ }
342
+ }
343
+ }
344
+ }
345
+ },
346
+ "/whatsapp/status": {
347
+ "get": {
348
+ "security": [
349
+ {
350
+ "BearerAuth": []
351
+ }
352
+ ],
353
+ "description": "Get the current status of the WhatsApp connection",
354
+ "consumes": [
355
+ "application/json"
356
+ ],
357
+ "produces": [
358
+ "application/json"
359
+ ],
360
+ "tags": [
361
+ "whatsapp"
362
+ ],
363
+ "summary": "Get WhatsApp connection status",
364
+ "responses": {
365
+ "200": {
366
+ "description": "OK",
367
+ "schema": {
368
+ "$ref": "#/definitions/dto.ConnectionStatusResponse"
369
+ }
370
+ },
371
+ "401": {
372
+ "description": "Unauthorized",
373
+ "schema": {
374
+ "$ref": "#/definitions/dto.ErrorResponse"
375
+ }
376
+ },
377
+ "500": {
378
+ "description": "Internal Server Error",
379
+ "schema": {
380
+ "$ref": "#/definitions/dto.ErrorResponse"
381
+ }
382
+ }
383
+ }
384
+ }
385
+ }
386
+ },
387
+ "definitions": {
388
+ "dto.AuthResponse": {
389
+ "type": "object",
390
+ "properties": {
391
+ "token": {
392
+ "type": "string"
393
+ },
394
+ "user_id": {
395
+ "type": "string"
396
+ },
397
+ "username": {
398
+ "type": "string"
399
+ }
400
+ }
401
+ },
402
+ "dto.ConnectRequest": {
403
+ "type": "object"
404
+ },
405
+ "dto.ConnectResponse": {
406
+ "type": "object",
407
+ "properties": {
408
+ "account_id": {
409
+ "type": "string"
410
+ },
411
+ "details": {
412
+ "type": "string"
413
+ },
414
+ "message": {
415
+ "type": "string"
416
+ },
417
+ "qr_code": {
418
+ "type": "string"
419
+ }
420
+ }
421
+ },
422
+ "dto.ConnectionStatusResponse": {
423
+ "type": "object",
424
+ "properties": {
425
+ "account_id": {
426
+ "type": "string"
427
+ },
428
+ "jid": {
429
+ "type": "string"
430
+ },
431
+ "status": {
432
+ "type": "string"
433
+ }
434
+ }
435
+ },
436
+ "dto.ContactResponse": {
437
+ "type": "object",
438
+ "properties": {
439
+ "full_name": {
440
+ "type": "string"
441
+ },
442
+ "jid": {
443
+ "type": "string"
444
+ },
445
+ "name": {
446
+ "type": "string"
447
+ },
448
+ "phone_number": {
449
+ "type": "string"
450
+ },
451
+ "push_name": {
452
+ "type": "string"
453
+ }
454
+ }
455
+ },
456
+ "dto.ErrorResponse": {
457
+ "type": "object",
458
+ "properties": {
459
+ "errors": {},
460
+ "message": {},
461
+ "meta_data": {},
462
+ "status": {
463
+ "type": "string"
464
+ }
465
+ }
466
+ },
467
+ "dto.FetchContactsResponse": {
468
+ "type": "object",
469
+ "properties": {
470
+ "account_id": {
471
+ "type": "string"
472
+ },
473
+ "contacts": {
474
+ "type": "array",
475
+ "items": {
476
+ "$ref": "#/definitions/dto.ContactResponse"
477
+ }
478
+ }
479
+ }
480
+ },
481
+ "dto.LoginRequest": {
482
+ "type": "object",
483
+ "required": [
484
+ "password",
485
+ "username"
486
+ ],
487
+ "properties": {
488
+ "password": {
489
+ "type": "string"
490
+ },
491
+ "username": {
492
+ "type": "string"
493
+ }
494
+ }
495
+ },
496
+ "dto.MeResponse": {
497
+ "type": "object",
498
+ "properties": {
499
+ "account_name": {
500
+ "type": "string"
501
+ },
502
+ "is_active": {
503
+ "type": "boolean"
504
+ },
505
+ "jid": {
506
+ "type": "string"
507
+ },
508
+ "phone": {
509
+ "type": "string"
510
+ },
511
+ "user_id": {
512
+ "type": "string"
513
+ },
514
+ "username": {
515
+ "type": "string"
516
+ }
517
+ }
518
+ },
519
+ "dto.RegisterRequest": {
520
+ "type": "object",
521
+ "required": [
522
+ "password",
523
+ "username"
524
+ ],
525
+ "properties": {
526
+ "password": {
527
+ "type": "string",
528
+ "minLength": 6
529
+ },
530
+ "username": {
531
+ "type": "string",
532
+ "minLength": 3
533
+ }
534
+ }
535
+ },
536
+ "dto.SendMessageRequest": {
537
+ "type": "object",
538
+ "required": [
539
+ "message",
540
+ "recipient"
541
+ ],
542
+ "properties": {
543
+ "message": {
544
+ "type": "string",
545
+ "example": "Hello world"
546
+ },
547
+ "recipient": {
548
+ "type": "string",
549
+ "example": "628123456789"
550
+ }
551
+ }
552
+ },
553
+ "dto.SendMessageResponse": {
554
+ "type": "object",
555
+ "properties": {
556
+ "message_id": {
557
+ "type": "string"
558
+ },
559
+ "status": {
560
+ "type": "string"
561
+ }
562
+ }
563
+ }
564
+ },
565
+ "securityDefinitions": {
566
+ "BearerAuth": {
567
+ "type": "apiKey",
568
+ "name": "Authorization",
569
+ "in": "header"
570
+ }
571
+ }
572
+ }`
573
+
574
+ // SwaggerInfo holds exported Swagger Info so clients can modify it
575
+ var SwaggerInfo = &swag.Spec{
576
+ Version: "1.0",
577
+ Host: "localhost:8080",
578
+ BasePath: "/api",
579
+ Schemes: []string{},
580
+ Title: "Whatsapp Backend API",
581
+ Description: "This is the API documentation for the Whatsapp Backend.",
582
+ InfoInstanceName: "swagger",
583
+ SwaggerTemplate: docTemplate,
584
+ LeftDelim: "{{",
585
+ RightDelim: "}}",
586
+ }
587
+
588
+ func init() {
589
+ swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
590
+ }
docs/swagger.json ADDED
@@ -0,0 +1,566 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "swagger": "2.0",
3
+ "info": {
4
+ "description": "This is the API documentation for the Whatsapp Backend.",
5
+ "title": "Whatsapp Backend API",
6
+ "termsOfService": "http://swagger.io/terms/",
7
+ "contact": {
8
+ "name": "API Support",
9
+ "url": "http://www.swagger.io/support",
10
+ "email": "support@swagger.io"
11
+ },
12
+ "license": {
13
+ "name": "Apache 2.0",
14
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
15
+ },
16
+ "version": "1.0"
17
+ },
18
+ "host": "localhost:8080",
19
+ "basePath": "/api",
20
+ "paths": {
21
+ "/auth/login": {
22
+ "post": {
23
+ "description": "Login a user with credentials",
24
+ "consumes": [
25
+ "application/json"
26
+ ],
27
+ "produces": [
28
+ "application/json"
29
+ ],
30
+ "tags": [
31
+ "auth"
32
+ ],
33
+ "summary": "Login a user",
34
+ "parameters": [
35
+ {
36
+ "description": "Login Request",
37
+ "name": "request",
38
+ "in": "body",
39
+ "required": true,
40
+ "schema": {
41
+ "$ref": "#/definitions/dto.LoginRequest"
42
+ }
43
+ }
44
+ ],
45
+ "responses": {
46
+ "200": {
47
+ "description": "OK",
48
+ "schema": {
49
+ "$ref": "#/definitions/dto.AuthResponse"
50
+ }
51
+ },
52
+ "400": {
53
+ "description": "Bad Request",
54
+ "schema": {
55
+ "$ref": "#/definitions/dto.ErrorResponse"
56
+ }
57
+ }
58
+ }
59
+ }
60
+ },
61
+ "/auth/logout": {
62
+ "post": {
63
+ "description": "Logout the current user",
64
+ "tags": [
65
+ "auth"
66
+ ],
67
+ "summary": "Logout a user",
68
+ "parameters": [
69
+ {
70
+ "type": "string",
71
+ "description": "CSRF Protection Token",
72
+ "name": "X-CSRF-Token",
73
+ "in": "header",
74
+ "required": true
75
+ }
76
+ ],
77
+ "responses": {
78
+ "200": {
79
+ "description": "OK"
80
+ }
81
+ }
82
+ }
83
+ },
84
+ "/auth/me": {
85
+ "get": {
86
+ "security": [
87
+ {
88
+ "BearerAuth": []
89
+ }
90
+ ],
91
+ "description": "Get the profile of the currently logged-in user",
92
+ "tags": [
93
+ "auth"
94
+ ],
95
+ "summary": "Get current user profile",
96
+ "responses": {
97
+ "200": {
98
+ "description": "OK",
99
+ "schema": {
100
+ "$ref": "#/definitions/dto.MeResponse"
101
+ }
102
+ },
103
+ "401": {
104
+ "description": "Unauthorized",
105
+ "schema": {
106
+ "$ref": "#/definitions/dto.ErrorResponse"
107
+ }
108
+ }
109
+ }
110
+ }
111
+ },
112
+ "/auth/refresh": {
113
+ "post": {
114
+ "description": "Refresh the access token using the refresh token cookie",
115
+ "tags": [
116
+ "auth"
117
+ ],
118
+ "summary": "Refresh access token",
119
+ "parameters": [
120
+ {
121
+ "type": "string",
122
+ "description": "CSRF Protection Token",
123
+ "name": "X-CSRF-Token",
124
+ "in": "header",
125
+ "required": true
126
+ }
127
+ ],
128
+ "responses": {
129
+ "200": {
130
+ "description": "OK",
131
+ "schema": {
132
+ "$ref": "#/definitions/dto.AuthResponse"
133
+ }
134
+ },
135
+ "401": {
136
+ "description": "Unauthorized",
137
+ "schema": {
138
+ "$ref": "#/definitions/dto.ErrorResponse"
139
+ }
140
+ }
141
+ }
142
+ }
143
+ },
144
+ "/auth/register": {
145
+ "post": {
146
+ "description": "Register a new user with the provided details",
147
+ "consumes": [
148
+ "application/json"
149
+ ],
150
+ "produces": [
151
+ "application/json"
152
+ ],
153
+ "tags": [
154
+ "auth"
155
+ ],
156
+ "summary": "Register a new user",
157
+ "parameters": [
158
+ {
159
+ "description": "Register Request",
160
+ "name": "request",
161
+ "in": "body",
162
+ "required": true,
163
+ "schema": {
164
+ "$ref": "#/definitions/dto.RegisterRequest"
165
+ }
166
+ }
167
+ ],
168
+ "responses": {
169
+ "200": {
170
+ "description": "OK",
171
+ "schema": {
172
+ "$ref": "#/definitions/dto.AuthResponse"
173
+ }
174
+ },
175
+ "400": {
176
+ "description": "Bad Request",
177
+ "schema": {
178
+ "$ref": "#/definitions/dto.ErrorResponse"
179
+ }
180
+ }
181
+ }
182
+ }
183
+ },
184
+ "/chat/fetch": {
185
+ "get": {
186
+ "security": [
187
+ {
188
+ "BearerAuth": []
189
+ }
190
+ ],
191
+ "description": "Fetch list of contacts from the connected WhatsApp account",
192
+ "consumes": [
193
+ "application/json"
194
+ ],
195
+ "produces": [
196
+ "application/json"
197
+ ],
198
+ "tags": [
199
+ "chat"
200
+ ],
201
+ "summary": "Fetch WhatsApp contacts",
202
+ "responses": {
203
+ "200": {
204
+ "description": "OK",
205
+ "schema": {
206
+ "$ref": "#/definitions/dto.FetchContactsResponse"
207
+ }
208
+ },
209
+ "400": {
210
+ "description": "Bad Request",
211
+ "schema": {
212
+ "$ref": "#/definitions/dto.ErrorResponse"
213
+ }
214
+ },
215
+ "401": {
216
+ "description": "Unauthorized",
217
+ "schema": {
218
+ "$ref": "#/definitions/dto.ErrorResponse"
219
+ }
220
+ }
221
+ }
222
+ }
223
+ },
224
+ "/chat/send": {
225
+ "post": {
226
+ "security": [
227
+ {
228
+ "BearerAuth": []
229
+ }
230
+ ],
231
+ "description": "Send a text message to a WhatsApp number",
232
+ "consumes": [
233
+ "application/json"
234
+ ],
235
+ "produces": [
236
+ "application/json"
237
+ ],
238
+ "tags": [
239
+ "chat"
240
+ ],
241
+ "summary": "Send WhatsApp message",
242
+ "parameters": [
243
+ {
244
+ "description": "Send Message Request",
245
+ "name": "request",
246
+ "in": "body",
247
+ "required": true,
248
+ "schema": {
249
+ "$ref": "#/definitions/dto.SendMessageRequest"
250
+ }
251
+ },
252
+ {
253
+ "type": "string",
254
+ "description": "CSRF Protection Token",
255
+ "name": "X-CSRF-Token",
256
+ "in": "header",
257
+ "required": true
258
+ }
259
+ ],
260
+ "responses": {
261
+ "200": {
262
+ "description": "OK",
263
+ "schema": {
264
+ "$ref": "#/definitions/dto.SendMessageResponse"
265
+ }
266
+ },
267
+ "400": {
268
+ "description": "Bad Request",
269
+ "schema": {
270
+ "$ref": "#/definitions/dto.ErrorResponse"
271
+ }
272
+ },
273
+ "401": {
274
+ "description": "Unauthorized",
275
+ "schema": {
276
+ "$ref": "#/definitions/dto.ErrorResponse"
277
+ }
278
+ }
279
+ }
280
+ }
281
+ },
282
+ "/whatsapp/connect": {
283
+ "post": {
284
+ "security": [
285
+ {
286
+ "BearerAuth": []
287
+ }
288
+ ],
289
+ "description": "Initiate a connection request for a new WhatsApp account. Returns a QR code if not connected.",
290
+ "consumes": [
291
+ "application/json"
292
+ ],
293
+ "produces": [
294
+ "application/json"
295
+ ],
296
+ "tags": [
297
+ "whatsapp"
298
+ ],
299
+ "summary": "Connect new WhatsApp account",
300
+ "parameters": [
301
+ {
302
+ "description": "Connect Request",
303
+ "name": "request",
304
+ "in": "body",
305
+ "required": true,
306
+ "schema": {
307
+ "$ref": "#/definitions/dto.ConnectRequest"
308
+ }
309
+ },
310
+ {
311
+ "type": "string",
312
+ "description": "CSRF Protection Token",
313
+ "name": "X-CSRF-Token",
314
+ "in": "header",
315
+ "required": true
316
+ }
317
+ ],
318
+ "responses": {
319
+ "200": {
320
+ "description": "OK",
321
+ "schema": {
322
+ "$ref": "#/definitions/dto.ConnectResponse"
323
+ }
324
+ },
325
+ "401": {
326
+ "description": "Unauthorized",
327
+ "schema": {
328
+ "$ref": "#/definitions/dto.ErrorResponse"
329
+ }
330
+ },
331
+ "500": {
332
+ "description": "Internal Server Error",
333
+ "schema": {
334
+ "$ref": "#/definitions/dto.ErrorResponse"
335
+ }
336
+ }
337
+ }
338
+ }
339
+ },
340
+ "/whatsapp/status": {
341
+ "get": {
342
+ "security": [
343
+ {
344
+ "BearerAuth": []
345
+ }
346
+ ],
347
+ "description": "Get the current status of the WhatsApp connection",
348
+ "consumes": [
349
+ "application/json"
350
+ ],
351
+ "produces": [
352
+ "application/json"
353
+ ],
354
+ "tags": [
355
+ "whatsapp"
356
+ ],
357
+ "summary": "Get WhatsApp connection status",
358
+ "responses": {
359
+ "200": {
360
+ "description": "OK",
361
+ "schema": {
362
+ "$ref": "#/definitions/dto.ConnectionStatusResponse"
363
+ }
364
+ },
365
+ "401": {
366
+ "description": "Unauthorized",
367
+ "schema": {
368
+ "$ref": "#/definitions/dto.ErrorResponse"
369
+ }
370
+ },
371
+ "500": {
372
+ "description": "Internal Server Error",
373
+ "schema": {
374
+ "$ref": "#/definitions/dto.ErrorResponse"
375
+ }
376
+ }
377
+ }
378
+ }
379
+ }
380
+ },
381
+ "definitions": {
382
+ "dto.AuthResponse": {
383
+ "type": "object",
384
+ "properties": {
385
+ "token": {
386
+ "type": "string"
387
+ },
388
+ "user_id": {
389
+ "type": "string"
390
+ },
391
+ "username": {
392
+ "type": "string"
393
+ }
394
+ }
395
+ },
396
+ "dto.ConnectRequest": {
397
+ "type": "object"
398
+ },
399
+ "dto.ConnectResponse": {
400
+ "type": "object",
401
+ "properties": {
402
+ "account_id": {
403
+ "type": "string"
404
+ },
405
+ "details": {
406
+ "type": "string"
407
+ },
408
+ "message": {
409
+ "type": "string"
410
+ },
411
+ "qr_code": {
412
+ "type": "string"
413
+ }
414
+ }
415
+ },
416
+ "dto.ConnectionStatusResponse": {
417
+ "type": "object",
418
+ "properties": {
419
+ "account_id": {
420
+ "type": "string"
421
+ },
422
+ "jid": {
423
+ "type": "string"
424
+ },
425
+ "status": {
426
+ "type": "string"
427
+ }
428
+ }
429
+ },
430
+ "dto.ContactResponse": {
431
+ "type": "object",
432
+ "properties": {
433
+ "full_name": {
434
+ "type": "string"
435
+ },
436
+ "jid": {
437
+ "type": "string"
438
+ },
439
+ "name": {
440
+ "type": "string"
441
+ },
442
+ "phone_number": {
443
+ "type": "string"
444
+ },
445
+ "push_name": {
446
+ "type": "string"
447
+ }
448
+ }
449
+ },
450
+ "dto.ErrorResponse": {
451
+ "type": "object",
452
+ "properties": {
453
+ "errors": {},
454
+ "message": {},
455
+ "meta_data": {},
456
+ "status": {
457
+ "type": "string"
458
+ }
459
+ }
460
+ },
461
+ "dto.FetchContactsResponse": {
462
+ "type": "object",
463
+ "properties": {
464
+ "account_id": {
465
+ "type": "string"
466
+ },
467
+ "contacts": {
468
+ "type": "array",
469
+ "items": {
470
+ "$ref": "#/definitions/dto.ContactResponse"
471
+ }
472
+ }
473
+ }
474
+ },
475
+ "dto.LoginRequest": {
476
+ "type": "object",
477
+ "required": [
478
+ "password",
479
+ "username"
480
+ ],
481
+ "properties": {
482
+ "password": {
483
+ "type": "string"
484
+ },
485
+ "username": {
486
+ "type": "string"
487
+ }
488
+ }
489
+ },
490
+ "dto.MeResponse": {
491
+ "type": "object",
492
+ "properties": {
493
+ "account_name": {
494
+ "type": "string"
495
+ },
496
+ "is_active": {
497
+ "type": "boolean"
498
+ },
499
+ "jid": {
500
+ "type": "string"
501
+ },
502
+ "phone": {
503
+ "type": "string"
504
+ },
505
+ "user_id": {
506
+ "type": "string"
507
+ },
508
+ "username": {
509
+ "type": "string"
510
+ }
511
+ }
512
+ },
513
+ "dto.RegisterRequest": {
514
+ "type": "object",
515
+ "required": [
516
+ "password",
517
+ "username"
518
+ ],
519
+ "properties": {
520
+ "password": {
521
+ "type": "string",
522
+ "minLength": 6
523
+ },
524
+ "username": {
525
+ "type": "string",
526
+ "minLength": 3
527
+ }
528
+ }
529
+ },
530
+ "dto.SendMessageRequest": {
531
+ "type": "object",
532
+ "required": [
533
+ "message",
534
+ "recipient"
535
+ ],
536
+ "properties": {
537
+ "message": {
538
+ "type": "string",
539
+ "example": "Hello world"
540
+ },
541
+ "recipient": {
542
+ "type": "string",
543
+ "example": "628123456789"
544
+ }
545
+ }
546
+ },
547
+ "dto.SendMessageResponse": {
548
+ "type": "object",
549
+ "properties": {
550
+ "message_id": {
551
+ "type": "string"
552
+ },
553
+ "status": {
554
+ "type": "string"
555
+ }
556
+ }
557
+ }
558
+ },
559
+ "securityDefinitions": {
560
+ "BearerAuth": {
561
+ "type": "apiKey",
562
+ "name": "Authorization",
563
+ "in": "header"
564
+ }
565
+ }
566
+ }
docs/swagger.yaml ADDED
@@ -0,0 +1,369 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ basePath: /api
2
+ definitions:
3
+ dto.AuthResponse:
4
+ properties:
5
+ token:
6
+ type: string
7
+ user_id:
8
+ type: string
9
+ username:
10
+ type: string
11
+ type: object
12
+ dto.ConnectRequest:
13
+ type: object
14
+ dto.ConnectResponse:
15
+ properties:
16
+ account_id:
17
+ type: string
18
+ details:
19
+ type: string
20
+ message:
21
+ type: string
22
+ qr_code:
23
+ type: string
24
+ type: object
25
+ dto.ConnectionStatusResponse:
26
+ properties:
27
+ account_id:
28
+ type: string
29
+ jid:
30
+ type: string
31
+ status:
32
+ type: string
33
+ type: object
34
+ dto.ContactResponse:
35
+ properties:
36
+ full_name:
37
+ type: string
38
+ jid:
39
+ type: string
40
+ name:
41
+ type: string
42
+ phone_number:
43
+ type: string
44
+ push_name:
45
+ type: string
46
+ type: object
47
+ dto.ErrorResponse:
48
+ properties:
49
+ errors: {}
50
+ message: {}
51
+ meta_data: {}
52
+ status:
53
+ type: string
54
+ type: object
55
+ dto.FetchContactsResponse:
56
+ properties:
57
+ account_id:
58
+ type: string
59
+ contacts:
60
+ items:
61
+ $ref: '#/definitions/dto.ContactResponse'
62
+ type: array
63
+ type: object
64
+ dto.LoginRequest:
65
+ properties:
66
+ password:
67
+ type: string
68
+ username:
69
+ type: string
70
+ required:
71
+ - password
72
+ - username
73
+ type: object
74
+ dto.MeResponse:
75
+ properties:
76
+ account_name:
77
+ type: string
78
+ is_active:
79
+ type: boolean
80
+ jid:
81
+ type: string
82
+ phone:
83
+ type: string
84
+ user_id:
85
+ type: string
86
+ username:
87
+ type: string
88
+ type: object
89
+ dto.RegisterRequest:
90
+ properties:
91
+ password:
92
+ minLength: 6
93
+ type: string
94
+ username:
95
+ minLength: 3
96
+ type: string
97
+ required:
98
+ - password
99
+ - username
100
+ type: object
101
+ dto.SendMessageRequest:
102
+ properties:
103
+ message:
104
+ example: Hello world
105
+ type: string
106
+ recipient:
107
+ example: "628123456789"
108
+ type: string
109
+ required:
110
+ - message
111
+ - recipient
112
+ type: object
113
+ dto.SendMessageResponse:
114
+ properties:
115
+ message_id:
116
+ type: string
117
+ status:
118
+ type: string
119
+ type: object
120
+ host: localhost:8080
121
+ info:
122
+ contact:
123
+ email: support@swagger.io
124
+ name: API Support
125
+ url: http://www.swagger.io/support
126
+ description: This is the API documentation for the Whatsapp Backend.
127
+ license:
128
+ name: Apache 2.0
129
+ url: http://www.apache.org/licenses/LICENSE-2.0.html
130
+ termsOfService: http://swagger.io/terms/
131
+ title: Whatsapp Backend API
132
+ version: "1.0"
133
+ paths:
134
+ /auth/login:
135
+ post:
136
+ consumes:
137
+ - application/json
138
+ description: Login a user with credentials
139
+ parameters:
140
+ - description: Login Request
141
+ in: body
142
+ name: request
143
+ required: true
144
+ schema:
145
+ $ref: '#/definitions/dto.LoginRequest'
146
+ produces:
147
+ - application/json
148
+ responses:
149
+ "200":
150
+ description: OK
151
+ schema:
152
+ $ref: '#/definitions/dto.AuthResponse'
153
+ "400":
154
+ description: Bad Request
155
+ schema:
156
+ $ref: '#/definitions/dto.ErrorResponse'
157
+ summary: Login a user
158
+ tags:
159
+ - auth
160
+ /auth/logout:
161
+ post:
162
+ description: Logout the current user
163
+ parameters:
164
+ - description: CSRF Protection Token
165
+ in: header
166
+ name: X-CSRF-Token
167
+ required: true
168
+ type: string
169
+ responses:
170
+ "200":
171
+ description: OK
172
+ summary: Logout a user
173
+ tags:
174
+ - auth
175
+ /auth/me:
176
+ get:
177
+ description: Get the profile of the currently logged-in user
178
+ responses:
179
+ "200":
180
+ description: OK
181
+ schema:
182
+ $ref: '#/definitions/dto.MeResponse'
183
+ "401":
184
+ description: Unauthorized
185
+ schema:
186
+ $ref: '#/definitions/dto.ErrorResponse'
187
+ security:
188
+ - BearerAuth: []
189
+ summary: Get current user profile
190
+ tags:
191
+ - auth
192
+ /auth/refresh:
193
+ post:
194
+ description: Refresh the access token using the refresh token cookie
195
+ parameters:
196
+ - description: CSRF Protection Token
197
+ in: header
198
+ name: X-CSRF-Token
199
+ required: true
200
+ type: string
201
+ responses:
202
+ "200":
203
+ description: OK
204
+ schema:
205
+ $ref: '#/definitions/dto.AuthResponse'
206
+ "401":
207
+ description: Unauthorized
208
+ schema:
209
+ $ref: '#/definitions/dto.ErrorResponse'
210
+ summary: Refresh access token
211
+ tags:
212
+ - auth
213
+ /auth/register:
214
+ post:
215
+ consumes:
216
+ - application/json
217
+ description: Register a new user with the provided details
218
+ parameters:
219
+ - description: Register Request
220
+ in: body
221
+ name: request
222
+ required: true
223
+ schema:
224
+ $ref: '#/definitions/dto.RegisterRequest'
225
+ produces:
226
+ - application/json
227
+ responses:
228
+ "200":
229
+ description: OK
230
+ schema:
231
+ $ref: '#/definitions/dto.AuthResponse'
232
+ "400":
233
+ description: Bad Request
234
+ schema:
235
+ $ref: '#/definitions/dto.ErrorResponse'
236
+ summary: Register a new user
237
+ tags:
238
+ - auth
239
+ /chat/fetch:
240
+ get:
241
+ consumes:
242
+ - application/json
243
+ description: Fetch list of contacts from the connected WhatsApp account
244
+ produces:
245
+ - application/json
246
+ responses:
247
+ "200":
248
+ description: OK
249
+ schema:
250
+ $ref: '#/definitions/dto.FetchContactsResponse'
251
+ "400":
252
+ description: Bad Request
253
+ schema:
254
+ $ref: '#/definitions/dto.ErrorResponse'
255
+ "401":
256
+ description: Unauthorized
257
+ schema:
258
+ $ref: '#/definitions/dto.ErrorResponse'
259
+ security:
260
+ - BearerAuth: []
261
+ summary: Fetch WhatsApp contacts
262
+ tags:
263
+ - chat
264
+ /chat/send:
265
+ post:
266
+ consumes:
267
+ - application/json
268
+ description: Send a text message to a WhatsApp number
269
+ parameters:
270
+ - description: Send Message Request
271
+ in: body
272
+ name: request
273
+ required: true
274
+ schema:
275
+ $ref: '#/definitions/dto.SendMessageRequest'
276
+ - description: CSRF Protection Token
277
+ in: header
278
+ name: X-CSRF-Token
279
+ required: true
280
+ type: string
281
+ produces:
282
+ - application/json
283
+ responses:
284
+ "200":
285
+ description: OK
286
+ schema:
287
+ $ref: '#/definitions/dto.SendMessageResponse'
288
+ "400":
289
+ description: Bad Request
290
+ schema:
291
+ $ref: '#/definitions/dto.ErrorResponse'
292
+ "401":
293
+ description: Unauthorized
294
+ schema:
295
+ $ref: '#/definitions/dto.ErrorResponse'
296
+ security:
297
+ - BearerAuth: []
298
+ summary: Send WhatsApp message
299
+ tags:
300
+ - chat
301
+ /whatsapp/connect:
302
+ post:
303
+ consumes:
304
+ - application/json
305
+ description: Initiate a connection request for a new WhatsApp account. Returns
306
+ a QR code if not connected.
307
+ parameters:
308
+ - description: Connect Request
309
+ in: body
310
+ name: request
311
+ required: true
312
+ schema:
313
+ $ref: '#/definitions/dto.ConnectRequest'
314
+ - description: CSRF Protection Token
315
+ in: header
316
+ name: X-CSRF-Token
317
+ required: true
318
+ type: string
319
+ produces:
320
+ - application/json
321
+ responses:
322
+ "200":
323
+ description: OK
324
+ schema:
325
+ $ref: '#/definitions/dto.ConnectResponse'
326
+ "401":
327
+ description: Unauthorized
328
+ schema:
329
+ $ref: '#/definitions/dto.ErrorResponse'
330
+ "500":
331
+ description: Internal Server Error
332
+ schema:
333
+ $ref: '#/definitions/dto.ErrorResponse'
334
+ security:
335
+ - BearerAuth: []
336
+ summary: Connect new WhatsApp account
337
+ tags:
338
+ - whatsapp
339
+ /whatsapp/status:
340
+ get:
341
+ consumes:
342
+ - application/json
343
+ description: Get the current status of the WhatsApp connection
344
+ produces:
345
+ - application/json
346
+ responses:
347
+ "200":
348
+ description: OK
349
+ schema:
350
+ $ref: '#/definitions/dto.ConnectionStatusResponse'
351
+ "401":
352
+ description: Unauthorized
353
+ schema:
354
+ $ref: '#/definitions/dto.ErrorResponse'
355
+ "500":
356
+ description: Internal Server Error
357
+ schema:
358
+ $ref: '#/definitions/dto.ErrorResponse'
359
+ security:
360
+ - BearerAuth: []
361
+ summary: Get WhatsApp connection status
362
+ tags:
363
+ - whatsapp
364
+ securityDefinitions:
365
+ BearerAuth:
366
+ in: header
367
+ name: Authorization
368
+ type: apiKey
369
+ swagger: "2.0"
go.mod CHANGED
@@ -5,9 +5,13 @@ go 1.24.5
5
  require (
6
  github.com/gin-contrib/gzip v1.2.5
7
  github.com/gin-gonic/gin v1.11.0
 
8
  github.com/golang-jwt/jwt/v5 v5.3.0
9
  github.com/google/uuid v1.6.0
10
  github.com/joho/godotenv v1.5.1
 
 
 
11
  go.mau.fi/whatsmeow v0.0.0-20251217143725-11cf47c62d32
12
  golang.org/x/crypto v0.46.0
13
  gorm.io/driver/postgres v1.6.0
@@ -16,6 +20,7 @@ require (
16
 
17
  require (
18
  filippo.io/edwards25519 v1.1.0 // indirect
 
19
  github.com/beeper/argo-go v1.1.2 // indirect
20
  github.com/bytedance/gopkg v0.1.3 // indirect
21
  github.com/bytedance/sonic v1.14.2 // indirect
@@ -25,9 +30,18 @@ require (
25
  github.com/elliotchance/orderedmap/v3 v3.1.0 // indirect
26
  github.com/gabriel-vasile/mimetype v1.4.12 // indirect
27
  github.com/gin-contrib/sse v1.1.0 // indirect
 
 
 
 
 
 
 
 
 
 
28
  github.com/go-playground/locales v0.14.1 // indirect
29
  github.com/go-playground/universal-translator v0.18.1 // indirect
30
- github.com/go-playground/validator/v10 v10.29.0 // indirect
31
  github.com/goccy/go-json v0.10.5 // indirect
32
  github.com/goccy/go-yaml v1.19.1 // indirect
33
  github.com/jackc/pgpassfile v1.0.0 // indirect
@@ -38,6 +52,7 @@ require (
38
  github.com/jinzhu/now v1.1.5 // indirect
39
  github.com/json-iterator/go v1.1.12 // indirect
40
  github.com/klauspost/cpuid/v2 v2.3.0 // indirect
 
41
  github.com/leodido/go-urn v1.4.0 // indirect
42
  github.com/mattn/go-colorable v0.1.14 // indirect
43
  github.com/mattn/go-isatty v0.0.20 // indirect
@@ -53,11 +68,14 @@ require (
53
  github.com/vektah/gqlparser/v2 v2.5.27 // indirect
54
  go.mau.fi/libsignal v0.2.1 // indirect
55
  go.mau.fi/util v0.9.4 // indirect
 
56
  golang.org/x/arch v0.23.0 // indirect
57
  golang.org/x/exp v0.0.0-20251209150349-8475f28825e9 // indirect
 
58
  golang.org/x/net v0.48.0 // indirect
59
  golang.org/x/sync v0.19.0 // indirect
60
  golang.org/x/sys v0.39.0 // indirect
61
  golang.org/x/text v0.32.0 // indirect
 
62
  google.golang.org/protobuf v1.36.11 // indirect
63
  )
 
5
  require (
6
  github.com/gin-contrib/gzip v1.2.5
7
  github.com/gin-gonic/gin v1.11.0
8
+ github.com/go-playground/validator/v10 v10.29.0
9
  github.com/golang-jwt/jwt/v5 v5.3.0
10
  github.com/google/uuid v1.6.0
11
  github.com/joho/godotenv v1.5.1
12
+ github.com/swaggo/files v1.0.1
13
+ github.com/swaggo/gin-swagger v1.6.1
14
+ github.com/swaggo/swag v1.16.6
15
  go.mau.fi/whatsmeow v0.0.0-20251217143725-11cf47c62d32
16
  golang.org/x/crypto v0.46.0
17
  gorm.io/driver/postgres v1.6.0
 
20
 
21
  require (
22
  filippo.io/edwards25519 v1.1.0 // indirect
23
+ github.com/KyleBanks/depth v1.2.1 // indirect
24
  github.com/beeper/argo-go v1.1.2 // indirect
25
  github.com/bytedance/gopkg v0.1.3 // indirect
26
  github.com/bytedance/sonic v1.14.2 // indirect
 
30
  github.com/elliotchance/orderedmap/v3 v3.1.0 // indirect
31
  github.com/gabriel-vasile/mimetype v1.4.12 // indirect
32
  github.com/gin-contrib/sse v1.1.0 // indirect
33
+ github.com/go-openapi/jsonpointer v0.22.4 // indirect
34
+ github.com/go-openapi/jsonreference v0.21.4 // indirect
35
+ github.com/go-openapi/spec v0.22.3 // indirect
36
+ github.com/go-openapi/swag/conv v0.25.4 // indirect
37
+ github.com/go-openapi/swag/jsonname v0.25.4 // indirect
38
+ github.com/go-openapi/swag/jsonutils v0.25.4 // indirect
39
+ github.com/go-openapi/swag/loading v0.25.4 // indirect
40
+ github.com/go-openapi/swag/stringutils v0.25.4 // indirect
41
+ github.com/go-openapi/swag/typeutils v0.25.4 // indirect
42
+ github.com/go-openapi/swag/yamlutils v0.25.4 // indirect
43
  github.com/go-playground/locales v0.14.1 // indirect
44
  github.com/go-playground/universal-translator v0.18.1 // indirect
 
45
  github.com/goccy/go-json v0.10.5 // indirect
46
  github.com/goccy/go-yaml v1.19.1 // indirect
47
  github.com/jackc/pgpassfile v1.0.0 // indirect
 
52
  github.com/jinzhu/now v1.1.5 // indirect
53
  github.com/json-iterator/go v1.1.12 // indirect
54
  github.com/klauspost/cpuid/v2 v2.3.0 // indirect
55
+ github.com/kr/text v0.2.0 // indirect
56
  github.com/leodido/go-urn v1.4.0 // indirect
57
  github.com/mattn/go-colorable v0.1.14 // indirect
58
  github.com/mattn/go-isatty v0.0.20 // indirect
 
68
  github.com/vektah/gqlparser/v2 v2.5.27 // indirect
69
  go.mau.fi/libsignal v0.2.1 // indirect
70
  go.mau.fi/util v0.9.4 // indirect
71
+ go.yaml.in/yaml/v3 v3.0.4 // indirect
72
  golang.org/x/arch v0.23.0 // indirect
73
  golang.org/x/exp v0.0.0-20251209150349-8475f28825e9 // indirect
74
+ golang.org/x/mod v0.31.0 // indirect
75
  golang.org/x/net v0.48.0 // indirect
76
  golang.org/x/sync v0.19.0 // indirect
77
  golang.org/x/sys v0.39.0 // indirect
78
  golang.org/x/text v0.32.0 // indirect
79
+ golang.org/x/tools v0.40.0 // indirect
80
  google.golang.org/protobuf v1.36.11 // indirect
81
  )
go.sum CHANGED
@@ -2,6 +2,8 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
2
  filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
3
  github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
4
  github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
 
 
5
  github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM=
6
  github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU=
7
  github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
@@ -19,6 +21,7 @@ github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gE
19
  github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g=
20
  github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg=
21
  github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
 
22
  github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
23
  github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
24
  github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -32,6 +35,33 @@ github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w
32
  github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
33
  github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
34
  github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
36
  github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
37
  github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
@@ -70,6 +100,10 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
70
  github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
71
  github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
72
  github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
 
 
 
 
73
  github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
74
  github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
75
  github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
@@ -81,8 +115,6 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
81
  github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
82
  github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs=
83
  github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
84
- github.com/mdp/qrterminal/v3 v3.2.1 h1:6+yQjiiOsSuXT5n9/m60E54vdgFsw0zhADHhHLrFet4=
85
- github.com/mdp/qrterminal/v3 v3.2.1/go.mod h1:jOTmXvnBsMy5xqLniO0R++Jmjs2sTm9dFSuQ5kpz/SU=
86
  github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
87
  github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
88
  github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -99,6 +131,8 @@ github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
99
  github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
100
  github.com/quic-go/quic-go v0.58.0 h1:ggY2pvZaVdB9EyojxL1p+5mptkuHyX5MOSv4dgWF4Ug=
101
  github.com/quic-go/quic-go v0.58.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
 
 
102
  github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
103
  github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
104
  github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
@@ -116,12 +150,19 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
116
  github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
117
  github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
118
  github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
 
 
 
 
 
 
119
  github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
120
  github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
121
  github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
122
  github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
123
  github.com/vektah/gqlparser/v2 v2.5.27 h1:RHPD3JOplpk5mP5JGX8RKZkt2/Vwj/PZv0HxTdwFp0s=
124
  github.com/vektah/gqlparser/v2 v2.5.27/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo=
 
125
  go.mau.fi/libsignal v0.2.1 h1:vRZG4EzTn70XY6Oh/pVKrQGuMHBkAWlGRC22/85m9L0=
126
  go.mau.fi/libsignal v0.2.1/go.mod h1:iVvjrHyfQqWajOUaMEsIfo3IqgVMrhWcPiiEzk7NgoU=
127
  go.mau.fi/util v0.9.4 h1:gWdUff+K2rCynRPysXalqqQyr2ahkSWaestH6YhSpso=
@@ -130,28 +171,60 @@ go.mau.fi/whatsmeow v0.0.0-20251217143725-11cf47c62d32 h1:NeE9eEYY4kEJVCfCXaAU27
130
  go.mau.fi/whatsmeow v0.0.0-20251217143725-11cf47c62d32/go.mod h1:S4OWR9+hTx+54+jRzl+NfRBXnGpPm5IRPyhXB7haSd0=
131
  go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
132
  go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
 
 
133
  golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg=
134
  golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
 
 
135
  golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
136
  golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
137
  golang.org/x/exp v0.0.0-20251209150349-8475f28825e9 h1:MDfG8Cvcqlt9XXrmEiD4epKn7VJHZO84hejP9Jmp0MM=
138
  golang.org/x/exp v0.0.0-20251209150349-8475f28825e9/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=
 
 
 
 
 
 
 
139
  golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
140
  golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
 
 
141
  golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
142
  golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
 
 
 
 
 
143
  golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 
144
  golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
145
  golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
146
  golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
147
  golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
148
- golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
149
- golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
 
 
 
 
 
150
  golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
151
  golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
 
 
 
 
 
 
152
  google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
153
  google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
154
  gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 
 
155
  gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
156
  gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
157
  gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@@ -159,5 +232,3 @@ gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
159
  gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
160
  gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
161
  gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
162
- rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY=
163
- rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs=
 
2
  filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
3
  github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
4
  github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
5
+ github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
6
+ github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
7
  github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM=
8
  github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU=
9
  github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
 
21
  github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g=
22
  github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg=
23
  github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
24
+ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
25
  github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
26
  github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
27
  github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 
35
  github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
36
  github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
37
  github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
38
+ github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4=
39
+ github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80=
40
+ github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8=
41
+ github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4=
42
+ github.com/go-openapi/spec v0.22.3 h1:qRSmj6Smz2rEBxMnLRBMeBWxbbOvuOoElvSvObIgwQc=
43
+ github.com/go-openapi/spec v0.22.3/go.mod h1:iIImLODL2loCh3Vnox8TY2YWYJZjMAKYyLH2Mu8lOZs=
44
+ github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
45
+ github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4=
46
+ github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU=
47
+ github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI=
48
+ github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag=
49
+ github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA=
50
+ github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY=
51
+ github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo=
52
+ github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM=
53
+ github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s=
54
+ github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE=
55
+ github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8=
56
+ github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0=
57
+ github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw=
58
+ github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE=
59
+ github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw=
60
+ github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc=
61
+ github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4=
62
+ github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg=
63
+ github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls=
64
+ github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
65
  github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
66
  github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
67
  github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
 
100
  github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
101
  github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
102
  github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
103
+ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
104
+ github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
105
+ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
106
+ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
107
  github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
108
  github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
109
  github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
 
115
  github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
116
  github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs=
117
  github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
 
 
118
  github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
119
  github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
120
  github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 
131
  github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
132
  github.com/quic-go/quic-go v0.58.0 h1:ggY2pvZaVdB9EyojxL1p+5mptkuHyX5MOSv4dgWF4Ug=
133
  github.com/quic-go/quic-go v0.58.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
134
+ github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
135
+ github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
136
  github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
137
  github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
138
  github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
 
150
  github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
151
  github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
152
  github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
153
+ github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
154
+ github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
155
+ github.com/swaggo/gin-swagger v1.6.1 h1:Ri06G4gc9N4t4k8hekMigJ9zKTFSlqj/9paAQCQs7cY=
156
+ github.com/swaggo/gin-swagger v1.6.1/go.mod h1:LQ+hJStHakCWRiK/YNYtJOu4mR2FP+pxLnILT/qNiTw=
157
+ github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI=
158
+ github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg=
159
  github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
160
  github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
161
  github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
162
  github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
163
  github.com/vektah/gqlparser/v2 v2.5.27 h1:RHPD3JOplpk5mP5JGX8RKZkt2/Vwj/PZv0HxTdwFp0s=
164
  github.com/vektah/gqlparser/v2 v2.5.27/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo=
165
+ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
166
  go.mau.fi/libsignal v0.2.1 h1:vRZG4EzTn70XY6Oh/pVKrQGuMHBkAWlGRC22/85m9L0=
167
  go.mau.fi/libsignal v0.2.1/go.mod h1:iVvjrHyfQqWajOUaMEsIfo3IqgVMrhWcPiiEzk7NgoU=
168
  go.mau.fi/util v0.9.4 h1:gWdUff+K2rCynRPysXalqqQyr2ahkSWaestH6YhSpso=
 
171
  go.mau.fi/whatsmeow v0.0.0-20251217143725-11cf47c62d32/go.mod h1:S4OWR9+hTx+54+jRzl+NfRBXnGpPm5IRPyhXB7haSd0=
172
  go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
173
  go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
174
+ go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
175
+ go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
176
  golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg=
177
  golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
178
+ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
179
+ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
180
  golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
181
  golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
182
  golang.org/x/exp v0.0.0-20251209150349-8475f28825e9 h1:MDfG8Cvcqlt9XXrmEiD4epKn7VJHZO84hejP9Jmp0MM=
183
  golang.org/x/exp v0.0.0-20251209150349-8475f28825e9/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=
184
+ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
185
+ golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
186
+ golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
187
+ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
188
+ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
189
+ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
190
+ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
191
  golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
192
  golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
193
+ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
194
+ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
195
  golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
196
  golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
197
+ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
198
+ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
199
+ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
200
+ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
201
+ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
202
  golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
203
+ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
204
  golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
205
  golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
206
  golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
207
  golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
208
+ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
209
+ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
210
+ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
211
+ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
212
+ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
213
+ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
214
+ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
215
  golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
216
  golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
217
+ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
218
+ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
219
+ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
220
+ golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
221
+ golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
222
+ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
223
  google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
224
  google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
225
  gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
226
+ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
227
+ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
228
  gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
229
  gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
230
  gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 
232
  gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
233
  gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
234
  gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
 
 
main.go CHANGED
@@ -5,6 +5,24 @@ import (
5
  "whatsapp-backend/router"
6
  )
7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  func main() {
9
  appProvider := provider.NewAppProvider()
10
  router.RunRouter(appProvider)
 
5
  "whatsapp-backend/router"
6
  )
7
 
8
+ // @title Whatsapp Backend API
9
+ // @version 1.0
10
+ // @description This is the API documentation for the Whatsapp Backend.
11
+ // @termsOfService http://swagger.io/terms/
12
+
13
+ // @contact.name API Support
14
+ // @contact.url http://www.swagger.io/support
15
+ // @contact.email support@swagger.io
16
+
17
+ // @license.name Apache 2.0
18
+ // @license.url http://www.apache.org/licenses/LICENSE-2.0.html
19
+
20
+ // @host localhost:8080
21
+ // @BasePath /api
22
+
23
+ // @securityDefinitions.apikey BearerAuth
24
+ // @in header
25
+ // @name Authorization
26
  func main() {
27
  appProvider := provider.NewAppProvider()
28
  router.RunRouter(appProvider)
models/dto/chat_dto.go ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package dto
2
+
3
+ import "github.com/google/uuid"
4
+
5
+ type SendMessageRequest struct {
6
+ Recipient string `json:"recipient" binding:"required" example:"628123456789"`
7
+ Message string `json:"message" binding:"required" example:"Hello world"`
8
+ }
9
+
10
+ type SendMessageResponse struct {
11
+ MessageID string `json:"message_id"`
12
+ Status string `json:"status"`
13
+ }
14
+
15
+ type ContactResponse struct {
16
+ JID string `json:"jid"`
17
+ Name string `json:"name"`
18
+ FullName string `json:"full_name"`
19
+ PushName string `json:"push_name"`
20
+ PhoneNumber string `json:"phone_number"`
21
+ }
22
+
23
+ type FetchContactsResponse struct {
24
+ AccountID uuid.UUID `json:"account_id"`
25
+ Contacts []ContactResponse `json:"contacts"`
26
+ }
models/error/error.go CHANGED
@@ -42,4 +42,8 @@ var (
42
  ERR_DB_CONNECTION_FAILED = errors.New("Failed to get database connection")
43
  ERR_SQLSTORE_FAILED = errors.New("Failed to initialize SQL store")
44
  ERR_DEVICE_STORE_FAILED = errors.New("Failed to retrieve device from store")
 
 
 
 
45
  )
 
42
  ERR_DB_CONNECTION_FAILED = errors.New("Failed to get database connection")
43
  ERR_SQLSTORE_FAILED = errors.New("Failed to initialize SQL store")
44
  ERR_DEVICE_STORE_FAILED = errors.New("Failed to retrieve device from store")
45
+
46
+ // Chat Errors
47
+ ERR_CLIENT_NOT_CONNECTED = errors.New("WhatsApp client is not connected")
48
+ ERR_BAD_REQUEST = errors.New("Bad request")
49
  )
provider/controller_provider.go CHANGED
@@ -5,21 +5,25 @@ import "whatsapp-backend/controllers"
5
  type ControllerProvider interface {
6
  ProvideConnectionController() controllers.ConnectionController
7
  ProvideAuthController() controllers.AuthController
 
8
  }
9
 
10
  type controllerProvider struct {
11
  connectionController controllers.ConnectionController
12
  authController controllers.AuthController
 
13
  }
14
 
15
  func NewControllerProvider(servicesProvider ServicesProvider) ControllerProvider {
16
 
17
  connectionController := controllers.NewConnectionController(servicesProvider.ProvideConnectionService())
18
  authController := controllers.NewAuthController(servicesProvider.ProvideAuthService())
 
19
 
20
  return &controllerProvider{
21
  connectionController: connectionController,
22
  authController: authController,
 
23
  }
24
  }
25
 
@@ -30,3 +34,7 @@ func (c *controllerProvider) ProvideConnectionController() controllers.Connectio
30
  func (c *controllerProvider) ProvideAuthController() controllers.AuthController {
31
  return c.authController
32
  }
 
 
 
 
 
5
  type ControllerProvider interface {
6
  ProvideConnectionController() controllers.ConnectionController
7
  ProvideAuthController() controllers.AuthController
8
+ ProvideChatController() controllers.ChatController
9
  }
10
 
11
  type controllerProvider struct {
12
  connectionController controllers.ConnectionController
13
  authController controllers.AuthController
14
+ chatController controllers.ChatController
15
  }
16
 
17
  func NewControllerProvider(servicesProvider ServicesProvider) ControllerProvider {
18
 
19
  connectionController := controllers.NewConnectionController(servicesProvider.ProvideConnectionService())
20
  authController := controllers.NewAuthController(servicesProvider.ProvideAuthService())
21
+ chatController := controllers.NewChatController(servicesProvider.ProvideChatService())
22
 
23
  return &controllerProvider{
24
  connectionController: connectionController,
25
  authController: authController,
26
+ chatController: chatController,
27
  }
28
  }
29
 
 
34
  func (c *controllerProvider) ProvideAuthController() controllers.AuthController {
35
  return c.authController
36
  }
37
+
38
+ func (c *controllerProvider) ProvideChatController() controllers.ChatController {
39
+ return c.chatController
40
+ }
provider/repositories_provider.go CHANGED
@@ -5,21 +5,25 @@ import "whatsapp-backend/repositories"
5
  type RepositoriesProvider interface {
6
  ProvideConnectionRepository() repositories.ConnectionRepository
7
  ProvideAuthRepository() repositories.AuthRepository
 
8
  }
9
 
10
  type repositoriesProvider struct {
11
  connectionRepository repositories.ConnectionRepository
12
  authRepository repositories.AuthRepository
 
13
  }
14
 
15
  func NewRepositoriesProvider(cfg ConfigProvider) RepositoriesProvider {
16
  db := cfg.ProvideDatabaseConfig().GetInstance()
17
  connectionRepository, _ := repositories.NewConnectionRepository(db)
18
  authRepository := repositories.NewAuthRepository(db)
 
19
 
20
  return &repositoriesProvider{
21
  connectionRepository: connectionRepository,
22
  authRepository: authRepository,
 
23
  }
24
  }
25
 
@@ -30,3 +34,7 @@ func (rp *repositoriesProvider) ProvideConnectionRepository() repositories.Conne
30
  func (rp *repositoriesProvider) ProvideAuthRepository() repositories.AuthRepository {
31
  return rp.authRepository
32
  }
 
 
 
 
 
5
  type RepositoriesProvider interface {
6
  ProvideConnectionRepository() repositories.ConnectionRepository
7
  ProvideAuthRepository() repositories.AuthRepository
8
+ ProvideChatRepository() repositories.ChatRepository
9
  }
10
 
11
  type repositoriesProvider struct {
12
  connectionRepository repositories.ConnectionRepository
13
  authRepository repositories.AuthRepository
14
+ chatRepository repositories.ChatRepository
15
  }
16
 
17
  func NewRepositoriesProvider(cfg ConfigProvider) RepositoriesProvider {
18
  db := cfg.ProvideDatabaseConfig().GetInstance()
19
  connectionRepository, _ := repositories.NewConnectionRepository(db)
20
  authRepository := repositories.NewAuthRepository(db)
21
+ chatRepository := repositories.NewChatRepository()
22
 
23
  return &repositoriesProvider{
24
  connectionRepository: connectionRepository,
25
  authRepository: authRepository,
26
+ chatRepository: chatRepository,
27
  }
28
  }
29
 
 
34
  func (rp *repositoriesProvider) ProvideAuthRepository() repositories.AuthRepository {
35
  return rp.authRepository
36
  }
37
+
38
+ func (rp *repositoriesProvider) ProvideChatRepository() repositories.ChatRepository {
39
+ return rp.chatRepository
40
+ }
provider/services_provider.go CHANGED
@@ -5,20 +5,24 @@ import "whatsapp-backend/services"
5
  type ServicesProvider interface {
6
  ProvideConnectionService() services.ConnectionService
7
  ProvideAuthService() services.AuthService
 
8
  }
9
 
10
  type servicesProvider struct {
11
  connectionService services.ConnectionService
12
  authService services.AuthService
 
13
  }
14
 
15
  func NewServicesProvider(repoProvider RepositoriesProvider, configProvider ConfigProvider) ServicesProvider {
16
  connectionService := services.NewConnectionService(repoProvider.ProvideConnectionRepository())
17
  authService := services.NewAuthService(repoProvider.ProvideAuthRepository(), repoProvider.ProvideConnectionRepository(), configProvider.ProvideJWTConfig())
 
18
 
19
  return &servicesProvider{
20
  connectionService: connectionService,
21
  authService: authService,
 
22
  }
23
  }
24
 
@@ -29,3 +33,7 @@ func (s *servicesProvider) ProvideConnectionService() services.ConnectionService
29
  func (s *servicesProvider) ProvideAuthService() services.AuthService {
30
  return s.authService
31
  }
 
 
 
 
 
5
  type ServicesProvider interface {
6
  ProvideConnectionService() services.ConnectionService
7
  ProvideAuthService() services.AuthService
8
+ ProvideChatService() services.ChatService
9
  }
10
 
11
  type servicesProvider struct {
12
  connectionService services.ConnectionService
13
  authService services.AuthService
14
+ chatService services.ChatService
15
  }
16
 
17
  func NewServicesProvider(repoProvider RepositoriesProvider, configProvider ConfigProvider) ServicesProvider {
18
  connectionService := services.NewConnectionService(repoProvider.ProvideConnectionRepository())
19
  authService := services.NewAuthService(repoProvider.ProvideAuthRepository(), repoProvider.ProvideConnectionRepository(), configProvider.ProvideJWTConfig())
20
+ chatService := services.NewChatService(connectionService, repoProvider.ProvideChatRepository())
21
 
22
  return &servicesProvider{
23
  connectionService: connectionService,
24
  authService: authService,
25
+ chatService: chatService,
26
  }
27
  }
28
 
 
33
  func (s *servicesProvider) ProvideAuthService() services.AuthService {
34
  return s.authService
35
  }
36
+
37
+ func (s *servicesProvider) ProvideChatService() services.ChatService {
38
+ return s.chatService
39
+ }
repositories/chat_repository.go ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package repositories
2
+
3
+ import (
4
+ "context"
5
+
6
+ "go.mau.fi/whatsmeow"
7
+ waProto "go.mau.fi/whatsmeow/binary/proto"
8
+ "go.mau.fi/whatsmeow/types"
9
+ "google.golang.org/protobuf/proto"
10
+ )
11
+
12
+ type ChatRepository interface {
13
+ FetchContacts(ctx context.Context, client *whatsmeow.Client) (map[types.JID]types.ContactInfo, error)
14
+ SendMessage(ctx context.Context, client *whatsmeow.Client, recipient string, message string) (string, error)
15
+ }
16
+
17
+ type chatRepository struct{}
18
+
19
+ func NewChatRepository() ChatRepository {
20
+ return &chatRepository{}
21
+ }
22
+
23
+ func (r *chatRepository) FetchContacts(ctx context.Context, client *whatsmeow.Client) (map[types.JID]types.ContactInfo, error) {
24
+ contacts, err := client.Store.Contacts.GetAllContacts(ctx)
25
+ if err != nil {
26
+ return nil, err
27
+ }
28
+
29
+ return contacts, nil
30
+ }
31
+
32
+ func (r *chatRepository) SendMessage(ctx context.Context, client *whatsmeow.Client, recipient string, message string) (string, error) {
33
+ jid, err := types.ParseJID(recipient + "@s.whatsapp.net")
34
+ if err != nil {
35
+ return "", err
36
+ }
37
+
38
+ msg := &waProto.Message{
39
+ Conversation: proto.String(message),
40
+ }
41
+
42
+ resp, err := client.SendMessage(ctx, jid, msg)
43
+ if err != nil {
44
+ return "", err
45
+ }
46
+
47
+ return resp.ID, nil
48
+ }
router/auth_router.go CHANGED
@@ -15,7 +15,7 @@ func AuthRouter(router *gin.Engine, controller provider.ControllerProvider, midd
15
  routerGroup.POST("/register", authController.Register)
16
  routerGroup.POST("/login", authController.Login)
17
  routerGroup.POST("/logout", middleware.ProvideAuthMiddleware().RequireAuth(), middleware.ProvideAuthMiddleware().RequireCSRF(), authController.Logout)
18
- routerGroup.POST("/refresh", authController.RefreshToken)
19
  routerGroup.GET("/me", middleware.ProvideAuthMiddleware().RequireAuth(), authController.Me)
20
  }
21
  }
 
15
  routerGroup.POST("/register", authController.Register)
16
  routerGroup.POST("/login", authController.Login)
17
  routerGroup.POST("/logout", middleware.ProvideAuthMiddleware().RequireAuth(), middleware.ProvideAuthMiddleware().RequireCSRF(), authController.Logout)
18
+ routerGroup.POST("/refresh", middleware.ProvideAuthMiddleware().RequireAuth(), middleware.ProvideAuthMiddleware().RequireCSRF(), authController.RefreshToken)
19
  routerGroup.GET("/me", middleware.ProvideAuthMiddleware().RequireAuth(), authController.Me)
20
  }
21
  }
router/chat_router.go ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package router
2
+
3
+ import (
4
+ "whatsapp-backend/provider"
5
+
6
+ "github.com/gin-contrib/gzip"
7
+ "github.com/gin-gonic/gin"
8
+ )
9
+
10
+ func ChatRouter(router *gin.Engine, controller provider.ControllerProvider, middleware provider.MiddlewareProvider) {
11
+ chatController := controller.ProvideChatController()
12
+ authMiddleware := middleware.ProvideAuthMiddleware()
13
+ routerGroup := router.Group("/api/chat", authMiddleware.RequireAuth(), authMiddleware.RequireCSRF())
14
+ routerGroup.Use(gzip.Gzip(gzip.DefaultCompression))
15
+ {
16
+ routerGroup.GET("/fetch", chatController.FetchContacts)
17
+ routerGroup.POST("/send", chatController.SendMessage)
18
+ }
19
+ }
router/router.go CHANGED
@@ -2,6 +2,11 @@ package router
2
 
3
  import (
4
  "whatsapp-backend/provider"
 
 
 
 
 
5
  )
6
 
7
  func RunRouter(appProvider provider.AppProvider) {
@@ -9,6 +14,9 @@ func RunRouter(appProvider provider.AppProvider) {
9
 
10
  ConnectionRouter(router, controller, middleware)
11
  AuthRouter(router, controller, middleware)
 
 
 
12
 
13
  err := router.Run(config.ProvideEnvConfig().GetTCPAddress())
14
  if err != nil {
 
2
 
3
  import (
4
  "whatsapp-backend/provider"
5
+
6
+ _ "whatsapp-backend/docs"
7
+
8
+ swaggerFiles "github.com/swaggo/files"
9
+ ginSwagger "github.com/swaggo/gin-swagger"
10
  )
11
 
12
  func RunRouter(appProvider provider.AppProvider) {
 
14
 
15
  ConnectionRouter(router, controller, middleware)
16
  AuthRouter(router, controller, middleware)
17
+ ChatRouter(router, controller, middleware)
18
+
19
+ router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
20
 
21
  err := router.Run(config.ProvideEnvConfig().GetTCPAddress())
22
  if err != nil {
services/chat_service.go ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package services
2
+
3
+ import (
4
+ "context"
5
+ "whatsapp-backend/models/dto"
6
+ http_error "whatsapp-backend/models/error"
7
+ "whatsapp-backend/repositories"
8
+
9
+ "github.com/google/uuid"
10
+ )
11
+
12
+ type ChatService interface {
13
+ FetchContacts(ctx context.Context, accountID uuid.UUID) ([]dto.ContactResponse, error)
14
+ SendMessage(ctx context.Context, accountID uuid.UUID, recipient string, message string) (string, error)
15
+ }
16
+
17
+ type chatService struct {
18
+ connectionService ConnectionService
19
+ chatRepository repositories.ChatRepository
20
+ }
21
+
22
+ func NewChatService(connectionService ConnectionService, chatRepository repositories.ChatRepository) ChatService {
23
+ return &chatService{
24
+ connectionService: connectionService,
25
+ chatRepository: chatRepository,
26
+ }
27
+ }
28
+
29
+ func (s *chatService) FetchContacts(ctx context.Context, accountID uuid.UUID) ([]dto.ContactResponse, error) {
30
+ client, err := s.connectionService.GetActiveClient(accountID)
31
+ if err != nil {
32
+ return nil, err
33
+ }
34
+
35
+ if !client.IsConnected() {
36
+ return nil, http_error.ERR_CLIENT_NOT_CONNECTED
37
+ }
38
+
39
+ contacts, err := s.chatRepository.FetchContacts(ctx, client)
40
+ if err != nil {
41
+ return nil, err
42
+ }
43
+
44
+ var response []dto.ContactResponse
45
+ for jid, contact := range contacts {
46
+ response = append(response, dto.ContactResponse{
47
+ JID: jid.String(),
48
+ Name: contact.FirstName,
49
+ FullName: contact.FullName,
50
+ PushName: contact.PushName,
51
+ PhoneNumber: jid.User,
52
+ })
53
+ }
54
+
55
+ return response, nil
56
+ }
57
+
58
+ func (s *chatService) SendMessage(ctx context.Context, accountID uuid.UUID, recipient string, message string) (string, error) {
59
+ client, err := s.connectionService.GetActiveClient(accountID)
60
+ if err != nil {
61
+ return "", err
62
+ }
63
+
64
+ if !client.IsConnected() {
65
+ return "", http_error.ERR_CLIENT_NOT_CONNECTED
66
+ }
67
+
68
+ msgID, err := s.chatRepository.SendMessage(ctx, client, recipient, message)
69
+ if err != nil {
70
+ return "", err
71
+ }
72
+
73
+ return msgID, nil
74
+ }