Initial commit: Open sourcing all of the Maple Open Technologies code.

This commit is contained in:
Bartlomiej Mika 2025-12-02 14:33:08 -05:00
commit 755d54a99d
2010 changed files with 448675 additions and 0 deletions

View file

@ -0,0 +1,108 @@
package tenant
import (
"encoding/json"
"fmt"
"net/http"
"go.uber.org/zap"
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/config/constants"
tenantdto "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/interface/http/dto/tenant"
tenantservice "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/service/tenant"
tenantusecase "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/usecase/tenant"
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/httperror"
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/httpresponse"
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/httpvalidation"
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/logger"
)
// CreateHandler handles tenant creation HTTP requests
type CreateHandler struct {
service tenantservice.CreateTenantService
logger *zap.Logger
}
// ProvideCreateHandler creates a new CreateHandler
func ProvideCreateHandler(service tenantservice.CreateTenantService, logger *zap.Logger) *CreateHandler {
return &CreateHandler{
service: service,
logger: logger,
}
}
// Handle handles the HTTP request for creating a tenant
// Note: This endpoint does NOT require tenant middleware since we're creating a tenant
// Security: CWE-20, CWE-79, CWE-117 - Comprehensive input validation and sanitization
func (h *CreateHandler) Handle(w http.ResponseWriter, r *http.Request) {
// CWE-436: Enforce strict Content-Type validation
if err := httpvalidation.ValidateJSONContentTypeStrict(r); err != nil {
h.logger.Warn("invalid content type", zap.String("content_type", r.Header.Get("Content-Type")))
httperror.ProblemBadRequest(w, err.Error())
return
}
// Parse request body
var req tenantdto.CreateRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
h.logger.Warn("invalid request body", zap.Error(err))
httperror.ProblemBadRequest(w, "Invalid request body format. Please check your JSON syntax.")
return
}
// CWE-20: Comprehensive input validation
if err := req.Validate(); err != nil {
h.logger.Warn("tenant creation validation failed", zap.Error(err))
httperror.ProblemBadRequest(w, err.Error())
return
}
// Extract user context for logging
userID := "unknown"
if uid := r.Context().Value(constants.SessionUserID); uid != nil {
if userIDUint, ok := uid.(uint64); ok {
userID = fmt.Sprintf("%d", userIDUint)
}
}
// CWE-532: Safe logging with hashed PII
h.logger.Info("creating tenant",
zap.String("user_id", userID),
logger.TenantSlugHash(req.Slug))
// Map DTO to use case input
input := &tenantusecase.CreateTenantInput{
Name: req.Name,
Slug: req.Slug,
}
// Call service
output, err := h.service.CreateTenant(r.Context(), input)
if err != nil {
// CWE-532: Log with safe identifiers
h.logger.Error("failed to create tenant",
zap.Error(err),
zap.String("user_id", userID),
logger.TenantSlugHash(req.Slug))
httperror.ProblemInternalServerError(w, "Failed to create tenant. Please try again later.")
return
}
// CWE-532: Log successful creation
h.logger.Info("tenant created successfully",
zap.String("user_id", userID),
zap.String("tenant_id", output.ID),
logger.TenantSlugHash(output.Slug))
// Map to response DTO
response := tenantdto.CreateResponse{
ID: output.ID,
Name: output.Name,
Slug: output.Slug,
Status: output.Status,
CreatedAt: output.CreatedAt,
}
// Write response
httpresponse.Created(w, response)
}

View file

@ -0,0 +1,113 @@
package tenant
import (
"net/http"
"go.uber.org/zap"
tenantdto "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/interface/http/dto/tenant"
tenantservice "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/service/tenant"
tenantusecase "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/usecase/tenant"
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/httperror"
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/httpresponse"
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/logger"
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/validation"
)
// GetHandler handles getting a tenant by ID or slug
type GetHandler struct {
service tenantservice.GetTenantService
logger *zap.Logger
}
// ProvideGetHandler creates a new GetHandler
func ProvideGetHandler(service tenantservice.GetTenantService, logger *zap.Logger) *GetHandler {
return &GetHandler{
service: service,
logger: logger,
}
}
// HandleByID handles the HTTP request for getting a tenant by ID
// Security: CWE-20 - Path parameter validation
func (h *GetHandler) HandleByID(w http.ResponseWriter, r *http.Request) {
// CWE-20: Validate UUID path parameter
id, err := validation.ValidatePathUUID(r, "id")
if err != nil {
h.logger.Warn("invalid tenant ID", zap.Error(err))
httperror.ProblemBadRequest(w, err.Error())
return
}
// Call service
input := &tenantusecase.GetTenantInput{ID: id}
output, err := h.service.GetTenant(r.Context(), input)
if err != nil {
// CWE-532: Don't log full error details to prevent information leakage
h.logger.Debug("failed to get tenant",
zap.String("tenant_id", id),
zap.Error(err))
httperror.ProblemNotFound(w, "The requested tenant could not be found.")
return
}
// CWE-532: Safe logging
h.logger.Info("tenant retrieved",
zap.String("tenant_id", output.ID),
logger.TenantSlugHash(output.Slug))
// Map to response DTO
response := tenantdto.GetResponse{
ID: output.ID,
Name: output.Name,
Slug: output.Slug,
Status: output.Status,
CreatedAt: output.CreatedAt,
UpdatedAt: output.UpdatedAt,
}
// Write response
httpresponse.OK(w, response)
}
// HandleBySlug handles the HTTP request for getting a tenant by slug
// Security: CWE-20 - Path parameter validation
func (h *GetHandler) HandleBySlug(w http.ResponseWriter, r *http.Request) {
// CWE-20: Validate slug path parameter
slug, err := validation.ValidatePathSlug(r, "slug")
if err != nil {
h.logger.Warn("invalid tenant slug", zap.Error(err))
httperror.ProblemBadRequest(w, err.Error())
return
}
// Call service
input := &tenantusecase.GetTenantBySlugInput{Slug: slug}
output, err := h.service.GetTenantBySlug(r.Context(), input)
if err != nil {
// CWE-532: Don't log full error details to prevent information leakage
h.logger.Debug("failed to get tenant by slug",
logger.TenantSlugHash(slug),
zap.Error(err))
httperror.ProblemNotFound(w, "The requested tenant could not be found.")
return
}
// CWE-532: Safe logging
h.logger.Info("tenant retrieved by slug",
zap.String("tenant_id", output.ID),
logger.TenantSlugHash(output.Slug))
// Map to response DTO
response := tenantdto.GetResponse{
ID: output.ID,
Name: output.Name,
Slug: output.Slug,
Status: output.Status,
CreatedAt: output.CreatedAt,
UpdatedAt: output.UpdatedAt,
}
// Write response
httpresponse.OK(w, response)
}