staging #11

Merged
skeris merged 11 commits from staging into main 2025-07-25 15:55:39 +00:00
18 changed files with 390 additions and 69 deletions
Showing only changes of commit 8760c1fb31 - Show all commits

26
Dockerfile Normal file

@ -0,0 +1,26 @@
# BUILD
FROM gitea.pena/penadevops/container-images/golang:main as build
# Update packages and clear cache
RUN apk add --no-cache curl
# Set work directory
WORKDIR /app
RUN apk add git
# Add main files to app
ADD . .
# Download go depences
RUN go mod download
# Build app
RUN GOOS=linux go build -o bin ./cmd/main.go
# PRODUCTION
FROM gitea.pena/penadevops/container-images/alpine:main
# Install packages
RUN apk --no-cache add ca-certificates
# Create home directory
WORKDIR /app
# Copy build file
COPY --from=build /app/bin ./app
# CMD
CMD ["./app"]

@ -0,0 +1,26 @@
version: "3.3"
services:
mailnotifier:
hostname: mailnotifier
container_name: mailnotifier
image: $CI_REGISTRY_IMAGE/main:$CI_COMMIT_REF_SLUG.$CI_PIPELINE_ID
tty: true
environment:
- MONGO_HOST=10.8.0.8
- MONGO_PORT=27017
- MONGO_USER=mailnotifier
- MONGO_PASSWORD=vWwbCSg4bf0p
- MONGO_DB=mailnotifier
- MONGO_AUTH=mailnotifier
- KAFKA_BROKERS=10.8.0.8:9092
- KAFKA_TOPIC=mailnotifier
- SMTP_API_URL=https://api.smtp.bz/v1/smtp/send
- SMTP_HOST=connect.mailclient.bz
- SMTP_PORT=587
- SMTP_UNAME=team@pena.digital
- SMTP_PASS=AyMfwqA9LkQH
- SMTP_API_KEY=8tv2xcsfCMBX3TCQxzgeeEwAEYyQrPUp0ggw
- SMTP_SENDER=recovery@noreply.pena.digital
- CUSTOMER_URL=10.8.0.8:8086
- QUIZ_RPC_URL=10.8.0.9:9000

@ -0,0 +1,16 @@
version: "3.3"
services:
mailnotifier:
image: gitea.pena:3000/penaside/notifier/staging:$GITHUB_RUN_NUMBER
tty: true
environment:
MONGO_URL: mongodb://mailnotifier:vWwbCSg4bf0p@10.7.0.6:27017/?authSource=verification
MONGO_DB_NAME: mailnotifier
KAFKA_BROKERS: 10.7.0.6:9092
KAFKA_TOPIC_MAIL_NOTIFIER: mailnotifier
API_URL: https://api.smtp.bz/v1/smtp/send
MAIL_SENDER: kotilion.95@gmail.com
MAIL_API_KEY: P0YsjUB137upXrr1NiJefHmXVKW1hmBWlpev
CUSTOMER_MICROSERVICE_GRPC_URL: 10.7.0.6:8086
QUIZ_CORE_MICROSERVICE_GRPC_URL: 10.7.0.5:9000

2
go.mod

