Restructure the monorepo into clear top-level directories: - backend/: Go module root (cmd, internal, pkg, configs, migrations, docs/swagger, scripts, tests, go.mod, Makefile, .air.toml) - deploy/: Docker (Dockerfile, docker-compose*), quickstart, fluentd - docs/: project documentation + reports/ (moved from repo root) - AGENTS.md: new AI coding-agent guide at repo root Update all references to the new layout: - Dockerfile: COPY backend/go.mod, COPY backend/ (context = repo root) - docker-compose files: context ../.., dockerfile deploy/docker/Dockerfile, env_file ../../.env, volume mounts ../../backend:/app - deploy/quickstart/compose.yaml: dockerfile deploy/docker/Dockerfile - CI: working-directory: backend for go commands, file deploy/docker/Dockerfile, coverage path backend/coverage.out, health_check backend/scripts/ - backend/Makefile: docker target uses -f ../deploy/docker/Dockerfile ../ - README: architecture tree, quickstart, config paths updated Move root stray scripts (rename_models.*, run_m11_tests.sh, verify_build.sh, gorm_bool_main.go) to backend/scripts/legacy/. All moves via git mv to preserve history. Build, vet, SQLite tests, and docker compose config verified.
576 lines
18 KiB
Go
576 lines
18 KiB
Go
package v1
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/gochat/gochat/internal/model"
|
|
"github.com/gochat/gochat/internal/search"
|
|
"github.com/gochat/gochat/internal/service"
|
|
"github.com/gochat/gochat/internal/ws"
|
|
applogger "github.com/gochat/gochat/pkg/logger"
|
|
"github.com/gochat/gochat/pkg/pagination"
|
|
"github.com/gochat/gochat/pkg/response"
|
|
)
|
|
|
|
// CompanyHandler handles Company CRUD + search + nested contacts/conversations/notes.
|
|
// Reference: Chatwoot app/controllers/api/v1/companies_controller.rb
|
|
type CompanyHandler struct {
|
|
svc *service.CompanyService
|
|
eventPublisher *ws.EventPublisher
|
|
}
|
|
|
|
// NewCompanyHandler creates a new CompanyHandler.
|
|
func NewCompanyHandler(svc *service.CompanyService) *CompanyHandler {
|
|
return &CompanyHandler{svc: svc}
|
|
}
|
|
|
|
func (h *CompanyHandler) WithEventPublisher(publisher *ws.EventPublisher) *CompanyHandler {
|
|
h.eventPublisher = publisher
|
|
return h
|
|
}
|
|
|
|
// List retrieves all companies for an account.
|
|
// GET /api/v1/accounts/:id/companies?sort=name&page=1&per_page=25
|
|
func (h *CompanyHandler) List(c *gin.Context) {
|
|
accountID := getAccountID(c)
|
|
if accountID == 0 {
|
|
response.AbortWithStatusError(c, http.StatusUnauthorized, response.ErrUnauthorized, "account not identified")
|
|
return
|
|
}
|
|
|
|
pg := parseCompanyPagination(c)
|
|
sort := c.DefaultQuery("sort", "")
|
|
|
|
companies, total, err := h.svc.List(c.Request.Context(), accountID, pg.Offset, pg.PerPage, sort)
|
|
if err != nil {
|
|
applogger.L().Errorf("List companies for account %d: %v", accountID, err)
|
|
handleServiceError(c, err)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, companyListResponse(c.Request.Context(), h.svc.DB(), companies, total, pg.Page))
|
|
}
|
|
|
|
// Search searches companies by query.
|
|
// GET /api/v1/accounts/:id/companies/search?q=Acme&sort=name&page=1&per_page=25
|
|
func (h *CompanyHandler) Search(c *gin.Context) {
|
|
accountID := getAccountID(c)
|
|
if accountID == 0 {
|
|
response.AbortWithStatusError(c, http.StatusUnauthorized, response.ErrUnauthorized, "account not identified")
|
|
return
|
|
}
|
|
|
|
pg := parseCompanyPagination(c)
|
|
query := c.DefaultQuery("q", "")
|
|
if query == "" {
|
|
c.JSON(http.StatusUnprocessableEntity, gin.H{"error": "Specify search string with parameter q"})
|
|
return
|
|
}
|
|
sort := c.DefaultQuery("sort", "")
|
|
searchMode := search.SearchMode(c.DefaultQuery("search_mode", "ilike"))
|
|
|
|
companies, total, err := h.svc.Search(c.Request.Context(), accountID, query, pg.Offset, pg.PerPage, sort, searchMode)
|
|
if err != nil {
|
|
applogger.L().Errorf("Search companies for account %d: %v", accountID, err)
|
|
handleServiceError(c, err)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, companyListResponse(c.Request.Context(), h.svc.DB(), companies, total, pg.Page))
|
|
}
|
|
|
|
// Get retrieves a single company by ID.
|
|
// GET /api/v1/accounts/:id/companies/:company_id
|
|
func (h *CompanyHandler) Get(c *gin.Context) {
|
|
accountID := getAccountID(c)
|
|
if accountID == 0 {
|
|
response.AbortWithStatusError(c, http.StatusUnauthorized, response.ErrUnauthorized, "account not identified")
|
|
return
|
|
}
|
|
|
|
companyID, err := strconv.ParseUint(c.Param("company_id"), 10, 32)
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid company id")
|
|
return
|
|
}
|
|
|
|
company, svcErr := h.svc.Get(c.Request.Context(), uint(companyID), accountID)
|
|
if svcErr != nil {
|
|
handleServiceError(c, svcErr)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, companyPayloadResponse(c.Request.Context(), h.svc.DB(), company))
|
|
}
|
|
|
|
func (h *CompanyHandler) publishCompanyEvent(accountID uint, eventType string, company *model.Company) {
|
|
if h.eventPublisher == nil || company == nil {
|
|
return
|
|
}
|
|
h.eventPublisher.PublishEvent(accountID, eventType, serializeCompany(context.Background(), h.svc.DB(), company))
|
|
}
|
|
|
|
// Create creates a new company.
|
|
// POST /api/v1/accounts/:id/companies
|
|
func (h *CompanyHandler) Create(c *gin.Context) {
|
|
accountID := getAccountID(c)
|
|
if accountID == 0 {
|
|
response.AbortWithStatusError(c, http.StatusUnauthorized, response.ErrUnauthorized, "account not identified")
|
|
return
|
|
}
|
|
|
|
var req service.CreateCompanyRequest
|
|
if err := bindCompanyRequest(c, &req); err != nil {
|
|
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, err.Error())
|
|
return
|
|
}
|
|
|
|
company, svcErr := h.svc.Create(c.Request.Context(), accountID, &req)
|
|
if svcErr != nil {
|
|
handleServiceError(c, svcErr)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, companyPayloadResponse(c.Request.Context(), h.svc.DB(), company))
|
|
}
|
|
|
|
// Update updates an existing company.
|
|
// PUT /api/v1/accounts/:id/companies/:company_id
|
|
func (h *CompanyHandler) Update(c *gin.Context) {
|
|
accountID := getAccountID(c)
|
|
if accountID == 0 {
|
|
response.AbortWithStatusError(c, http.StatusUnauthorized, response.ErrUnauthorized, "account not identified")
|
|
return
|
|
}
|
|
|
|
companyID, err := strconv.ParseUint(c.Param("company_id"), 10, 32)
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid company id")
|
|
return
|
|
}
|
|
|
|
var req service.UpdateCompanyRequest
|
|
if err := bindCompanyRequest(c, &req); err != nil {
|
|
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, err.Error())
|
|
return
|
|
}
|
|
|
|
company, svcErr := h.svc.Update(c.Request.Context(), uint(companyID), accountID, &req)
|
|
if svcErr != nil {
|
|
handleServiceError(c, svcErr)
|
|
return
|
|
}
|
|
|
|
payload := companyPayloadResponse(c.Request.Context(), h.svc.DB(), company)
|
|
h.publishCompanyEvent(accountID, ws.EventCompanyUpdated, company)
|
|
c.JSON(http.StatusOK, payload)
|
|
}
|
|
|
|
// Delete deletes a company.
|
|
// DELETE /api/v1/accounts/:id/companies/:company_id
|
|
func (h *CompanyHandler) Delete(c *gin.Context) {
|
|
accountID := getAccountID(c)
|
|
if accountID == 0 {
|
|
response.AbortWithStatusError(c, http.StatusUnauthorized, response.ErrUnauthorized, "account not identified")
|
|
return
|
|
}
|
|
|
|
companyID, err := strconv.ParseUint(c.Param("company_id"), 10, 32)
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid company id")
|
|
return
|
|
}
|
|
|
|
if svcErr := h.svc.Delete(c.Request.Context(), uint(companyID), accountID); svcErr != nil {
|
|
handleServiceError(c, svcErr)
|
|
return
|
|
}
|
|
|
|
c.Status(http.StatusOK)
|
|
}
|
|
|
|
func (h *CompanyHandler) DestroyCustomAttributes(c *gin.Context) {
|
|
accountID := getAccountID(c)
|
|
if accountID == 0 {
|
|
response.AbortWithStatusError(c, http.StatusUnauthorized, response.ErrUnauthorized, "account not identified")
|
|
return
|
|
}
|
|
companyID, err := strconv.ParseUint(c.Param("company_id"), 10, 32)
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid company id")
|
|
return
|
|
}
|
|
var req struct {
|
|
CustomAttributes []string `json:"custom_attributes"`
|
|
}
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
if req.CustomAttributes == nil {
|
|
c.JSON(http.StatusUnprocessableEntity, gin.H{"error": "custom_attributes must be an array"})
|
|
return
|
|
}
|
|
company, svcErr := h.svc.DestroyCustomAttributes(c.Request.Context(), uint(companyID), accountID, req.CustomAttributes)
|
|
if svcErr != nil {
|
|
handleServiceError(c, svcErr)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, companyPayloadResponse(c.Request.Context(), h.svc.DB(), company))
|
|
}
|
|
|
|
func (h *CompanyHandler) DeleteAvatar(c *gin.Context) {
|
|
accountID := getAccountID(c)
|
|
if accountID == 0 {
|
|
response.AbortWithStatusError(c, http.StatusUnauthorized, response.ErrUnauthorized, "account not identified")
|
|
return
|
|
}
|
|
companyID, err := strconv.ParseUint(c.Param("company_id"), 10, 32)
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid company id")
|
|
return
|
|
}
|
|
company, svcErr := h.svc.DeleteAvatar(c.Request.Context(), uint(companyID), accountID)
|
|
if svcErr != nil {
|
|
handleServiceError(c, svcErr)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, companyPayloadResponse(c.Request.Context(), h.svc.DB(), company))
|
|
}
|
|
|
|
// ListContacts retrieves contacts associated with a company.
|
|
// GET /api/v1/accounts/:id/companies/:company_id/contacts?page=1&per_page=25
|
|
func (h *CompanyHandler) ListContacts(c *gin.Context) {
|
|
accountID := getAccountID(c)
|
|
if accountID == 0 {
|
|
response.AbortWithStatusError(c, http.StatusUnauthorized, response.ErrUnauthorized, "account not identified")
|
|
return
|
|
}
|
|
|
|
companyID, err := strconv.ParseUint(c.Param("company_id"), 10, 32)
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid company id")
|
|
return
|
|
}
|
|
|
|
pg := parseCompanyPagination(c)
|
|
|
|
contacts, total, svcErr := h.svc.ListContacts(c.Request.Context(), uint(companyID), accountID, pg.Offset, pg.PerPage)
|
|
if svcErr != nil {
|
|
handleServiceError(c, svcErr)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, companyContactsResponse(c.Request.Context(), h.svc.DB(), uint(companyID), contacts, total, pg.Page))
|
|
}
|
|
|
|
// ListConversations retrieves conversations for contacts of a company.
|
|
// GET /api/v1/accounts/:id/companies/:company_id/conversations?page=1&per_page=25
|
|
func (h *CompanyHandler) ListConversations(c *gin.Context) {
|
|
accountID := getAccountID(c)
|
|
if accountID == 0 {
|
|
response.AbortWithStatusError(c, http.StatusUnauthorized, response.ErrUnauthorized, "account not identified")
|
|
return
|
|
}
|
|
|
|
companyID, err := strconv.ParseUint(c.Param("company_id"), 10, 32)
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid company id")
|
|
return
|
|
}
|
|
|
|
conversations, _, svcErr := h.svc.ListConversations(c.Request.Context(), uint(companyID), accountID, 0, 20)
|
|
if svcErr != nil {
|
|
handleServiceError(c, svcErr)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"payload": serializeConversationPayloads(c.Request.Context(), h.svc.DB(), conversations)})
|
|
}
|
|
|
|
func (h *CompanyHandler) SearchContacts(c *gin.Context) {
|
|
accountID := getAccountID(c)
|
|
if accountID == 0 {
|
|
response.AbortWithStatusError(c, http.StatusUnauthorized, response.ErrUnauthorized, "account not identified")
|
|
return
|
|
}
|
|
|
|
companyID, err := strconv.ParseUint(c.Param("company_id"), 10, 32)
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid company id")
|
|
return
|
|
}
|
|
|
|
query := c.Query("q")
|
|
if query == "" {
|
|
c.JSON(http.StatusUnprocessableEntity, gin.H{"error": "Specify search string with parameter q"})
|
|
return
|
|
}
|
|
pg := parseCompanyPagination(c)
|
|
contacts, total, svcErr := h.svc.SearchContacts(c.Request.Context(), uint(companyID), accountID, query, pg.Offset, pg.PerPage)
|
|
if svcErr != nil {
|
|
handleServiceError(c, svcErr)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, companyContactsResponse(c.Request.Context(), h.svc.DB(), uint(companyID), contacts, total, pg.Page))
|
|
}
|
|
|
|
// ListNotes retrieves notes for a company.
|
|
// GET /api/v1/accounts/:id/companies/:company_id/notes?page=1&per_page=25
|
|
func (h *CompanyHandler) ListNotes(c *gin.Context) {
|
|
accountID := getAccountID(c)
|
|
if accountID == 0 {
|
|
response.AbortWithStatusError(c, http.StatusUnauthorized, response.ErrUnauthorized, "account not identified")
|
|
return
|
|
}
|
|
|
|
companyID, err := strconv.ParseUint(c.Param("company_id"), 10, 32)
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid company id")
|
|
return
|
|
}
|
|
|
|
pg := pagination.Parse(c)
|
|
|
|
notes, total, svcErr := h.svc.ListNotes(c.Request.Context(), uint(companyID), accountID, pg.Offset, pg.PerPage)
|
|
if svcErr != nil {
|
|
handleServiceError(c, svcErr)
|
|
return
|
|
}
|
|
|
|
_ = total
|
|
c.JSON(http.StatusOK, companyNotesResponse(c.Request.Context(), h.svc.DB(), notes, accountID))
|
|
}
|
|
|
|
// CreateNote creates a note for a company.
|
|
// POST /api/v1/accounts/:id/companies/:company_id/notes
|
|
func (h *CompanyHandler) CreateNote(c *gin.Context) {
|
|
accountID := getAccountID(c)
|
|
if accountID == 0 {
|
|
response.AbortWithStatusError(c, http.StatusUnauthorized, response.ErrUnauthorized, "account not identified")
|
|
return
|
|
}
|
|
|
|
companyID, err := strconv.ParseUint(c.Param("company_id"), 10, 32)
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid company id")
|
|
return
|
|
}
|
|
|
|
userID := getUserID(c)
|
|
if userID == 0 {
|
|
response.AbortWithStatusError(c, http.StatusUnauthorized, response.ErrUnauthorized, "user not identified")
|
|
return
|
|
}
|
|
|
|
var req service.CreateCompanyNoteRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, err.Error())
|
|
return
|
|
}
|
|
|
|
note, svcErr := h.svc.CreateNote(c.Request.Context(), uint(companyID), accountID, userID, &req)
|
|
if svcErr != nil {
|
|
handleServiceError(c, svcErr)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"payload": serializeCompanyNote(c.Request.Context(), h.svc.DB(), note, accountID)})
|
|
}
|
|
|
|
// DeleteNote deletes a note from a company.
|
|
// DELETE /api/v1/accounts/:id/companies/:company_id/notes/:note_id
|
|
func (h *CompanyHandler) DeleteNote(c *gin.Context) {
|
|
accountID := getAccountID(c)
|
|
if accountID == 0 {
|
|
response.AbortWithStatusError(c, http.StatusUnauthorized, response.ErrUnauthorized, "account not identified")
|
|
return
|
|
}
|
|
|
|
companyID, err := strconv.ParseUint(c.Param("company_id"), 10, 32)
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid company id")
|
|
return
|
|
}
|
|
|
|
noteID, err := strconv.ParseUint(c.Param("note_id"), 10, 32)
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid note id")
|
|
return
|
|
}
|
|
|
|
if err := h.svc.DeleteNote(c.Request.Context(), uint(noteID), uint(companyID), accountID); err != nil {
|
|
handleServiceError(c, err)
|
|
return
|
|
}
|
|
|
|
c.Status(http.StatusOK)
|
|
}
|
|
|
|
// AddContact adds a contact to a company.
|
|
// POST /api/v1/accounts/:id/companies/:company_id/contacts/:contact_id
|
|
func (h *CompanyHandler) AddContact(c *gin.Context) {
|
|
accountID := getAccountID(c)
|
|
if accountID == 0 {
|
|
response.AbortWithStatusError(c, http.StatusUnauthorized, response.ErrUnauthorized, "account not identified")
|
|
return
|
|
}
|
|
|
|
companyID, err := strconv.ParseUint(c.Param("company_id"), 10, 32)
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid company id")
|
|
return
|
|
}
|
|
|
|
contactID, err := parseCompanyContactID(c)
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid contact id")
|
|
return
|
|
}
|
|
|
|
if err := h.svc.AddContact(c.Request.Context(), uint(companyID), accountID, uint(contactID)); err != nil {
|
|
handleServiceError(c, err)
|
|
return
|
|
}
|
|
|
|
contact, svcErr := h.svc.GetContact(c.Request.Context(), uint(companyID), accountID, contactID)
|
|
if svcErr != nil {
|
|
handleServiceError(c, svcErr)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"payload": serializeCompanyContact(c.Request.Context(), h.svc.DB(), contact, uint(companyID))})
|
|
}
|
|
|
|
// RemoveContact removes a contact from a company.
|
|
// DELETE /api/v1/accounts/:id/companies/:company_id/contacts/:contact_id
|
|
func (h *CompanyHandler) RemoveContact(c *gin.Context) {
|
|
accountID := getAccountID(c)
|
|
if accountID == 0 {
|
|
response.AbortWithStatusError(c, http.StatusUnauthorized, response.ErrUnauthorized, "account not identified")
|
|
return
|
|
}
|
|
|
|
companyID, err := strconv.ParseUint(c.Param("company_id"), 10, 32)
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid company id")
|
|
return
|
|
}
|
|
|
|
contactID, err := strconv.ParseUint(c.Param("contact_id"), 10, 32)
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid contact id")
|
|
return
|
|
}
|
|
|
|
if err := h.svc.RemoveContact(c.Request.Context(), uint(companyID), accountID, uint(contactID)); err != nil {
|
|
handleServiceError(c, err)
|
|
return
|
|
}
|
|
|
|
c.Status(http.StatusOK)
|
|
}
|
|
|
|
func parseCompanyContactID(c *gin.Context) (uint, error) {
|
|
if c.Param("contact_id") != "" {
|
|
return parseUintParam(c, "contact_id")
|
|
}
|
|
var req struct {
|
|
ContactID uint `json:"contact_id"`
|
|
}
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
return 0, err
|
|
}
|
|
if req.ContactID == 0 {
|
|
return 0, strconv.ErrSyntax
|
|
}
|
|
return req.ContactID, nil
|
|
}
|
|
|
|
type companyRequest interface {
|
|
*service.CreateCompanyRequest | *service.UpdateCompanyRequest
|
|
}
|
|
|
|
func parseCompanyPagination(c *gin.Context) pagination.Params {
|
|
pg := pagination.Parse(c)
|
|
pg.PerPage = service.CompanyResultsPerPage
|
|
pg.Offset = (pg.Page - 1) * pg.PerPage
|
|
return pg
|
|
}
|
|
|
|
func bindCompanyRequest[T companyRequest](c *gin.Context, req T) error {
|
|
if !strings.HasPrefix(c.GetHeader("Content-Type"), "multipart/form-data") {
|
|
return c.ShouldBindJSON(req)
|
|
}
|
|
if err := c.Request.ParseMultipartForm(32 << 20); err != nil {
|
|
return err
|
|
}
|
|
name := companyFormValue(c, "name")
|
|
description := companyFormValue(c, "description")
|
|
websiteURL := companyFormValue(c, "website_url")
|
|
faviconURL := companyFormValue(c, "favicon_url")
|
|
domain := companyFormValue(c, "domain")
|
|
if file, err := c.FormFile("company[avatar]"); err == nil && file != nil {
|
|
faviconURL = file.Filename
|
|
}
|
|
additionalAttributes := companyFormJSON(c, "additional_attributes")
|
|
customAttributes := companyFormJSON(c, "custom_attributes")
|
|
|
|
switch r := any(req).(type) {
|
|
case *service.CreateCompanyRequest:
|
|
r.Name = name
|
|
r.Description = description
|
|
r.WebsiteURL = websiteURL
|
|
r.FaviconURL = faviconURL
|
|
r.Domain = domain
|
|
r.AdditionalAttributes = additionalAttributes
|
|
r.CustomAttributes = customAttributes
|
|
case *service.UpdateCompanyRequest:
|
|
r.Name = name
|
|
r.Description = description
|
|
r.WebsiteURL = websiteURL
|
|
r.FaviconURL = faviconURL
|
|
r.Domain = domain
|
|
r.AdditionalAttributes = additionalAttributes
|
|
r.CustomAttributes = customAttributes
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func companyFormValue(c *gin.Context, key string) string {
|
|
if value := c.PostForm("company[" + key + "]"); value != "" {
|
|
return value
|
|
}
|
|
return c.PostForm(key)
|
|
}
|
|
|
|
func companyFormJSON(c *gin.Context, key string) []byte {
|
|
if c.Request.MultipartForm == nil {
|
|
return nil
|
|
}
|
|
if value := companyFormValue(c, key); value != "" {
|
|
if json.Valid([]byte(value)) {
|
|
return []byte(value)
|
|
}
|
|
}
|
|
prefix := "company[" + key + "]["
|
|
attrs := map[string]any{}
|
|
for formKey, values := range c.Request.MultipartForm.Value {
|
|
if !strings.HasPrefix(formKey, prefix) || !strings.HasSuffix(formKey, "]") || len(values) == 0 {
|
|
continue
|
|
}
|
|
attrKey := strings.TrimSuffix(strings.TrimPrefix(formKey, prefix), "]")
|
|
attrs[attrKey] = values[0]
|
|
}
|
|
if len(attrs) == 0 {
|
|
return nil
|
|
}
|
|
data, _ := json.Marshal(attrs)
|
|
return data
|
|
}
|