This commit is contained in:
Lizandro Guarnizo
2026-05-18 23:50:50 -05:00
parent bbe83c79ba
commit 7f309186d6
10 changed files with 73 additions and 22 deletions
+2
View File
@@ -20,6 +20,8 @@ server:
name: "iSend.to"
host: localhost
port: 8080
# URL pública para enlaces en correos, Telegram y webhooks (producción)
public_url: https://admin.u-site.app
redis:
instances:
+1
View File
@@ -30,6 +30,7 @@ server: &server
name: "USITE "
host: 0.0.0.0
port: 8080
public_url: https://admin.u-site.app
profiler:
enabled: false
+1
View File
@@ -28,6 +28,7 @@ type ServerConfig struct {
Env string `mapstructure:"APP_ENV" yaml:"env" env:"APP_ENV" env-default:"dev"`
Key string `mapstructure:"APP_KEY" yaml:"key" env:"APP_KEY" env-default:"1894cde6c936a294a478cff0a9227fd276d86df6573b51af5dc59c9064edf426"`
Url string `mapstructure:"APP_URL" yaml:"url" env:"APP_URL" env-default:"http://localhost"`
PublicUrl string `mapstructure:"APP_PUBLIC_URL" yaml:"public_url" env:"APP_PUBLIC_URL"`
Host string `mapstructure:"APP_HOST" yaml:"host" env:"APP_HOST" env-default:"localhost"`
Port string `mapstructure:"APP_PORT" yaml:"port" env:"APP_PORT" env-default:"8080"`
Path string `mapstructure:"APP_PATH" yaml:"path" env:"APP_PATH"`
+29 -5
View File
@@ -10,8 +10,21 @@ import (
"github.com/sujit-baniya/fiber-boilerplate/app"
)
// getAppURL devuelve la URL base absoluta (sin slash final).
// Si APP_URL es http://localhost sin puerto, añade APP_PORT para enlaces en desarrollo.
// GetPublicURL devuelve la URL pública para enlaces en correos, Telegram y webhooks.
func GetPublicURL() string {
return getPublicURL()
}
// getPublicURL usa APP_PUBLIC_URL si está configurada; si no, APP_URL (con puerto en localhost).
func getPublicURL() string {
if u := normalizeBaseURL(app.Http.Server.PublicUrl); u != "" {
return u
}
return getAppURL()
}
// getAppURL devuelve la URL base de la app (sin slash final).
// Si APP_URL es http://localhost sin puerto, añade APP_PORT para desarrollo local.
func getAppURL() string {
u := strings.TrimRight(app.Http.Server.Url, "/")
if u == "" {
@@ -33,10 +46,21 @@ func getAppURL() string {
return u
}
// absAppURL convierte una ruta relativa en URL absoluta para correo/Telegram.
func normalizeBaseURL(raw string) string {
u := strings.TrimSpace(strings.TrimRight(raw, "/"))
if u == "" {
return ""
}
if !strings.Contains(u, "://") {
u = "https://" + u
}
return strings.TrimRight(u, "/")
}
// absAppURL convierte una ruta relativa en URL absoluta pública (correo/Telegram).
func absAppURL(path string) string {
if path == "" {
return getAppURL()
return getPublicURL()
}
if strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://") {
return path
@@ -44,7 +68,7 @@ func absAppURL(path string) string {
if !strings.HasPrefix(path, "/") {
path = "/" + path
}
return getAppURL() + path
return getPublicURL() + path
}
func adminTicketPath(ticketID uint) string {
+31 -3
View File
@@ -19,7 +19,35 @@ func TestGetAppURLLocalhostAddsPort(t *testing.T) {
}
}
func TestAbsAppURLRelativePath(t *testing.T) {
func TestGetPublicURLPrefersPublicURL(t *testing.T) {
app.Http = &config.AppConfig{
Server: config.ServerConfig{
Url: "http://localhost:8080",
PublicUrl: "admin.u-site.app",
Port: "8080",
},
}
if got := getPublicURL(); got != "https://admin.u-site.app" {
t.Fatalf("getPublicURL() = %q, want https://admin.u-site.app", got)
}
}
func TestAbsAppURLUsesPublicURL(t *testing.T) {
app.Http = &config.AppConfig{
Server: config.ServerConfig{
Url: "http://localhost:8080",
PublicUrl: "https://admin.u-site.app",
Port: "8080",
},
}
got := absAppURL("/portal/proyecto/bot?tab=Tickets&ticket=1")
want := "https://admin.u-site.app/portal/proyecto/bot?tab=Tickets&ticket=1"
if got != want {
t.Fatalf("absAppURL() = %q, want %q", got, want)
}
}
func TestAbsAppURLFallsBackToAppURL(t *testing.T) {
app.Http = &config.AppConfig{
Server: config.ServerConfig{
Url: "http://localhost",
@@ -34,8 +62,8 @@ func TestAbsAppURLRelativePath(t *testing.T) {
}
func TestTelegramHTMLLinkEscapesAmpersand(t *testing.T) {
link := telegramHTMLLink("http://localhost:8084/portal/p?tab=Tickets&ticket=1", "Ver portal")
if link != `<a href="http://localhost:8084/portal/p?tab=Tickets&amp;ticket=1">Ver portal</a>` {
link := telegramHTMLLink("https://admin.u-site.app/portal/p?tab=Tickets&ticket=1", "Ver portal")
if link != `<a href="https://admin.u-site.app/portal/p?tab=Tickets&amp;ticket=1">Ver portal</a>` {
t.Fatalf("telegramHTMLLink() = %q", link)
}
}
+1 -1
View File
@@ -80,7 +80,7 @@ func GeneratePasswordResetURL(email string, baseURL string) string {
// SendPortalCredentialsEmail envía al usuario del portal su URL de acceso y contraseña inicial.
func SendPortalCredentialsEmail(email, nombre, password string) {
loginURL := fmt.Sprintf("%s/portal/login", app.Http.Server.Url)
loginURL := absAppURL("/portal/login")
htmlBody := fmt.Sprintf(`<!DOCTYPE html>
<html><body style="font-family:Inter,sans-serif;background:#f1f5f9;padding:32px">
<div style="max-width:480px;margin:0 auto;background:#fff;border-radius:16px;padding:32px;border:1px solid #e2e8f0">
+1 -1
View File
@@ -84,7 +84,7 @@ func ApiRegisterPost(c *fiber.Ctx) error {
store.Set("user_id", user.ID) // save to storage
_ = store.Save()
go services.SendConfirmationEmail(user.Email, c.BaseURL(), user.NombreUsuario)
go services.SendConfirmationEmail(user.Email, services.GetPublicURL(), user.NombreUsuario)
return c.JSON(fiber.Map{
"success": true,
"message": "Registered successfully! Please confirm your email",
+4 -4
View File
@@ -42,7 +42,7 @@ func RegisterPost(c *fiber.Ctx) error {
store.Set("user_id", user.ID) // guardar en el almacenamiento
_ = store.Save()
go services.SendConfirmationEmail(user.Email, app.Http.Server.Url, user.NombreUsuario)
go services.SendConfirmationEmail(user.Email, services.GetPublicURL(), user.NombreUsuario)
return c.Redirect("/")
}
@@ -55,7 +55,7 @@ func VerifyRegisteredEmail(c *fiber.Ctx) error {
// Función para reenviar el correo de confirmación
func ResendConfirmEmail(c *fiber.Ctx) error {
user, _ := auth.User(c)
go services.SendConfirmationEmail(user.Email, app.Http.Server.Url, user.NombreUsuario)
go services.SendConfirmationEmail(user.Email, services.GetPublicURL(), user.NombreUsuario)
return c.Redirect("/")
}
@@ -70,7 +70,7 @@ func ReenvioEmail(c *fiber.Ctx) error {
}
// Reenvío del correo electrónico de forma asíncrona
go services.SendConfirmationEmail(user.Email, app.Http.Server.Url, user.NombreUsuario)
go services.SendConfirmationEmail(user.Email, services.GetPublicURL(), user.NombreUsuario)
return c.Status(fiber.StatusOK).JSON(fiber.Map{
"success": true,
@@ -94,7 +94,7 @@ func RequestPasswordResetPost(c *fiber.Ctx) error {
usuario_email := user.Email
log.Println("Sending password reset email to:", usuario_email)
go services.SendPasswordResetEmail(usuario_email, app.Http.Server.Url)
go services.SendPasswordResetEmail(usuario_email, services.GetPublicURL())
return c.JSON(fiber.Map{
"success": true,
+2 -7
View File
@@ -12,8 +12,8 @@ import (
"strings"
"github.com/gofiber/fiber/v2"
"github.com/sujit-baniya/fiber-boilerplate/app"
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
"github.com/sujit-baniya/fiber-boilerplate/pkg/services"
)
// TelegramIndex renderiza la vista de configuración de Telegram.
@@ -222,12 +222,7 @@ func SetPortalWebhook(c *fiber.Ctx) error {
return c.Status(404).JSON(fiber.Map{"error": "Configuración no encontrada"})
}
// Construir URL base desde la configuración de la app
baseURL := app.Http.Server.Url
if len(baseURL) > 0 && baseURL[len(baseURL)-1] == '/' {
baseURL = baseURL[:len(baseURL)-1]
}
webhookURL := baseURL + "/webhooks/telegram-portal"
webhookURL := services.GetPublicURL() + "/webhooks/telegram-portal"
apiURL := fmt.Sprintf("https://api.telegram.org/bot%s/setWebhook", cfg.BotToken)
payload, _ := json.Marshal(map[string]string{"url": webhookURL})
+1 -1
View File
@@ -486,7 +486,7 @@ func CreateUserGas(c *fiber.Ctx) error {
}
// Envía un correo de confirmación
go services.SendConfirmationEmail(m.Email, app.Http.Server.Url, m.NombreUsuario)
go services.SendConfirmationEmail(m.Email, services.GetPublicURL(), m.NombreUsuario)
return c.Status(fiber.StatusCreated).JSON(fiber.Map{
"message": "Usuario creado con éxito",