6 Commits

Author SHA1 Message Date
Frank John Begornia
0a235a0ed2 Update README.md to enhance project documentation and clarify setup instructions
All checks were successful
Deploy Development / deploy (push) Successful in 6m23s
2025-12-18 14:07:01 +08:00
Frank John Begornia
c950a72fc8 Update Dockerfile to revert to PHP 7.0 and adjust Debian repository sources 2025-12-18 14:05:22 +08:00
Frank John Begornia
564719412b Add entrypoint script to set up permissions and create storage directories
Some checks failed
Deploy Development / deploy (push) Failing after 2m11s
2025-12-18 13:58:13 +08:00
Frank John Begornia
cd4c7086bf Update environment variables in deployment files for improved configuration
Some checks failed
Deploy Development / deploy (push) Failing after 2m35s
2025-12-18 13:48:54 +08:00
Frank John Begornia
0052044d6a Update Dockerfile to use PHP 7.3 and improve compatibility with Laravel 5.0
Some checks failed
Deploy Development / deploy (push) Failing after 4m26s
2025-12-18 13:36:54 +08:00
Frank John Begornia
d6a98811eb Add CI/CD workflows for building and deploying Docker images
Some checks failed
Deploy Development / deploy (push) Failing after 17s
2025-12-18 13:30:00 +08:00
7 changed files with 583 additions and 83 deletions

View File

@@ -0,0 +1,57 @@
name: Build and Push Docker Image
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
tag:
description: 'Docker image tag (e.g., v1.0.0, latest)'
required: false
default: 'latest'
push_to_registry:
description: 'Push to registry?'
required: false
default: 'true'
jobs:
build-and-push:
runs-on: ubuntu-latest
container:
image: catthehacker/ubuntu:act-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Docker Registry
uses: docker/login-action@v2
with:
registry: ${{ secrets.DOCKER_REGISTRY_URL }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ secrets.DOCKER_REGISTRY_URL }}/merchbay
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=raw,value=latest
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=${{ secrets.DOCKER_REGISTRY_URL }}/merchbay:buildcache
cache-to: type=registry,ref=${{ secrets.DOCKER_REGISTRY_URL }}/merchbay:buildcache,mode=max

View File

@@ -0,0 +1,151 @@
name: Deploy Development
on:
push:
branches:
- dev
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
container:
image: catthehacker/ubuntu:act-latest
steps:
# 1⃣ Checkout code
- name: Checkout code
shell: sh
run: |
git clone $GITHUB_SERVER_URL/$GITHUB_REPOSITORY.git /workspace/repo
cd /workspace/repo
git checkout $GITHUB_REF_NAME
# 2⃣ Build image
- name: Build Docker image
shell: sh
run: |
cd /workspace/repo
docker build -t merchbay:dev .
docker save merchbay:dev | gzip > merchbay_dev.tar.gz
# 3⃣ Setup SSH
- name: Setup SSH
shell: sh
env:
DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
run: |
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "$DEPLOY_SSH_KEY" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -H "$DEPLOY_HOST" >> ~/.ssh/known_hosts
# 4⃣ Upload artifacts
- name: Upload image and compose
shell: sh
env:
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
run: |
scp -i ~/.ssh/id_ed25519 \
/workspace/repo/merchbay_dev.tar.gz \
/workspace/repo/docker-compose.yml \
${DEPLOY_USER}@${DEPLOY_HOST}:/tmp/
# 5⃣ Deploy on server
- name: Deploy on server
shell: sh
env:
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
run: |
ssh -i ~/.ssh/id_ed25519 $DEPLOY_USER@$DEPLOY_HOST << 'EOF'
set -e
DEPLOY_DIR="/var/www/apps/merchbay_dev"
mkdir -p "$DEPLOY_DIR"
echo "📦 Loading image"
docker load < /tmp/merchbay_dev.tar.gz
echo "📄 Updating compose file"
cp /tmp/docker-compose.yml "$DEPLOY_DIR/"
cd "$DEPLOY_DIR"
echo "🔍 Checking .env file"
if [ ! -f .env ]; then
echo "❌ .env file not found at $DEPLOY_DIR/.env"
echo "Please create it first with required variables:"
echo " - DB_*, PROD_PRIVATE, IMAGES_URL, UPLOAD_URL"
echo " - MAIL_*, CAPTCHA_*, ANALYTICS_*"
exit 1
fi
echo "🔧 Fixing .env permissions"
sudo chown $USER:$USER .env
sudo chmod 600 .env
echo "🌐 Ensure networks"
docker network inspect traefik-public >/dev/null 2>&1 || \
docker network create traefik-public
docker network inspect crew-app-net >/dev/null 2>&1 || \
docker network create crew-app-net
echo "🚀 Starting containers (env vars from .env file)"
docker compose up -d
echo "⏳ Waiting for app container"
sleep 15
if docker ps --format '{{.Names}}' | grep -q merchbay_app; then
echo "🧹 Clearing and rebuilding config cache"
docker compose exec -T app php artisan config:clear
docker compose exec -T app php artisan config:cache
else
echo "❌ App container not running"
docker compose logs
exit 1
fi
echo "🧹 Cleanup"
rm -f /tmp/merchbay_dev.tar.gz /tmp/docker-compose.yml
docker image prune -f
echo "✅ Deployment completed"
EOF
# 6⃣ Health check
- name: Health check
shell: sh
run: |
echo "⏳ Waiting for app to be ready..."
sleep 20
echo "🔍 Testing health check (ignoring SSL cert for now)..."
HTTP_CODE=$(curl -k -s -o /dev/null -w "%{http_code}" --max-time 30 https://dev.merchbay.app || echo "000")
if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "302" ] || [ "$HTTP_CODE" = "301" ]; then
echo "✅ Health check passed! (HTTP $HTTP_CODE)"
echo "⚠️ Note: Using -k to ignore SSL cert. Check Traefik logs if cert not ready."
else
echo "❌ Health check failed! (HTTP $HTTP_CODE)"
echo ""
echo "💡 Troubleshooting:"
echo " 1. Check if container is running:"
echo " docker ps | grep merchbay_app"
echo ""
echo " 2. Check app logs:"
echo " docker logs merchbay_app"
echo ""
echo " 3. Check Traefik logs:"
echo " docker logs traefik"
echo ""
echo " 4. Test manually:"
echo " curl -Ik https://dev.merchbay.app"
exit 1
fi

