Inicial
This commit is contained in:
@@ -0,0 +1,242 @@
|
||||
package tenant
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
|
||||
"github.com/techxcar/backend/internal/auth"
|
||||
"github.com/techxcar/backend/internal/config"
|
||||
"github.com/techxcar/backend/pkg/database"
|
||||
)
|
||||
|
||||
func listTenantsHandler(repo *Repository) fiber.Handler {
|
||||
return func(c *fiber.Ctx) error {
|
||||
list, err := repo.ListTenants(c.Context())
|
||||
if err != nil {
|
||||
return fiber.NewError(500, "erro ao listar oficinas")
|
||||
}
|
||||
if list == nil {
|
||||
list = []*Tenant{}
|
||||
}
|
||||
return c.JSON(fiber.Map{"data": list, "error": nil})
|
||||
}
|
||||
}
|
||||
|
||||
type createTenantRequest struct {
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
AdminEmail string `json:"admin_email"`
|
||||
AdminPassword string `json:"admin_password"`
|
||||
AdminName string `json:"admin_name"`
|
||||
}
|
||||
|
||||
func createTenantHandler(repo *Repository, db *database.DB, cfg *config.Config) fiber.Handler {
|
||||
return func(c *fiber.Ctx) error {
|
||||
var req createTenantRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return fiber.NewError(400, "corpo do pedido inválido")
|
||||
}
|
||||
if req.Name == "" || req.Slug == "" || req.AdminEmail == "" || req.AdminPassword == "" || req.AdminName == "" {
|
||||
return fiber.NewError(400, "todos os campos são obrigatórios")
|
||||
}
|
||||
existing, err := repo.GetTenantBySlug(c.Context(), req.Slug)
|
||||
if err != nil {
|
||||
return fiber.NewError(500, "erro interno")
|
||||
}
|
||||
if existing != nil {
|
||||
return fiber.NewError(409, "slug já existe")
|
||||
}
|
||||
|
||||
ten, err := repo.CreateTenant(c.Context(), req.Slug, req.Name)
|
||||
if err != nil {
|
||||
return fiber.NewError(500, "erro ao criar oficina")
|
||||
}
|
||||
|
||||
if db != nil {
|
||||
if err := db.ProvisionTenantSchema(c.Context(), ten.ID, "migrations/tenant"); err != nil {
|
||||
return fiber.NewError(500, "erro ao provisionar schema")
|
||||
}
|
||||
}
|
||||
|
||||
hash, err := auth.HashPassword(req.AdminPassword)
|
||||
if err != nil {
|
||||
return fiber.NewError(500, "erro interno")
|
||||
}
|
||||
if db != nil {
|
||||
if _, err := repo.CreateTenantUser(c.Context(), ten.ID, req.AdminEmail, hash, req.AdminName, "tenant_admin"); err != nil {
|
||||
return fiber.NewError(500, "erro ao criar utilizador admin")
|
||||
}
|
||||
}
|
||||
|
||||
return c.Status(201).JSON(fiber.Map{"data": ten, "error": nil})
|
||||
}
|
||||
}
|
||||
|
||||
func generateInviteHandler(repo *Repository) fiber.Handler {
|
||||
return func(c *fiber.Ctx) error {
|
||||
tenantID := c.Params("id")
|
||||
ten, err := repo.GetTenantByID(c.Context(), tenantID)
|
||||
if err != nil || ten == nil {
|
||||
return fiber.NewError(404, "oficina não encontrada")
|
||||
}
|
||||
|
||||
invite, err := repo.CreateInvite(c.Context(), &ten.ID, 72*time.Hour)
|
||||
if err != nil {
|
||||
return fiber.NewError(500, "erro ao gerar convite")
|
||||
}
|
||||
|
||||
return c.Status(201).JSON(fiber.Map{
|
||||
"data": fiber.Map{"token": invite.Token, "expires_at": invite.ExpiresAt},
|
||||
"error": nil,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func generatePlatformInviteHandler(repo *Repository) fiber.Handler {
|
||||
return func(c *fiber.Ctx) error {
|
||||
invite, err := repo.CreateInvite(c.Context(), nil, 72*time.Hour)
|
||||
if err != nil {
|
||||
return fiber.NewError(500, "erro ao gerar convite")
|
||||
}
|
||||
return c.Status(201).JSON(fiber.Map{
|
||||
"data": fiber.Map{"token": invite.Token, "expires_at": invite.ExpiresAt},
|
||||
"error": nil,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func getInviteHandler(repo *Repository) fiber.Handler {
|
||||
return func(c *fiber.Ctx) error {
|
||||
token := c.Params("token")
|
||||
invite, err := repo.GetInviteByToken(c.Context(), token)
|
||||
if err != nil {
|
||||
return fiber.NewError(500, "erro interno")
|
||||
}
|
||||
if invite == nil {
|
||||
return fiber.NewError(404, "convite não encontrado")
|
||||
}
|
||||
if invite.UsedAt != nil {
|
||||
return fiber.NewError(410, "convite já foi utilizado")
|
||||
}
|
||||
if invite.ExpiresAt.Before(time.Now()) {
|
||||
return fiber.NewError(410, "convite expirado")
|
||||
}
|
||||
return c.JSON(fiber.Map{"data": invite, "error": nil})
|
||||
}
|
||||
}
|
||||
|
||||
type redeemRequest struct {
|
||||
TenantName string `json:"tenant_name"`
|
||||
TenantSlug string `json:"tenant_slug"`
|
||||
AdminEmail string `json:"admin_email"`
|
||||
AdminPassword string `json:"admin_password"`
|
||||
AdminName string `json:"admin_name"`
|
||||
}
|
||||
|
||||
func tenantAccessHandler(repo *Repository, cfg *config.Config) fiber.Handler {
|
||||
return func(c *fiber.Ctx) error {
|
||||
claims, ok := c.Locals("claims").(*auth.Claims)
|
||||
if !ok {
|
||||
return fiber.NewError(401, "autenticação necessária")
|
||||
}
|
||||
|
||||
tenantID := c.Params("id")
|
||||
ten, err := repo.GetTenantByID(c.Context(), tenantID)
|
||||
if err != nil {
|
||||
return fiber.NewError(500, "erro interno")
|
||||
}
|
||||
if ten == nil {
|
||||
return fiber.NewError(404, "oficina não encontrada")
|
||||
}
|
||||
if ten.Status != "active" {
|
||||
return fiber.NewError(404, "oficina não encontrada ou inativa")
|
||||
}
|
||||
|
||||
token, err := auth.GenerateAccessToken(claims.UserID, ten.ID, "tenant_admin", cfg.JWTSecret)
|
||||
if err != nil {
|
||||
return fiber.NewError(500, "erro ao gerar token")
|
||||
}
|
||||
|
||||
return c.JSON(fiber.Map{
|
||||
"data": fiber.Map{
|
||||
"access_token": token,
|
||||
"tenant": fiber.Map{
|
||||
"id": ten.ID,
|
||||
"name": ten.Name,
|
||||
"slug": ten.Slug,
|
||||
},
|
||||
},
|
||||
"error": nil,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func redeemInviteHandler(repo *Repository, db *database.DB, cfg *config.Config) fiber.Handler {
|
||||
return func(c *fiber.Ctx) error {
|
||||
token := c.Params("token")
|
||||
invite, err := repo.GetInviteByToken(c.Context(), token)
|
||||
if err != nil || invite == nil {
|
||||
return fiber.NewError(404, "convite não encontrado")
|
||||
}
|
||||
if invite.UsedAt != nil || invite.ExpiresAt.Before(time.Now()) {
|
||||
return fiber.NewError(410, "convite inválido ou expirado")
|
||||
}
|
||||
|
||||
var req redeemRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return fiber.NewError(400, "corpo do pedido inválido")
|
||||
}
|
||||
if req.TenantName == "" || req.TenantSlug == "" || req.AdminEmail == "" || req.AdminPassword == "" || req.AdminName == "" {
|
||||
return fiber.NewError(400, "todos os campos são obrigatórios")
|
||||
}
|
||||
if len(req.AdminPassword) < 8 {
|
||||
return fiber.NewError(400, "password deve ter pelo menos 8 caracteres")
|
||||
}
|
||||
|
||||
existing, _ := repo.GetTenantBySlug(c.Context(), req.TenantSlug)
|
||||
if existing != nil {
|
||||
return fiber.NewError(409, "slug já existe")
|
||||
}
|
||||
|
||||
ten, err := repo.CreateTenant(c.Context(), req.TenantSlug, req.TenantName)
|
||||
if err != nil {
|
||||
return fiber.NewError(500, "erro ao criar oficina")
|
||||
}
|
||||
|
||||
if db != nil {
|
||||
if err := db.ProvisionTenantSchema(c.Context(), ten.ID, "migrations/tenant"); err != nil {
|
||||
return fiber.NewError(500, "erro ao provisionar schema")
|
||||
}
|
||||
}
|
||||
|
||||
hash, err := auth.HashPassword(req.AdminPassword)
|
||||
if err != nil {
|
||||
return fiber.NewError(500, "erro interno")
|
||||
}
|
||||
|
||||
var userID string
|
||||
if db != nil {
|
||||
user, err := repo.CreateTenantUser(c.Context(), ten.ID, req.AdminEmail, hash, req.AdminName, "tenant_admin")
|
||||
if err != nil {
|
||||
return fiber.NewError(500, "erro ao criar utilizador")
|
||||
}
|
||||
userID = user.ID
|
||||
} else {
|
||||
userID = "mock-user-id"
|
||||
}
|
||||
|
||||
if err := repo.UseInvite(c.Context(), invite.ID); err != nil {
|
||||
return fiber.NewError(500, "erro ao registar utilização do convite")
|
||||
}
|
||||
|
||||
access, _ := auth.GenerateAccessToken(userID, ten.ID, "tenant_admin", cfg.JWTSecret)
|
||||
refresh, _ := auth.GenerateRefreshToken(userID, ten.ID, "tenant_admin", cfg.JWTSecret)
|
||||
auth.SetRefreshCookie(c, refresh, cfg)
|
||||
|
||||
return c.Status(201).JSON(fiber.Map{
|
||||
"data": fiber.Map{"access_token": access, "tenant_slug": ten.Slug},
|
||||
"error": nil,
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user