Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions cmd/magebox/domain.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,14 @@ var domainListCmd = &cobra.Command{

var (
domainStoreCode string
domainStoreType string
domainRoot string
domainSSL bool
)

func init() {
domainAddCmd.Flags().StringVar(&domainStoreCode, "store-code", "", "Magento store code (default: \"default\")")
domainAddCmd.Flags().StringVar(&domainStoreCode, "store-code", "", "Magento store/website code for multi-store setup")
domainAddCmd.Flags().StringVar(&domainStoreType, "store-type", "", "Magento run type: \"store\" or \"website\" (default: \"store\")")
domainAddCmd.Flags().StringVar(&domainRoot, "root", "", "Document root relative to project (default: \"pub\" for Magento, \"public\" for Laravel)")
domainAddCmd.Flags().BoolVar(&domainSSL, "ssl", true, "Enable SSL for the domain")

Expand Down Expand Up @@ -92,9 +94,10 @@ func runDomainAdd(cmd *cobra.Command, args []string) error {

// Create new domain
newDomain := config.Domain{
Host: host,
Root: domainRoot,
MageRunCode: domainStoreCode,
Host: host,
Root: domainRoot,
StoreCode: domainStoreCode,
StoreType: domainStoreType,
}

// Only set SSL if explicitly changed from default (true)
Expand Down Expand Up @@ -163,7 +166,9 @@ func runDomainAdd(cmd *cobra.Command, args []string) error {
}

fmt.Println()
cli.PrintInfo("Domain %s configured with store code: %s", host, newDomain.GetStoreCode())
if newDomain.StoreCode != "" {
cli.PrintInfo("Domain %s configured with store code: %s (%s)", host, newDomain.StoreCode, newDomain.GetStoreType())
}

return nil
}
Expand Down Expand Up @@ -284,7 +289,9 @@ func runDomainList(cmd *cobra.Command, args []string) error {
fmt.Printf(" %s\n", cli.Highlight(d.Host))
fmt.Printf(" URL: %s://%s\n", protocol, d.Host)
fmt.Printf(" Root: %s\n", d.GetRoot())
fmt.Printf(" Store Code: %s\n", d.GetStoreCode())
if d.StoreCode != "" {
fmt.Printf(" Store Code: %s (%s)\n", d.StoreCode, d.GetStoreType())
}
fmt.Printf(" SSL: %s\n", sslStatus)
fmt.Println()
}
Expand Down
32 changes: 17 additions & 15 deletions internal/config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,11 @@ func (c *Command) UnmarshalYAML(unmarshal func(interface{}) error) error {

// Domain represents a domain configuration
type Domain struct {
Host string `yaml:"host"`
Root string `yaml:"root,omitempty"`
SSL *bool `yaml:"ssl,omitempty"`
MageRunCode string `yaml:"mage_run_code,omitempty"` // Magento store/website code for multi-store setup
MageRunType string `yaml:"mage_run_type,omitempty"` // "store" or "website" (default: "store")
Host string `yaml:"host"`
Root string `yaml:"root,omitempty"`
SSL *bool `yaml:"ssl,omitempty"`
StoreCode string `yaml:"store_code,omitempty"` // Magento store/website code for multi-store setup
StoreType string `yaml:"store_type,omitempty"` // "store" or "website" (default: "store")
}

// Services represents the services configuration
Expand Down Expand Up @@ -320,20 +320,22 @@ func (d *Domain) IsSSLEnabled() bool {
return *d.SSL
}

// GetStoreCode returns the Magento store code, defaulting to "default"
func (d *Domain) GetStoreCode() string {
if d.MageRunCode == "" {
return "default"
// GetStoreType returns the Magento run type, defaulting to "store"
func (d *Domain) GetStoreType() string {
if d.StoreType == "" {
return "store"
}
return d.MageRunCode
return d.StoreType
}

// GetMageRunType returns the Magento run type, defaulting to "store"
func (d *Domain) GetMageRunType() string {
if d.MageRunType == "" {
return "store"
// HasMultiStore returns true if any domain has a store code configured
func (c *Config) HasMultiStore() bool {
for _, d := range c.Domains {
if d.StoreCode != "" {
return true
}
}
return d.MageRunType
return false
}

// Validate checks if the configuration is valid
Expand Down
1 change: 1 addition & 0 deletions internal/lib/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ var TemplateNames = map[string][]string{
"vhost.conf.tmpl",
"proxy.conf.tmpl",
"upstream.conf.tmpl",
"map.conf.tmpl",
},
TemplatePHP: {
"pool.conf.tmpl",
Expand Down
17 changes: 17 additions & 0 deletions internal/nginx/templates/map.conf.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# MageBox multi-store map for {{.ProjectName}}
# Generated from store_code settings in domains configuration
# Do not edit manually - regenerated on magebox start

map $host $MAGE_RUN_CODE {
hostnames;
{{- range .Domains}}{{if .StoreCode}}
.{{.Host}} {{.StoreCode}};
{{- end}}{{end}}
}

map $host $MAGE_RUN_TYPE {
hostnames;
{{- range .Domains}}{{if .StoreCode}}
.{{.Host}} {{.GetStoreType}};
{{- end}}{{end}}
}
8 changes: 4 additions & 4 deletions internal/nginx/templates/vhost.conf.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,6 @@ server {

set $MAGE_ROOT {{.DocumentRoot}};
set $MAGE_MODE developer;
set $MAGE_RUN_CODE {{.StoreCode}};
set $MAGE_RUN_TYPE {{.MageRunType}};

root $MAGE_ROOT;
index index.php;
Expand Down Expand Up @@ -221,8 +219,10 @@ server {

fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param MAGE_RUN_CODE $MAGE_RUN_CODE;
fastcgi_param MAGE_RUN_TYPE $MAGE_RUN_TYPE;
{{- if .HasStoreCodes}}
fastcgi_param MAGE_RUN_CODE $MAGE_RUN_CODE if_not_empty;
fastcgi_param MAGE_RUN_TYPE $MAGE_RUN_TYPE if_not_empty;
{{- end}}
include fastcgi_params;
}

Expand Down
57 changes: 53 additions & 4 deletions internal/nginx/vhost.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,16 @@ var proxyTemplateEmbed string
//go:embed templates/upstream.conf.tmpl
var upstreamTemplateEmbed string

//go:embed templates/map.conf.tmpl
var mapTemplateEmbed string

func init() {
// Register embedded templates as fallbacks
lib.RegisterFallbackTemplate(lib.TemplateNginx, "vhost.conf.tmpl", vhostTemplateEmbed)
lib.RegisterFallbackTemplate(lib.TemplateNginx, "vhost-laravel.conf.tmpl", vhostLaravelTemplateEmbed)
lib.RegisterFallbackTemplate(lib.TemplateNginx, "proxy.conf.tmpl", proxyTemplateEmbed)
lib.RegisterFallbackTemplate(lib.TemplateNginx, "upstream.conf.tmpl", upstreamTemplateEmbed)
lib.RegisterFallbackTemplate(lib.TemplateNginx, "map.conf.tmpl", mapTemplateEmbed)
}

// Template variables available in vhost.conf.tmpl:
Expand Down Expand Up @@ -74,13 +78,18 @@ type VhostConfig struct {
HTTPSPort int // 443 on Linux, 8443 on macOS (port forwarding)
BackendPort int // Backend port for Varnish (always 8080 when Varnish enabled)
EnableIPv6 bool // true on Linux to add [::]:port listen directives
StoreCode string // Magento store code for multi-store setup (default: "default")
MageRunType string // Magento run type: "store" or "website" (default: "store")
HasStoreCodes bool // True when any domain has a store_code; enables MAGE_RUN_* fastcgi params
AccessLog string // Path to access log file
ErrorLog string // Path to error log file
CustomNginxDir string // Path to project-level custom nginx snippets directory (if it exists)
}

// MapConfig contains data needed to generate the multi-store map config
type MapConfig struct {
ProjectName string
Domains []config.Domain
}

// ProxyConfig contains data needed to generate a proxy vhost
type ProxyConfig struct {
Name string
Expand Down Expand Up @@ -132,6 +141,18 @@ func (g *VhostGenerator) Generate(cfg *config.Config, projectPath string) error
return fmt.Errorf("failed to generate upstream config: %w", err)
}

// Generate or remove the multi-store map config depending on whether any domain has a store_code
hasStoreCodes := cfg.HasMultiStore()
mapFile := filepath.Join(g.vhostsDir, fmt.Sprintf("%s-map.conf", cfg.Name))
if hasStoreCodes {
mapCfg := MapConfig{ProjectName: cfg.Name, Domains: cfg.Domains}
if err := g.generateMap(mapCfg, mapFile); err != nil {
return fmt.Errorf("failed to generate map config: %w", err)
}
} else {
os.Remove(mapFile) // clean up stale map file if store codes were removed
}

// Determine ports based on platform
// macOS uses port forwarding (80->8080, 443->8443), Linux uses standard ports
httpPort := 80
Expand Down Expand Up @@ -167,8 +188,7 @@ func (g *VhostGenerator) Generate(cfg *config.Config, projectPath string) error
HTTPSPort: httpsPort,
BackendPort: backendPort,
EnableIPv6: enableIPv6,
StoreCode: domain.GetStoreCode(),
MageRunType: domain.GetMageRunType(),
HasStoreCodes: hasStoreCodes,
AccessLog: filepath.Join(logsDir, fmt.Sprintf("%s-access.log", sanitizedDomain)),
ErrorLog: filepath.Join(logsDir, fmt.Sprintf("%s-error.log", sanitizedDomain)),
}
Expand Down Expand Up @@ -269,6 +289,35 @@ func (g *VhostGenerator) renderVhost(cfg VhostConfig) (string, error) {
return buf.String(), nil
}

// generateMap generates the multi-store map config file
func (g *VhostGenerator) generateMap(cfg MapConfig, destFile string) error {
content, err := g.renderMap(cfg)
if err != nil {
return err
}
return os.WriteFile(destFile, []byte(content), 0644)
}

// renderMap renders the map template
func (g *VhostGenerator) renderMap(cfg MapConfig) (string, error) {
tmplContent, err := lib.GetTemplate(lib.TemplateNginx, "map.conf.tmpl")
if err != nil {
return "", err
}

tmpl, err := template.New("map").Parse(tmplContent)
if err != nil {
return "", err
}

var buf bytes.Buffer
if err := tmpl.Execute(&buf, cfg); err != nil {
return "", err
}

return buf.String(), nil
}

// generateUpstream generates the upstream config file for a project
func (g *VhostGenerator) generateUpstream(cfg UpstreamConfig) error {
content, err := g.renderUpstream(cfg)
Expand Down
Loading
Loading