10 Commits

Author SHA1 Message Date
Frank John Begornia
e2fd4df26a Update deployment directory path in workflow to /var/www/apps/merchbay
Some checks failed
Deploy Development / deploy (push) Has been cancelled
2025-12-31 03:28:39 +08:00
Frank John Begornia
fa2956e8b6 Refactor deployment workflows and add SSL setup documentation for production
All checks were successful
Deploy Development / deploy (push) Successful in 2m13s
2025-12-22 23:23:10 +08:00
Frank John Begornia
e8d21d22f8 Add local environment configuration and setup documentation for development
All checks were successful
Deploy Development / deploy (push) Successful in 2m30s
2025-12-18 16:30:14 +08:00
Frank John Begornia
03434cede5 Fix potential undefined index notices in UserController by adding isset checks for store data
All checks were successful
Deploy Development / deploy (push) Successful in 2m22s
2025-12-18 15:22:24 +08:00
Frank John Begornia
609d014459 Enhance Docker cleanup in deployment workflows to reclaim space
All checks were successful
Deploy Development / deploy (push) Successful in 6m50s
2025-12-18 15:03:49 +08:00
Frank John Begornia
1d4b33ef9f Enhance deployment scripts by removing old Docker images and pruning unused resources
All checks were successful
Deploy Development / deploy (push) Successful in 2m27s
2025-12-18 14:51:20 +08:00
Frank John Begornia
14449ec0c1 Remove yakpro-po obfuscation steps from Dockerfile due to PHP 7.0 compatibility issues
All checks were successful
Deploy Development / deploy (push) Successful in 2m14s
2025-12-18 14:32:59 +08:00
Frank John Begornia
89201a8432 Add yakpro-po for code obfuscation and configuration for Laravel 5.0
Some checks failed
Deploy Development / deploy (push) Failing after 54s
2025-12-18 14:24:24 +08:00
Frank John Begornia
e6ffc878dd Add HTTPS schema enforcement in AppServiceProvider and set FORCE_HTTPS in Docker Compose
All checks were successful
Deploy Development / deploy (push) Successful in 2m31s
2025-12-18 14:22:09 +08:00
Frank John Begornia
807ac03d03 Refactor asset paths in Blade templates to remove 'public' prefix for improved consistency
All checks were successful
Deploy Development / deploy (push) Successful in 2m15s
2025-12-18 14:18:28 +08:00
17 changed files with 793 additions and 143 deletions

36
.env.local Normal file
View File

@@ -0,0 +1,36 @@
APP_ENV=local
APP_DEBUG=true
APP_KEY=base64:YOUR_APP_KEY_HERE
DB_HOST=db
DB_DATABASE=merchbay
DB_USERNAME=merchbay
DB_PASSWORD=secret
DB_PORT=3306
CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=sync
# Local mail (logs to storage/logs/laravel.log)
MAIL_DRIVER=log
MAIL_HOST=localhost
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
# Local URLs
APP_URL=http://localhost:8080
PROD_PRIVATE=http://localhost:8080
IMAGES_URL=http://localhost:8080
UPLOAD_URL=http://localhost:8080/uploads/
# Test Captcha (for local dev)
CAPTCHA_SITE_KEY=test_key
CAPTCHA_SECRET_KEY=test_secret
# Analytics (optional for local)
ANALYTICS_SITE_ID=
ANALYTICS_CLIENT_ID=
ANALYTICS_SERVICE_EMAIL=

View File

@@ -51,7 +51,7 @@ jobs:
run: |
scp -i ~/.ssh/id_ed25519 \
/workspace/repo/merchbay_dev.tar.gz \
/workspace/repo/docker-compose.yml \
/workspace/repo/docker-compose.dev.yml \
${DEPLOY_USER}@${DEPLOY_HOST}:/tmp/
# 5⃣ Deploy on server
@@ -71,8 +71,11 @@ jobs:
echo "📦 Loading image"
docker load < /tmp/merchbay_dev.tar.gz
echo "🧹 Removing old merchbay images"
docker images | grep merchbay | grep -v "$(docker images merchbay:dev -q)" | awk '{print $3}' | xargs -r docker rmi -f || true
echo "📄 Updating compose file"
cp /tmp/docker-compose.yml "$DEPLOY_DIR/"
cp /tmp/docker-compose.dev.yml "$DEPLOY_DIR/docker-compose.yml"
cd "$DEPLOY_DIR"
@@ -112,8 +115,15 @@ jobs:
fi
echo "🧹 Cleanup"
rm -f /tmp/merchbay_dev.tar.gz /tmp/docker-compose.yml
docker image prune -f
rm -f /tmp/merchbay_dev.tar.gz /tmp/docker-compose.dev.yml
echo "🧹 Aggressive Docker cleanup to reclaim space"
docker image prune -af --filter "until=24h" || true
docker container prune -f || true
docker volume prune -f || true
docker builder prune -af --filter "until=48h" || true
echo "📊 Docker space usage:"
docker system df
echo "✅ Deployment completed"
EOF

