v1.4: Segurança multi-tenant, file serving via API e UX humanizada
- Validação cross-tenant no login e rotas protegidas
- File serving via /api/files/{bucket}/{path} (eliminação DNS)
- Mensagens de erro humanizadas inline (sem pop-ups)
- Middleware tenant detection via headers customizados
- Upload de logos retorna URLs via API
- README atualizado com changelog v1.4 completo
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"aggios-app/backend/internal/api/middleware"
|
||||
@@ -172,20 +173,28 @@ func (h *AgencyHandler) UploadLogo(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Generate public URL
|
||||
logoURL := fmt.Sprintf("http://localhost:9000/%s/%s", bucketName, filename)
|
||||
// Generate public URL through API (not direct MinIO access)
|
||||
// This is more secure and doesn't require DNS configuration
|
||||
logoURL := fmt.Sprintf("http://api.localhost/api/files/%s/%s", bucketName, filename)
|
||||
|
||||
log.Printf("Logo uploaded successfully: %s", logoURL)
|
||||
|
||||
// Delete old logo file from MinIO if exists
|
||||
if currentLogoURL != "" && currentLogoURL != "https://via.placeholder.com/150" {
|
||||
// Extract object key from URL
|
||||
// Example: http://localhost:9000/aggios-logos/tenants/uuid/logo-123.png -> tenants/uuid/logo-123.png
|
||||
// Example: http://api.localhost/api/files/aggios-logos/tenants/uuid/logo-123.png -> tenants/uuid/logo-123.png
|
||||
oldFilename := ""
|
||||
if len(currentLogoURL) > 0 {
|
||||
// Split by bucket name
|
||||
if idx := len("http://localhost:9000/aggios-logos/"); idx < len(currentLogoURL) {
|
||||
oldFilename = currentLogoURL[idx:]
|
||||
// Split by /api/files/{bucket}/ to get the file path
|
||||
apiPrefix := fmt.Sprintf("http://api.localhost/api/files/%s/", bucketName)
|
||||
if strings.HasPrefix(currentLogoURL, apiPrefix) {
|
||||
oldFilename = strings.TrimPrefix(currentLogoURL, apiPrefix)
|
||||
} else {
|
||||
// Fallback for old MinIO URLs
|
||||
baseURL := fmt.Sprintf("%s/%s/", h.config.Minio.PublicURL, bucketName)
|
||||
if len(currentLogoURL) > len(baseURL) {
|
||||
oldFilename = currentLogoURL[len(baseURL):]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,6 +211,8 @@ func (h *AgencyHandler) UploadLogo(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Update tenant record in database
|
||||
var err2 error
|
||||
log.Printf("Updating database: tenant_id=%s, logo_type=%s, logo_url=%s", tenantID, logoType, logoURL)
|
||||
|
||||
if logoType == "horizontal" {
|
||||
_, err2 = h.tenantRepo.DB().Exec("UPDATE tenants SET logo_horizontal_url = $1, updated_at = NOW() WHERE id = $2", logoURL, tenantID)
|
||||
} else {
|
||||
@@ -209,10 +220,12 @@ func (h *AgencyHandler) UploadLogo(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if err2 != nil {
|
||||
log.Printf("Failed to update logo: %v", err2)
|
||||
http.Error(w, "Failed to update database", http.StatusInternalServerError)
|
||||
log.Printf("ERROR: Failed to update logo in database: %v", err2)
|
||||
http.Error(w, fmt.Sprintf("Failed to update database: %v", err2), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("SUCCESS: Logo saved to database successfully!")
|
||||
|
||||
// Return success response
|
||||
response := map[string]string{
|
||||
|
||||
Reference in New Issue
Block a user