View File

@@ -0,0 +1,82 @@
name: Deploy Production
on:
push:
branches:
- main
- master
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
container:
image: catthehacker/ubuntu:act-latest
steps:
- name: Checkout code
shell: sh
run: |
git clone $GITHUB_SERVER_URL/$GITHUB_REPOSITORY.git /workspace/repo || true
cd /workspace/repo
git fetch origin $GITHUB_REF_NAME
git checkout $GITHUB_REF_NAME
git pull origin $GITHUB_REF_NAME
- name: Build Docker Image
shell: sh
run: |
cd /workspace/repo
docker build -t merchbay:latest .
docker save merchbay:latest | gzip > merchbay.tar.gz
- name: Setup SSH and Deploy
shell: sh
run: |
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "$PROD_DEPLOY_SSH_KEY" > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
ssh-keygen -y -f ~/.ssh/deploy_key > /dev/null 2>&1 || { echo "Error: Invalid SSH key format"; exit 1; }
cd /workspace/repo
scp -o StrictHostKeyChecking=no -i ~/.ssh/deploy_key merchbay.tar.gz docker-compose.yml "$PROD_DEPLOY_USER@$PROD_DEPLOY_HOST:/tmp/"
ssh -o StrictHostKeyChecking=no -i ~/.ssh/deploy_key "$PROD_DEPLOY_USER@$PROD_DEPLOY_HOST" "
DEPLOY_DIR='/var/www/merchbay'
mkdir -p \$DEPLOY_DIR
cd /tmp
docker load < merchbay.tar.gz
cp docker-compose.yml \$DEPLOY_DIR/
cd \$DEPLOY_DIR
# .env file should already exist on server with all required variables
# Required: DB_*, PROD_PRIVATE, IMAGES_URL, UPLOAD_URL
# Required: MAIL_*, CAPTCHA_*, ANALYTICS_*
# If it doesn't exist, deployment will fail (this is intentional for security)
docker compose down || true
docker image prune -f
docker network inspect traefik-public >/dev/null 2>&1 || docker network create traefik-public
export DOMAIN=merchbay.app
export APP_URL=https://merchbay.app
docker compose up -d
sleep 10
docker compose exec -T app php artisan migrate --force
docker compose exec -T app php artisan config:cache
docker compose exec -T app php artisan route:cache
docker compose exec -T app php artisan view:cache
rm -f /tmp/merchbay.tar.gz /tmp/docker-compose.yml
echo 'Production deployment completed successfully!'
echo 'Application available at: https://merchbay.app'
"
env:
PROD_DEPLOY_SSH_KEY: ${{ secrets.PROD_DEPLOY_SSH_KEY }}
PROD_DEPLOY_USER: ${{ secrets.PROD_DEPLOY_USER }}
PROD_DEPLOY_HOST: ${{ secrets.PROD_DEPLOY_HOST }}
- name: Health Check
shell: sh
run: |
sleep 10
curl -f https://merchbay.app || exit 1

