diff --git a/notification/.env.example b/notification/.env.example deleted file mode 100644 index 54202b8..0000000 --- a/notification/.env.example +++ /dev/null @@ -1,15 +0,0 @@ -RABBITMQ_URL=amqp://guest:guest@127.0.0.1:5672/ - - -REDIS_ADDRESS=127.0.0.1:6379 -REDIS_PASSWORD= -REDIS_DB=0 - -ESKIZ_DOMAIN="https://notify.eskiz.uz/api" -ESKIZ_USER="admin@gmail.com" -ESKIZ_PASSWORD="password" -ESKIZ_FROM="4546" - -PMB_DOMAIN="" -PMB_USER="" -PMB_PASSWORD="" \ No newline at end of file diff --git a/notification/.gitignore b/notification/.gitignore deleted file mode 100644 index 61a32ec..0000000 --- a/notification/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -.env -./bin -./main \ No newline at end of file diff --git a/notification/Dockerfile b/notification/Dockerfile deleted file mode 100644 index 8f77c01..0000000 --- a/notification/Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -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 . -COPY ./.env /app/ - -CMD ["./notification"] diff --git a/notification/README.MD b/notification/README.MD deleted file mode 100644 index 9577df7..0000000 --- a/notification/README.MD +++ /dev/null @@ -1,133 +0,0 @@ -# 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"] -} -``` - -Python example -```python -from kombu import Connection, Exchange, Producer - -# RabbitMQ ulanishi -rabbit_url = 'amqp://guest:guest@127.0.0.1:5672/' -connection = Connection(rabbit_url) -channel = connection.channel() - -exchange = Exchange('notification', type='direct') - -# Producer yaratish -producer = Producer(channel, exchange=exchange, routing_key="notification") - -# Xabar yuborish -message = {'type': 'sms', 'message': "classcom.uz sayti va mobil ilovasiga ro'yxatdan o'tishingingiz uchun tasdiqlash kodi: 1234", "to": ["+998888112309", "+998943990509"]} -producer.publish(message) - -print("Message sent to all workers!") -``` - -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) diff --git a/notification/cmd/main.go b/notification/cmd/main.go deleted file mode 100644 index 0a1748b..0000000 --- a/notification/cmd/main.go +++ /dev/null @@ -1,20 +0,0 @@ -package main - -import ( - "context" - - "github.com/JscorpTech/notification/internal/consumer" - "github.com/JscorpTech/notification/internal/redis" - "github.com/joho/godotenv" -) - -var ctx = context.Background() - -func main() { - if err := godotenv.Load(); err != nil { - panic(err) - } - redis.InitRedis() - notification := consumer.NewNotificationConsumer(ctx) - notification.Start() -} diff --git a/notification/docker-compose.yml b/notification/docker-compose.yml deleted file mode 100644 index 4ca2736..0000000 --- a/notification/docker-compose.yml +++ /dev/null @@ -1,12 +0,0 @@ -networks: - lamenu: - external: true - name: lamenu - -services: - notification: - build: - context: . - dockerfile: Dockerfile - networks: - - lamenu diff --git a/notification/go.mod b/notification/go.mod deleted file mode 100644 index 9c1fe81..0000000 --- a/notification/go.mod +++ /dev/null @@ -1,16 +0,0 @@ -module github.com/JscorpTech/notification - -go 1.24.0 - -require ( - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/joho/godotenv v1.5.1 // indirect - 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/redis/go-redis/v9 v9.7.3 // 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 -) diff --git a/notification/go.sum b/notification/go.sum deleted file mode 100644 index c6bb9eb..0000000 --- a/notification/go.sum +++ /dev/null @@ -1,21 +0,0 @@ -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= -github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -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/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM= -github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA= -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= diff --git a/notification/internal/config/config.go b/notification/internal/config/config.go deleted file mode 100644 index 2f86d1c..0000000 --- a/notification/internal/config/config.go +++ /dev/null @@ -1,13 +0,0 @@ -package config - -import ( - "os" -) - -func GetEnv(key, fallback string) string { - val := os.Getenv(key) - if val == "" { - return fallback - } - return val -} diff --git a/notification/internal/consumer/notification.go b/notification/internal/consumer/notification.go deleted file mode 100644 index 7543116..0000000 --- a/notification/internal/consumer/notification.go +++ /dev/null @@ -1,67 +0,0 @@ -package consumer - -import ( - "context" - "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 { - Ctx context.Context -} - -func NewNotificationConsumer(ctx context.Context) domain.NotificationConsumerPort { - return ¬ificationConsumer{ - Ctx: ctx, - } -} - -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(n.Ctx) - case "email": - ntf = notifier.NewEmailNotifier() - } - ntf.SendMessage(notification.To, notification.Message) -} diff --git a/notification/internal/domain/eskiz.go b/notification/internal/domain/eskiz.go deleted file mode 100644 index 9cccfaa..0000000 --- a/notification/internal/domain/eskiz.go +++ /dev/null @@ -1,27 +0,0 @@ -package domain - -type EskizLogin struct { - Email string `json:"email"` - Password string `json:"password"` -} - -type EskizLoginRes struct { - Message string - TokenType string - Data struct { - Token string - } -} - -type EskizMessage struct { - Phone string `json:"mobile_phone"` - Message string `json:"message"` - From string `json:"from"` - CallbackURL string `json:"callback_url"` -} - -type EskizMessageRes struct { - ID string `json:"id"` - Message string `json:"message"` - Status string `json:"status"` -} diff --git a/notification/internal/domain/notification.go b/notification/internal/domain/notification.go deleted file mode 100644 index 8442b1d..0000000 --- a/notification/internal/domain/notification.go +++ /dev/null @@ -1,21 +0,0 @@ -package domain - -import "github.com/streadway/amqp" - -type NotificationConsumerPort interface { - Start() - Handler(amqp.Delivery) -} - -type SMSServicePort interface { - SendSMS(string, string) error -} -type NotifierPort interface { - SendMessage([]string, string) -} - -type NotificationMsg struct { - Type string `json:"type"` - Message string `json:"message"` - To []string `json:"to"` -} diff --git a/notification/internal/domain/playmobile.go b/notification/internal/domain/playmobile.go deleted file mode 100644 index 0516a12..0000000 --- a/notification/internal/domain/playmobile.go +++ /dev/null @@ -1,20 +0,0 @@ -package domain - -type PmbContent struct { - Text string `json:"text"` -} - -type PmbSMS struct { - Originator string `json:"originator"` - Content PmbContent `json:"content"` -} - -type PmbMessage struct { - Recipient string `json:"recipient"` - MessageID string `json:"message-id"` - Sms PmbSMS `json:"sms"` -} - -type PmbPayload struct { - Messages []PmbMessage -} diff --git a/notification/internal/models/fcm.go b/notification/internal/models/fcm.go deleted file mode 100644 index 2640e7f..0000000 --- a/notification/internal/models/fcm.go +++ /dev/null @@ -1 +0,0 @@ -package models diff --git a/notification/internal/notifier/email.go b/notification/internal/notifier/email.go deleted file mode 100644 index fb4b52d..0000000 --- a/notification/internal/notifier/email.go +++ /dev/null @@ -1,16 +0,0 @@ -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) -} diff --git a/notification/internal/notifier/sms.go b/notification/internal/notifier/sms.go deleted file mode 100644 index a4668f2..0000000 --- a/notification/internal/notifier/sms.go +++ /dev/null @@ -1,25 +0,0 @@ -package notifier - -import ( - "context" - - "github.com/JscorpTech/notification/internal/domain" - "github.com/JscorpTech/notification/internal/services" -) - -type smsNotifier struct { - SMSServie domain.SMSServicePort - Ctx context.Context -} - -func NewSmsNotifier(ctx context.Context) domain.NotifierPort { - return &smsNotifier{ - SMSServie: services.NewEskizSMSService(ctx), - } -} - -func (n *smsNotifier) SendMessage(to []string, body string) { - for _, user := range to { - n.SMSServie.SendSMS(user, body) - } -} diff --git a/notification/internal/rabbitmq/connection.go b/notification/internal/rabbitmq/connection.go deleted file mode 100644 index 7aba1e6..0000000 --- a/notification/internal/rabbitmq/connection.go +++ /dev/null @@ -1,22 +0,0 @@ -package rabbitmq - -import ( - "log" - "os" - - "github.com/streadway/amqp" -) - -func Connect() (*amqp.Connection, *amqp.Channel, error) { - conn, err := amqp.Dial(os.Getenv("RABBITMQ_URL")) - 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 -} diff --git a/notification/internal/redis/client.go b/notification/internal/redis/client.go deleted file mode 100644 index 7969a67..0000000 --- a/notification/internal/redis/client.go +++ /dev/null @@ -1,27 +0,0 @@ -package redis - -import ( - "context" - "log" - "os" - "strconv" - - "github.com/redis/go-redis/v9" -) - -var RDB *redis.Client - -func InitRedis() { - DB, _ := strconv.Atoi(os.Getenv("REDIS_DB")) - RDB = redis.NewClient(&redis.Options{ - Addr: os.Getenv("REDIS_ADDRESS"), - Password: os.Getenv("REDIS_PASSWORD"), - DB: DB, - }) - - // Test connection - _, err := RDB.Ping(context.Background()).Result() - if err != nil { - log.Fatalf("Redisga ulanib bo'lmadi: %v", err) - } -} diff --git a/notification/internal/services/eskiz.go b/notification/internal/services/eskiz.go deleted file mode 100644 index 91309ca..0000000 --- a/notification/internal/services/eskiz.go +++ /dev/null @@ -1,89 +0,0 @@ -package services - -import ( - "bytes" - "context" - "encoding/json" - "net/http" - "os" - "time" - - "github.com/JscorpTech/notification/internal/domain" - "github.com/JscorpTech/notification/internal/redis" - "github.com/k0kubun/pp/v3" -) - -type eskizSMSService struct { - BaseURL string - Ctx context.Context -} - -// /broker-api/send -func NewEskizSMSService(ctx context.Context) domain.SMSServicePort { - return &eskizSMSService{ - Ctx: ctx, - BaseURL: os.Getenv("ESKIZ_DOMAIN"), - } -} - -func (e *eskizSMSService) Request(payload any, path string, isAuth bool, retry bool) (*http.Response, error) { - var buf bytes.Buffer - _ = json.NewEncoder(&buf).Encode(payload) - client := http.Client{ - Timeout: 60 * time.Second, - } - req, err := http.NewRequest("POST", e.BaseURL+path, &buf) - req.Header.Add("Content-Type", "application/json") - if isAuth { - req.Header.Add("Authorization", "Bearer "+e.GetToken(true)) - } - - if err != nil { - return nil, err - } - res, err := client.Do(req) - if res.StatusCode == http.StatusUnauthorized && retry { - pp.Print("Qayta urunish") - e.GetToken(false) - return e.Request(payload, path, isAuth, false) - } - return res, err -} - -func (e *eskizSMSService) GetToken(cache bool) string { - token, err := redis.RDB.Get(e.Ctx, "eskiz_token").Result() - if err == nil && cache { - pp.Print("Eskiz token topildi 😁") - return token - } - payload := domain.EskizLogin{ - Email: os.Getenv("ESKIZ_USER"), - Password: os.Getenv("ESKIZ_PASSWORD"), - } - res, err := e.Request(payload, "/auth/login", false, true) - if err != nil { - pp.Print(err.Error()) - } - var data domain.EskizLoginRes - _ = json.NewDecoder(res.Body).Decode(&data) - token = data.Data.Token - redis.RDB.Set(e.Ctx, "eskiz_token", token, 30*24*time.Hour) - pp.Print("Eskiz yangi token olindi 😔") - return token -} - -func (e *eskizSMSService) SendSMS(to, body string) error { - payload := domain.EskizMessage{ - Phone: to, - Message: body, - From: os.Getenv("ESKIZ_FROM"), - } - res, err := e.Request(payload, "/message/sms/send", true, true) - if err != nil { - return err - } - var data domain.EskizMessageRes - json.NewDecoder(res.Body).Decode(&data) - pp.Print(data) - return nil -} diff --git a/notification/internal/services/playmobile.go b/notification/internal/services/playmobile.go deleted file mode 100644 index b2990cc..0000000 --- a/notification/internal/services/playmobile.go +++ /dev/null @@ -1,53 +0,0 @@ -package services - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" - "time" - - "github.com/JscorpTech/notification/internal/domain" -) - -type pmbSMSService struct { - BaseURL string -} - -// /broker-api/send -func NewPmbSMSService() domain.SMSServicePort { - return &pmbSMSService{ - BaseURL: "https://send.smsxabar.uz", - } -} - -func (e *pmbSMSService) SendSMS(to, body string) error { - client := http.Client{ - Timeout: 60 * time.Second, - } - payload := domain.PmbPayload{ - Messages: []domain.PmbMessage{ - { - Recipient: "+998888112309", - MessageID: "salomsdfs", - Sms: domain.PmbSMS{ - Originator: "3600", - Content: domain.PmbContent{ - Text: "salom", - }, - }, - }, - }, - } - var buf bytes.Buffer - _ = json.NewEncoder(&buf).Encode(payload) - req, _ := http.NewRequest("POST", e.BaseURL+"/broker-api/send", &buf) - res, err := client.Do(req) - if err != nil { - return err - } - var data map[string]interface{} - json.NewDecoder(res.Body).Decode(&data) - fmt.Print(data) - return nil -} diff --git a/notification/tmp/build-errors.log b/notification/tmp/build-errors.log deleted file mode 100644 index 94241fd..0000000 --- a/notification/tmp/build-errors.log +++ /dev/null @@ -1 +0,0 @@ -exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1 \ No newline at end of file