View File

@@ -1,4 +1,4 @@
name: Deploy Production
name: Deploy Production (merchbay.com)
on:
push:
@@ -12,71 +12,170 @@ jobs:
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 || true
git clone $GITHUB_SERVER_URL/$GITHUB_REPOSITORY.git /workspace/repo
cd /workspace/repo
git fetch origin $GITHUB_REF_NAME
git checkout $GITHUB_REF_NAME
git pull origin $GITHUB_REF_NAME
- name: Build Docker Image
# 2⃣ Build image
- 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
# 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 "$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 }}
echo "$DEPLOY_SSH_KEY" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -H "$DEPLOY_HOST" >> ~/.ssh/known_hosts
- name: Health Check
# 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.tar.gz \
/workspace/repo/docker-compose.prod.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"
sudo mkdir -p "$DEPLOY_DIR"
sudo chown $USER:$USER "$DEPLOY_DIR"
echo "<22> Stopping dev environment"
DEV_DIR="/var/www/apps/merchbay_dev"
if [ -d "$DEV_DIR" ]; then
cd "$DEV_DIR"
docker compose down || true
cd -
echo "✅ Dev environment stopped"
echo "📋 Copying .env from dev to production"
if [ -f "$DEV_DIR/.env" ]; then
cp "$DEV_DIR/.env" "$DEPLOY_DIR/.env"
echo "✅ .env file copied from dev to production"
else
echo "⚠️ No .env file found in dev directory"
fi else
echo " No dev environment found"
fi
echo "📦 Loading image"
docker load < /tmp/merchbay.tar.gz
echo "🧹 Removing dev images and old merchbay images"
docker images | grep "merchbay:dev" | awk '{print $3}' | xargs -r docker rmi -f || true
docker images | grep merchbay | grep -v "$(docker images merchbay:latest -q)" | awk '{print $3}' | xargs -r docker rmi -f || true
echo "📄 Updating compose file"
cp /tmp/docker-compose.prod.yml "$DEPLOY_DIR/docker-compose.yml"
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.tar.gz /tmp/docker-compose.prod.yml
echo "🧹 Aggressive Docker cleanup to reclaim space"
docker image prune -af --filter "until=24h" || true
docker container prune -f || true
docker volume prune -f || true
docker builder prune -af --filter "until=48h" || true
echo "📊 Docker space usage:"
docker system df
echo "✅ Production deployment completed!"
echo "🌐 Application available at: https://merchbay.com"
EOF
# 6⃣ Health check
- name: Health check
shell: sh
run: |
sleep 10
curl -f https://merchbay.app || exit 1
echo "⏳ Waiting for app to be ready..."
sleep 20
echo "🔍 Testing health check..."
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 30 https://merchbay.com || echo "000")
if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "302" ] || [ "$HTTP_CODE" = "301" ]; then
echo "✅ Health check passed! (HTTP $HTTP_CODE)"
echo "🎉 Production deployment successful!"
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://merchbay.com"
exit 1
fi

125
DIGICERT_SSL_SETUP.md Normal file
View File