View File

@@ -1,46 +1,80 @@
# Use the official PHP image based on Alpine Linux # Use PHP 7.0 with Apache (has native mcrypt support for Laravel 5.0)
FROM php:5.6-fpm-alpine FROM php:7.0-apache
# Install system dependencies and PHP extensions # Update to use archived Debian repositories
RUN apk --update --no-cache add \ RUN sed -i 's|deb.debian.org|archive.debian.org|g' /etc/apt/sources.list \
nginx \ && sed -i 's|security.debian.org|archive.debian.org|g' /etc/apt/sources.list \
&& sed -i '/stretch-updates/d' /etc/apt/sources.list
# Install system dependencies
RUN apt-get update && apt-get install -y --allow-unauthenticated \
git \
curl \
libpng-dev \ libpng-dev \
libjpeg-turbo-dev \ libxml2-dev \
freetype-dev \ libmcrypt-dev \
libzip-dev \
zip \ zip \
unzip \ unzip \
libmcrypt-dev \ libfreetype6-dev \
&& docker-php-ext-configure gd --with-freetype --with-jpeg \ libjpeg62-turbo-dev \
&& docker-php-ext-install gd pdo pdo_mysql zip mcrypt openssh-client \
&& docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
&& docker-php-ext-install -j$(nproc) gd
# Set the working directory in the container # Install PHP extensions (mcrypt is built-in for PHP 7.0)
WORKDIR /var/www RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath mcrypt tokenizer zip
# Clear cache # Enable Apache mod_rewrite
# RUN apt-get clean && rm -rf /var/lib/apt/lists/* RUN a2enmod rewrite
# Copy the Laravel application files to the container # Install Composer (version 1.x for better compatibility with Laravel 5.0)
COPY . . COPY --from=composer:1.10 /usr/bin/composer /usr/bin/composer
# Set appropriate permissions for Laravel storage and bootstrap cache # Set working directory
RUN chown -R www-data:www-data storage bootstrap WORKDIR /var/www/html
# Install Composer # Copy existing application directory contents
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer COPY . /var/www/html
# Install Laravel dependencies # Create storage directories and set permissions
RUN composer install --no-plugins --no-scripts RUN mkdir -p storage/framework/views \
storage/framework/cache \
storage/framework/sessions \
storage/logs \
bootstrap/cache
# Generate Laravel application key # Set proper ownership and permissions
RUN php artisan key:generate RUN chown -R www-data:www-data /var/www/html \
&& chmod -R 775 /var/www/html/storage \
&& chmod -R 775 /var/www/html/bootstrap/cache
# Create directory for the socket and set permissions # Create .env file if it doesn't exist
RUN mkdir -p /run/php && chown www-data:www-data /run/php RUN if [ ! -f .env ]; then cp .env.example .env; fi
# Copy the www.conf file to PHP-FPM pool.d directory # Install PHP dependencies (Laravel 5.0 compatible)
# COPY www.conf /usr/local/etc/php-fpm.d/www.conf RUN composer install --no-dev --no-interaction --prefer-dist
# Expose port 9000 and start php-fpm server # Generate application key
EXPOSE 9000 RUN php artisan key:generate || true
CMD ["php-fpm"]
# Run Laravel 5.0 optimization
RUN php artisan clear-compiled && php artisan optimize
# Configure Apache DocumentRoot to point to Laravel's public directory
ENV APACHE_DOCUMENT_ROOT=/var/www/html/public
RUN sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf
RUN sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf
# Suppress Apache ServerName warning
RUN echo "ServerName localhost" >> /etc/apache2/apache2.conf
# Copy entrypoint script
COPY docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
# Expose port 80
EXPOSE 80
# Use entrypoint to set up permissions before starting Apache
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["apache2-foreground"]

View File

