From 00f31d92cec89e70a531995620c92d6dc3bdc6a5 Mon Sep 17 00:00:00 2001 From: nilo Date: Sun, 9 Feb 2025 19:12:00 -0300 Subject: [PATCH] completed test endpoint --- .gitignore | 1 + controllers/userController.go | 9 +- controllers/webhookController.go | 125 ++++++++++++++++- database/database.go | 6 +- globals/globals.go | 4 + models/externals.go | 60 ++++++++ models/models.go | 41 ++++++ services/userservices.go | 12 +- utils/utils.go | 226 ++++++++++++++++++++++++++++++- 9 files changed, 467 insertions(+), 17 deletions(-) create mode 100644 models/externals.go diff --git a/.gitignore b/.gitignore index 6d439bf..254e7df 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ api api.exe +*.exe .env diff --git a/controllers/userController.go b/controllers/userController.go index db9eb49..4bae0ab 100644 --- a/controllers/userController.go +++ b/controllers/userController.go @@ -1,10 +1,9 @@ package controllers import ( - "api/database" + "api/globals" "api/models" "api/services" - "api/utils" "fmt" "github.com/gofiber/fiber/v2" @@ -13,7 +12,7 @@ import ( func GetUsers(c *fiber.Ctx) error { var users []models.SystemUser - database.DB.Find(&users) + globals.DB.Find(&users) return c.JSON(users) } @@ -21,7 +20,7 @@ func GetUsers(c *fiber.Ctx) error { func GetGroups(c *fiber.Ctx) error { var groups []models.SystemGroup - database.DB.Find(&groups) + globals.DB.Find(&groups) return c.JSON(groups) } @@ -41,7 +40,5 @@ func CreateUser(c *fiber.Ctx) error { fmt.Println("inexistent user") } - utils.SendEmail(email) - return c.JSON(user) } diff --git a/controllers/webhookController.go b/controllers/webhookController.go index b5c4407..d990fbc 100644 --- a/controllers/webhookController.go +++ b/controllers/webhookController.go @@ -1,12 +1,16 @@ package controllers import ( + "api/globals" "api/models" + "api/services" "api/utils" "bytes" "encoding/json" + "fmt" "log" "net/http" + "strings" "github.com/gofiber/fiber/v2" ) @@ -126,9 +130,126 @@ func OnPubStop(c *fiber.Ctx) error { return c.SendString("On_Pub_Stop: " + string(c.Body())) } +type ResponseTest struct { + Data DataTest `json:"data"` +} + +type DataTest struct { + CNPJ string `json:"cnpj"` + Email string `json:"email"` + Name string `json:"name"` +} + func WixTest(c *fiber.Ctx) error { - log.Println(string(c.Body())) - return c.SendString("On Test: " + string(c.Body())) + + // Get the data from the callback + var r ResponseTest + if err := c.BodyParser(&r); err != nil { + return err + } + + // Pass the data to variables + cnpj := strings.TrimSpace(r.Data.CNPJ) + name := strings.TrimSpace(r.Data.Name) + email := strings.TrimSpace(r.Data.Email) + + // Check if it is a valid CNPJ + if !utils.IsValid(cnpj) { + utils.SendTestEmail(email, name, "Foi informado um CNPJ inválido") + return c.SendString("CNPJ Inválido") + } + + // Formats the CNPJ + cnpjf := fmt.Sprintf("%s.%s.%s/%s-%s", + cnpj[:2], + cnpj[2:5], + cnpj[5:8], + cnpj[8:12], + cnpj[12:]) + + // Let's see if it is a real CNPJ + log.Printf("CNPJ %s [%s] requisitou um teste usando o email %s (%s)\n", cnpj, cnpjf, name, email) + empresa := utils.GetEmpresa(cnpj) + + if empresa.Situacao != "ATIVA" { + utils.SendTestEmail(email, name, "Foi informado um CNPJ pertencente à uma empresa inativa") + return c.SendString("Empresa inativa") + } + + // Let's see if its already on database + customer := services.GetCustomerByCNPJ(cnpjf) + + user := services.GetUserByEmail(email) + + var userdb models.SystemUser + var customerdb models.Customer + + if user.ID == 0 { + // Email not in database. Let's insert it + userdb = models.SystemUser{ + Email: email, + } + + err := globals.DB.Create(&userdb) + if err != nil { + log.Printf("Cannot create test user: %v\n", err) + } + + user.ID = userdb.ID + } + + if customer.ID == 0 { + // Inexistent customer; insert it into database + customerwww := utils.GetEmpresa(cnpj) + + customerdb = models.Customer{ + CNPJ: customerwww.CNPJ, + Nome: customerwww.Nome, + NomeFantasia: customerwww.Nome, + Tipo: customerwww.Tipo, + Porte: customerwww.Porte, + Situacao: customerwww.Situacao, + Abertura: customerwww.Abertura, + NaturezaJuridica: customerwww.NaturezaJuridica, + Logradouro: customerwww.Logradouro, + Numero: customerwww.Numero, + Complemento: customerwww.Complemento, + Municipio: customerwww.Municipio, + Bairro: customerwww.Bairro, + UF: customerwww.UF, + CEP: customerwww.CEP, + UserID: user.ID, + } + + err := globals.DB.Create(&customerdb) + if err.Error != nil { + log.Printf("Cannot create test user: %v\n", err) + } + + // Now let's create the customer channel + opts := utils.GenerationOptions{ + Length: 10, + } + + tkey, _ := utils.GenerateString(opts) + + channel := models.Channel{ + Name: cnpj + "-01", + TransmissionKey: tkey, + CustomerID: customerdb.UserID, + ServerID: 1, + } + err = globals.DB.Create(&channel) + if err.Error != nil { + log.Printf("Cannot create test channel: %v\n", err) + } + + // Finally, send an email + utils.SendTestEmailApproval(email, name, "tkey", "url") + + } + + return c.SendString("On Test: " + empresa.Nome) } func WixIntegration(c *fiber.Ctx) error { diff --git a/database/database.go b/database/database.go index 1fe12b1..e3aec07 100644 --- a/database/database.go +++ b/database/database.go @@ -2,6 +2,7 @@ package database import ( "api/config" + "api/globals" "fmt" "log" @@ -9,9 +10,6 @@ import ( "gorm.io/gorm" ) -// Our database -var DB *gorm.DB - // ConnectDB - returns a pointer to a new database connection func ConnectDB() error { // var erre error @@ -40,7 +38,7 @@ func ConnectDB() error { return err } - DB = db + globals.DB = db return nil } diff --git a/globals/globals.go b/globals/globals.go index cc99ff1..1fd9ec5 100644 --- a/globals/globals.go +++ b/globals/globals.go @@ -1,6 +1,10 @@ package globals +import "gorm.io/gorm" + var ( API_VERSION = "" API_RELEASE = "" + + DB *gorm.DB ) diff --git a/models/externals.go b/models/externals.go new file mode 100644 index 0000000..a0cd19b --- /dev/null +++ b/models/externals.go @@ -0,0 +1,60 @@ +package models + +type Empresa struct { + Abertura string `json:"abertura"` + Situacao string `json:"situacao"` + Tipo string `json:"tipo"` + Nome string `json:"nome"` + Porte string `json:"porte"` + NaturezaJuridica string `json:"natureza_juridica"` + AtividadePrincipal []Atividade `json:"atividade_principal"` + AtividadesSecundarias []Atividade `json:"atividades_secundarias"` + Logradouro string `json:"logradouro"` + Numero string `json:"numero"` + Complemento string `json:"complemento"` + Municipio string `json:"municipio"` + Bairro string `json:"bairro"` + UF string `json:"uf"` + CEP string `json:"cep"` + Email string `json:"email"` + Telefone string `json:"telefone"` + DataSituacao string `json:"data_situacao"` + CNPJ string `json:"cnpj"` + UltimaAtualizacao string `json:"ultima_atualizacao"` + Status string `json:"status"` + Fantasia string `json:"fantasia"` + EFR string `json:"efr"` + MotivoSituacao string `json:"motivo_situacao"` + SituacaoEspecial string `json:"situacao_especial"` + DataSituacaoEspecial string `json:"data_situacao_especial"` + CapitalSocial string `json:"capital_social"` + QSA []interface{} `json:"qsa"` + Simples Simples `json:"simples"` + Simei Simei `json:"simei"` + Extra map[string]interface{} `json:"extra"` + Billing Billing `json:"billing"` +} + +type Atividade struct { + Code string `json:"code"` + Text string `json:"text"` +} + +type Simples struct { + Optante bool `json:"optante"` + DataOpcao string `json:"data_opcao"` + DataExclusao string `json:"data_exclusao"` + UltimaAtualizacao string `json:"ultima_atualizacao"` +} + +type Simei struct { + Optante bool `json:"optante"` + DataOpcao string `json:"data_opcao"` + DataExclusao string `json:"data_exclusao"` + UltimaAtualizacao string `json:"ultima_atualizacao"` +} + +type Billing struct { + Free bool `json:"free"` + Database bool `json:"database"` +} diff --git a/models/models.go b/models/models.go index 340d3ca..33ebb23 100644 --- a/models/models.go +++ b/models/models.go @@ -1,5 +1,8 @@ package models +// ==================================================================== +// Adianti tables + type SystemGroup struct { ID int `gorm:"primaryKey"` Name string `gorm:"size:256"` @@ -111,3 +114,41 @@ type SystemProgramMethodRole struct { SystemProgram SystemProgram `gorm:"foreignKey:SystemProgramID"` SystemRole SystemRole `gorm:"foreignKey:SystemRoleID"` } + +// ==================================================================== +// PCast tables + +type Customer struct { + ID int `gorm:"primaryKey;autoIncrement"` + CNPJ string `gorm:"unique;not null"` + Nome string `gorm:"not null"` + NomeFantasia string `gorm:"not null"` + Tipo string `gorm:"not null"` + Porte string `gorm:"not null"` + Situacao string `gorm:"not null"` + Abertura string `gorm:"not null"` + NaturezaJuridica string `gorm:"not null"` + Logradouro string `gorm:"not null"` + Numero string `gorm:"not null"` + Complemento string `gorm:"not null"` + Municipio string `gorm:"not null"` + Bairro string `gorm:"not null"` + UF string `gorm:"not null"` + CEP string `gorm:"not null"` + UserID int `gorm:"not null"` + User SystemUser `gorm:"foreignKey:UserID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"` +} + +type Email struct { + ID int + Name string + EmailText string +} + +type Channel struct { + ID int + Name string + TransmissionKey string + CustomerID int + ServerID int +} diff --git a/services/userservices.go b/services/userservices.go index 0bda84f..404f868 100644 --- a/services/userservices.go +++ b/services/userservices.go @@ -1,14 +1,22 @@ package services import ( - "api/database" + "api/globals" "api/models" ) func GetUserByEmail(email string) models.SystemUser { var user models.SystemUser - database.DB.Where("email = ?", email).Find(&user) + globals.DB.Where("email = ?", email).Find(&user) + + return user +} + +func GetCustomerByCNPJ(cnpj string) models.Customer { + var user models.Customer + + globals.DB.Where("cnpj = ?", cnpj).Find(&user) return user } diff --git a/utils/utils.go b/utils/utils.go index a4d0711..25e332a 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -1,15 +1,106 @@ package utils import ( + "api/globals" + "api/models" + "crypto/rand" "encoding/json" + "errors" "fmt" + "io/ioutil" "log" + "math/big" + "net/http" + "strconv" + "strings" "gopkg.in/gomail.v2" "golang.org/x/crypto/bcrypt" ) +type Charset string + +const ( + Alphanumeric = Charset("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") + Lowercase = Charset("abcdefghijklmnopqrstuvwxyz") + Uppercase = Charset("ABCDEFGHIJKLMNOPQRSTUVWXYZ") + Numeric = Charset("0123456789") + SpecialCharacters = Charset("!@#$%^&*()-_=+[]{}|;:'<>,.?/~") +) + +var ( + GenerateString = generateString +) + +type GenerationOptions struct { + Length int + DisableNumeric bool + DisableLowercase bool + DisableUppercase bool + EnableSpecialCharacter bool + CustomCharset Charset +} + +// generateStringFromCharset generates a random string from a given charset +func generateStringFromCharset(charset Charset, length int) (string, error) { + if len(charset) == 0 || length <= 0 { + return "", errors.New("invalid charset or length") + } + + result := make([]byte, length) + charsetLen := big.NewInt(int64(len(charset))) + for i := 0; i < length; i++ { + randomIndex, err := rand.Int(rand.Reader, charsetLen) + if err != nil { + return "", err + } + result[i] = charset[randomIndex.Int64()] + } + + return string(result), nil +} + +// modifyCharset modifies the charset based on the options +func modifyCharset(opts GenerationOptions, charsetMappings map[string]Charset, charset Charset) Charset { + if opts.DisableNumeric { + charset = Charset(strings.ReplaceAll(string(charset), string(charsetMappings["numeric"]), "")) + } + if opts.DisableLowercase { + charset = Charset(strings.ReplaceAll(string(charset), string(charsetMappings["lowercase"]), "")) + } + if opts.DisableUppercase { + charset = Charset(strings.ReplaceAll(string(charset), string(charsetMappings["uppercase"]), "")) + } + if opts.EnableSpecialCharacter { + charset += charsetMappings["specialCharater"] + } + return charset +} + +// generateString generates a random string based on the options +func generateString(opts GenerationOptions) (string, error) { + charsetMappings := map[string]Charset{ + "numeric": Numeric, + "lowercase": Lowercase, + "uppercase": Uppercase, + "specialCharater": SpecialCharacters, + } + + charset := Alphanumeric + if opts.CustomCharset != "" { + charset = opts.CustomCharset + } else { + charset = modifyCharset(opts, charsetMappings, charset) + } + + if len(charset) == 0 { + return "", errors.New("resulting charset is empty. adjust your options") + } + + return generateStringFromCharset(charset, opts.Length) +} + func HashPassword(password string) ([]byte, error) { return bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) } @@ -22,12 +113,141 @@ func PrettyPrintJson(data interface{}) { log.Println(string(b)) } -func SendEmail(email string) { +// IsValid checks if a CNPJ is valid +func IsValid(cnpj string) bool { + // Remove non-numeric characters + cnpj = removeNonDigits(cnpj) + + if len(cnpj) != 14 || allDigitsEqual(cnpj) { + return false + } + + // Validate check digits + return checkCNPJCheckDigits(cnpj) +} + +// removeNonDigits removes all non-numeric characters from a string +func removeNonDigits(input string) string { + var result strings.Builder + for _, r := range input { + if r >= '0' && r <= '9' { + result.WriteRune(r) + } + } + return result.String() +} + +// allDigitsEqual checks if all characters in a string are the same +func allDigitsEqual(cnpj string) bool { + for i := 1; i < len(cnpj); i++ { + if cnpj[i] != cnpj[0] { + return false + } + } + return true +} + +// checkCNPJCheckDigits validates the two check digits of a CNPJ +func checkCNPJCheckDigits(cnpj string) bool { + weights1 := []int{5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2} + weights2 := []int{6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2} + + // Calculate first check digit + digit1 := calculateCheckDigit(cnpj[:12], weights1) + + // Calculate second check digit + digit2 := calculateCheckDigit(cnpj[:13], weights2) + + // Check if the calculated digits match the actual ones + return cnpj[12] == digit1 && cnpj[13] == digit2 +} + +// calculateCheckDigit calculates a single check digit for a given CNPJ slice and weight array +func calculateCheckDigit(numbers string, weights []int) byte { + sum := 0 + for i, weight := range weights { + num, _ := strconv.Atoi(string(numbers[i])) + sum += num * weight + } + + remainder := sum % 11 + if remainder < 2 { + return '0' + } + return byte('0' + (11 - remainder)) +} + +func GetEmpresa(cnpj string) models.Empresa { + url := "https://receitaws.com.br/v1/cnpj/" + cnpj + + // Call the external URL + resp, err := http.Get(url) + if err != nil { + fmt.Println("Error fetching data:", err) + return models.Empresa{} + } + defer resp.Body.Close() + + // Read the response body + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + fmt.Println("Error reading response:", err) + return models.Empresa{} + } + + // Parse the JSON response into the struct + var response models.Empresa + err = json.Unmarshal(body, &response) + if err != nil { + fmt.Println("Error parsing JSON:", err) + return models.Empresa{} + } + + // Print the parsed response + return response +} + +func SendTestEmail(email, name, message string) { + + var emaildb models.Email + + globals.DB.First(&emaildb, "name = ?", "TESTE-ERRO") + + errText := emaildb.EmailText + + errText = strings.Replace(errText, "[[ERRO]]", message, -1) + errText = strings.Replace(errText, "[[NOME]]", name, -1) + m := gomail.NewMessage() m.SetHeader("From", "suporte@pcastlive.com") m.SetHeader("To", email) - m.SetHeader("Subject", "Test Email from Go") - m.SetBody("text/plain", "This is a test email sent from a Go application.") + m.SetHeader("Subject", "Solicitação de teste da plataforma PCastLive") + m.SetBody("text/html", errText) + + d := gomail.NewDialer("smtp.kinghost.net", 465, "suporte@pcastlive.com", "@407Smc837") + + if err := d.DialAndSend(m); err != nil { + panic(err) + } +} + +func SendTestEmailApproval(email, name, transmkey, url string) { + + var emaildb models.Email + + globals.DB.First(&emaildb, "name = ?", "TESTE-APROVADO") + + errText := emaildb.EmailText + + errText = strings.Replace(errText, "[[NOME]]", name, -1) + errText = strings.Replace(errText, "[[TRANSMKEY]]", transmkey, -1) + errText = strings.Replace(errText, "[[URL]]", url, -1) + + m := gomail.NewMessage() + m.SetHeader("From", "suporte@pcastlive.com") + m.SetHeader("To", email) + m.SetHeader("Subject", "Solicitação de teste da plataforma PCastLive") + m.SetBody("text/html", errText) d := gomail.NewDialer("smtp.kinghost.net", 465, "suporte@pcastlive.com", "@407Smc837")