feat: Notification service qo'shildi va RabbitMQ integratsiya qilindi
This commit is contained in:
19
Dockerfile
Normal file
19
Dockerfile
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
FROM golang:alpine as build
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN go build -o notification ./cmd/main.go
|
||||||
|
|
||||||
|
FROM alpine
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY --from=build /app/notification .
|
||||||
|
|
||||||
|
CMD ["./notification"]
|
||||||
112
README.MD
Normal file
112
README.MD
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
# Notification Service
|
||||||
|
|
||||||
|
A microservice for handling and delivering notifications through various channels like SMS and email using RabbitMQ as a message broker.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This notification service is designed as a standalone microservice that consumes notification requests from a RabbitMQ queue and routes them to the appropriate notification provider based on the notification type. Currently, it supports SMS and email notifications.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Message consumption from RabbitMQ
|
||||||
|
- Support for multiple notification channels (SMS, email)
|
||||||
|
- Extensible architecture for adding new notification types
|
||||||
|
- Asynchronous notification handling
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
The notification service follows a clean architecture approach:
|
||||||
|
|
||||||
|
- **Domain Layer**: Contains core business logic and port interfaces
|
||||||
|
- **Infrastructure Layer**: Implements the ports with concrete adapters
|
||||||
|
- **RabbitMQ**: Used as a message broker for consuming notification requests
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- Go 1.x+
|
||||||
|
- RabbitMQ server
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
|
||||||
|
1. Clone the repository:
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/JscorpTech/notification.git
|
||||||
|
cd notification
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Install dependencies:
|
||||||
|
```bash
|
||||||
|
go mod download
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Build the application:
|
||||||
|
```bash
|
||||||
|
go build -o notification-service
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Configure your RabbitMQ connection and other settings in the appropriate configuration files.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Running the service
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./notification-service
|
||||||
|
```
|
||||||
|
|
||||||
|
This will start the notification consumer that listens for incoming notification requests.
|
||||||
|
|
||||||
|
### Sending a notification
|
||||||
|
|
||||||
|
Notifications should be published to the RabbitMQ exchange with the following JSON format:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "email",
|
||||||
|
"message": "Hello, this is a test notification.",
|
||||||
|
"to": ["user@example.com"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Available notification types:
|
||||||
|
- `email`: For email notifications
|
||||||
|
- `sms`: For SMS notifications
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
notification/
|
||||||
|
├── cmd/
|
||||||
|
│ └── main.go # Entry point
|
||||||
|
├── internal/
|
||||||
|
│ ├── domain/
|
||||||
|
│ │ └── ports.go # Interfaces
|
||||||
|
│ ├── notifier/
|
||||||
|
│ │ ├── email.go # Email notification implementation
|
||||||
|
│ │ └── sms.go # SMS notification implementation
|
||||||
|
│ ├── rabbitmq/
|
||||||
|
│ │ └── connection.go # RabbitMQ connection handling
|
||||||
|
│ └── consumer/
|
||||||
|
│ └── consumer.go # Implementation of the notification consumer
|
||||||
|
└── README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
1. Fork the repository
|
||||||
|
2. Create your feature branch: `git checkout -b feature/my-new-feature`
|
||||||
|
3. Commit your changes: `git commit -am 'Add some feature'`
|
||||||
|
4. Push to the branch: `git push origin feature/my-new-feature`
|
||||||
|
5. Submit a pull request
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[Add your license here]
|
||||||
|
|
||||||
|
## Contact
|
||||||
|
|
||||||
|
JscorpTech - [GitHub](https://github.com/JscorpTech)
|
||||||
10
cmd/main.go
Normal file
10
cmd/main.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/JscorpTech/notification/internal/consumer"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
notification := consumer.NewNotificationConsumer()
|
||||||
|
notification.Start()
|
||||||
|
}
|
||||||
12
go.mod
Normal file
12
go.mod
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
module github.com/JscorpTech/notification
|
||||||
|
|
||||||
|
go 1.24.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/k0kubun/pp/v3 v3.4.1 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||||
|
github.com/streadway/amqp v1.1.0 // indirect
|
||||||
|
golang.org/x/sys v0.5.0 // indirect
|
||||||
|
golang.org/x/text v0.19.0 // indirect
|
||||||
|
)
|
||||||
13
go.sum
Normal file
13
go.sum
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
github.com/k0kubun/pp/v3 v3.4.1 h1:1WdFZDRRqe8UsR61N/2RoOZ3ziTEqgTPVqKrHeb779Y=
|
||||||
|
github.com/k0kubun/pp/v3 v3.4.1/go.mod h1:+SiNiqKnBfw1Nkj82Lh5bIeKQOAkPy6Xw9CAZUZ8npI=
|
||||||
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
|
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||||
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
|
github.com/streadway/amqp v1.1.0 h1:py12iX8XSyI7aN/3dUT8DFIDJazNJsVJdxNVEpnQTZM=
|
||||||
|
github.com/streadway/amqp v1.1.0/go.mod h1:WYSrTEYHOXHd0nwFeUXAe2G2hRnQT+deZJJf88uS9Bg=
|
||||||
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||||
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||||
|
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||||
13
internal/config/config.go
Normal file
13
internal/config/config.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetEnv(key, fallback string) string {
|
||||||
|
val := os.Getenv(key)
|
||||||
|
if val == "" {
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
62
internal/consumer/notification.go
Normal file
62
internal/consumer/notification.go
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package consumer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/JscorpTech/notification/internal/domain"
|
||||||
|
"github.com/JscorpTech/notification/internal/notifier"
|
||||||
|
"github.com/JscorpTech/notification/internal/rabbitmq"
|
||||||
|
"github.com/streadway/amqp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type notificationConsumer struct{}
|
||||||
|
|
||||||
|
func NewNotificationConsumer() domain.NotificationConsumerPort {
|
||||||
|
return ¬ificationConsumer{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *notificationConsumer) Start() {
|
||||||
|
conn, ch, err := rabbitmq.Connect()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
defer ch.Close()
|
||||||
|
|
||||||
|
exchangeName := "notification"
|
||||||
|
queueName := "notification"
|
||||||
|
routingKey := "notification"
|
||||||
|
|
||||||
|
ch.ExchangeDeclare(exchangeName, "direct", true, false, false, false, nil)
|
||||||
|
q, _ := ch.QueueDeclare(queueName, true, false, false, false, nil)
|
||||||
|
ch.QueueBind(q.Name, routingKey, exchangeName, false, nil)
|
||||||
|
|
||||||
|
msgs, _ := ch.Consume(q.Name, "", true, false, false, false, nil)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for msg := range msgs {
|
||||||
|
go n.Handler(msg)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
fmt.Println("🚀 Server started. Ctrl+C to quit.")
|
||||||
|
select {}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *notificationConsumer) Handler(msg amqp.Delivery) {
|
||||||
|
var notification domain.NotificationMsg
|
||||||
|
err := json.Unmarshal(msg.Body, ¬ification)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print(err.Error())
|
||||||
|
}
|
||||||
|
var ntf domain.NotifierPort
|
||||||
|
switch notification.Type {
|
||||||
|
case "sms":
|
||||||
|
ntf = notifier.NewSmsNotifier()
|
||||||
|
case "email":
|
||||||
|
ntf = notifier.NewEmailNotifier()
|
||||||
|
}
|
||||||
|
ntf.SendMessage(notification.To, notification.Message)
|
||||||
|
}
|
||||||
18
internal/domain/notification.go
Normal file
18
internal/domain/notification.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package domain
|
||||||
|
|
||||||
|
import "github.com/streadway/amqp"
|
||||||
|
|
||||||
|
type NotificationConsumerPort interface {
|
||||||
|
Start()
|
||||||
|
Handler(amqp.Delivery)
|
||||||
|
}
|
||||||
|
|
||||||
|
type NotifierPort interface {
|
||||||
|
SendMessage([]string, string)
|
||||||
|
}
|
||||||
|
|
||||||
|
type NotificationMsg struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
To []string `json:"to"`
|
||||||
|
}
|
||||||
16
internal/notifier/email.go
Normal file
16
internal/notifier/email.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package notifier
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/JscorpTech/notification/internal/domain"
|
||||||
|
"github.com/k0kubun/pp/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type emailNotifier struct{}
|
||||||
|
|
||||||
|
func NewEmailNotifier() domain.NotifierPort {
|
||||||
|
return &emailNotifier{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *emailNotifier) SendMessage(to []string, body string) {
|
||||||
|
pp.Print(to, body)
|
||||||
|
}
|
||||||
16
internal/notifier/sms.go
Normal file
16
internal/notifier/sms.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package notifier
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/JscorpTech/notification/internal/domain"
|
||||||
|
"github.com/k0kubun/pp/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type smsNotifier struct{}
|
||||||
|
|
||||||
|
func NewSmsNotifier() domain.NotifierPort {
|
||||||
|
return &smsNotifier{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *smsNotifier) SendMessage(to []string, body string) {
|
||||||
|
pp.Print(to, body)
|
||||||
|
}
|
||||||
21
internal/rabbitmq/connection.go
Normal file
21
internal/rabbitmq/connection.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package rabbitmq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/streadway/amqp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Connect() (*amqp.Connection, *amqp.Channel, error) {
|
||||||
|
conn, err := amqp.Dial("amqp://guest:guest@127.0.0.1:5672/")
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
ch, err := conn.Channel()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
log.Println("🐇 Connected to RabbitMQ")
|
||||||
|
|
||||||
|
return conn, ch, nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user