@ -5,7 +5,7 @@ go 1.23.2
toolchain go1.23.3 toolchain go1.23.3
require ( require (
gitea.pena/PenaSide/common v0.0.0-20241216115106-ecdff721f8a3 gitea.pena/PenaSide/common v0.0.0-20241231090536-2454377ad2a0
gitea.pena/PenaSide/linters-golang v0.0.0-20241207122018-933207374735 gitea.pena/PenaSide/linters-golang v0.0.0-20241207122018-933207374735
github.com/caarlos0/env/v8 v8.0.0 github.com/caarlos0/env/v8 v8.0.0
github.com/gofiber/fiber/v2 v2.51.0 github.com/gofiber/fiber/v2 v2.51.0

4
go.sum

@ -1,5 +1,5 @@
gitea.pena/PenaSide/common v0.0.0-20241216115106-ecdff721f8a3 h1:oZenr/Am7+/8ZPLhpW13cJQ0c+usnqKZiWw8cXE6E1Q= gitea.pena/PenaSide/common v0.0.0-20241231090536-2454377ad2a0 h1:B4+DAND6gJnCsc9DZY6XyMVLzD23nI8whk8xI7XKGrM=
gitea.pena/PenaSide/common v0.0.0-20241216115106-ecdff721f8a3/go.mod h1:U7QFuvkrIWyb/m/SOyrsroS7DJntjcr9k7kNy3vtPdU= gitea.pena/PenaSide/common v0.0.0-20241231090536-2454377ad2a0/go.mod h1:U7QFuvkrIWyb/m/SOyrsroS7DJntjcr9k7kNy3vtPdU=
gitea.pena/PenaSide/linters-golang v0.0.0-20241207122018-933207374735 h1:jDVeUhGBTXBibmW5dmtJg2m2+z5z2Rf6J4G0LpjVoJ0= gitea.pena/PenaSide/linters-golang v0.0.0-20241207122018-933207374735 h1:jDVeUhGBTXBibmW5dmtJg2m2+z5z2Rf6J4G0LpjVoJ0=
gitea.pena/PenaSide/linters-golang v0.0.0-20241207122018-933207374735/go.mod h1:gdd+vOT6up9STkEbxa2qESLIMZFjCmRbkcheFQCVgZU= gitea.pena/PenaSide/linters-golang v0.0.0-20241207122018-933207374735/go.mod h1:gdd+vOT6up9STkEbxa2qESLIMZFjCmRbkcheFQCVgZU=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=

@ -3,9 +3,9 @@ package clients
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"gitea.pena/PenaSide/notifier/internal/models"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"go.uber.org/zap" "go.uber.org/zap"
"gitea.pena/PenaSide/notifier/internal/models"
) )
type Customer struct { type Customer struct {
@ -41,7 +41,7 @@ func (c *Customer) GetAccount(userID string) (models.Account, error) {
statusCode, body, errs := resp.Bytes() statusCode, body, errs := resp.Bytes()
if errs != nil { if errs != nil {
c.logger.Error("Error sending request:", zap.Error(errs[0])) c.logger.Error(errSendingRequest.Error(), zap.Error(errs[0]))
return account, errs[0] return account, errs[0]
} }

@ -0,0 +1,7 @@
package clients
import "errors"
var (
errSendingRequest = errors.New("error sending request")
)

@ -79,7 +79,7 @@ func (m *MailClient) MailSender(data SenderDeps) error {
statusCode, body, errs := req.Bytes() statusCode, body, errs := req.Bytes()
if errs != nil { if errs != nil {
m.logger.Error("Error sending request:", zap.Error(errs[0])) m.logger.Error(errSendingRequest.Error(), zap.Error(errs[0]))
return errs[0] return errs[0]
} }

@ -2,10 +2,10 @@ package clients
import ( import (
"context" "context"
"gitea.pena/PenaSide/notifier/internal/proto/notifyer"
"go.uber.org/zap" "go.uber.org/zap"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/credentials/insecure"
"gitea.pena/PenaSide/notifier/internal/proto/notifyer"
) )
type QuizClient struct { type QuizClient struct {
@ -20,10 +20,13 @@ func NewQuizClient(address string, logger *zap.Logger) *QuizClient {
} }
} }
var coreRpcHost = "core rpc host"
var request = "request"
func (q *QuizClient) GetQuizzes(ctx context.Context, req *notifyer.GetQuizzesRequest) (*notifyer.GetQuizzesResponse, error) { func (q *QuizClient) GetQuizzes(ctx context.Context, req *notifyer.GetQuizzesRequest) (*notifyer.GetQuizzesResponse, error) {
connection, err := grpc.Dial(q.address, grpc.WithTransportCredentials(insecure.NewCredentials())) connection, err := grpc.NewClient(q.address, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil { if err != nil {
q.logger.Error("failed to connect on GetQuizzes of core rpc", zap.Error(err), zap.String("core rpc host", q.address)) q.logger.Error("failed to connect on GetQuizzes of core rpc", zap.Error(err), zap.String(coreRpcHost, q.address))
return nil, err return nil, err
} }
defer func() { defer func() {
@ -36,7 +39,7 @@ func (q *QuizClient) GetQuizzes(ctx context.Context, req *notifyer.GetQuizzesReq
response, err := client.GetQuizzes(ctx, req) response, err := client.GetQuizzes(ctx, req)
if err != nil { if err != nil {
q.logger.Error("failed to GetQuizzes core rpc", zap.Error(err), zap.Any("request", req)) q.logger.Error("failed to GetQuizzes core rpc", zap.Error(err), zap.Any(request, req))
return nil, err return nil, err
} }
@ -44,9 +47,9 @@ func (q *QuizClient) GetQuizzes(ctx context.Context, req *notifyer.GetQuizzesReq
} }
func (q *QuizClient) GetStartedQuizzes(ctx context.Context, req *notifyer.GetStartedQuizzesRequest) (*notifyer.GetStartedQuizzesResponse, error) { func (q *QuizClient) GetStartedQuizzes(ctx context.Context, req *notifyer.GetStartedQuizzesRequest) (*notifyer.GetStartedQuizzesResponse, error) {
connection, err := grpc.Dial(q.address, grpc.WithTransportCredentials(insecure.NewCredentials())) connection, err := grpc.NewClient(q.address, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil { if err != nil {
q.logger.Error("failed to connect on GetStartedQuizzes of core rpc", zap.Error(err), zap.String("core rpc host", q.address)) q.logger.Error("failed to connect on GetStartedQuizzes of core rpc", zap.Error(err), zap.String(coreRpcHost, q.address))
return nil, err return nil, err
} }
defer func() { defer func() {
@ -59,7 +62,7 @@ func (q *QuizClient) GetStartedQuizzes(ctx context.Context, req *notifyer.GetSta
response, err := client.GetStartedQuizzes(ctx, req) response, err := client.GetStartedQuizzes(ctx, req)
if err != nil { if err != nil {
q.logger.Error("failed to GetStartedQuizzes core rpc", zap.Error(err), zap.Any("request", req)) q.logger.Error("failed to GetStartedQuizzes core rpc", zap.Error(err), zap.Any(request, req))
return nil, err return nil, err
} }

@ -2,11 +2,9 @@ package repository
import ( import (
"context" "context"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"gitea.pena/PenaSide/notifier/internal/models" "gitea.pena/PenaSide/notifier/internal/models"
"time" "go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
) )
type Repository struct { type Repository struct {
@ -22,7 +20,6 @@ func NewRepository(mdb *mongo.Collection) *Repository {
// записываем каждый месседж по одному // записываем каждый месседж по одному
func (r *Repository) Insert(ctx context.Context, mes models.Message) error { func (r *Repository) Insert(ctx context.Context, mes models.Message) error {
mes.ID = primitive.NewObjectID()
_, err := r.mdb.InsertOne(ctx, mes) _, err := r.mdb.InsertOne(ctx, mes)
if err != nil { if err != nil {
return err return err
@ -35,24 +32,25 @@ func (r *Repository) Insert(ctx context.Context, mes models.Message) error {
// получаем сразу все в tools метод распределения // получаем сразу все в tools метод распределения
func (r *Repository) GetMany(ctx context.Context) ([]models.Message, error) { func (r *Repository) GetMany(ctx context.Context) ([]models.Message, error) {
to := time.Now().AddDate(0, 0, -15) // to := time.Now().AddDate(0, 0, -15)
from := time.Now().AddDate(0, 0, -7) // from := time.Now().AddDate(0, 0, -7)
//
filter := bson.D{ filter := bson.D{
{"$and", bson.A{ {Key: "$and", Value: bson.A{
bson.D{ bson.D{
{"$or", bson.A{ {Key: "$or", Value: bson.A{
bson.D{{"sendRegistration", false}}, bson.D{{Key: "sendRegistration", Value: false}},
bson.D{{"sendNoneCreated", false}}, bson.D{{Key: "sendNoneCreated", Value: false}},
bson.D{{"sendUnpublished", false}}, bson.D{{Key: "sendUnpublished", Value: false}},
}}, bson.D{{Key: "sendPaid", Value: false}},
},
bson.D{
{"sendAt", bson.D{
{"$gte", to},
{"$lte", from},
}}, }},
}, },
// bson.D{
// {"sendAt", bson.D{
// {"$gte", to},
// {"$lte", from},
// }},
// },
}}, }},
} }

@ -6,6 +6,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/twmb/franz-go/pkg/kgo" "github.com/twmb/franz-go/pkg/kgo"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.uber.org/zap" "go.uber.org/zap"
"gitea.pena/PenaSide/notifier/internal/clients" "gitea.pena/PenaSide/notifier/internal/clients"
"gitea.pena/PenaSide/notifier/internal/models" "gitea.pena/PenaSide/notifier/internal/models"
@ -79,6 +80,7 @@ func (c *Consumer) consumeMessages(ctx context.Context) {
} }
func (c *Consumer) processMsg(ctx context.Context, message models.Message) error { func (c *Consumer) processMsg(ctx context.Context, message models.Message) error {
message.ID = primitive.NewObjectID()
err := c.repo.Insert(ctx, message) err := c.repo.Insert(ctx, message)
if err != nil { if err != nil {
return fmt.Errorf("error insert kafka data to mongo: %w", err) return fmt.Errorf("error insert kafka data to mongo: %w", err)

@ -1,12 +1,74 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8" />
<title>Paid</title> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head> <title>Document</title>
<body> <style>
<h1>Hello dude!</h1> /* Сброс стилей */
<p>Get money.</p> body,
<p>Pena Co Ltd</p> h1,
</body> h2,
h3,
p,
div,
img,
button,
table,
th,
td {
margin: 0;
padding: 0;
border: 0;
font: inherit;
vertical-align: baseline;
}
body {
background-color: #f2f2f7;
font-family: Arial, sans-serif;
}
@media (max-width: 600px) {
h1 {
font-size: 25px !important;
}
h4 {
font-size: 20px !important;
}
}
</style>
</head>
<body style="background-color: #f2f2f7; font-family: Arial, sans-serif">
<table style="width: 100%; padding: 16px">
<tr>
<td>
<img class="image" style="width: 103px; height: 40px" src="https://storage.yandexcloud.net/squizimages/logo-email-squiz.png" />
</td>
<td>
<p style="text-align: end; color: #9a9aaf; font-size: 14px">Квиз для вашего бизнеса</p>
</td>
</tr>
<tr>
<td colspan="2" style="height: 100%">
<p style="color: #9a9aaf; font-size: 20px; margin-bottom: 50px">
Здравствуйте, мы заметили что вы не стали пользоваться нашим продуктом PenaQuiz. Напишите пожалуйста нам в техническую поддержку что вам не понравилось, может не хватает какого то функционала или же возникли затруднения. </p>
<p style="color: #9a9aaf; font-size: 20px; margin-bottom: 50px">
Информация обязательно будет доведена до лиц принимающих решения, так как мы хотим и готовы развиваться совместно с нашими клиентами.
</p>
</td>
</tr>
<tr>
<td colspan="2" style="text-align: center; padding: 0">
<a style="color: #7e2aea; font-size: 20px; font-style: normal; font-weight: 400; line-height: normal">
quiz.pena.digital
</a>
</td>
</tr>
</table>
</body>
</html> </html>

@ -1,12 +1,71 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8" />
<title>Create Quiz</title> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head> <title>Document</title>
<body> <style>
<h1>Hello dude!</h1> /* Сброс стилей */
<p>Create QUIZ.</p> body,
<p>Pena Co Ltd</p> h1,
</body> h2,
</html> h3,
p,
div,
img,
button,
table,
th,
td {
margin: 0;
padding: 0;
border: 0;
font: inherit;
vertical-align: baseline;
}
body {
background-color: #f2f2f7;
font-family: Arial, sans-serif;
}
@media (max-width: 600px) {
h1 {
font-size: 25px !important;
}
h4 {
font-size: 20px !important;
}
}
</style>
</head>
<body style="background-color: #f2f2f7; font-family: Arial, sans-serif">
<table style="width: 100%; padding: 16px">
<tr>
<td>
<img class="image" style="width: 103px; height: 40px" src="https://storage.yandexcloud.net/squizimages/logo-email-squiz.png" />
</td>
<td>
<p style="text-align: end; color: #9a9aaf; font-size: 14px">Квиз для вашего бизнеса</p>
</td>
</tr>
<tr>
<td colspan="2" style="height: 100%">
<p style="color: #9a9aaf; font-size: 20px; margin-bottom: 50px">
Здравствуйте, мы заметили что у вас возникли сложности в использовании продукта PenaQuiz, расскажите пожалуйста нашей технической поддержке,о сложностях которые у вас возникли и мы обязательно разберемся и в течении рабочего дня вместе составим необходимый опрос для запуска рекламы или исследования.</p>
</td>
</tr>
<tr>
<td colspan="2" style="text-align: center; padding: 0">
<a style="color: #7e2aea; font-size: 20px; font-style: normal; font-weight: 400; line-height: normal">
quiz.pena.digital
</a>
</td>
</tr>
</table>
</body>
<

@ -1,12 +1,72 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8" />
<title>Start Quiz</title> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head> <title>Document</title>
<body> <style>
<h1>Hello dude!</h1> /* Сброс стилей */
<p>Publish QUIZ.</p> body,
<p>Pena Co Ltd</p> h1,
</body> h2,
h3,
p,
div,
img,
button,
table,
th,
td {
margin: 0;
padding: 0;
border: 0;
font: inherit;
vertical-align: baseline;
}
body {
background-color: #f2f2f7;
font-family: Arial, sans-serif;
}
@media (max-width: 600px) {
h1 {
font-size: 25px !important;
}
h4 {
font-size: 20px !important;
}
}
</style>
</head>
<body style="background-color: #f2f2f7; font-family: Arial, sans-serif">
<table style="width: 100%; padding: 16px">
<tr>
<td>
<img class="image" style="width: 103px; height: 40px" src="https://storage.yandexcloud.net/squizimages/logo-email-squiz.png" />
</td>
<td>
<p style="text-align: end; color: #9a9aaf; font-size: 14px">Квиз для вашего бизнеса</p>
</td>
</tr>
<tr>
<td colspan="2" style="height: 100%">
<p style="color: #9a9aaf; font-size: 20px; margin-bottom: 50px">
Здравствуйте, мы заметили что у вас возникли сложности в использовании продукта PenaQuiz, расскажите пожалуйста нашей технической поддержке,о сложностях которые у вас возникли и мы обязательно разберемся и в течении рабочего дня вместе составим необходимый опрос для запуска рекламы или исследования.
</p>
</td>
</tr>
<tr>
<td colspan="2" style="text-align: center; padding: 0">
<a style="color: #7e2aea; font-size: 20px; font-style: normal; font-weight: 400; line-height: normal">
quiz.pena.digital
</a>
</td>
</tr>
</table>
</body>
</html> </html>

@ -1,12 +1,74 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8" />
<title>Start Quiz</title> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head> <title>Document</title>
<body> <style>
<h1>Hello dude!</h1> /* Сброс стилей */
<p>❤️</p> body,
<p>Pena Co Ltd</p> h1,
</body> h2,
h3,
p,
div,
img,
button,
table,
th,
td {
margin: 0;
padding: 0;
border: 0;
font: inherit;
vertical-align: baseline;
}
body {
background-color: #f2f2f7;
font-family: Arial, sans-serif;
}
@media (max-width: 600px) {
h1 {
font-size: 25px !important;
}
h4 {
font-size: 20px !important;
}
}
</style>
</head>
<body style="background-color: #f2f2f7; font-family: Arial, sans-serif">
<table style="width: 100%; padding: 16px">
<tr>
<td>
<img class="image" style="width: 103px; height: 40px" src="https://storage.yandexcloud.net/squizimages/logo-email-squiz.png" />
</td>
<td>
<p style="text-align: end; color: #9a9aaf; font-size: 14px">Квиз для вашего бизнеса</p>
</td>
</tr>
<tr>
<td colspan="2" style="height: 100%">
<p style="color: #9a9aaf; font-size: 20px; margin-bottom: 50px">
Здравствуйте вы зарегистрировались на продукте PenaQuiz и в течении 14 дней вы можете создавать квизы, и получать данные от любого количества заявок абсолютно бесплатно. </p>
<p style="color: #9a9aaf; font-size: 20px; margin-bottom: 50px">
При возникновении любых вопросов или проблем пишите онлайн консультанту (значок вопроса в нижнем правом углу экрана на сайте). Так же вы можете заказать создание квиза под ваш бизнес и требования на нашей платформе, для заказа пишите <a href="https://t.me/penaquiz">@penaquiz</a>
</p>
</td>
</tr>
<tr>
<td colspan="2" style="text-align: center; padding: 0">
<a style="color: #7e2aea; font-size: 20px; font-style: normal; font-weight: 400; line-height: normal">
quiz.pena.digital
</a>
</td>
</tr>
</table>
</body>
</html> </html>