@@ -1,45 +1,56 @@
version: '3'
services: services:
#PHP Service
app: app:
build: image: merchbay:dev
context: . container_name: merchbay_app
dockerfile: Dockerfile
image: digitalocean.com/php
container_name: app
restart: unless-stopped restart: unless-stopped
tty: true
environment: environment:
SERVICE_NAME: app - APP_ENV=${APP_ENV:-production}
SERVICE_TAGS: dev - APP_DEBUG=${APP_DEBUG:-false}
working_dir: /var/www - APP_URL=${APP_URL:-http://localhost}
- DB_CONNECTION=mysql
- DB_HOST=${DB_HOST}
- DB_PORT=${DB_PORT:-3306}
- DB_DATABASE=${DB_DATABASE}
- DB_USERNAME=${DB_USERNAME}
- DB_PASSWORD=${DB_PASSWORD}
- PROD_PRIVATE=${PROD_PRIVATE}
- IMAGES_URL=${IMAGES_URL}
- UPLOAD_URL=${UPLOAD_URL}
- MAIL_DRIVER=${MAIL_DRIVER}
- MAIL_HOST=${MAIL_HOST}
- MAIL_PORT=${MAIL_PORT}
- MAIL_USERNAME=${MAIL_USERNAME}
- MAIL_PASSWORD=${MAIL_PASSWORD}
- MAIL_ENCRYPTION=${MAIL_ENCRYPTION}
- CAPTCHA_SITE_KEY=${CAPTCHA_SITE_KEY}
- CAPTCHA_SECRET_KEY=${CAPTCHA_SECRET_KEY}
- ANALYTICS_SITE_ID=${ANALYTICS_SITE_ID}
- ANALYTICS_CLIENT_ID=${ANALYTICS_CLIENT_ID}
- ANALYTICS_SERVICE_EMAIL=${ANALYTICS_SERVICE_EMAIL}
volumes: volumes:
- ./:/var/www - ./storage:/var/www/html/storage
- ./php/local.ini:/usr/local/etc/php/conf.d/local.ini - ./public/uploads:/var/www/html/public/uploads
labels:
- "traefik.enable=true"
- "traefik.http.routers.merchbay-dev.rule=Host(`dev.merchbay.app`)"
- "traefik.http.routers.merchbay-dev.entrypoints=websecure"
- "traefik.http.routers.merchbay-dev.tls=true"
- "traefik.http.routers.merchbay-dev.tls.certresolver=le"
- "traefik.http.services.merchbay-dev.loadbalancer.server.port=80"
# HTTP to HTTPS redirect
- "traefik.http.routers.merchbay-dev-http.rule=Host(`dev.merchbay.app`)"
- "traefik.http.routers.merchbay-dev-http.entrypoints=web"
- "traefik.http.routers.merchbay-dev-http.middlewares=https-redirect"
- "traefik.http.middlewares.https-redirect.redirectscheme.scheme=https"
networks: networks:
- app-network - traefik-public
- crew-app-net
- default
#Nginx Service
webserver:
image: nginx:alpine
container_name: webserver
restart: unless-stopped
tty: true
ports:
- "10003:80"
- "10443:443"
volumes:
- ./:/var/www
- ./nginx/conf.d/:/etc/nginx/conf.d/
networks:
- app-network
#Docker Networks
networks: networks:
app-network: traefik-public:
driver: bridge external: true
#Volumes crew-app-net:
volumes: external: true
dbdata: default:
driver: local driver: bridge

17
docker-entrypoint.sh Normal file
View File

@@ -0,0 +1,17 @@
#!/bin/bash
set -e
# Create storage directory structure if it doesn't exist
mkdir -p storage/framework/views
mkdir -p storage/framework/cache
mkdir -p storage/framework/sessions
mkdir -p storage/logs
mkdir -p storage/app/public
mkdir -p bootstrap/cache
# Set proper permissions
chown -R www-data:www-data storage bootstrap/cache
chmod -R 775 storage bootstrap/cache
# Execute the main command
exec "$@"

176
readme.md
View File

@@ -1,23 +1,171 @@
## Laravel PHP Framework # MerchBay
[![Build Status](https://travis-ci.org/laravel/framework.svg)](https://travis-ci.org/laravel/framework) A custom merchandise and apparel design platform built with Laravel 5.0, enabling users to create, customize, and order personalized products.
[![Total Downloads](https://poser.pugx.org/laravel/framework/downloads.svg)](https://packagist.org/packages/laravel/framework)
[![Latest Stable Version](https://poser.pugx.org/laravel/framework/v/stable.svg)](https://packagist.org/packages/laravel/framework)
[![Latest Unstable Version](https://poser.pugx.org/laravel/framework/v/unstable.svg)](https://packagist.org/packages/laravel/framework)
[![License](https://poser.pugx.org/laravel/framework/license.svg)](https://packagist.org/packages/laravel/framework)
Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable, creative experience to be truly fulfilling. Laravel attempts to take the pain out of development by easing common tasks used in the majority of web projects, such as authentication, routing, sessions, queueing, and caching. ## 🚀 Tech Stack
Laravel is accessible, yet powerful, providing powerful tools needed for large, robust applications. A superb inversion of control container, expressive migration system, and tightly integrated unit testing support give you the tools you need to build any application with which you are tasked. - **Framework:** Laravel 5.0
- **PHP:** 7.0 with native mcrypt support
- **Web Server:** Apache 2.4
- **Database:** MySQL
- **Container:** Docker with Apache
- **Reverse Proxy:** Traefik (for SSL/TLS and routing)
## Official Documentation ## 📋 Prerequisites
Documentation for the framework can be found on the [Laravel website](http://laravel.com/docs). - Docker and Docker Compose
- Git
- Access to deployment server (for production/dev deployments)
## Contributing ## 🛠️ Local Development
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](http://laravel.com/docs/contributions). ### Building the Docker Image
### License ```bash
docker build -t merchbay:dev .
```
The Laravel framework is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT) ### Running Locally
```bash
# Create .env file from example
cp .env.example .env
# Update .env with your local configuration
# Set database credentials, mail settings, etc.
# Run with docker-compose (customize docker-compose.yml for local setup)
docker-compose up -d
```
### Environment Variables
The following environment variables are required:
#### Database Configuration
- `DB_HOST` - Database host
- `DB_PORT` - Database port (default: 3306)
- `DB_DATABASE` - Database name
- `DB_USERNAME` - Database username
- `DB_PASSWORD` - Database password
#### Application URLs
- `APP_URL` - Application base URL
- `PROD_PRIVATE` - Production private server URL
- `IMAGES_URL` - Images server URL
- `UPLOAD_URL` - Upload directory URL
#### Mail Configuration
- `MAIL_DRIVER` - Mail driver (smtp)
- `MAIL_HOST` - SMTP host
- `MAIL_PORT` - SMTP port
- `MAIL_USERNAME` - SMTP username
- `MAIL_PASSWORD` - SMTP password
- `MAIL_ENCRYPTION` - Encryption type (tls/ssl)
#### Third-Party Services
- `CAPTCHA_SITE_KEY` - reCAPTCHA site key
- `CAPTCHA_SECRET_KEY` - reCAPTCHA secret key
- `ANALYTICS_SITE_ID` - Google Analytics site ID
- `ANALYTICS_CLIENT_ID` - Google Analytics client ID
- `ANALYTICS_SERVICE_EMAIL` - Google Analytics service email
## 🚢 Deployment
### Automated CI/CD with Gitea Actions
This project includes automated deployment workflows using Gitea Actions:
#### Development Deployment
Push to the `dev` branch to automatically deploy to dev environment:
```bash
git push origin dev
```
- Deploys to: `https://dev.merchbay.app`
#### Production Deployment
Push to the `main` or `master` branch to automatically deploy to production:
```bash
git push origin main
```
- Deploys to: `https://merchbay.app`
#### Docker Registry
Create version tags to build and push to Docker registry:
```bash
git tag -a v1.0.0 -m "Release v1.0.0"
git push origin v1.0.0
```
### Workflow Files
- `.gitea/workflows/deploy-dev.yml` - Development deployment
- `.gitea/workflows/deploy.yml` - Production deployment
- `.gitea/workflows/build-push.yml` - Docker image build and push
## 📁 Project Structure
```
merchbay/
├── app/ # Application core
│ ├── Http/ # Controllers, middleware, routes
│ ├── Models/ # Database models
│ └── Services/ # Business logic services
├── config/ # Configuration files
├── database/ # Migrations and seeds
├── public/ # Public assets (images, CSS, JS)
├── resources/ # Views and frontend assets
├── storage/ # Application storage
├── docker-compose.yml # Docker compose configuration
├── Dockerfile # Docker image definition
└── docker-entrypoint.sh # Container startup script
```
## 🔧 Development Notes
### Storage Permissions
The Docker entrypoint automatically creates and sets proper permissions for:
- `storage/framework/views`
- `storage/framework/cache`
- `storage/framework/sessions`
- `storage/logs`
- `bootstrap/cache`
### PHP Extensions
The following PHP extensions are installed:
- pdo_mysql
- mbstring
- exif
- pcntl
- bcmath
- mcrypt (native in PHP 7.0)
- tokenizer
- zip
- gd (with freetype and jpeg support)
## 🐛 Troubleshooting
### 500 Internal Server Error
1. Check container logs: `docker logs merchbay_app`
2. Verify storage permissions are set correctly
3. Ensure all environment variables are configured in `.env`
4. Check database connectivity
### Storage Permission Issues
The entrypoint script automatically fixes permissions on container start. If issues persist:
```bash
docker exec merchbay_app chown -R www-data:www-data storage bootstrap/cache
docker exec merchbay_app chmod -R 775 storage bootstrap/cache
```
## 📄 License
Proprietary - All rights reserved
## 🤝 Support
For support and questions, contact the development team.