@@ -0,0 +1,125 @@
# DigiCert SSL Certificate Setup for Production
## Certificate Files Required
From DigiCert, you'll receive these files:
- `merchbay_com.crt` - Your domain certificate
- `merchbay_com.key` - Private key (generated during CSR creation)
- `DigiCertCA.crt` - Intermediate certificate
- `TrustedRoot.crt` - Root certificate (optional)
## Step 1: Combine Certificate Chain (on your local machine)
```bash
# Create full chain certificate
cat merchbay_com.crt DigiCertCA.crt > merchbay.com.crt
# Copy private key
cp merchbay_com.key merchbay.com.key
```
## Step 2: Upload to Production Server
```bash
# SSH to production server
ssh PROD_DEPLOY_USER@PROD_DEPLOY_HOST
# Create certificates directory
sudo mkdir -p /srv/certs
sudo chmod 700 /srv/certs
# Exit SSH, then upload from local machine
scp merchbay.com.crt PROD_DEPLOY_USER@PROD_DEPLOY_HOST:/tmp/
scp merchbay.com.key PROD_DEPLOY_USER@PROD_DEPLOY_HOST:/tmp/
# SSH back to server and move files
ssh PROD_DEPLOY_USER@PROD_DEPLOY_HOST
sudo mv /tmp/merchbay.com.crt /srv/certs/
sudo mv /tmp/merchbay.com.key /srv/certs/
sudo chmod 600 /srv/certs/*
sudo chown root:root /srv/certs/*
```
## Step 3: Upload Traefik Configuration
```bash
# From local machine
scp traefik-certs.yml PROD_DEPLOY_USER@PROD_DEPLOY_HOST:/tmp/
# SSH to server
ssh PROD_DEPLOY_USER@PROD_DEPLOY_HOST
sudo mkdir -p /srv/traefik
sudo mv /tmp/traefik-certs.yml /srv/traefik/dynamic-certs.yml
sudo chmod 644 /srv/traefik/dynamic-certs.yml
```
## Step 4: Update Traefik Container
Ensure your Traefik docker-compose.yml includes:
```yaml
services:
traefik:
image: traefik:v2.10
command:
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --providers.file.filename=/dynamic-certs.yml
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --entrypoints.web.http.redirections.entrypoint.to=websecure
- --entrypoints.web.http.redirections.entrypoint.scheme=https
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- /srv/certs:/srv/certs:ro
- /srv/traefik/dynamic-certs.yml:/dynamic-certs.yml:ro
networks:
- traefik-public
restart: unless-stopped
```
## Step 5: Restart Traefik
```bash
cd /opt/traefik # or wherever your traefik docker-compose.yml is
docker compose restart traefik
# Verify certificate is loaded
docker compose logs traefik | grep -i certificate
```
## Step 6: Deploy merchbay Application
Once Traefik is configured, deploy merchbay:
```bash
cd /var/www/merchbay
docker compose up -d
```
## Verification
```bash
# Check certificate
openssl s_client -connect merchbay.com:443 -servername merchbay.com < /dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -dates
# Should show:
# subject=CN = merchbay.com
# issuer=O = DigiCert Inc, CN = DigiCert TLS RSA SHA256 2020 CA1
# notBefore=...
# notAfter=...
```
## Certificate Renewal
DigiCert certificates typically last 1-2 years. Set a reminder to renew 30 days before expiration and repeat Steps 1-3 and 5.
## Security Notes
- Never commit `.key` files to git
- Keep private keys secure (600 permissions)
- Use strong encryption for private key storage
- Consider using a certificate management system for automatic renewal

View File

@@ -60,6 +60,12 @@ RUN php artisan key:generate || true
# Run Laravel 5.0 optimization
RUN php artisan clear-compiled && php artisan optimize
# Note: yakpro-po obfuscation requires PHP 7.1+, incompatible with PHP 7.0
# For code protection with PHP 7.0, consider:
# 1. ionCube Encoder (commercial, most secure)
# 2. Keeping source code private and using proper access controls
# 3. Using --optimize flag in composer (already done above)
# 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

133
LOCAL_DEVELOPMENT.md Normal file
View File

@@ -0,0 +1,133 @@
# Local Development Setup for Merchbay
## Quick Start
1. **Copy local environment file:**
```bash
cp .env.local .env
```
2. **Build and start containers:**
```bash
docker-compose -f docker-compose.local.yml up -d --build
```
3. **Generate application key:**
```bash
docker exec merchbay_app_local php artisan key:generate
```
4. **Run migrations:**
```bash
docker exec merchbay_app_local php artisan migrate
```
5. **Access the application:**
- **App:** http://localhost:8080
- **phpMyAdmin:** http://localhost:8081
- Username: `merchbay`
- Password: `secret`
## Development Commands
### View logs
```bash
# Application logs
docker exec merchbay_app_local tail -f storage/logs/laravel.log
# Apache logs
docker logs -f merchbay_app_local
```
### Run artisan commands
```bash
docker exec merchbay_app_local php artisan [command]
```
### Access container shell
```bash
docker exec -it merchbay_app_local bash
```
### Database access
```bash
docker exec -it merchbay_db_local mysql -u merchbay -psecret merchbay
```
### Clear caches
```bash
docker exec merchbay_app_local php artisan cache:clear
docker exec merchbay_app_local php artisan config:clear
docker exec merchbay_app_local php artisan view:clear
```
### Stop containers
```bash
docker-compose -f docker-compose.local.yml down
```
### Stop and remove volumes (clean slate)
```bash
docker-compose -f docker-compose.local.yml down -v
```
## Debugging
### Enable Xdebug (if needed)
Add to Dockerfile:
```dockerfile
RUN pecl install xdebug-2.9.8 && docker-php-ext-enable xdebug
```
### Check container status
```bash
docker-compose -f docker-compose.local.yml ps
```
### View all logs
```bash
docker-compose -f docker-compose.local.yml logs -f
```
## Hot Reload Development
For live code changes without rebuilding:
- Code changes in `.php` files are reflected immediately (via volume mount)
- View changes are reflected immediately
- Config/route changes require cache clearing
## Database Management
### Import SQL dump
```bash
docker exec -i merchbay_db_local mysql -u merchbay -psecret merchbay < dump.sql
```
### Export database
```bash
docker exec merchbay_db_local mysqldump -u merchbay -psecret merchbay > dump.sql
```
## Troubleshooting
### Permission issues
```bash
docker exec merchbay_app_local chown -R www-data:www-data storage bootstrap/cache
docker exec merchbay_app_local chmod -R 775 storage bootstrap/cache
```
### Reset everything
```bash
docker-compose -f docker-compose.local.yml down -v
docker rmi merchbay_app:local
rm .env
cp .env.local .env
docker-compose -f docker-compose.local.yml up -d --build
```
## Notes
- The local setup uses a separate MySQL container
- All code changes are live-mounted for easy development
- Mail is logged to `storage/logs/laravel.log` instead of being sent
- phpMyAdmin is available for easy database management

