qr vcard
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
|
||||
var seededRand = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
// RandomString genera una cadena alfanumérica aleatoria de longitud n
|
||||
func RandomString(n int) string {
|
||||
result := make([]byte, n)
|
||||
for i := range result {
|
||||
result[i] = charset[seededRand.Intn(len(charset))]
|
||||
}
|
||||
return string(result)
|
||||
}
|
||||
@@ -12,6 +12,7 @@ type ConxSsh struct {
|
||||
Usuario string `json:"usuario" gorm:"column:usuario"`
|
||||
Puerto string `json:"puerto" gorm:"column:puerto"`
|
||||
ServidorID uint `json:"servidor_id" gorm:"column:servidor_id;foreignKey:ServidorID"`
|
||||
Password string `json:"password" gorm:"column:password"`
|
||||
Servidor Servidor `json:"servidor" gorm:"foreignKey:ServidorID"`
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/sujit-baniya/fiber-boilerplate/app"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type OssApi struct {
|
||||
gorm.Model
|
||||
Name string `gorm:"size:100;not null" json:"name"` // Alias o nombre interno
|
||||
Endpoint string `gorm:"not null" json:"endpoint"` // Endpoint del access point
|
||||
AccessKeyID string `gorm:"not null" json:"access_key_id"` // Access Key ID
|
||||
AccessKeySecret string `gorm:"not null" json:"access_key_secret"` // Access Key Secret
|
||||
BucketName string `gorm:"not null" json:"bucket_name"` // Nombre del bucket o access point
|
||||
Region string `gorm:"size:50" json:"region"` // Región, opcional
|
||||
IsActive bool `gorm:"default:true" json:"is_active"` // Activar o desactivar config
|
||||
Notes string `gorm:"type:text" json:"notes"`
|
||||
}
|
||||
|
||||
// TableName asegura que GORM use la tabla 'qrvcard'
|
||||
func (OssApi) TableName() string {
|
||||
return "oss_api"
|
||||
}
|
||||
|
||||
// GetAllQrVcard obtiene todos los registros de QrVcard con paginación y búsqueda
|
||||
func GetAllOssApi(limit, offset int, search string) ([]OssApi, int64, error) {
|
||||
var items []OssApi
|
||||
var total int64
|
||||
db := app.Http.Database.DB.Model(&OssApi{})
|
||||
|
||||
// Si se pasa un término de búsqueda, lo aplicamos en la consulta
|
||||
if search != "" {
|
||||
db = db.Where("name LIKE ?", "%"+search+"%")
|
||||
}
|
||||
|
||||
// Contamos el total de registros
|
||||
if err := db.Count(&total).Error; err != nil {
|
||||
log.Printf("Error counting OssApi: %v", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// Obtenemos los registros con paginación
|
||||
if err := db.Order("id DESC").Limit(limit).Offset(offset).Find(&items).Error; err != nil {
|
||||
log.Printf("Error retrieving OssApi: %v", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return items, total, nil
|
||||
}
|
||||
|
||||
// CreateQrVcard crea un nuevo registro de QrVcard en la base de datos
|
||||
func CreateOssApi(OssApi *OssApi) error {
|
||||
if err := app.Http.Database.DB.Create(&OssApi).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateQrVcard actualiza un registro de QrVcard existente
|
||||
func UpdateOssApi(OssApi *OssApi) error {
|
||||
if err := app.Http.Database.DB.Model(&OssApi).Updates(OssApi).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteQrVcard elimina un registro de QrVcard de la base de datos
|
||||
func DeleteOssApi(OssApi *OssApi) error {
|
||||
if err := app.Http.Database.DB.Delete(&OssApi).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetLastActiveOssApi obtiene el último registro activo
|
||||
func GetLastActiveOssApi() (*OssApi, error) {
|
||||
var ossConfig OssApi
|
||||
err := app.Http.Database.DB.
|
||||
Where("is_active = ?", true).
|
||||
Order("id DESC").
|
||||
First(&ossConfig).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ossConfig, nil
|
||||
}
|
||||
+1
-1
@@ -191,7 +191,7 @@ func GetUserByUsuario(usuario string) (*Users, error) {
|
||||
fmt.Println(usuario) // Verifica el usuario recibido
|
||||
db := app.Http.Database.DB.Model(&Users{})
|
||||
// Usamos 1 explícitamente para indicar que el estado debe ser "activo"
|
||||
if err := db.Where("nombre_usuario = ? AND email_verified = ? AND estado = ?", usuario, true, 1).First(&user).Error; err != nil {
|
||||
if err := db.Where("nombre_usuario = ? AND email_verified = ? AND estado = ?", usuario, true, true).First(&user).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/sujit-baniya/fiber-boilerplate/app"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type QrVcard struct {
|
||||
gorm.Model
|
||||
Qr string `json:"Qr" gorm:"column:qr"`
|
||||
UsuarioID string `json:"usuario" gorm:"column:usuario"`
|
||||
Email string `json:"email" gorm:"column:email"`
|
||||
VcardID string `json:"vcard_id" gorm:"column:vcard_id"`
|
||||
}
|
||||
|
||||
// TableName asegura que GORM use la tabla 'qrvcard'
|
||||
func (QrVcard) TableName() string {
|
||||
return "qrvcard"
|
||||
}
|
||||
|
||||
// GetAllQrVcard obtiene todos los registros de QrVcard con paginación y búsqueda
|
||||
func GetAllQrVcard(limit, offset int, search string) ([]QrVcard, int64, error) {
|
||||
var items []QrVcard
|
||||
var total int64
|
||||
db := app.Http.Database.DB.Model(&QrVcard{})
|
||||
|
||||
// Si se pasa un término de búsqueda, lo aplicamos en la consulta
|
||||
if search != "" {
|
||||
db = db.Where("email LIKE ?", "%"+search+"%")
|
||||
}
|
||||
|
||||
// Contamos el total de registros
|
||||
if err := db.Count(&total).Error; err != nil {
|
||||
log.Printf("Error counting qrvcard: %v", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// Obtenemos los registros con paginación
|
||||
if err := db.Order("id DESC").Limit(limit).Offset(offset).Find(&items).Error; err != nil {
|
||||
log.Printf("Error retrieving qrvcard: %v", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return items, total, nil
|
||||
}
|
||||
|
||||
// CreateQrVcard crea un nuevo registro de QrVcard en la base de datos
|
||||
func CreateQrVcard(qrVcard *QrVcard) error {
|
||||
if err := app.Http.Database.DB.Create(&qrVcard).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateQrVcard actualiza un registro de QrVcard existente
|
||||
func UpdateQrVcard(qrVcard *QrVcard) error {
|
||||
if err := app.Http.Database.DB.Model(&qrVcard).Updates(qrVcard).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteQrVcard elimina un registro de QrVcard de la base de datos
|
||||
func DeleteQrVcard(qrVcard *QrVcard) error {
|
||||
if err := app.Http.Database.DB.Delete(&qrVcard).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
||||
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
|
||||
)
|
||||
|
||||
type OSSService struct {
|
||||
Client *oss.Client
|
||||
Bucket *oss.Bucket
|
||||
BucketName string
|
||||
Endpoint string
|
||||
}
|
||||
|
||||
// Configuración del OSS
|
||||
type OSSConfig struct {
|
||||
Endpoint string
|
||||
AccessKeyID string
|
||||
AccessKeySecret string
|
||||
BucketName string
|
||||
}
|
||||
|
||||
// Constructor del servicio OSS
|
||||
func NewOSSService(cfg OSSConfig) (*OSSService, error) {
|
||||
client, err := oss.New(cfg.Endpoint, cfg.AccessKeyID, cfg.AccessKeySecret)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creando cliente OSS: %w", err)
|
||||
}
|
||||
|
||||
bucket, err := client.Bucket(cfg.BucketName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error obteniendo bucket: %w", err)
|
||||
}
|
||||
|
||||
return &OSSService{
|
||||
Client: client,
|
||||
Bucket: bucket,
|
||||
BucketName: cfg.BucketName,
|
||||
Endpoint: cfg.Endpoint,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Subir un archivo
|
||||
func (s *OSSService) UploadFile(objectKey string, filePath string) error {
|
||||
err := s.Bucket.PutObjectFromFile(objectKey, filePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error subiendo archivo: %w", err)
|
||||
}
|
||||
log.Printf("Archivo '%s' subido como '%s'", filePath, objectKey)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Listar archivos en el bucket
|
||||
func (s *OSSService) ListFiles() ([]string, error) {
|
||||
objects, err := s.Bucket.ListObjects()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error listando archivos: %w", err)
|
||||
}
|
||||
|
||||
var keys []string
|
||||
for _, obj := range objects.Objects {
|
||||
keys = append(keys, obj.Key)
|
||||
}
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
// NewOSSServiceFromDB construye OSSService desde el último registro activo
|
||||
func NewOSSServiceFromDB() (*OSSService, error) {
|
||||
cfg, err := models.GetLastActiveOssApi()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error obteniendo configuración OSS activa: %w", err)
|
||||
}
|
||||
|
||||
return NewOSSService(OSSConfig{
|
||||
Endpoint: cfg.Endpoint,
|
||||
AccessKeyID: cfg.AccessKeyID,
|
||||
AccessKeySecret: cfg.AccessKeySecret,
|
||||
BucketName: cfg.BucketName,
|
||||
})
|
||||
}
|
||||
|
||||
// Eliminar un archivo del bucket
|
||||
func (s *OSSService) DeleteFile(objectKey string) error {
|
||||
err := s.Bucket.DeleteObject(objectKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error eliminando archivo '%s' de OSS: %w", objectKey, err)
|
||||
}
|
||||
log.Printf("Archivo eliminado de OSS: %s", objectKey)
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image/color"
|
||||
"image/png"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/skip2/go-qrcode"
|
||||
)
|
||||
|
||||
// ContactData estructura para la vCard extendida
|
||||
type ContactData struct {
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
Nickname string `json:"nickname"`
|
||||
Phone string `json:"phone"`
|
||||
Email string `json:"email"`
|
||||
Org string `json:"org"`
|
||||
Title string `json:"title"`
|
||||
Role string `json:"role"`
|
||||
Website string `json:"website"`
|
||||
PhotoURL string `json:"photo_url"`
|
||||
Address string `json:"address"` // Ejemplo: ";;Calle 123;Ciudad;Estado;12345;País"
|
||||
Birthday string `json:"birthday"` // Formato: "1990-01-01"
|
||||
Gender string `json:"gender"` // "M", "F", "O", etc.
|
||||
UID string `json:"uid"` // UUID o identificador único
|
||||
Categories string `json:"categories"` // Ejemplo: "Amigos,Trabajo"
|
||||
Note string `json:"note"`
|
||||
IMPP string `json:"impp"` // Ejemplo: "aim:usuario"
|
||||
Timezone string `json:"timezone"` // Ejemplo: "-0500"
|
||||
Language string `json:"language"` // Ejemplo: "es"
|
||||
ColorHex string `json:"color"`
|
||||
BgColor string `json:"bg_color"`
|
||||
Instagram string `json:"instagram"`
|
||||
Facebook string `json:"facebook"`
|
||||
TikTok string `json:"tiktok"`
|
||||
LinkedIn string `json:"linkedin"`
|
||||
VcardID string `json:"vcard_id"`
|
||||
}
|
||||
|
||||
// hexToRGBA convierte "#rrggbb" a color.RGBA
|
||||
func hexToRGBA(hex string) color.RGBA {
|
||||
var r, g, b uint8
|
||||
fmt.Sscanf(hex, "#%02x%02x%02x", &r, &g, &b)
|
||||
return color.RGBA{r, g, b, 255}
|
||||
}
|
||||
|
||||
// GenerateVCardQR genera un QR con una vCard extendida incluyendo redes sociales
|
||||
func GenerateVCardQR(data ContactData) ([]byte, error) {
|
||||
now := time.Now().UTC().Format("2006-01-02T15:04:05Z")
|
||||
|
||||
// Usamos strings.Builder para construir dinámicamente la vCard
|
||||
var vcardBuilder strings.Builder
|
||||
vcardBuilder.WriteString("BEGIN:VCARD\n")
|
||||
vcardBuilder.WriteString("VERSION:3.0\n")
|
||||
|
||||
// N y FN son obligatorios para una vCard válida
|
||||
vcardBuilder.WriteString(fmt.Sprintf("N:%s;%s;;;\n", data.LastName, data.FirstName))
|
||||
vcardBuilder.WriteString(fmt.Sprintf("FN:%s %s\n", data.FirstName, data.LastName))
|
||||
|
||||
if data.Nickname != "" {
|
||||
vcardBuilder.WriteString(fmt.Sprintf("NICKNAME:%s\n", data.Nickname))
|
||||
}
|
||||
if data.Org != "" {
|
||||
vcardBuilder.WriteString(fmt.Sprintf("ORG:%s\n", data.Org))
|
||||
}
|
||||
if data.Title != "" {
|
||||
vcardBuilder.WriteString(fmt.Sprintf("TITLE:%s\n", data.Title))
|
||||
}
|
||||
if data.Role != "" {
|
||||
vcardBuilder.WriteString(fmt.Sprintf("ROLE:%s\n", data.Role))
|
||||
}
|
||||
if data.Phone != "" {
|
||||
vcardBuilder.WriteString(fmt.Sprintf("TEL;TYPE=CELL:%s\n", data.Phone))
|
||||
}
|
||||
if data.Email != "" {
|
||||
vcardBuilder.WriteString(fmt.Sprintf("EMAIL:%s\n", data.Email))
|
||||
}
|
||||
if data.Website != "" {
|
||||
vcardBuilder.WriteString(fmt.Sprintf("URL:%s\n", data.Website))
|
||||
}
|
||||
if data.PhotoURL != "" {
|
||||
vcardBuilder.WriteString(fmt.Sprintf("PHOTO;VALUE=URI:%s\n", data.PhotoURL))
|
||||
}
|
||||
if data.Address != "" {
|
||||
vcardBuilder.WriteString(fmt.Sprintf("ADR:%s\n", data.Address))
|
||||
}
|
||||
if data.Birthday != "" {
|
||||
vcardBuilder.WriteString(fmt.Sprintf("BDAY:%s\n", data.Birthday))
|
||||
}
|
||||
if data.Gender != "" {
|
||||
vcardBuilder.WriteString(fmt.Sprintf("GENDER:%s\n", data.Gender))
|
||||
}
|
||||
if data.UID != "" {
|
||||
vcardBuilder.WriteString(fmt.Sprintf("UID:%s\n", data.UID))
|
||||
}
|
||||
if data.Note != "" {
|
||||
vcardBuilder.WriteString(fmt.Sprintf("NOTE:%s\n", data.Note))
|
||||
}
|
||||
if data.Categories != "" {
|
||||
vcardBuilder.WriteString(fmt.Sprintf("CATEGORIES:%s\n", data.Categories))
|
||||
}
|
||||
if data.IMPP != "" {
|
||||
vcardBuilder.WriteString(fmt.Sprintf("IMPP:%s\n", data.IMPP))
|
||||
}
|
||||
if data.Timezone != "" {
|
||||
vcardBuilder.WriteString(fmt.Sprintf("TZ:%s\n", data.Timezone))
|
||||
}
|
||||
if data.Language != "" {
|
||||
vcardBuilder.WriteString(fmt.Sprintf("LANG:%s\n", data.Language))
|
||||
}
|
||||
|
||||
// Agregando las redes sociales
|
||||
if data.Instagram != "" {
|
||||
vcardBuilder.WriteString(fmt.Sprintf("X-INSTAGRAM:%s\n", data.Instagram))
|
||||
}
|
||||
if data.Facebook != "" {
|
||||
vcardBuilder.WriteString(fmt.Sprintf("X-FACEBOOK:%s\n", data.Facebook))
|
||||
}
|
||||
if data.TikTok != "" {
|
||||
vcardBuilder.WriteString(fmt.Sprintf("X-TIKTOK:%s\n", data.TikTok))
|
||||
}
|
||||
if data.LinkedIn != "" {
|
||||
vcardBuilder.WriteString(fmt.Sprintf("X-LINKEDIN:%s\n", data.LinkedIn))
|
||||
}
|
||||
|
||||
vcardBuilder.WriteString(fmt.Sprintf("REV:%s\n", now))
|
||||
vcardBuilder.WriteString("END:VCARD")
|
||||
|
||||
vcard := vcardBuilder.String()
|
||||
|
||||
qr, err := qrcode.New(vcard, qrcode.Medium)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
qr.ForegroundColor = hexToRGBA(data.ColorHex)
|
||||
qr.BackgroundColor = hexToRGBA(data.BgColor)
|
||||
|
||||
img := qr.Image(300)
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := png.Encode(&buf, img); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
type TelegramService struct {
|
||||
BotToken string
|
||||
}
|
||||
|
||||
type TelegramMessage struct {
|
||||
ChatID interface{} `json:"chat_id"`
|
||||
Text string `json:"text"`
|
||||
ParseMode string `json:"parse_mode"`
|
||||
}
|
||||
|
||||
func NewTelegramService() *TelegramService {
|
||||
botToken := os.Getenv("TELEGRAM_BOT_TOKEN")
|
||||
if botToken == "" {
|
||||
fmt.Println("Warning: TELEGRAM_BOT_TOKEN not set in environment variables")
|
||||
}
|
||||
return &TelegramService{BotToken: botToken}
|
||||
}
|
||||
|
||||
func (ts *TelegramService) SendMessage(chatID interface{}, message string) error {
|
||||
if ts.BotToken == "" {
|
||||
return fmt.Errorf("Telegram bot token is not configured")
|
||||
}
|
||||
|
||||
apiURL := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", ts.BotToken)
|
||||
|
||||
telegramMessage := TelegramMessage{
|
||||
ChatID: chatID,
|
||||
Text: message,
|
||||
ParseMode: "HTML",
|
||||
}
|
||||
|
||||
jsonData, err := json.Marshal(telegramMessage)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error marshalling request body: %v", err)
|
||||
}
|
||||
|
||||
resp, err := http.Post(apiURL, "application/json", bytes.NewBuffer(jsonData))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error sending request to Telegram API: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("received non-OK response from Telegram API: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user