Delete notification service

This commit is contained in:
A'zamov Samandar
2025-04-21 20:51:24 +05:00
parent a777752e17
commit 96aa4b27a8
21 changed files with 0 additions and 622 deletions

View File

@@ -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=""

View File

@@ -1,3 +0,0 @@
.env
./bin
./main

View File

@@ -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"]

View File

@@ -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)

View File

@@ -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()
}

View File

@@ -1,12 +0,0 @@
networks:
lamenu:
external: true
name: lamenu
services:
notification:
build:
context: .
dockerfile: Dockerfile
networks:
- lamenu

View File

@@ -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
)

View File

@@ -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=

View File

@@ -1,13 +0,0 @@
package config
import (
"os"
)
func GetEnv(key, fallback string) string {
val := os.Getenv(key)
if val == "" {
return fallback
}
return val
}

View File

@@ -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 &notificationConsumer{
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, &notification)
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)
}

View File

@@ -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"`
}

View File

@@ -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"`
}

View File

@@ -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
}

View File

@@ -1 +0,0 @@
package models

View File

@@ -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)
}

View File

@@ -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)
}
}

View File

@@ -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
}

View File

@@ -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)
}
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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