View File

@@ -34,10 +34,10 @@ class UserController extends Controller
$post_data = array(
'isStoreOwner' => true,
'store_order' => $countStoreOrder[0]->count_order,
'store_income' => $storeIncome[0]->store_income,
'store_product_count' => $countStoreProduct[0]->store_product_count,
'store_published_product' => $countStorePublishedProduct[0]->store_published_product
'store_order' => isset($countStoreOrder[0]->count_order) ? $countStoreOrder[0]->count_order : 0,
'store_income' => isset($storeIncome[0]->store_income) ? $storeIncome[0]->store_income : 0,
'store_product_count' => isset($countStoreProduct[0]->store_product_count) ? $countStoreProduct[0]->store_product_count : 0,
'store_published_product' => isset($countStorePublishedProduct[0]->store_published_product) ? $countStorePublishedProduct[0]->store_published_product : 0
);
} else {
$post_data = array(

View File

@@ -15,7 +15,11 @@ class AppServiceProvider extends ServiceProvider {
*/
public function boot()
{
//
// Force HTTPS URLs when behind a proxy (Traefik)
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
\URL::forceSchema('https');
}
Blade::extend(function($value) {
return preg_replace('/\@define(.+)/', '<?php ${1}; ?>', $value);
});

58
docker-compose.dev.yml Normal file
View File

@@ -0,0 +1,58 @@
services:
app:
image: merchbay:dev
container_name: merchbay_app_dev
restart: unless-stopped
environment:
- APP_ENV=${APP_ENV:-development}
- APP_DEBUG=${APP_DEBUG:-true}
- APP_URL=${APP_URL:-https://dev.merchbay.app}
- 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}
- FORCE_HTTPS=true
- 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:
- ./storage:/var/www/html/storage
- ./public/uploads:/var/www/html/public/uploads
labels:
- "traefik.enable=true"
# Development environment (dev.merchbay.app)
- "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:
- traefik-public
- crew-app-net
- default
networks:
traefik-public:
external: true
crew-app-net:
external: true
default:
driver: bridge

81
docker-compose.local.yml Normal file
View File

@@ -0,0 +1,81 @@
services:
db:
image: mariadb:10.6
platform: linux/arm64
container_name: merchbay_db_local
restart: unless-stopped
environment:
MYSQL_DATABASE: merchbay
MYSQL_ROOT_PASSWORD: root
MYSQL_USER: merchbay
MYSQL_PASSWORD: secret
ports:
- "3306:3306"
volumes:
- db_data:/var/lib/mysql
networks:
- merchbay-local
app:
build:
context: .
dockerfile: Dockerfile
container_name: merchbay_app_local
restart: unless-stopped
ports:
- "8080:80"
environment:
- APP_ENV=local
- APP_DEBUG=true
- APP_URL=http://localhost:8080
- DB_CONNECTION=mysql
- DB_HOST=db
- DB_PORT=3306
- DB_DATABASE=merchbay
- DB_USERNAME=merchbay
- DB_PASSWORD=secret
- PROD_PRIVATE=http://localhost:8080
- IMAGES_URL=http://localhost:8080
- UPLOAD_URL=http://localhost:8080/uploads/
- MAIL_DRIVER=log
- MAIL_HOST=localhost
- MAIL_PORT=1025
- MAIL_USERNAME=null
- MAIL_PASSWORD=null
- MAIL_ENCRYPTION=null
- CAPTCHA_SITE_KEY=test_key
- CAPTCHA_SECRET_KEY=test_secret
- ANALYTICS_SITE_ID=
- ANALYTICS_CLIENT_ID=
- ANALYTICS_SERVICE_EMAIL=
volumes:
- ./:/var/www/html
- ./storage:/var/www/html/storage
- ./public/uploads:/var/www/html/public/uploads
depends_on:
- db
networks:
- merchbay-local
phpmyadmin:
image: arm64v8/phpmyadmin
platform: linux/arm64
container_name: merchbay_phpmyadmin
restart: unless-stopped
ports:
- "8081:80"
environment:
PMA_HOST: db
PMA_PORT: 3306
MYSQL_ROOT_PASSWORD: root
depends_on:
- db
networks:
- merchbay-local
networks:
merchbay-local:
driver: bridge
volumes:
db_data:

57
docker-compose.prod.yml Normal file
View File

@@ -0,0 +1,57 @@
services:
app:
image: merchbay:latest
container_name: merchbay_app_prod
restart: unless-stopped
environment:
- APP_ENV=${APP_ENV:-production}
- APP_DEBUG=${APP_DEBUG:-false}
- APP_URL=${APP_URL:-https://merchbay.com}
- 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}
- FORCE_HTTPS=true
- 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:
- ./storage:/var/www/html/storage
- ./public/uploads:/var/www/html/public/uploads
labels:
- "traefik.enable=true"
# Production environment (merchbay.com) - Uses DigiCert SSL
- "traefik.http.routers.merchbay-prod.rule=Host(`merchbay.com`) || Host(`www.merchbay.com`)"
- "traefik.http.routers.merchbay-prod.entrypoints=websecure"
- "traefik.http.routers.merchbay-prod.tls=true"
- "traefik.http.services.merchbay-prod.loadbalancer.server.port=80"
# HTTP to HTTPS redirect
- "traefik.http.routers.merchbay-prod-http.rule=Host(`merchbay.com`) || Host(`www.merchbay.com`)"
- "traefik.http.routers.merchbay-prod-http.entrypoints=web"
- "traefik.http.routers.merchbay-prod-http.middlewares=https-redirect"
- "traefik.http.middlewares.https-redirect.redirectscheme.scheme=https"
networks:
- traefik-public
- crew-app-net
- default
networks:
traefik-public:
external: true
crew-app-net:
external: true
default:
driver: bridge

View File

@@ -1,6 +1,13 @@
# ⚠️ DEPRECATED: Use docker-compose.dev.yml or docker-compose.prod.yml instead
# This file is kept for reference only
#
# For development: docker-compose.dev.yml (dev.merchbay.app)
# For production: docker-compose.prod.yml (merchbay.com)
# For local dev: docker-compose.local.yml (localhost:8080)
services:
app:
image: merchbay:dev
image: merchbay:latest
container_name: merchbay_app
restart: unless-stopped
environment:
@@ -16,6 +23,7 @@ services:
- PROD_PRIVATE=${PROD_PRIVATE}
- IMAGES_URL=${IMAGES_URL}
- UPLOAD_URL=${UPLOAD_URL}
- FORCE_HTTPS=true
- MAIL_DRIVER=${MAIL_DRIVER}
- MAIL_HOST=${MAIL_HOST}
- MAIL_PORT=${MAIL_PORT}
@@ -32,15 +40,25 @@ services:
- ./public/uploads:/var/www/html/public/uploads
labels:
- "traefik.enable=true"
# Development environment (dev.merchbay.app)
- "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.routers.merchbay-dev.tls.certresolver=letsencrypt"
- "traefik.http.services.merchbay-dev.loadbalancer.server.port=80"
# HTTP to HTTPS redirect
# Production environment (merchbay.com) - Uses DigiCert SSL
- "traefik.http.routers.merchbay-prod.rule=Host(`merchbay.com`) || Host(`www.merchbay.com`)"
- "traefik.http.routers.merchbay-prod.entrypoints=websecure"
- "traefik.http.routers.merchbay-prod.tls=true"
- "traefik.http.services.merchbay-prod.loadbalancer.server.port=80"
# HTTP to HTTPS redirect - Development
- "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"
# HTTP to HTTPS redirect - Production
- "traefik.http.routers.merchbay-prod-http.rule=Host(`merchbay.com`) || Host(`www.merchbay.com`)"
- "traefik.http.routers.merchbay-prod-http.entrypoints=web"
- "traefik.http.routers.merchbay-prod-http.middlewares=https-redirect"
- "traefik.http.middlewares.https-redirect.redirectscheme.scheme=https"
networks:
- traefik-public

View File

@@ -10,13 +10,13 @@
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<title>Merchbay</title>
<link rel="icon" href="{{ asset('public/favicon.ico') }}">
<link rel="icon" href="{{ asset('/favicon.ico') }}">
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@100;200;400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="{{ asset('public/assets/css/merchbay/styles.css') }}" rel="stylesheet">
<!-- <link href="{{ asset('public/assets/login/css/style.css') }}" rel="stylesheet">
<link href="{{ asset('public/assets/login/css/form-elements.css') }}" rel="stylesheet"> -->
<link href="{{ asset('/assets/css/merchbay/styles.css') }}" rel="stylesheet">
<!-- <link href="{{ asset('/assets/login/css/style.css') }}" rel="stylesheet">
<link href="{{ asset('/assets/login/css/form-elements.css') }}" rel="stylesheet"> -->
<script src='https://www.google.com/recaptcha/api.js'></script>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-SB3QK6BR1N"></script>
@@ -258,7 +258,7 @@
}
function fetchCanada() {
$.getJSON("{{ asset('/public/api/canada.json') }}", function(items) {
$.getJSON("{{ asset('//api/canada.json') }}", function(items) {
var states = [];
Object.keys(items).forEach(function(state) {
@@ -307,7 +307,7 @@
}
function fetchUSA() {
$.getJSON("{{ asset('/public/api/usaCities.json') }}", function(data) {
$.getJSON("{{ asset('//api/usaCities.json') }}", function(data) {
var states = [];
for (i = 0; i < data.length; i++) {

View File

@@ -86,54 +86,6 @@
</div>
<!-- ./col -->
</div>
{{-- <div class="row">
<div class="col-lg-12">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Total Sales</h3>
<div class="box-tools">
<button type="button" class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i>
</button>
<button type="button" class="btn btn-box-tool" data-widget="remove"><i class="fa fa-times"></i></button>
</div>
</div>
<div class="box-body">
<div class="well">
<div class="form-group col-md-3">
<label class="control-label">Select Year: <span class="required">*</span></label>
<select type="text" class="form-control" placeholder="Select Select Year">
<option value="2020">2020</option>
<option value="2019">2019</option>
</select>
</div>
<div class="form-group col-md-3">
<label class="control-label">Select Month <span class="required">*</span></label>
<select class="form-control" placeholder="Select Date">
<option value="">January</option>
<option value="">February</option>
<option value="">March</option>
<option value="">April</option>
<option value="">May</option>
<option value="">June</option>
<option value="">July</option>
<option value="">August</option>
<option value="">September</option>
<option value="">October</option>
<option value="">November</option>
<option value="">December</option>
</select>
</div>
<div class="clearfix"></div>
</div>
<div class="chart">
<canvas id="myChart" style="height:350px"></canvas>
</div>
</div>
<!-- /.box-body -->
</div>
</div>
</div> --}}
@else
<div class="text-center" id="homepage-logo">

View File

@@ -13,37 +13,37 @@
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<!-- Bootstrap 3.3.6 -->
<link rel="stylesheet" href="{{asset('/public/bower_components/AdminLTE/bootstrap/css/bootstrap.min.css')}}">
<link rel="stylesheet" href="{{asset('/bower_components/AdminLTE/bootstrap/css/bootstrap.min.css')}}">
<!-- Font Awesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css">
<!-- Ionicons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ionicons/2.0.1/css/ionicons.min.css">
<!-- Theme style -->
<link rel="stylesheet" href="{{asset('/public/bower_components/AdminLTE/dist/css/AdminLTE.min.css')}}">
<link rel="stylesheet" href="{{asset('/bower_components/AdminLTE/dist/css/AdminLTE.min.css')}}">
<!-- AdminLTE Skins. Choose a skin from the css/skins
folder instead of downloading all of them to reduce the load. -->
<link rel="stylesheet" href="{{asset('/public/bower_components/AdminLTE/dist/css/skins/_all-skins.min.css')}}">
<link rel="stylesheet" href="{{asset('/bower_components/AdminLTE/dist/css/skins/_all-skins.min.css')}}">
<!-- iCheck -->
<link rel="stylesheet" href="{{asset('/public/bower_components/AdminLTE/plugins/iCheck/flat/blue.css')}}">
<link rel="stylesheet" href="{{asset('/bower_components/AdminLTE/plugins/iCheck/flat/blue.css')}}">
<!-- Date Picker -->
<link rel="stylesheet" href="{{asset('/public/bower_components/AdminLTE/plugins/datepicker/datepicker3.css')}}">
<link rel="stylesheet" href="{{asset('/bower_components/AdminLTE/plugins/datepicker/datepicker3.css')}}">
<!-- Daterange picker -->
<link rel="stylesheet" href="{{asset('/public/bower_components/AdminLTE/plugins/daterangepicker/daterangepicker.css')}}">
<link rel="stylesheet" href="{{asset('/bower_components/AdminLTE/plugins/daterangepicker/daterangepicker.css')}}">
<!-- bootstrap wysihtml5 - text editor -->
<link rel="stylesheet" href="{{asset('/public/bower_components/AdminLTE/plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.min.css')}}">
<link rel="stylesheet" href="{{asset('/bower_components/AdminLTE/plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.min.css')}}">
<!-- Select2 -->
<link href="{{asset('/public/bower_components/AdminLTE/plugins/select2/select2.min.css')}}" rel="stylesheet" />
<link href="{{asset('/bower_components/AdminLTE/plugins/select2/select2.min.css')}}" rel="stylesheet" />
<!-- ekko-lightbox -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/ekko-lightbox/5.3.0/ekko-lightbox.css" rel="stylesheet">
<!-- bootstrap-toggle -->
<link href="https://gitcdn.github.io/bootstrap-toggle/2.2.2/css/bootstrap-toggle.min.css" rel="stylesheet">
<!-- DataTables -->
<link rel="stylesheet" href="{{ asset('/public/bower_components/AdminLTE/plugins/datatables.net-bs/css/dataTables.bootstrap.min.css') }}">
<link rel="stylesheet" href="{{ asset('/bower_components/AdminLTE/plugins/datatables.net-bs/css/dataTables.bootstrap.min.css') }}">
<!-- datepicker -->
<link rel="stylesheet" href="{{ asset('/public/bower_components/AdminLTE/plugins/datepicker/datepicker3.css') }}">
<link rel="stylesheet" href="{{ asset('/bower_components/AdminLTE/plugins/datepicker/datepicker3.css') }}">
<!-- jquery-ui -->
<link href="{{ asset('/public/assets/css/jquery-ui.css') }}" rel="stylesheet">
<link href="{{asset('/public/designer/css/build.css')}}" rel="stylesheet">
<link href="{{ asset('/assets/css/jquery-ui.css') }}" rel="stylesheet">
<link href="{{asset('/designer/css/build.css')}}" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14"></script>
@@ -628,41 +628,41 @@
<!-- ./wrapper -->
<!-- jQuery 2.2.3 -->
<script src="{{asset('/public/bower_components/AdminLTE/plugins/jQuery/jquery-2.2.3.min.js')}}"></script>
<script src="{{asset('/bower_components/AdminLTE/plugins/jQuery/jquery-2.2.3.min.js')}}"></script>
<!-- Bootstrap 3.3.6 -->
<script src="{{asset('/public/bower_components/AdminLTE/bootstrap/js/bootstrap.min.js')}}"></script>
<script src="{{asset('/bower_components/AdminLTE/bootstrap/js/bootstrap.min.js')}}"></script>
<!-- daterangepicker -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.11.2/moment.min.js"></script>
<script src="{{asset('/public/bower_components/AdminLTE/plugins/daterangepicker/daterangepicker.js')}}"></script>
<script src="{{asset('/bower_components/AdminLTE/plugins/daterangepicker/daterangepicker.js')}}"></script>
<!-- datepicker -->
<script src="{{asset('/public/bower_components/AdminLTE/plugins/datepicker/bootstrap-datepicker.js')}}"></script>
<script src="{{asset('/bower_components/AdminLTE/plugins/datepicker/bootstrap-datepicker.js')}}"></script>
<!-- Bootstrap WYSIHTML5 -->
<script src="{{asset('/public/bower_components/AdminLTE/plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.all.min.js')}}"></script>
<script src="{{asset('/bower_components/AdminLTE/plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.all.min.js')}}"></script>
<!-- Slimscroll -->
<script src="{{asset('/public/bower_components/AdminLTE/plugins/slimScroll/jquery.slimscroll.min.js')}}"></script>
<script src="{{asset('/bower_components/AdminLTE/plugins/slimScroll/jquery.slimscroll.min.js')}}"></script>
<!-- FastClick -->
<script src="{{asset('/public/bower_components/AdminLTE/plugins/fastclick/fastclick.js')}}"></script>
<script src="{{asset('/bower_components/AdminLTE/plugins/fastclick/fastclick.js')}}"></script>
<!-- AdminLTE App -->
<script src="{{asset('/public/bower_components/AdminLTE/dist/js/app.min.js')}}"></script>
<script src="{{asset('/bower_components/AdminLTE/dist/js/app.min.js')}}"></script>
<!-- AdminLTE dashboard demo (This is only for demo purposes)
<script src="{{asset('/bower_components/AdminLTE/dist/js/pages/dashboard.js')}}"></script>-->
<!-- AdminLTE for demo purposes -->
<script src="{{asset('/public/bower_components/AdminLTE/dist/js/demo.js')}}"></script>
<script src="{{asset('/bower_components/AdminLTE/dist/js/demo.js')}}"></script>
<!-- Select2 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.min.js"></script>
<!-- validate jquery -->
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.11.1/jquery.validate.min.js"></script>
<!-- \priceformat -->
<script src="{{asset('/public/designer/js/jquery.priceformat.min.js')}}"></script>
<script src="{{asset('/designer/js/jquery.priceformat.min.js')}}"></script>
<!-- ekko-lightbox -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/ekko-lightbox/5.3.0/ekko-lightbox.js"></script>
<!-- bootstrap-toggle -->
<script src="https://gitcdn.github.io/bootstrap-toggle/2.2.2/js/bootstrap-toggle.min.js"></script>
<!-- DataTables -->
<script src="{{ asset('/public/bower_components/AdminLTE/plugins/datatables.net/js/jquery.dataTables.min.js') }}"></script>
<script src="{{ asset('/public/bower_components/AdminLTE/plugins/datatables.net-bs/js/dataTables.bootstrap.min.js') }}"></script>
<script src="{{ asset('/bower_components/AdminLTE/plugins/datatables.net/js/jquery.dataTables.min.js') }}"></script>
<script src="{{ asset('/bower_components/AdminLTE/plugins/datatables.net-bs/js/dataTables.bootstrap.min.js') }}"></script>
<script src="https://cdn.datatables.net/buttons/1.5.6/js/dataTables.buttons.min.js"></script>
<script src="https://cdn.datatables.net/buttons/1.5.6/js/buttons.flash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.3/jszip.min.js"></script>
@@ -671,9 +671,9 @@
<script src="https://cdn.datatables.net/buttons/1.5.6/js/buttons.html5.min.js"></script>
<script src="https://cdn.datatables.net/buttons/1.5.6/js/buttons.print.min.js"></script>
<!-- datepicker -->
<script src="{{asset('/public/bower_components/AdminLTE/plugins/datepicker/bootstrap-datepicker.js')}}"></script>
<script src="{{asset('/bower_components/AdminLTE/plugins/datepicker/bootstrap-datepicker.js')}}"></script>
<!-- jquery-ui -->
<script src="{{ asset('/public/assets/js/jquery-ui.js') }}"></script>
<script src="{{ asset('/assets/js/jquery-ui.js') }}"></script>
{{-- Chartjs --}}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
@@ -1791,7 +1791,7 @@
}
function fetchCanada() {
$.getJSON("{{ asset('/public/api/canada.json') }}", function(items) {
$.getJSON("{{ asset('/api/canada.json') }}", function(items) {
var states = [];
Object.keys(items).forEach(function(state) {
@@ -1840,7 +1840,7 @@
}
function fetchUSA() {
$.getJSON("{{ asset('/public/api/usaCities.json') }}", function(data) {
$.getJSON("{{ asset('/api/usaCities.json') }}", function(data) {
var states = [];
for (i = 0; i < data.length; i++) {

17
traefik-certs.yml Normal file
View File

@@ -0,0 +1,17 @@
# Traefik Dynamic Configuration for Custom SSL Certificates
# This file should be uploaded to /srv/traefik/dynamic/traefik-certs.yml on production server
tls:
certificates:
# Production SSL Certificate (DigiCert)
# Note: merchbay.com.crt should be a bundle containing www_merchbay_com.crt + DigiCertCA.crt
- certFile: /srv/certs/merchbay.com.crt
keyFile: /srv/certs/merchbay.com.key
stores:
- default
stores:
default:
defaultCertificate:
certFile: /srv/certs/merchbay.com.crt
keyFile: /srv/certs/merchbay.com.key

54
yakpro-po.cnf Normal file
View File

@@ -0,0 +1,54 @@
<?php
// yakpro-po configuration for Laravel 5.0
$conf = new StdClass;
// Directories to obfuscate (relative to project root)
$conf->t_directories = [
'app'
];
// Directories/files to skip
$conf->t_skip = [
'vendor',
'storage',
'bootstrap',
'config',
'database',
'public',
'resources',
'tests',
'.env',
'.env.example',
'artisan',
'server.php'
];
// Obfuscation options
$conf->obfuscate_string_literal = false; // Don't obfuscate strings (can break Laravel)
$conf->obfuscate_function_name = true; // Obfuscate function names
$conf->obfuscate_class_name = true; // Obfuscate class names (except Laravel core)
$conf->obfuscate_variable_name = true; // Obfuscate variable names
$conf->obfuscate_property_name = true; // Obfuscate property names
$conf->obfuscate_class_constant_name = true;
$conf->obfuscate_constant_name = true;
$conf->obfuscate_namespace_name = false; // Keep namespaces readable
$conf->obfuscate_label_name = true;
// Keep Laravel framework methods/classes readable
$conf->t_ignore_constants = ['APP_ENV', 'APP_DEBUG', 'APP_URL', 'APP_KEY'];
$conf->t_ignore_methods = [
'__construct', '__destruct', '__call', '__get', '__set',
'boot', 'register', 'handle', 'middleware', 'authorize'
];
// Scrambler mode
$conf->scrambler = true;
// Allow multiple PHP versions
$conf->allow_and_operator = true;
// Output directory (will be overridden in command)
$conf->t_dir = null;
return $conf;