1 Commits

Author SHA1 Message Date
Frank John Begornia
02c7f4e2aa Refactor Docker setup for Laravel 5.0 compatibility and optimize deployment process
- Updated Dockerfile configurations for various environments (alpine, basic, cloudrun, minimal, simple, test) to ensure compatibility with Laravel 5.0 and PHP 5.6.
- Added PHP compatibility documentation outlining mcrypt requirements and upgrade paths.
- Created cloudbuild.yaml for Google Cloud Build integration to streamline deployment to Cloud Run.
- Introduced docker-compose.local.yml for local development with MySQL and Redis services.
- Enhanced rebuild.sh script for easier local development and migration handling.
- Updated Laravel Blade views to correct asset paths.
- Added start-local.sh script for quick local setup and service management.
2025-08-11 23:14:31 +08:00
70 changed files with 1676 additions and 2227 deletions

BIN
.DS_Store vendored

Binary file not shown.

63
.dockerignore Normal file
View File

@@ -0,0 +1,63 @@
# Git files
.git
.gitignore
.gitattributes
# Docker files
Dockerfile*
docker-compose*.yml
.dockerignore
# Documentation
README.md
DEPLOYMENT_GUIDE.md
*.md
# Development files
.env.example
.env.local
.env.testing
# IDE files
.vscode/
.idea/
*.swp
*.swo
# OS files
.DS_Store
Thumbs.db
# Node modules and build files
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Laravel specific
/vendor
/storage/logs/*
!/storage/logs/.gitkeep
/storage/framework/cache/*
!/storage/framework/cache/.gitkeep
/storage/framework/sessions/*
!/storage/framework/sessions/.gitkeep
/storage/framework/views/*
!/storage/framework/views/.gitkeep
/bootstrap/cache/*
!/bootstrap/cache/.gitkeep
# Testing
/tests
phpunit.xml
phpspec.yml
# Build tools
gulpfile.js
package.json
package-lock.json
yarn.lock
# Scripts
start-local.sh
rebuild.sh

View File

@@ -1,36 +0,0 @@
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

@@ -1,57 +0,0 @@
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

@@ -1,161 +0,0 @@
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.dev.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 "🧹 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.dev.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_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
# 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

@@ -1,181 +0,0 @@
name: Deploy Production (merchbay.com)
on:
push:
branches:
- main
- master
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:latest .
docker save merchbay:latest | gzip > merchbay.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.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/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: |
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

229
DEPLOYMENT_GUIDE.md Normal file
View File

@@ -0,0 +1,229 @@
# Google Cloud Run Deployment Guide for Merchbay Laravel Application
This guide will help you deploy your Laravel 5 application to Google Cloud Run using the provided `cloudbuild.yaml` configuration.
## Prerequisites
1. **Google Cloud Project**: Ensure you have a Google Cloud Project with billing enabled
2. **APIs Enabled**: Enable the following APIs:
- Cloud Build API
- Cloud Run API
- Container Registry API (or Artifact Registry API)
- Cloud SQL API (if using Cloud SQL)
- Secret Manager API (recommended for secrets)
3. **gcloud CLI**: Install and configure the Google Cloud CLI
## Pre-deployment Setup
### 1. Create Cloud SQL Instance (Recommended)
```bash
# Create MySQL instance
gcloud sql instances create merchbay-db \
--database-version=MYSQL_8_0 \
--tier=db-f1-micro \
--region=us-central1
# Create database
gcloud sql databases create merchbay --instance=merchbay-db
# Create user
gcloud sql users create laravel_user \
--instance=merchbay-db \
--password=YOUR_SECURE_PASSWORD
```
### 2. Store Secrets in Secret Manager
```bash
# Application key (generate a new one)
echo "base64:$(openssl rand -base64 32)" | gcloud secrets create APP_KEY --data-file=-
# Database password
echo "YOUR_DB_PASSWORD" | gcloud secrets create DB_PASSWORD --data-file=-
# PayPal secrets
echo "YOUR_PAYPAL_LIVE_SECRET" | gcloud secrets create PAYPAL_LIVE_SECRET --data-file=-
echo "YOUR_PAYPAL_SANDBOX_SECRET" | gcloud secrets create PAYPAL_SANDBOX_SECRET --data-file=-
# Mail password
echo "YOUR_MAIL_PASSWORD" | gcloud secrets create MAIL_PASSWORD --data-file=-
```
### 3. Update cloudbuild.yaml Variables
Update the substitution variables in `cloudbuild.yaml`:
```yaml
substitutions:
_SERVICE_NAME: 'merchbay-laravel'
_REGION: 'us-central1' # Change to your preferred region
_CLOUDSQL_INSTANCE: 'YOUR_PROJECT_ID:us-central1:merchbay-db'
_DB_DATABASE: 'merchbay'
_DB_USERNAME: 'laravel_user'
# ... other variables
```
## Deployment Steps
### 1. Set up Cloud Build Trigger
```bash
# Connect your repository to Cloud Build
gcloud builds triggers create github \
--repo-name=YOUR_REPO_NAME \
--repo-owner=YOUR_GITHUB_USERNAME \
--branch-pattern="^main$" \
--build-config=cloudbuild.yaml
# Or trigger manually
gcloud builds submit --config=cloudbuild.yaml .
```
### 2. Manual Deployment (Alternative)
```bash
# Set your project ID
export PROJECT_ID=your-project-id
# Build and deploy
gcloud builds submit --config=cloudbuild.yaml \
--substitutions=_PROJECT_ID=$PROJECT_ID,_SERVICE_NAME=merchbay-laravel
```
## Post-deployment Tasks
### 1. Run Database Migrations
```bash
# Create a one-time job for migrations
gcloud run jobs create laravel-migrate \
--image=gcr.io/$PROJECT_ID/merchbay-laravel:latest \
--region=us-central1 \
--set-env-vars="APP_ENV=production" \
--set-cloudsql-instances=$PROJECT_ID:us-central1:merchbay-db \
--command="php" \
--args="artisan,migrate,--force" \
--max-retries=1
# Execute the migration
gcloud run jobs execute laravel-migrate --region=us-central1 --wait
```
### 2. Set up Custom Domain (Optional)
```bash
# Map custom domain
gcloud run domain-mappings create \
--service=merchbay-laravel \
--domain=your-domain.com \
--region=us-central1
```
### 3. Configure Load Balancer and CDN (Optional)
For better performance, consider setting up:
- Cloud Load Balancer
- Cloud CDN for static assets
- Cloud Storage for file uploads
## Environment Variables Reference
The following environment variables are configured in the Cloud Run service:
### Application Settings
- `APP_ENV=production`
- `APP_DEBUG=false`
- `APP_KEY` (from Secret Manager)
### Database Settings
- `DB_HOST=127.0.0.1` (Cloud SQL Proxy)
- `DB_DATABASE=merchbay`
- `DB_USERNAME=laravel_user`
- `DB_PASSWORD` (from Secret Manager)
### Cache & Session
- `CACHE_DRIVER=redis` (requires Redis setup)
- `SESSION_DRIVER=redis`
- `QUEUE_DRIVER=database`
### PayPal Configuration
- `PAYPAL_MODE=live` (or 'sandbox' for testing)
- `PAYPAL_LIVE_CLIENT_ID`
- `PAYPAL_LIVE_SECRET`
- `PAYPAL_SANDBOX_CLIENT_ID`
- `PAYPAL_SANDBOX_SECRET`
### Mail Configuration
- `MAIL_DRIVER=smtp`
- `MAIL_HOST`
- `MAIL_PORT`
- `MAIL_USERNAME`
- `MAIL_PASSWORD`
## Monitoring and Logging
### Enable Application Insights
```bash
# Enable Cloud Logging
gcloud logging sinks create laravel-logs \
bigquery.googleapis.com/projects/$PROJECT_ID/datasets/app_logs
```
### Set up Uptime Monitoring
```bash
# Create uptime check
gcloud alpha monitoring uptime create \
--display-name="Merchbay Laravel App" \
--http-check-path="/" \
--hostname=your-service-url.run.app
```
## Troubleshooting
### Common Issues
1. **502 Bad Gateway**: Check logs with `gcloud run services logs read merchbay-laravel`
2. **Database Connection Issues**: Verify Cloud SQL instance and VPC configuration
3. **Memory Issues**: Increase memory allocation in cloudbuild.yaml
4. **Timeout Issues**: Increase timeout and check for long-running operations
### Debugging Commands
```bash
# View service logs
gcloud run services logs read merchbay-laravel --region=us-central1
# Get service details
gcloud run services describe merchbay-laravel --region=us-central1
# Check revisions
gcloud run revisions list --service=merchbay-laravel --region=us-central1
```
## Security Considerations
1. **Use Secret Manager** for all sensitive data
2. **Enable VPC** for database connections
3. **Configure IAM** with least privilege principle
4. **Enable Cloud Armor** for DDoS protection
5. **Use HTTPS** with managed SSL certificates
6. **Regular Updates** for dependencies and base images
## Cost Optimization
1. **Set Min Instances to 0** for cost savings
2. **Use Appropriate CPU/Memory** settings
3. **Implement Caching** (Redis/Memcached)
4. **Optimize Images** and use multi-stage builds
5. **Monitor Usage** with Cloud Monitoring
## Support
For issues specific to:
- **Cloud Run**: Check [Cloud Run documentation](https://cloud.google.com/run/docs)
- **Laravel**: Check [Laravel documentation](https://laravel.com/docs)
- **PayPal Integration**: Check [PayPal developer documentation](https://developer.paypal.com/)

View File

@@ -1,125 +0,0 @@
# 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

100
Dockerfile Executable file → Normal file
View File

@@ -1,86 +1,40 @@
# Use PHP 7.0 with Apache (has native mcrypt support for Laravel 5.0) # Use the official PHP image based on Alpine Linux
FROM php:7.0-apache FROM php:5.6-fpm-alpine
# Update to use archived Debian repositories # Install system dependencies and PHP extensions
RUN sed -i 's|deb.debian.org|archive.debian.org|g' /etc/apt/sources.list \ RUN apk --update --no-cache add \
&& sed -i 's|security.debian.org|archive.debian.org|g' /etc/apt/sources.list \ nginx \
&& 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 \
libxml2-dev \ libjpeg-turbo-dev \
libmcrypt-dev \ freetype-dev \
libzip-dev \
zip \ zip \
unzip \ unzip \
libfreetype6-dev \ libmcrypt-dev \
libjpeg62-turbo-dev \
openssh-client \
&& docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \ && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
&& docker-php-ext-install -j$(nproc) gd && docker-php-ext-install gd pdo pdo_mysql zip mcrypt
# Install PHP extensions (mcrypt is built-in for PHP 7.0) # Set the working directory in the container
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath mcrypt tokenizer zip WORKDIR /var/www
# Enable Apache mod_rewrite # Copy the Laravel application files to the container
RUN a2enmod rewrite COPY . .
# Install Composer (version 1.x for better compatibility with Laravel 5.0) # Set appropriate permissions for Laravel storage and bootstrap cache
COPY --from=composer:1.10 /usr/bin/composer /usr/bin/composer RUN chown -R www-data:www-data storage bootstrap/cache
# Set working directory # Install Composer
WORKDIR /var/www/html RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# Copy existing application directory contents # Install Laravel dependencies
COPY . /var/www/html RUN composer install --no-interaction --no-plugins --no-scripts
# Create storage directories and set permissions # Create directory for the socket and set permissions
RUN mkdir -p storage/framework/views \ RUN mkdir -p /run/php && chown www-data:www-data /run/php
storage/framework/cache \
storage/framework/sessions \
storage/logs \
bootstrap/cache
# Set proper ownership and permissions # Copy the www.conf file to PHP-FPM pool.d directory
RUN chown -R www-data:www-data /var/www/html \ # COPY www.conf /usr/local/etc/php-fpm.d/www.conf
&& chmod -R 775 /var/www/html/storage \
&& chmod -R 775 /var/www/html/bootstrap/cache
# Create .env file if it doesn't exist # Expose port 9000 and start php-fpm server
RUN if [ ! -f .env ]; then cp .env.example .env; fi EXPOSE 9000
CMD ["php-fpm"]
# Install PHP dependencies (Laravel 5.0 compatible)
RUN composer install --no-dev --no-interaction --prefer-dist
# Generate application key
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
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"]

71
Dockerfile.alpine Normal file
View File

@@ -0,0 +1,71 @@
# Debug Dockerfile - Let's try the original approach that was working
FROM php:5.6-fpm-alpine
# Set working directory
WORKDIR /var/www
# Install system dependencies and PHP extensions
RUN apk --update --no-cache add \
nginx \
libpng-dev \
libjpeg-turbo-dev \
freetype-dev \
libzip-dev \
zip \
unzip \
libmcrypt-dev \
curl \
git \
&& docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
&& docker-php-ext-install gd pdo pdo_mysql zip mcrypt mbstring
# Install Composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# Copy the Laravel application files to the container
COPY . .
# Set appropriate permissions for Laravel storage and bootstrap cache
RUN chown -R www-data:www-data storage bootstrap/cache
# Install Laravel dependencies
RUN composer install --no-interaction --no-plugins --no-scripts --no-dev
# Create .env file
RUN if [ ! -f .env ]; then cp .env.example .env; fi
# Generate application key
RUN php artisan key:generate || true
# Create directory for the socket and set permissions
RUN mkdir -p /run/php && chown www-data:www-data /run/php
# Configure nginx for Laravel
RUN echo 'server {
listen 80;
server_name localhost;
root /var/www/public;
index index.php index.html index.htm;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}' > /etc/nginx/conf.d/default.conf
# Create startup script
RUN echo '#!/bin/sh
php-fpm -D
nginx -g "daemon off;"' > /start.sh && chmod +x /start.sh
# Expose port 80
EXPOSE 80
# Start both PHP-FPM and Nginx
CMD ["/start.sh"]

39
Dockerfile.basic Normal file
View File

@@ -0,0 +1,39 @@
# Bare minimum working Dockerfile for Laravel 5.0
FROM php:5.6-apache
WORKDIR /var/www/html
# Install absolute essentials only
RUN apt-get update \
&& apt-get install -y \
curl \
git \
libmcrypt-dev \
&& docker-php-ext-install \
pdo \
pdo_mysql \
mcrypt \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Install Composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# Enable Apache rewrite
RUN a2enmod rewrite
# Configure Apache for Laravel
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
# Copy app and set permissions
COPY . /var/www/html/
RUN chown -R www-data:www-data /var/www/html
# Basic Laravel setup
RUN if [ ! -f .env ]; then cp .env.example .env; fi
RUN composer install --no-dev --no-interaction --ignore-platform-reqs
RUN php artisan key:generate || true
EXPOSE 80
CMD ["apache2-foreground"]

132
Dockerfile.cloudrun Normal file
View File

@@ -0,0 +1,132 @@
# Dockerfile optimized for Google Cloud Run
# Use PHP 5.6 with Apache (matches Laravel 5.0 requirements perfectly)
FROM php:5.6-apache
# Set working directory
WORKDIR /var/www/html
# Install system dependencies and PHP extensions required for Laravel
RUN apt-get update && apt-get install -y \
libpng-dev \
libjpeg62-turbo-dev \
libfreetype6-dev \
libmcrypt-dev \
zlib1g-dev \
zip \
unzip \
git \
curl \
libxml2-dev \
&& docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
&& docker-php-ext-install \
pdo \
pdo_mysql \
mbstring \
zip \
gd \
xml \
mcrypt \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Install Composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# Enable Apache modules and configure
RUN a2enmod rewrite headers
RUN echo "ServerName localhost" >> /etc/apache2/apache2.conf
# Configure Apache document root for Laravel
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
# Copy Apache virtual host configuration
COPY <<EOF /etc/apache2/sites-available/000-default.conf
<VirtualHost *:80>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html/public
<Directory /var/www/html/public>
AllowOverride All
Require all granted
</Directory>
# Logging
ErrorLog \${APACHE_LOG_DIR}/error.log
CustomLog \${APACHE_LOG_DIR}/access.log combined
# Security headers
Header always set X-Content-Type-Options nosniff
Header always set X-Frame-Options DENY
Header always set X-XSS-Protection "1; mode=block"
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
</VirtualHost>
EOF
# Copy application files
COPY . /var/www/html/
# Fix Git ownership issue for the repository
RUN git config --global --add safe.directory /var/www/html || true
# Install PHP dependencies
RUN composer install --no-dev --optimize-autoloader --no-interaction
# Set proper permissions for Laravel
RUN chown -R www-data:www-data /var/www/html \
&& chmod -R 755 /var/www/html \
&& chmod -R 775 /var/www/html/storage \
&& chmod -R 775 /var/www/html/bootstrap/cache
# Create .env file from .env.example if it doesn't exist
RUN if [ ! -f /var/www/html/.env ]; then \
cp /var/www/html/.env.example /var/www/html/.env; \
fi
# Switch to www-data user for Laravel commands to avoid ownership issues
USER www-data
# Generate application key (will be overridden by environment variables in Cloud Run)
RUN php artisan key:generate || true
# Clear and cache configuration for production
RUN php artisan config:clear || true \
&& php artisan route:clear || true \
&& php artisan view:clear || true
# Switch back to root for the remaining setup
USER root
# Create startup script
COPY <<EOF /usr/local/bin/start.sh
#!/bin/bash
set -e
# Fix Git ownership issue
git config --global --add safe.directory /var/www/html || true
# Wait for database to be ready (if using Cloud SQL)
echo "Starting Laravel application..."
# Run Laravel optimizations
php artisan config:cache || true
php artisan route:cache || true
php artisan view:cache || true
# Start Apache in foreground
exec apache2-foreground
EOF
RUN chmod +x /usr/local/bin/start.sh
# Expose port 80 (Cloud Run will map this to 8080)
EXPOSE 80
# Health check for Cloud Run
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD curl -f http://localhost/ || exit 1
# Use the startup script
CMD ["/usr/local/bin/start.sh"]

86
Dockerfile.minimal Normal file
View File

@@ -0,0 +1,86 @@
# Minimal Dockerfile for Laravel 5.0 with PHP 5.6
FROM php:5.6-apache
# Set working directory
WORKDIR /var/www/html
# Install only essential system dependencies
RUN apt-get update && apt-get install -y \
libmcrypt-dev \
libpng-dev \
libjpeg62-turbo-dev \
libfreetype6-dev \
zlib1g-dev \
zip \
unzip \
git \
curl \
&& docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
&& docker-php-ext-install \
pdo \
pdo_mysql \
mcrypt \
gd \
zip \
mbstring \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Install Composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# Enable Apache modules
RUN a2enmod rewrite
RUN echo "ServerName localhost" >> /etc/apache2/apache2.conf
# Configure Apache document root for Laravel
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
# Simple Apache configuration
RUN echo '<VirtualHost *:80>\n\
DocumentRoot /var/www/html/public\n\
<Directory /var/www/html/public>\n\
AllowOverride All\n\
Require all granted\n\
</Directory>\n\
</VirtualHost>' > /etc/apache2/sites-available/000-default.conf
# Copy application files
COPY . /var/www/html/
# Fix Git ownership issue
RUN git config --global --add safe.directory /var/www/html || true
# Install PHP dependencies
RUN composer install --no-dev --optimize-autoloader --no-interaction
# Set proper permissions
RUN chown -R www-data:www-data /var/www/html \
&& chmod -R 755 /var/www/html \
&& chmod -R 775 /var/www/html/storage \
&& chmod -R 775 /var/www/html/bootstrap/cache
# Create .env file if it doesn't exist
RUN if [ ! -f /var/www/html/.env ]; then \
cp /var/www/html/.env.example /var/www/html/.env; \
fi
# Switch to www-data for Laravel commands
USER www-data
RUN php artisan key:generate || true
USER root
# Create simple startup script
RUN echo '#!/bin/bash\n\
set -e\n\
echo "Starting Laravel application..."\n\
exec apache2-foreground' > /usr/local/bin/start.sh \
&& chmod +x /usr/local/bin/start.sh
# Expose port 80
EXPOSE 80
# Start Apache
CMD ["/usr/local/bin/start.sh"]

75
Dockerfile.simple Normal file
View File

@@ -0,0 +1,75 @@
# Ultra-minimal Dockerfile for Laravel 5.0 with PHP 5.6
FROM php:5.6-apache
# Set working directory
WORKDIR /var/www/html
# Install packages one by one to identify issues
RUN apt-get update
# Install core development tools first
RUN apt-get install -y \
curl \
git \
zip \
unzip
# Install mcrypt (most important for Laravel 5.0)
RUN apt-get install -y libmcrypt-dev \
&& docker-php-ext-install mcrypt
# Install basic PHP extensions
RUN docker-php-ext-install \
pdo \
pdo_mysql \
mbstring
# Try to install GD dependencies
RUN apt-get install -y \
libpng-dev \
libjpeg-dev \
libfreetype6-dev \
&& docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
&& docker-php-ext-install gd
# Install zip extension
RUN apt-get install -y zlib1g-dev \
&& docker-php-ext-install zip
# Clean up
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
# Install Composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# Enable Apache rewrite module
RUN a2enmod rewrite
# Configure Apache
RUN echo "ServerName localhost" >> /etc/apache2/apache2.conf
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
# Copy application files
COPY . /var/www/html/
# Set basic permissions
RUN chown -R www-data:www-data /var/www/html \
&& chmod -R 755 /var/www/html
# Create .env file if needed
RUN if [ ! -f /var/www/html/.env ]; then \
cp /var/www/html/.env.example /var/www/html/.env; \
fi
# Install dependencies without dev packages
RUN composer install --no-dev --no-interaction || true
# Generate Laravel key
RUN php artisan key:generate || true
# Expose port 80
EXPOSE 80
# Start Apache
CMD ["apache2-foreground"]

60
Dockerfile.test Normal file
View File

@@ -0,0 +1,60 @@
# Test Dockerfile to identify available packages
FROM php:5.6-apache
WORKDIR /var/www/html
# Update package lists first
RUN apt-get update
# Test each package individually
RUN apt-get install -y curl || echo "curl failed"
RUN apt-get install -y git || echo "git failed"
RUN apt-get install -y zip || echo "zip failed"
RUN apt-get install -y unzip || echo "unzip failed"
# Test mcrypt (most important)
RUN apt-get install -y libmcrypt-dev || echo "libmcrypt-dev failed"
# Test image libraries one by one
RUN apt-get install -y libpng-dev || echo "libpng-dev failed"
RUN apt-get install -y libjpeg-dev || echo "libjpeg-dev failed"
RUN apt-get install -y libfreetype6-dev || echo "libfreetype6-dev failed"
# Test zip library
RUN apt-get install -y zlib1g-dev || echo "zlib1g-dev failed"
# Try to install PHP extensions
RUN docker-php-ext-install mcrypt || echo "mcrypt extension failed"
RUN docker-php-ext-install pdo || echo "pdo extension failed"
RUN docker-php-ext-install pdo_mysql || echo "pdo_mysql extension failed"
RUN docker-php-ext-install mbstring || echo "mbstring extension failed"
# Test GD configuration
RUN docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ || echo "gd configure failed"
RUN docker-php-ext-install gd || echo "gd extension failed"
# Test zip extension
RUN docker-php-ext-install zip || echo "zip extension failed"
# Clean up
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
# Install Composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# Basic Apache setup
RUN a2enmod rewrite
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
# Test copy and basic setup
COPY . /var/www/html/
RUN chown -R www-data:www-data /var/www/html
# Test Laravel commands
RUN if [ ! -f .env ]; then cp .env.example .env; fi || echo ".env creation failed"
RUN composer install --no-dev --no-interaction || echo "composer install failed"
RUN php artisan key:generate || echo "key generation failed"
EXPOSE 80
CMD ["apache2-foreground"]

View File

@@ -1,133 +0,0 @@
# 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

100
PHP_COMPATIBILITY.md Normal file
View File

@@ -0,0 +1,100 @@
# Laravel 5.0 and PHP Compatibility Guide
## Issue: mcrypt Extension Required
Laravel 5.0 requires the `mcrypt` PHP extension, which was:
- **Deprecated** in PHP 7.1
- **Removed** in PHP 7.2+
## Solutions
### Option 1: Use PHP 5.6 (Current Implementation)
The `Dockerfile.minimal` has been updated to use PHP 5.6 which is the ideal match for Laravel 5.0.
**Pros:**
- Perfect compatibility with Laravel 5.0
- Native mcrypt support
- All packages available and tested
- Matches original development environment
**Cons:**
- PHP 5.6 reached end-of-life in December 2018
- Security concerns for long-term production use
### Option 2: Upgrade Laravel (Recommended for Production)
Upgrade to Laravel 5.5+ which doesn't require mcrypt.
```bash
# Update composer.json
"laravel/framework": "5.8.*"
# Remove mcrypt dependencies and update encryption
php artisan key:generate
```
### Option 3: Use OpenSSL Instead (Laravel 5.2+)
If upgrading Laravel, update encryption configuration:
```php
// config/app.php
'cipher' => 'AES-256-CBC',
```
## Current Docker Configuration
The Dockerfile now uses:
- **Base Image**: `php:5.6-apache`
- **mcrypt Extension**: Native support (no installation issues)
- **Other Extensions**: All Laravel 5.0 requirements met
- **Package Compatibility**: Perfect match for PHP 5.6
## Production Recommendations
For a production deployment, consider:
1. **Upgrade Laravel** to 5.8 or later (LTS)
2. **Use PHP 7.4+** for better performance and security
3. **Replace mcrypt** with OpenSSL encryption
## Quick Upgrade Path (Optional)
If you want to modernize the application:
### Step 1: Update composer.json
```json
{
"require": {
"laravel/framework": "5.8.*"
}
}
```
### Step 2: Update Dockerfile to use PHP 7.4
```dockerfile
FROM php:7.4-apache
# Remove mcrypt installation
```
### Step 3: Update configuration
```bash
php artisan key:generate
php artisan config:cache
```
## Security Note
Since PHP 7.1 is end-of-life, monitor for security updates and consider upgrading the Laravel version for long-term production use.
## Testing the Current Setup
The current configuration should work with:
```bash
./start-local.sh
```
This will use PHP 7.1 with mcrypt support for Laravel 5.0 compatibility.

View File

@@ -399,8 +399,7 @@ class PaypalController extends Controller
public function getTax($cartKey) public function getTax($cartKey)
{ {
$m = new TeamStoreModel; $m = new TeamStoreModel;
$updated_getSubtotal = $m->getSubtotalNew($cartKey); $updated_getSubtotal = $m->getSubtotal($cartKey);
$original_subtotal = $m->getSubtotal($cartKey); // withoutTanle
$grouped_item = $m->selectTeamStoreGroupByCartKey($cartKey); $grouped_item = $m->selectTeamStoreGroupByCartKey($cartKey);
if (count($grouped_item) <= 0) { if (count($grouped_item) <= 0) {
@@ -415,11 +414,10 @@ class PaypalController extends Controller
$order_grandtotal = $updated_getSubtotal[0]->Subtotal; $order_grandtotal = $updated_getSubtotal[0]->Subtotal;
$order_subtotal = $original_subtotal[0]->Subtotal;
$tax = $order_subtotal * $tax_value; $tax = $order_grandtotal * $tax_value;
return [ return [
'tax' => $tax, 'tax' => $tax,

View File

@@ -499,30 +499,6 @@ class TeamStoreController extends Controller
'ShippingCostId' => $shipping_cost_id 'ShippingCostId' => $shipping_cost_id
); );
} }
} elseif ($product_form == "number-size-form") {
// $order_names = $post['order_names'];
$order_number = $post['order_number'];
$order_size = $post['order_size'];
foreach ($order_size as $key => $val) {
$items[] = array(
'ProductId' => $product_id,
'StoreURL' => $store_url,
'StoreId' => $store_id,
'FormUsed' => $product_form,
'CartKey' => $cartKey,
'DesignCode' => $design_code,
'ProductURL' => $ProductURL,
'ProductName' => $product_name,
'Name' => $product_name,
'Size' => $order_size[$key],
'Number' => $order_number[$key],
'Price' => $ProductPrice,
'Quantity' => 1,
'ShippingCostId' => $shipping_cost_id
);
}
} elseif ($product_form == "number-form") { } elseif ($product_form == "number-form") {
$order_number = $post['order_number']; $order_number = $post['order_number'];
@@ -978,7 +954,6 @@ class TeamStoreController extends Controller
'shippingFee' => number_format($shippingFee, 2), 'shippingFee' => number_format($shippingFee, 2),
'total' => number_format($finalSubTotal + $shippingFee + $tax["tax"], 2), 'total' => number_format($finalSubTotal + $shippingFee + $tax["tax"], 2),
'subtotal' => number_format($finalSubTotal, 2), 'subtotal' => number_format($finalSubTotal, 2),
'tax' => $tax["tax"],
'remaining_shippingfee' => number_format(99 - $finalSubTotal, 2) 'remaining_shippingfee' => number_format(99 - $finalSubTotal, 2)
)); ));
} else { } else {

View File

@@ -34,10 +34,10 @@ class UserController extends Controller
$post_data = array( $post_data = array(
'isStoreOwner' => true, 'isStoreOwner' => true,
'store_order' => isset($countStoreOrder[0]->count_order) ? $countStoreOrder[0]->count_order : 0, 'store_order' => $countStoreOrder[0]->count_order,
'store_income' => isset($storeIncome[0]->store_income) ? $storeIncome[0]->store_income : 0, 'store_income' => $storeIncome[0]->store_income,
'store_product_count' => isset($countStoreProduct[0]->store_product_count) ? $countStoreProduct[0]->store_product_count : 0, 'store_product_count' => $countStoreProduct[0]->store_product_count,
'store_published_product' => isset($countStorePublishedProduct[0]->store_published_product) ? $countStorePublishedProduct[0]->store_published_product : 0 'store_published_product' => $countStorePublishedProduct[0]->store_published_product
); );
} else { } else {
$post_data = array( $post_data = array(
@@ -348,16 +348,14 @@ class UserController extends Controller
$newTeamStoreModel = new TeamStoreModel; $newTeamStoreModel = new TeamStoreModel;
$product_array = $newTeamStoreModel->selectTeamStoreProducts('ProductURL', $url); $product_array = $newTeamStoreModel->selectTeamStoreProducts('ProductURL', $url);
$roster = $newUserModel->getRoster($product_array[0]->Id);
$thumbnails_array = $newTeamStoreModel->getThumbnails($product_array[0]->Id); $thumbnails_array = $newTeamStoreModel->getThumbnails($product_array[0]->Id);
$available_size = explode(",", $product_array[0]->AvailableSizes); $available_size = explode(",", $product_array[0]->AvailableSizes);
$shipping_cost = $newUserModel->selectShippingCost(); $shipping_cost = $newUserModel->selectShippingCost();
return view('user-layouts.view-store-item')->with('product_array', $product_array) return view('user-layouts.view-store-item')->with('product_array', $product_array)
->with('available_size', $available_size) ->with('available_size', $available_size)
->with('thumbnails_array', $thumbnails_array) ->with('thumbnails_array', $thumbnails_array)
->with('shipping_cost', $shipping_cost) ->with('shipping_cost', $shipping_cost);
->with('roster', $roster);
} }
@@ -1346,76 +1344,4 @@ class UserController extends Controller
return $array_address_book; return $array_address_book;
} }
function roster(Request $request)
{
$UserModel = new UserModel;
$post = $request->all();
$response = $UserModel->insertRoster($post['data']);
if($response) {
return response()->json(array(
'status' => true,
'message' => "Roster is successfully saved."
));
}
return response()->json(array(
'status' => false,
'message' => "Something went wrong. Please try again"
));
}
function deleteRoster(Request $request) {
$UserModel = new UserModel;
$post = $request->all();
$response = $UserModel->deleteRoster($post['data']);
if($response) {
return response()->json(array(
'status' => true,
'message' => "Roster is successfully deleted."
));
}
return response()->json(array(
'status' => false,
'message' => "Something went wrong. Please try again"
));
}
function rosterUpdate(Request $request)
{
$UserModel = new UserModel;
$post = $request->all();
$response = $UserModel->updateRoster($post['data']);
if($response) {
return response()->json(array(
'status' => true,
'message' => "Roster is successfully updated."
));
}
return response()->json(array(
'status' => false,
'message' => "Something went wrong. Please try again"
));
}
function getCurrentRoster(Request $request) {
$productId = $request->query('product-id');
$newUserModel = new UserModel;
$roster = $newUserModel->getRoster($productId);
return response()->json($roster);
}
} }

View File

@@ -149,11 +149,6 @@ Route::group(['middleware' => 'normaluser'], function () {
Route::post('user/store-items/personal-design', 'user\UserController@saveNewItemFromDesigner'); Route::post('user/store-items/personal-design', 'user\UserController@saveNewItemFromDesigner');
Route::post('user/roster', 'user\UserController@roster');
Route::get('user/roster', 'user\UserController@getCurrentRoster');
Route::post('user/roster-delete', 'user\UserController@deleteRoster');
Route::post('user/roster-update', 'user\UserController@rosterUpdate');
}); });
Route::group(['middleware' => 'auth'], function () { Route::group(['middleware' => 'auth'], function () {

View File

@@ -189,16 +189,6 @@ class TeamStoreModel extends Model
return $i; return $i;
} }
function getSubtotalNew($cartKey)
{
$i = DB::table('cart_tmp')->select(DB::raw('SUM(Quantity * Price) AS Subtotal'))
->where('CartKey', '=', $cartKey)
// ->where('VoucherId', '=', null)
->get();
return $i;
}
function updateStoreItem($data, $url) function updateStoreItem($data, $url)
{ {
$i = DB::table('teamstore_products')->where('ProductURL', $url) $i = DB::table('teamstore_products')->where('ProductURL', $url)

View File

@@ -425,44 +425,4 @@ class UserModel extends Model
->update($data); ->update($data);
return $i; return $i;
} }
function insertRoster($data) {
$i = DB::table('roster')
->insert($data);
return $i;
}
function getRoster($productId)
{
$i = DB::table('roster')
->where('ProductId', $productId)
->get();
return $i;
}
function deleteRoster($idArray)
{
$deletedRows = DB::table('roster')
->whereIn('Id', $idArray) // Replace 'id' with the actual column name if different
->delete();
return $deletedRows; // Returns the number of rows deleted
}
function updateRoster($data)
{
$updatedRows = 0;
foreach ($data as $item) {
// Assuming each item contains an 'id' and the fields to update
$id = $item['Id'];
unset($item['Id']); // Remove 'id' from the update data
$updatedRows += DB::table('roster')
->where('Id', $id)
->update($item);
}
return $updatedRows; // Returns the total number of rows updated
}
} }

View File

@@ -15,11 +15,7 @@ class AppServiceProvider extends ServiceProvider {
*/ */
public function boot() 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) { Blade::extend(function($value) {
return preg_replace('/\@define(.+)/', '<?php ${1}; ?>', $value); return preg_replace('/\@define(.+)/', '<?php ${1}; ?>', $value);
}); });

156
cloudbuild.yaml Normal file
View File

@@ -0,0 +1,156 @@
# Google Cloud Build configuration for Laravel 5 application deployment to Cloud Run
steps:
# Step 1: Build the Docker image
- name: 'gcr.io/cloud-builders/docker'
args:
- 'build'
- '-t'
- 'gcr.io/$PROJECT_ID/merchbay-laravel:$COMMIT_SHA'
- '-t'
- 'gcr.io/$PROJECT_ID/merchbay-laravel:latest'
- '-f'
- 'Dockerfile.simple'
- '.'
id: 'build-image'
# Step 2: Push the Docker image to Google Container Registry
- name: 'gcr.io/cloud-builders/docker'
args:
- 'push'
- 'gcr.io/$PROJECT_ID/merchbay-laravel:$COMMIT_SHA'
id: 'push-image'
waitFor: ['build-image']
# Step 3: Push the latest tag as well
- name: 'gcr.io/cloud-builders/docker'
args:
- 'push'
- 'gcr.io/$PROJECT_ID/merchbay-laravel:latest'
id: 'push-latest'
waitFor: ['build-image']
# Step 4: Run database migrations (optional - only if you have Cloud SQL configured)
# Uncomment and configure if you need to run migrations
# - name: 'gcr.io/cloud-builders/gcloud'
# entrypoint: 'bash'
# args:
# - '-c'
# - |
# gcloud run jobs create laravel-migrate \
# --image=gcr.io/$PROJECT_ID/merchbay-laravel:$COMMIT_SHA \
# --region=$_REGION \
# --set-env-vars="APP_ENV=production,APP_KEY=$_APP_KEY,DB_HOST=$_DB_HOST,DB_DATABASE=$_DB_DATABASE,DB_USERNAME=$_DB_USERNAME,DB_PASSWORD=$_DB_PASSWORD" \
# --set-cloudsql-instances=$_CLOUDSQL_INSTANCE \
# --command="php" \
# --args="artisan,migrate,--force" \
# --max-retries=1 \
# --replace || true
# gcloud run jobs execute laravel-migrate --region=$_REGION --wait
# id: 'run-migrations'
# waitFor: ['push-image']
# Step 5: Deploy to Cloud Run
- name: 'gcr.io/cloud-builders/gcloud'
args:
- 'run'
- 'deploy'
- '$_SERVICE_NAME'
- '--image'
- 'gcr.io/$PROJECT_ID/merchbay-laravel:$COMMIT_SHA'
- '--region'
- '$_REGION'
- '--platform'
- 'managed'
- '--allow-unauthenticated'
- '--port'
- '8080'
- '--memory'
- '$_MEMORY'
- '--cpu'
- '$_CPU'
- '--timeout'
- '$_TIMEOUT'
- '--concurrency'
- '$_CONCURRENCY'
- '--min-instances'
- '$_MIN_INSTANCES'
- '--max-instances'
- '$_MAX_INSTANCES'
- '--set-env-vars'
- 'APP_ENV=production,APP_DEBUG=false,APP_KEY=$_APP_KEY,DB_HOST=$_DB_HOST,DB_DATABASE=$_DB_DATABASE,DB_USERNAME=$_DB_USERNAME,DB_PASSWORD=$_DB_PASSWORD,CACHE_DRIVER=redis,SESSION_DRIVER=redis,QUEUE_DRIVER=database,PAYPAL_MODE=$_PAYPAL_MODE,PAYPAL_LIVE_CLIENT_ID=$_PAYPAL_LIVE_CLIENT_ID,PAYPAL_LIVE_SECRET=$_PAYPAL_LIVE_SECRET,PAYPAL_SANDBOX_CLIENT_ID=$_PAYPAL_SANDBOX_CLIENT_ID,PAYPAL_SANDBOX_SECRET=$_PAYPAL_SANDBOX_SECRET,MAIL_DRIVER=$_MAIL_DRIVER,MAIL_HOST=$_MAIL_HOST,MAIL_PORT=$_MAIL_PORT,MAIL_USERNAME=$_MAIL_USERNAME,MAIL_PASSWORD=$_MAIL_PASSWORD,GOOGLE_ANALYTICS_VIEW_ID=$_GOOGLE_ANALYTICS_VIEW_ID'
- '--set-cloudsql-instances'
- '$_CLOUDSQL_INSTANCE'
- '--vpc-connector'
- '$_VPC_CONNECTOR'
- '--add-cloudsql-instances'
- '$_CLOUDSQL_INSTANCE'
id: 'deploy-service'
waitFor: ['push-image']
# Substitution variables - you can override these in your Cloud Build trigger
substitutions:
# Service configuration
_SERVICE_NAME: 'merchbay-laravel'
_REGION: 'us-central1'
# Resource limits
_MEMORY: '1Gi'
_CPU: '1000m'
_TIMEOUT: '300s'
_CONCURRENCY: '80'
_MIN_INSTANCES: '0'
_MAX_INSTANCES: '10'
# Database configuration (Cloud SQL)
_CLOUDSQL_INSTANCE: 'YOUR_PROJECT_ID:REGION:INSTANCE_NAME'
_DB_HOST: '127.0.0.1'
_DB_DATABASE: 'merchbay'
_DB_USERNAME: 'laravel_user'
_DB_PASSWORD: 'YOUR_DB_PASSWORD'
# VPC configuration (if needed)
_VPC_CONNECTOR: 'projects/YOUR_PROJECT_ID/locations/REGION/connectors/CONNECTOR_NAME'
# Application configuration
_APP_KEY: 'base64:YOUR_APP_KEY_HERE'
# PayPal configuration
_PAYPAL_MODE: 'live'
_PAYPAL_LIVE_CLIENT_ID: 'YOUR_PAYPAL_LIVE_CLIENT_ID'
_PAYPAL_LIVE_SECRET: 'YOUR_PAYPAL_LIVE_SECRET'
_PAYPAL_SANDBOX_CLIENT_ID: 'YOUR_PAYPAL_SANDBOX_CLIENT_ID'
_PAYPAL_SANDBOX_SECRET: 'YOUR_PAYPAL_SANDBOX_SECRET'
# Mail configuration
_MAIL_DRIVER: 'smtp'
_MAIL_HOST: 'smtp.gmail.com'
_MAIL_PORT: '587'
_MAIL_USERNAME: 'your-email@domain.com'
_MAIL_PASSWORD: 'your-app-password'
# Google Analytics
_GOOGLE_ANALYTICS_VIEW_ID: 'YOUR_GA_VIEW_ID'
# Build options
options:
# Use a more powerful machine for faster builds
machineType: 'E2_HIGHCPU_8'
# Enable detailed logging
logging: CLOUD_LOGGING_ONLY
# Build timeout
timeout: '1200s'
# Store build logs in Cloud Logging
logsBucket: 'gs://YOUR_PROJECT_ID_cloudbuild-logs'
# Available logs for debugging
availableSecrets:
secretManager:
- versionName: 'projects/YOUR_PROJECT_ID/secrets/APP_KEY/versions/latest'
env: 'APP_KEY'
- versionName: 'projects/YOUR_PROJECT_ID/secrets/DB_PASSWORD/versions/latest'
env: 'DB_PASSWORD'
- versionName: 'projects/YOUR_PROJECT_ID/secrets/PAYPAL_LIVE_SECRET/versions/latest'
env: 'PAYPAL_LIVE_SECRET'

View File

@@ -1,58 +0,0 @@
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

View File

@@ -1,81 +1,75 @@
services: # Docker Compose for local development and testing
db: version: '3.8'
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
services:
app: app:
build: build:
context: . context: .
dockerfile: Dockerfile dockerfile: Dockerfile.basic
container_name: merchbay_app_local
restart: unless-stopped
ports: ports:
- "8080:80" - "8080:80"
environment: environment:
- APP_ENV=local - APP_ENV=local
- APP_DEBUG=true - APP_DEBUG=true
- APP_URL=http://localhost:8080 - APP_KEY=base64:your-app-key-here
- DB_CONNECTION=mysql - DB_HOST=mysql
- DB_HOST=db
- DB_PORT=3306
- DB_DATABASE=merchbay - DB_DATABASE=merchbay
- DB_USERNAME=merchbay - DB_USERNAME=laravel_user
- DB_PASSWORD=secret - DB_PASSWORD=secret
- PROD_PRIVATE=http://localhost:8080 - CACHE_DRIVER=file
- IMAGES_URL=http://localhost:8080 - SESSION_DRIVER=file
- UPLOAD_URL=http://localhost:8080/uploads/ - REDIS_HOST=redis
- QUEUE_DRIVER=sync
- PAYPAL_MODE=sandbox
- PAYPAL_SANDBOX_CLIENT_ID=your-sandbox-client-id
- PAYPAL_SANDBOX_SECRET=your-sandbox-secret
- MAIL_DRIVER=log - 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: volumes:
- ./:/var/www/html
- ./storage:/var/www/html/storage - ./storage:/var/www/html/storage
- ./public/uploads:/var/www/html/public/uploads - ./bootstrap/cache:/var/www/html/bootstrap/cache
depends_on: depends_on:
- db - mysql
- redis
networks: networks:
- merchbay-local - app-network
mysql:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=root
- MYSQL_DATABASE=merchbay
- MYSQL_USER=laravel_user
- MYSQL_PASSWORD=secret
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
networks:
- app-network
redis:
image: redis:7-alpine
ports:
- "6379:6379"
networks:
- app-network
phpmyadmin: phpmyadmin:
image: arm64v8/phpmyadmin image: phpmyadmin/phpmyadmin
platform: linux/arm64 environment:
container_name: merchbay_phpmyadmin - PMA_HOST=mysql
restart: unless-stopped - PMA_USER=root
- PMA_PASSWORD=root
ports: ports:
- "8081:80" - "8081:80"
environment:
PMA_HOST: db
PMA_PORT: 3306
MYSQL_ROOT_PASSWORD: root
depends_on: depends_on:
- db - mysql
networks: networks:
- merchbay-local - app-network
networks:
merchbay-local:
driver: bridge
volumes: volumes:
db_data: mysql_data:
networks:
app-network:
driver: bridge

View File

@@ -1,57 +0,0 @@
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

111
docker-compose.yml Executable file → Normal file
View File

@@ -1,74 +1,45 @@
# ⚠️ DEPRECATED: Use docker-compose.dev.yml or docker-compose.prod.yml instead version: '3'
# 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: services:
app:
image: merchbay:latest
container_name: merchbay_app
restart: unless-stopped
environment:
- APP_ENV=${APP_ENV:-production}
- APP_DEBUG=${APP_DEBUG:-false}
- 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}
- 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=letsencrypt"
- "traefik.http.services.merchbay-dev.loadbalancer.server.port=80"
# 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
- crew-app-net
- default
#PHP Service
app:
build:
context: .
dockerfile: Dockerfile
image: digitalocean.com/php
container_name: app
restart: unless-stopped
tty: true
environment:
SERVICE_NAME: app
SERVICE_TAGS: dev
working_dir: /var/www
volumes:
- ./:/var/www
- ./php/local.ini:/usr/local/etc/php/conf.d/local.ini
networks:
- app-network
#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:
traefik-public: app-network:
external: true
crew-app-net:
external: true
default:
driver: bridge driver: bridge
#Volumes
volumes:
dbdata:
driver: local

View File

@@ -1,17 +0,0 @@
#!/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 "$@"

0
favicon.ico Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

34
nginx/conf.d/app.conf Executable file → Normal file
View File

@@ -3,37 +3,29 @@ server {
index index.php index.html; index index.php index.html;
error_log /var/log/nginx/error.log; error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log; access_log /var/log/nginx/access.log;
root /var/www; root /var/www/public;
index index.php index.html;
location / { location / {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE, HEAD';
add_header 'Access-Control-Max-Age' '1728000';
add_header 'Access-Control-Allow-Headers' '*';
#add_header 'Content-Type: text/plain; charset=UTF-8';
#add_header 'Content-Length: 0';
return 204;
}
try_files $uri $uri/ /index.php?$query_string; try_files $uri $uri/ /index.php?$query_string;
} }
location ~ \.php$ { location ~ \.php$ {
fastcgi_index index.php; try_files $uri =404;
fastcgi_pass app:9000;
include fastcgi_params;
fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_pass app:9000;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info; fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
} }
location ~ /\.ht {
deny all;
}
location ~* \.(css|less|js|jpg|png|gif)$ { location ~* \.(css|less|js|jpg|png|gif)$ {
expires 0;
add_header Cache-Control "no-cache, no-store, must-revalidate"; add_header Cache-Control "no-cache, no-store, must-revalidate";
add_header Pragma "no-cache"; add_header Pragma "no-cache";
expires 0;
} }
} }

0
php/local.ini Executable file → Normal file
View File

0
public/api/canada.json Executable file → Normal file
View File

0
public/api/usa.json Executable file → Normal file
View File

0
public/api/usaCities.old.json Executable file → Normal file
View File

0
public/assets/css/jquery-ui.css vendored Executable file → Normal file
View File

0
public/assets/files/Terms of Use.pdf Executable file → Normal file
View File

0
public/assets/js/jquery-ui.js vendored Executable file → Normal file
View File

0
public/images/MERCHBAY-LOGO.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

0
public/images/merchbay-black.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

0
public/images/merchbay-white.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

176
readme.md
View File

@@ -1,171 +1,23 @@
# MerchBay ## Laravel PHP Framework
A custom merchandise and apparel design platform built with Laravel 5.0, enabling users to create, customize, and order personalized products. [![Build Status](https://travis-ci.org/laravel/framework.svg)](https://travis-ci.org/laravel/framework)
[![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)
## 🚀 Tech Stack 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.
- **Framework:** Laravel 5.0 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.
- **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)
## 📋 Prerequisites ## Official Documentation
- Docker and Docker Compose Documentation for the framework can be found on the [Laravel website](http://laravel.com/docs).
- Git
- Access to deployment server (for production/dev deployments)
## 🛠️ Local Development ## Contributing
### Building the Docker Image Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](http://laravel.com/docs/contributions).
```bash ### License
docker build -t merchbay:dev .
```
### Running Locally The Laravel framework is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT)
```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.

106
rebuild.sh Executable file
View File

@@ -0,0 +1,106 @@
#!/bin/bash
# Check if docker compose (new) or docker-compose (old) is available
if command -v docker &>/dev/null; then
if docker compose version &>/dev/null; then
DOCKER_COMPOSE="docker compose"
elif command -v docker-compose &>/dev/null; then
DOCKER_COMPOSE="docker-compose"
else
echo "❌ Error: Neither 'docker compose' nor 'docker-compose' found."
echo "Please install Docker Desktop from https://www.docker.com/products/docker-desktop/"
exit 1
fi
else
echo "❌ Error: Docker is not installed."
echo "Please install Docker Desktop from https://www.docker.com/products/docker-desktop/"
exit 1
fi
echo "🚀 Starting rebuild process..."
# Function to display usage
show_usage() {
echo "Usage: ./rebuild.sh [OPTIONS]"
echo "Options:"
echo " -h, --help Show this help message"
echo " -c, --clean Perform a clean rebuild (no cache)"
echo " -f, --fresh Perform a fresh migration"
}
# Default values
CLEAN_BUILD=false
FRESH_MIGRATION=false
# Parse arguments
while [[ "$#" -gt 0 ]]; do
case $1 in
-h|--help) show_usage; exit 0 ;;
-c|--clean) CLEAN_BUILD=true ;;
-f|--fresh) FRESH_MIGRATION=true ;;
*) echo "Unknown parameter: $1"; show_usage; exit 1 ;;
esac
shift
done
# Pull latest changes from git
echo "📥 Pulling latest changes from git..."
if ! git pull; then
echo "❌ Git pull failed. Please resolve any conflicts and try again."
exit 1
fi
# Stop running containers
echo "📥 Stopping running containers..."
$DOCKER_COMPOSE down
# Remove old images if clean build
if [ "$CLEAN_BUILD" = true ] ; then
echo "🧹 Cleaning Docker cache..."
$DOCKER_COMPOSE rm -f
docker system prune -f
fi
# Build and start containers
echo "🏗️ Building containers..."
if [ "$CLEAN_BUILD" = true ] ; then
$DOCKER_COMPOSE build --no-cache
else
$DOCKER_COMPOSE build
fi
# Start containers
echo "🚀 Starting containers..."
$DOCKER_COMPOSE up -d
# Wait for the containers to be ready
echo "⏳ Waiting for containers to be ready..."
sleep 10
# Clear Laravel cache
echo "🧹 Clearing Laravel cache..."
$DOCKER_COMPOSE exec app php artisan cache:clear
$DOCKER_COMPOSE exec app php artisan config:clear
$DOCKER_COMPOSE exec app php artisan view:clear
# Run composer install
echo "📦 Installing dependencies..."
$DOCKER_COMPOSE exec app composer install
# Generate application key if .env exists and APP_KEY is empty
if [ -f .env ] && ! grep -q "^APP_KEY=[A-Za-z0-9+/]\{40\}$" .env; then
echo "🔑 Generating application key..."
$DOCKER_COMPOSE exec app php artisan key:generate
fi
# Run migrations if requested
if [ "$FRESH_MIGRATION" = true ] ; then
echo "🔄 Running fresh migrations..."
$DOCKER_COMPOSE exec app php artisan migrate:fresh
else
echo "🔄 Running migrations..."
$DOCKER_COMPOSE exec app php artisan migrate
fi
echo "✨ Rebuild completed!"
echo "🌐 Your application should be available at http://localhost:8080"

0
resources/views/designer/vue_designer.blade.php Executable file → Normal file
View File

0
resources/views/emails/contact_us.blade.php Executable file → Normal file
View File

View File

@@ -540,13 +540,6 @@
<td><b>Price</b></td> <td><b>Price</b></td>
<td><b>Quantity</b></td> <td><b>Quantity</b></td>
</tr> </tr>
@elseif($item->FormUsed == 'number-size-form')
<tr>
<td><b>Number</b></td>
<td><b>Size</b></td>
<td><b>Price</b></td>
<td><b>Quantity</b></td>
</tr>
@else @else
@endif @endif
<!-- table header --> <!-- table header -->
@@ -673,17 +666,6 @@
<td>{{ $sub_item->Quantity }} <td>{{ $sub_item->Quantity }}
</td> </td>
</tr> </tr>
@elseif($item->FormUsed == 'number-size-form')
<tr>
<td>{{ $sub_item->Number }}
</td>
<td>{{ $sub_item->Size }}
</td>
<td>{{ $sub_item->Price }}
</td>
<td>{{ $sub_item->Quantity }}
</td>
</tr>
@else @else
@endif @endif
@endif @endif

0
resources/views/errors/404.blade.php Executable file → Normal file
View File

View File

@@ -174,15 +174,6 @@
<th>Quantity</th> <th>Quantity</th>
<th></th> <th></th>
</tr> </tr>
@elseif($item->FormUsed == 'number-size-form')
<tr>
<th>Number</th>
<th>Size</th>
<th>Price</th>
<th>Quantity</th>
<th></th>
</tr>
@else @else
@endif @endif
@@ -305,17 +296,6 @@
href="{{ url('removeitem') }}/{{ $sub_item->Id }}"><i href="{{ url('removeitem') }}/{{ $sub_item->Id }}"><i
class="fa fa-times"></i></a></td> class="fa fa-times"></i></a></td>
</tr> </tr>
@elseif($item->FormUsed == 'number-size-form')
<tr>
<td>{{ $sub_item->Number }}</td>
<td>{{ $sub_item->Size }}</td>
<td>{{ $sub_item->Price . ' ' . $store_array[0]->StoreCurrency }}
</td>
<td>{{ $sub_item->Quantity }}</td>
<td><a class="btn btn-xs btn-link pull-right"
href="{{ url('removeitem') }}/{{ $sub_item->Id }}"><i
class="fa fa-times"></i></a></td>
</tr>
@else @else
@endif @endif
@endif @endif

0
resources/views/merchbay/not_found.blade.php Executable file → Normal file
View File

0
resources/views/merchbay/privacy_policy.blade.php Executable file → Normal file
View File

0
resources/views/merchbay/terms_of_use.blade.php Executable file → Normal file
View File

0
resources/views/merchbay/track_order.blade.php Executable file → Normal file
View File

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"> <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> <title>Merchbay</title>
<link rel="icon" href="{{ asset('/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://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="https://fonts.googleapis.com/css2?family=Montserrat:wght@100;200;400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="{{ asset('/assets/css/merchbay/styles.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('public/assets/login/css/style.css') }}" rel="stylesheet">
<link href="{{ asset('/assets/login/css/form-elements.css') }}" rel="stylesheet"> --> <link href="{{ asset('public/assets/login/css/form-elements.css') }}" rel="stylesheet"> -->
<script src='https://www.google.com/recaptcha/api.js'></script> <script src='https://www.google.com/recaptcha/api.js'></script>
<!-- Global site tag (gtag.js) - Google Analytics --> <!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-SB3QK6BR1N"></script> <script async src="https://www.googletagmanager.com/gtag/js?id=G-SB3QK6BR1N"></script>
@@ -258,7 +258,7 @@
} }
function fetchCanada() { function fetchCanada() {
$.getJSON("{{ asset('//api/canada.json') }}", function(items) { $.getJSON("{{ asset('/api/canada.json') }}", function(items) {
var states = []; var states = [];
Object.keys(items).forEach(function(state) { Object.keys(items).forEach(function(state) {
@@ -307,7 +307,7 @@
} }
function fetchUSA() { function fetchUSA() {
$.getJSON("{{ asset('//api/usaCities.json') }}", function(data) { $.getJSON("{{ asset('/api/usaCities.json') }}", function(data) {
var states = []; var states = [];
for (i = 0; i < data.length; i++) { for (i = 0; i < data.length; i++) {

0
resources/views/paypal/payment_success.blade.php Executable file → Normal file
View File

View File

@@ -1,45 +0,0 @@
<div class="panel-design-details" id="orderListPanel">
<table class="table" id="tableRow" style="font-size:12px;">
<thead>
<tr>
<!-- <th>#</th> -->
{{-- <th class="col-md-5">Name</th> --}}
<th class="col-md-6">Number</th>
<th class="col-md-5">Size</th>
<th class="text-center"></th>
</tr>
</thead>
<tbody id="orderTableBody">
<tr class="table-tr-0">
{{-- <td>
<input type="text" name="order_names[]" id="order_names" class="form-control input-sm inputName roster-input" placeholder="Name">
</td> --}}
<td>
<select class="form-control input-sm roster-input" name="order_number[]">
<option value="none">none</option>
@for($i = 0; $i <= 99; $i++)
<option value="{{ $i }}">{{ $i }}</option>
@endfor
<option value="00">00</option>
</select>
</td>
<td>
<select class="form-control input-sm order-size roster-input" name="order_size[]" style="border-right: 1px solid #ccc;" data-row-number="1">
@foreach($sizes_array as $size)
<option value="{{ $size->Size }}">{{ $size->SizeDisplay }}</option>
@endforeach
</select>
</td>
<td id="action-column" class="text-center" style="padding: 4px !important; border-top: none">
<span class="tr-remove-btn">
<button type="button" id="addNewRow" class="btn btn-success btn-sm btn-roster-action" data-toggle="tooltip" title="Add Another"><i class="fa fa-plus" aria-hidden="true"></i></button>
</span>
</td>
</tr>
</tbody>
</table>
<div id="addnew-btn-tbl-row">
</div>
</div>

View File

@@ -7,12 +7,11 @@
margin-top:20px; margin-top:20px;
} }
</style> </style>
<div class="content-wrapper" id="addItem" style="min-height: 916px;"> <div class="content-wrapper" style="min-height: 916px;">
<!-- Content Header (Page header) --> <!-- Content Header (Page header) -->
<section class="content-header"> <section class="content-header">
<h1> <h1>
Add Store Item Add Store Item
{{-- <p>@{{ message }}</p> --}}
</h1> </h1>
<ol class="breadcrumb"> <ol class="breadcrumb">
<li><a href="{{ url('user') }}"><i class="fa fa-home"></i> Home</a></li> <li><a href="{{ url('user') }}"><i class="fa fa-home"></i> Home</a></li>
@@ -93,9 +92,6 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label>Item Form</label> <label>Item Form</label>
<div class="row">
<div class="col-lg-10"></div>
</div>
<select class="form-control" name="itemForm"> <select class="form-control" name="itemForm">
<option value="jersey-and-shorts-form">Jersey and Shorts Form</option> <option value="jersey-and-shorts-form">Jersey and Shorts Form</option>
<option value="tshirt-form">T-Shirt Form</option> <option value="tshirt-form">T-Shirt Form</option>
@@ -106,7 +102,6 @@
<option value="name-size-form">Name and Size Form</option> <option value="name-size-form">Name and Size Form</option>
<option value="jersey-and-shorts-quantity-form">Jersey, Shorts and Quantity Form</option> <option value="jersey-and-shorts-quantity-form">Jersey, Shorts and Quantity Form</option>
<option value="number-jersey-shorts-form">Number, Jersey and Shorts Form</option> <option value="number-jersey-shorts-form">Number, Jersey and Shorts Form</option>
<option value="number-size-form">Number and Size Form</option>
<option value="roster-name-number-size-form">Roster and Size Form</option> <option value="roster-name-number-size-form">Roster and Size Form</option>
</select> </select>
</div> </div>

View File

@@ -125,8 +125,7 @@
<option value="tshirt-form">T-Shirt Form</option> <option value="tshirt-form">T-Shirt Form</option>
<option value="quantity-form">Quantity Form</option> <option value="quantity-form">Quantity Form</option>
<option value="name-and-number-form">Name and Number Form</option> <option value="name-and-number-form">Name and Number Form</option>
<option value="name-and-number-form">Name and Number Form</option> <option value="name-number-size-form">Name, Number and Size Form</option>
<option value="number-size-form">Number and Size Form</option>
<option value="number-form">Number Only Form</option> <option value="number-form">Number Only Form</option>
</select> </select>
</div> </div>

View File

@@ -86,6 +86,54 @@
</div> </div>
<!-- ./col --> <!-- ./col -->
</div> </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 @else
<div class="text-center" id="homepage-logo"> <div class="text-center" id="homepage-logo">

View File

@@ -114,13 +114,6 @@
<th>Price</th> <th>Price</th>
<th>Quantity</th> <th>Quantity</th>
</tr> </tr>
@elseif($array_item[0]->FormUsed=="number-size-form")
<tr>
<th>Number</th>
<th>Size</th>
<th>Price</th>
<th>Quantity</th>
</tr>
@else @else
@endif @endif
@@ -202,13 +195,6 @@
<td>{{ $array_item[0]->Price . ' ' . $store_array[0]->StoreCurrency }}</td> <td>{{ $array_item[0]->Price . ' ' . $store_array[0]->StoreCurrency }}</td>
<td>{{ $array_item[0]->Quantity }}</td> <td>{{ $array_item[0]->Quantity }}</td>
</tr> </tr>
@elseif($array_item[0]->FormUsed=="number-size-form")
<tr>
<td>{{ $array_item[0]->Number }}</td>
<td>{{ $array_item[0]->Size }}</td>
<td>{{ $array_item[0]->Price . ' ' . $store_array[0]->StoreCurrency }}</td>
<td>{{ $array_item[0]->Quantity }}</td>
</tr>
@else @else
@endif @endif

View File

@@ -158,14 +158,6 @@
<th>Quantity</th> <th>Quantity</th>
<th></th> <th></th>
</tr> </tr>
@elseif($item->FormUsed=="number-size-form")
<tr>
<th>Number</th>
<th>Size</th>
<th>Price</th>
<th>Quantity</th>
<th></th>
</tr>
@else @else
@endif @endif
@@ -227,13 +219,6 @@
<td>{{ $sub_item->Price . ' ' . $array_payment_details[0]->Currency }}</td> <td>{{ $sub_item->Price . ' ' . $array_payment_details[0]->Currency }}</td>
<td>{{ $sub_item->Quantity }}</td> <td>{{ $sub_item->Quantity }}</td>
</tr> </tr>
@elseif($item->FormUsed=="number-size-form")
<tr>
<td>{{ $sub_item->Number }}</td>
<td>{{ $sub_item->Size }}</td>
<td>{{ $sub_item->Price . ' ' . $array_payment_details[0]->Currency }}</td>
<td>{{ $sub_item->Quantity }}</td>
</tr>
@else @else
@endif @endif

View File

@@ -127,7 +127,6 @@
<option value="name-and-number-form">Name and Number Form</option> <option value="name-and-number-form">Name and Number Form</option>
<option value="name-number-size-form">Name, Number and Size Form</option> <option value="name-number-size-form">Name, Number and Size Form</option>
<option value="number-form">Number Only Form</option> <option value="number-form">Number Only Form</option>
<option value="number-size-eform">Number and Size Form</option>
</select> </select>
</div> </div>
<div class="form-group"> <div class="form-group">

View File

@@ -13,41 +13,37 @@
<meta http-equiv="Pragma" content="no-cache" /> <meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" /> <meta http-equiv="Expires" content="0" />
<!-- Bootstrap 3.3.6 --> <!-- Bootstrap 3.3.6 -->
<link rel="stylesheet" href="{{asset('/bower_components/AdminLTE/bootstrap/css/bootstrap.min.css')}}"> <link rel="stylesheet" href="{{asset('/public/bower_components/AdminLTE/bootstrap/css/bootstrap.min.css')}}">
<!-- Font Awesome --> <!-- Font Awesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css">
<!-- Ionicons --> <!-- Ionicons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ionicons/2.0.1/css/ionicons.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ionicons/2.0.1/css/ionicons.min.css">
<!-- Theme style --> <!-- Theme style -->
<link rel="stylesheet" href="{{asset('/bower_components/AdminLTE/dist/css/AdminLTE.min.css')}}"> <link rel="stylesheet" href="{{asset('/public/bower_components/AdminLTE/dist/css/AdminLTE.min.css')}}">
<!-- AdminLTE Skins. Choose a skin from the css/skins <!-- AdminLTE Skins. Choose a skin from the css/skins
folder instead of downloading all of them to reduce the load. --> folder instead of downloading all of them to reduce the load. -->
<link rel="stylesheet" href="{{asset('/bower_components/AdminLTE/dist/css/skins/_all-skins.min.css')}}"> <link rel="stylesheet" href="{{asset('/public/bower_components/AdminLTE/dist/css/skins/_all-skins.min.css')}}">
<!-- iCheck --> <!-- iCheck -->
<link rel="stylesheet" href="{{asset('/bower_components/AdminLTE/plugins/iCheck/flat/blue.css')}}"> <link rel="stylesheet" href="{{asset('/public/bower_components/AdminLTE/plugins/iCheck/flat/blue.css')}}">
<!-- Date Picker --> <!-- Date Picker -->
<link rel="stylesheet" href="{{asset('/bower_components/AdminLTE/plugins/datepicker/datepicker3.css')}}"> <link rel="stylesheet" href="{{asset('/public/bower_components/AdminLTE/plugins/datepicker/datepicker3.css')}}">
<!-- Daterange picker --> <!-- Daterange picker -->
<link rel="stylesheet" href="{{asset('/bower_components/AdminLTE/plugins/daterangepicker/daterangepicker.css')}}"> <link rel="stylesheet" href="{{asset('/public/bower_components/AdminLTE/plugins/daterangepicker/daterangepicker.css')}}">
<!-- bootstrap wysihtml5 - text editor --> <!-- bootstrap wysihtml5 - text editor -->
<link rel="stylesheet" href="{{asset('/bower_components/AdminLTE/plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.min.css')}}"> <link rel="stylesheet" href="{{asset('/public/bower_components/AdminLTE/plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.min.css')}}">
<!-- Select2 --> <!-- Select2 -->
<link href="{{asset('/bower_components/AdminLTE/plugins/select2/select2.min.css')}}" rel="stylesheet" /> <link href="{{asset('/public/bower_components/AdminLTE/plugins/select2/select2.min.css')}}" rel="stylesheet" />
<!-- ekko-lightbox --> <!-- ekko-lightbox -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/ekko-lightbox/5.3.0/ekko-lightbox.css" rel="stylesheet"> <link href="https://cdnjs.cloudflare.com/ajax/libs/ekko-lightbox/5.3.0/ekko-lightbox.css" rel="stylesheet">
<!-- bootstrap-toggle --> <!-- bootstrap-toggle -->
<link href="https://gitcdn.github.io/bootstrap-toggle/2.2.2/css/bootstrap-toggle.min.css" rel="stylesheet"> <link href="https://gitcdn.github.io/bootstrap-toggle/2.2.2/css/bootstrap-toggle.min.css" rel="stylesheet">
<!-- DataTables --> <!-- DataTables -->
<link rel="stylesheet" href="{{ asset('/bower_components/AdminLTE/plugins/datatables.net-bs/css/dataTables.bootstrap.min.css') }}"> <link rel="stylesheet" href="{{ asset('/public/bower_components/AdminLTE/plugins/datatables.net-bs/css/dataTables.bootstrap.min.css') }}">
<!-- datepicker --> <!-- datepicker -->
<link rel="stylesheet" href="{{ asset('/bower_components/AdminLTE/plugins/datepicker/datepicker3.css') }}"> <link rel="stylesheet" href="{{ asset('/public/bower_components/AdminLTE/plugins/datepicker/datepicker3.css') }}">
<!-- jquery-ui --> <!-- jquery-ui -->
<link href="{{ asset('/assets/css/jquery-ui.css') }}" rel="stylesheet"> <link href="{{ asset('/public/assets/css/jquery-ui.css') }}" rel="stylesheet">
<link href="{{asset('/designer/css/build.css')}}" rel="stylesheet"> <link href="{{asset('/public/designer/css/build.css')}}" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// --> <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]> <!--[if lt IE 9]>
@@ -628,41 +624,41 @@
<!-- ./wrapper --> <!-- ./wrapper -->
<!-- jQuery 2.2.3 --> <!-- jQuery 2.2.3 -->
<script src="{{asset('/bower_components/AdminLTE/plugins/jQuery/jquery-2.2.3.min.js')}}"></script> <script src="{{asset('/public/bower_components/AdminLTE/plugins/jQuery/jquery-2.2.3.min.js')}}"></script>
<!-- Bootstrap 3.3.6 --> <!-- Bootstrap 3.3.6 -->
<script src="{{asset('/bower_components/AdminLTE/bootstrap/js/bootstrap.min.js')}}"></script> <script src="{{asset('/public/bower_components/AdminLTE/bootstrap/js/bootstrap.min.js')}}"></script>
<!-- daterangepicker --> <!-- daterangepicker -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.11.2/moment.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.11.2/moment.min.js"></script>
<script src="{{asset('/bower_components/AdminLTE/plugins/daterangepicker/daterangepicker.js')}}"></script> <script src="{{asset('/public/bower_components/AdminLTE/plugins/daterangepicker/daterangepicker.js')}}"></script>
<!-- datepicker --> <!-- datepicker -->
<script src="{{asset('/bower_components/AdminLTE/plugins/datepicker/bootstrap-datepicker.js')}}"></script> <script src="{{asset('/public/bower_components/AdminLTE/plugins/datepicker/bootstrap-datepicker.js')}}"></script>
<!-- Bootstrap WYSIHTML5 --> <!-- Bootstrap WYSIHTML5 -->
<script src="{{asset('/bower_components/AdminLTE/plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.all.min.js')}}"></script> <script src="{{asset('/public/bower_components/AdminLTE/plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.all.min.js')}}"></script>
<!-- Slimscroll --> <!-- Slimscroll -->
<script src="{{asset('/bower_components/AdminLTE/plugins/slimScroll/jquery.slimscroll.min.js')}}"></script> <script src="{{asset('/public/bower_components/AdminLTE/plugins/slimScroll/jquery.slimscroll.min.js')}}"></script>
<!-- FastClick --> <!-- FastClick -->
<script src="{{asset('/bower_components/AdminLTE/plugins/fastclick/fastclick.js')}}"></script> <script src="{{asset('/public/bower_components/AdminLTE/plugins/fastclick/fastclick.js')}}"></script>
<!-- AdminLTE App --> <!-- AdminLTE App -->
<script src="{{asset('/bower_components/AdminLTE/dist/js/app.min.js')}}"></script> <script src="{{asset('/public/bower_components/AdminLTE/dist/js/app.min.js')}}"></script>
<!-- AdminLTE dashboard demo (This is only for demo purposes) <!-- AdminLTE dashboard demo (This is only for demo purposes)
<script src="{{asset('/bower_components/AdminLTE/dist/js/pages/dashboard.js')}}"></script>--> <script src="{{asset('/bower_components/AdminLTE/dist/js/pages/dashboard.js')}}"></script>-->
<!-- AdminLTE for demo purposes --> <!-- AdminLTE for demo purposes -->
<script src="{{asset('/bower_components/AdminLTE/dist/js/demo.js')}}"></script> <script src="{{asset('/public/bower_components/AdminLTE/dist/js/demo.js')}}"></script>
<!-- Select2 --> <!-- Select2 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.min.js"></script>
<!-- validate jquery --> <!-- validate jquery -->
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.11.1/jquery.validate.min.js"></script> <script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.11.1/jquery.validate.min.js"></script>
<!-- \priceformat --> <!-- \priceformat -->
<script src="{{asset('/designer/js/jquery.priceformat.min.js')}}"></script> <script src="{{asset('/public/designer/js/jquery.priceformat.min.js')}}"></script>
<!-- ekko-lightbox --> <!-- ekko-lightbox -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/ekko-lightbox/5.3.0/ekko-lightbox.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/ekko-lightbox/5.3.0/ekko-lightbox.js"></script>
<!-- bootstrap-toggle --> <!-- bootstrap-toggle -->
<script src="https://gitcdn.github.io/bootstrap-toggle/2.2.2/js/bootstrap-toggle.min.js"></script> <script src="https://gitcdn.github.io/bootstrap-toggle/2.2.2/js/bootstrap-toggle.min.js"></script>
<!-- DataTables --> <!-- DataTables -->
<script src="{{ asset('/bower_components/AdminLTE/plugins/datatables.net/js/jquery.dataTables.min.js') }}"></script> <script src="{{ asset('/public/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="{{ asset('/public/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/dataTables.buttons.min.js"></script>
<script src="https://cdn.datatables.net/buttons/1.5.6/js/buttons.flash.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> <script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.3/jszip.min.js"></script>
@@ -671,9 +667,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.html5.min.js"></script>
<script src="https://cdn.datatables.net/buttons/1.5.6/js/buttons.print.min.js"></script> <script src="https://cdn.datatables.net/buttons/1.5.6/js/buttons.print.min.js"></script>
<!-- datepicker --> <!-- datepicker -->
<script src="{{asset('/bower_components/AdminLTE/plugins/datepicker/bootstrap-datepicker.js')}}"></script> <script src="{{asset('/public/bower_components/AdminLTE/plugins/datepicker/bootstrap-datepicker.js')}}"></script>
<!-- jquery-ui --> <!-- jquery-ui -->
<script src="{{ asset('/assets/js/jquery-ui.js') }}"></script> <script src="{{ asset('/public/assets/js/jquery-ui.js') }}"></script>
{{-- Chartjs --}} {{-- Chartjs --}}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
@@ -1791,7 +1787,7 @@
} }
function fetchCanada() { function fetchCanada() {
$.getJSON("{{ asset('/api/canada.json') }}", function(items) { $.getJSON("{{ asset('/public/api/canada.json') }}", function(items) {
var states = []; var states = [];
Object.keys(items).forEach(function(state) { Object.keys(items).forEach(function(state) {
@@ -1840,7 +1836,7 @@
} }
function fetchUSA() { function fetchUSA() {
$.getJSON("{{ asset('/api/usaCities.json') }}", function(data) { $.getJSON("{{ asset('/public/api/usaCities.json') }}", function(data) {
var states = []; var states = [];
for (i = 0; i < data.length; i++) { for (i = 0; i < data.length; i++) {
@@ -2154,6 +2150,7 @@
function submitFormItemDetails() { function submitFormItemDetails() {
var data = $("#frm-item-details").serialize(); var data = $("#frm-item-details").serialize();
// console.log(data)
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
url: "{{ url('user/store-items/update') }}", url: "{{ url('user/store-items/update') }}",

View File

@@ -1,19 +1,17 @@
@extends('user-layouts.user_template') @extends('user-layouts.user_template')
@section('content') @section('content')
<style> <style>
.hide-bullets { .hide-bullets {
list-style: none; list-style: none;
margin-left: -40px; margin-left: -40px;
margin-top: 20px; margin-top: 20px;
} }
</style> </style>
<div class="content-wrapper" style="min-height: 916px;">
<div class="content-wrapper" id="viewStoreItem" style="min-height: 916px;">
<!-- Content Header (Page header) --> <!-- Content Header (Page header) -->
<section class="content-header"> <section class="content-header">
<h1> <h1>
@{{ title }} Store Item
<small>{{ $product_array[0]->ProductName }}</small> <small>{{ $product_array[0]->ProductName }}</small>
</h1> </h1>
<ol class="breadcrumb"> <ol class="breadcrumb">
@@ -28,22 +26,17 @@
<div class="col-md-7"> <div class="col-md-7">
<div class="box box-primary"> <div class="box box-primary">
<div class="box-header with-border"> <div class="box-header with-border">
<button type="button" class="btn btn-default pull-right" data-toggle="modal" <button type="button" class="btn btn-default pull-right" data-toggle="modal" data-target="#myModal">Re-arrange / Delete thumbnail</button>
data-target="#myModal">Re-arrange / Delete thumbnail</button> <button type="button" class="btn btn-danger pull-right" id="btn_delete_store_id" style="margin-right: 5px;" data-id="{{ $product_array[0]->Id }}">Delete this Item</button>
<button type="button" class="btn btn-danger pull-right" id="btn_delete_store_id"
style="margin-right: 5px;" data-id="{{ $product_array[0]->Id }}">Delete this
Item</button>
</div> </div>
<div class="box-body custom-box-body"> <div class="box-body custom-box-body">
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<div class="row"> <div class="row">
<div class="col-md-12 text-center"> <div class="col-md-12 text-center">
@foreach ($thumbnails_array as $thumbnail) @foreach($thumbnails_array as $thumbnail)
@if ($thumbnail->ImageClass == 'active') @if($thumbnail->ImageClass == 'active')
<img style="height:400px" <img style="height:400px" src="{{ config('site_config.images_url') }}/{{ $thumbnail->Image . '?t=' . time() }}" id="main-thumbnail">
src="{{ config('site_config.images_url') }}/{{ $thumbnail->Image . '?t=' . time() }}"
id="main-thumbnail">
@endif @endif
@endforeach @endforeach
</div> </div>
@@ -52,35 +45,25 @@
<div class="col-md-12"> <div class="col-md-12">
<ul class="hide-bullets"> <ul class="hide-bullets">
<li class="col-sm-3 col-xs-4"> <li class="col-sm-3 col-xs-4">
<a class="thumbnail btn-add-thumbnail" <a class="thumbnail btn-add-thumbnail" style="border-bottom-left-radius: 0px; border-bottom-right-radius: 0px; margin-bottom: -28px; cursor: pointer;">
style="border-bottom-left-radius: 0px; border-bottom-right-radius: 0px; margin-bottom: -28px; cursor: pointer;">
<!-- <span class="close">&times;</span> --> <!-- <span class="close">&times;</span> -->
<img class="img img-responsive product-center " <img class="img img-responsive product-center " style="height: 65.45px;" src="{{ asset('/public/images/add-new-img.svg') }}" />
style="height: 65.45px;"
src="{{ asset('/public/images/add-new-img.svg') }}" />
<!-- <p class="center">Add Image</p> --> <!-- <p class="center">Add Image</p> -->
<p class="text-center"> <p class="text-center">
Add Image Add Image
</p> </p>
</a> </a>
</li> </li>
@foreach ($thumbnails_array as $thumbnail) @foreach($thumbnails_array as $thumbnail)
<li class="col-sm-3 col-xs-4"> <li class="col-sm-3 col-xs-4">
<a class="thumbnail a_thumbnail {{ $thumbnail->ImageClass }}" <a class="thumbnail a_thumbnail {{ $thumbnail->ImageClass }}" style="border-bottom-left-radius: 0px; border-bottom-right-radius: 0px; margin-bottom: -28px;">
style="border-bottom-left-radius: 0px; border-bottom-right-radius: 0px; margin-bottom: -28px;">
<!-- <span class="close">&times;</span> --> <!-- <span class="close">&times;</span> -->
<img class="img img-responsive product-center image-thumbnails" <img class="img img-responsive product-center image-thumbnails" style="height: 59.45px;" src="{{ config('site_config.images_url') }}/{{ $thumbnail->Image . '?t=' . time() }}" />
style="height: 59.45px;"
src="{{ config('site_config.images_url') }}/{{ $thumbnail->Image . '?t=' . time() }}" />
</a> </a>
<div class="funkyradio"> <div class="funkyradio">
<div class="funkyradio-primary"> <div class="funkyradio-primary">
<input type="radio" id="{{ 'radio-' . $thumbnail->Id }}" <input type="radio" id="{{ 'radio-' .$thumbnail->Id }}" data-product-id="{{ $product_array[0]->Id }}" data-id="{{ $thumbnail->Id }}" name="setActive" @if($thumbnail->ImageClass != null) checked @endif />
data-product-id="{{ $product_array[0]->Id }}" <label for="{{ 'radio-' .$thumbnail->Id }}" style="border-top-left-radius: 0px; border-top-right-radius: 0px;">active</label>
data-id="{{ $thumbnail->Id }}" name="setActive"
@if ($thumbnail->ImageClass != null) checked @endif />
<label for="{{ 'radio-' . $thumbnail->Id }}"
style="border-top-left-radius: 0px; border-top-right-radius: 0px;">active</label>
</div> </div>
</div> </div>
</li> </li>
@@ -108,17 +91,14 @@
<div class="box-body custom-box-body"> <div class="box-body custom-box-body">
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<input type="hidden" class="form-control" name="item_url" <input type="hidden" class="form-control" name="item_url" value="{{ $product_array[0]->ProductURL }}" placeholder="Item Name">
value="{{ $product_array[0]->ProductURL }}" placeholder="Item Name">
<div class="form-group"> <div class="form-group">
<label>SKU</label> <label>SKU</label>
<input type="text" class="form-control" name="sku" <input type="text" class="form-control" name="sku" value="{{ $product_array[0]->ProductCode }}" placeholder="SKU">
value="{{ $product_array[0]->ProductCode }}" placeholder="SKU">
</div> </div>
<div class="form-group"> <div class="form-group">
<label>Item Name</label> <label>Item Name</label>
<input type="text" class="form-control" name="itemName" <input type="text" class="form-control" name="itemName" value="{{ $product_array[0]->ProductName }}" placeholder="Item Name">
value="{{ $product_array[0]->ProductName }}" placeholder="Item Name">
</div> </div>
<div class="form-group"> <div class="form-group">
<label>Item Desription</label> <label>Item Desription</label>
@@ -139,99 +119,46 @@
</div> --> </div> -->
<div class="form-group"> <div class="form-group">
<label>Item Price</label> <label>Item Price</label>
<input id="item_price" name="item_price" class="form-control price_format" <input id="item_price" name="item_price" class="form-control price_format" type="text" value="{{ $product_array[0]->ProductPrice }}" data-error="#err-price" />
type="text" value="{{ $product_array[0]->ProductPrice }}"
data-error="#err-price" />
</div> </div>
<div class="form-group"> <div class="form-group">
<label>Item Form</label> <label>Item Form</label>
<select class="form-control" name="itemForm" v-model="itemFormSelected" <select class="form-control" name="itemForm">
@change="handleSelectItemForm"> <option value="jersey-and-shorts-form" @if($product_array[0]->ProductForm == "jersey-and-shorts-form") selected @endif>Jersey and Shorts Form</option>
<option value="jersey-and-shorts-form" <option value="tshirt-form" @if($product_array[0]->ProductForm == "tshirt-form") selected @endif>T-Shirt Form</option>
@if ($product_array[0]->ProductForm == 'jersey-and-shorts-form') selected @endif>Jersey and Shorts <option value="quantity-form" @if($product_array[0]->ProductForm == "quantity-form") selected @endif>Quantity Form</option>
Form</option> <option value="name-number-form" @if($product_array[0]->ProductForm == "name-number-form") selected @endif>Name and Number Form</option>
<option value="tshirt-form" <option value="name-number-size-form" @if($product_array[0]->ProductForm == "name-number-size-form") selected @endif>Name, Number and Size Form</option>
@if ($product_array[0]->ProductForm == 'tshirt-form') selected @endif>T-Shirt Form <option value="number-form" @if($product_array[0]->ProductForm == "number-form") selected @endif>Number Only Form</option>
</option> <option value="name-size-form" @if($product_array[0]->ProductForm == "name-size-form") selected @endif>Name and Size Form</option>
<option value="quantity-form" <option value="jersey-and-shorts-quantity-form" @if($product_array[0]->ProductForm == "jersey-and-shorts-quantity-form") selected @endif>Jersey, Shorts and Quantity Form</option>
@if ($product_array[0]->ProductForm == 'quantity-form') selected @endif>Quantity Form <option value="number-jersey-shorts-form" @if($product_array[0]->ProductForm == "number-jersey-shorts-form") selected @endif>Number, Jersey and Shorts Form</option>
</option> <option value="roster-name-number-size-form" @if($product_array[0]->ProductForm == "roster-name-number-size-form") selected @endif>Roster and Size Form</option>
<option value="name-number-form"
@if ($product_array[0]->ProductForm == 'name-number-form') selected @endif>Name and Number
Form</option>
<option value="name-number-size-form"
@if ($product_array[0]->ProductForm == 'name-number-size-form') selected @endif>Name, Number and
Size Form</option>
<option value="number-size-form"
@if ($product_array[0]->ProductForm == 'number-size-form') selected @endif>Number and Size
Form</option>
<option value="number-form"
@if ($product_array[0]->ProductForm == 'number-form') selected @endif>Number Only Form
</option>
<option value="name-size-form"
@if ($product_array[0]->ProductForm == 'name-size-form') selected @endif>Name and Size Form
</option>
<option value="jersey-and-shorts-quantity-form"
@if ($product_array[0]->ProductForm == 'jersey-and-shorts-quantity-form') selected @endif>Jersey, Shorts
and Quantity Form</option>
<option value="number-jersey-shorts-form"
@if ($product_array[0]->ProductForm == 'number-jersey-shorts-form') selected @endif>Number, Jersey
and Shorts Form</option>
<option value="roster-name-number-size-form"
@if ($product_array[0]->ProductForm == 'roster-name-number-size-form') selected @endif>Roster and Size
Form</option>
</select> </select>
</div> </div>
<div class="form-group d-flex"
v-if="itemFormSelected == 'roster-name-number-size-form'">
<button type="button" class="btn btn-success btn-sm"
@click="addRosterModal">
<i class="fa fa-plus"></i> Add Roster
</button>
<button type="button" class="btn btn-warning btn-sm"
@click="viewRosterModal">
<i class="fa fa-eye"></i> View Roster
</button>
</div>
<div class="form-group"> <div class="form-group">
<label>Available Size</label> <label>Available Size</label>
<select class="form-control select2" data-error="#err_available_size" <select class="form-control select2" data-error="#err_available_size" data-placeholder="Select Size" name="available_size[]" multiple="multiple" required>
data-placeholder="Select Size" name="available_size[]" <option value="toddler" @if(in_array("toddler", $available_size)) selected @endif>Toddler</option>
multiple="multiple" required> <option value="youth" @if(in_array("youth", $available_size)) selected @endif>Youth</option>
<option value="toddler" <option value="adult" @if(in_array("adult", $available_size)) selected @endif>Adult</option>
@if (in_array('toddler', $available_size)) selected @endif>Toddler</option> <option value="mask" @if(in_array("mask", $available_size)) selected @endif>Mask</option>
<option value="youth" @if (in_array('youth', $available_size)) selected @endif> <option value="gaiter" @if(in_array("gaiter", $available_size)) selected @endif>Gaiter</option>
Youth</option> <option value="buckethat" @if(in_array("buckethat", $available_size)) selected @endif>Buckethat</option>
<option value="adult" @if (in_array('adult', $available_size)) selected @endif> <option value="none" @if(in_array("none", $available_size)) selected @endif>None</option>
Adult</option>
<option value="mask" @if (in_array('mask', $available_size)) selected @endif>
Mask</option>
<option value="gaiter" @if (in_array('gaiter', $available_size)) selected @endif>
Gaiter</option>
<option value="buckethat"
@if (in_array('buckethat', $available_size)) selected @endif>Buckethat
</option>
<option value="none" @if (in_array('none', $available_size)) selected @endif>
None</option>
</select> </select>
<span id="err_available_size"></span> <span id="err_available_size"></span>
</div> </div>
<div class="form-group"> <div class="form-group">
<label>Item Quantity <small>(Optional)</small></label> <label>Item Quantity <small>(Optional)</small></label>
<input id="item_quantity" name="item_quantity" class="form-control" <input id="item_quantity" name="item_quantity" class="form-control" type="number" min="0" value="{{ $product_array[0]->ProductAvailableQty }}" data-error="#err-quantity" />
type="number" min="0"
value="{{ $product_array[0]->ProductAvailableQty }}"
data-error="#err-quantity" />
</div> </div>
{{-- {{ var_dump($product_array[0]) }} --}} {{-- {{ var_dump($product_array[0]) }} --}}
<div class="form-group"> <div class="form-group">
<label>Item Privacy</label> <label>Item Privacy</label>
<select class="form-control" name="item_privacy"> <select class="form-control" name="item_privacy">
<option value="public" @if ($product_array[0]->PrivacyStatus == 'public') selected @endif> <option value="public" @if($product_array[0]->PrivacyStatus == "public") selected @endif>Public</option>
Public</option> <option value="private" @if($product_array[0]->PrivacyStatus == "private") selected @endif>Private</option>
<option value="private"
@if ($product_array[0]->PrivacyStatus == 'private') selected @endif>Private</option>
</select> </select>
</div> </div>
@@ -240,9 +167,7 @@
<select class="form-control" name="shipping_cost" required> <select class="form-control" name="shipping_cost" required>
<option value="0">None</option> <option value="0">None</option>
@foreach ($shipping_cost as $sc) @foreach ($shipping_cost as $sc)
<option value="{{ $sc->Id }}" <option value="{{ $sc->Id }}" @if($sc->Id == $product_array[0]->ShippingCostId) selected @endif>{{ $sc->DisplayName }}</option>
@if ($sc->Id == $product_array[0]->ShippingCostId) selected @endif>
{{ $sc->DisplayName }}</option>
@endforeach @endforeach
</select> </select>
<span id="err_available_size"></span> <span id="err_available_size"></span>
@@ -261,9 +186,10 @@
</div> </div>
</section> </section>
<!-- /.content --> <!-- /.content -->
</div>
<!-- Modal --> <!-- Modal -->
<div class="modal fade" id="myModal" role="dialog"> <div class="modal fade" id="myModal" role="dialog">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
@@ -280,17 +206,14 @@
</tr> </tr>
</thead> </thead>
<tbody id="sortable"> <tbody id="sortable">
@foreach ($thumbnails_array as $thumbnail) @foreach($thumbnails_array as $thumbnail)
<tr id="{{ 'item-' . $thumbnail->Id }}"> <tr id="{{ 'item-' . $thumbnail->Id }}">
<td class="text-center" style="width: 50px"><i class="fa fa-bars"></i></td> <td class="text-center" style="width: 50px"><i class="fa fa-bars"></i></td>
<td><img class="img img-responsive product-center" style="height: 59.45px;" <td><img class="img img-responsive product-center" style="height: 59.45px;" src="{{ config('site_config.images_url') }}/{{ $thumbnail->Image . '?t=' . time() }}" /></td>
src="{{ config('site_config.images_url') }}/{{ $thumbnail->Image . '?t=' . time() }}" />
</td>
<td class="text-center"> <td class="text-center">
<!-- <button class="btn btn-default btn-xs btn-edit-clipart" data-id="#"><i class="fa fa-edit"></i></button> --> <!-- <button class="btn btn-default btn-xs btn-edit-clipart" data-id="#"><i class="fa fa-edit"></i></button> -->
<button class="btn btn-default btn-sm btn-delete-item-image" <button class="btn btn-default btn-sm btn-delete-item-image" data-id="{{ $thumbnail->Id }}" data-filename="{{ $thumbnail->Image }}" title="Delete Image"><i class="fa fa-trash"></i></button>
data-id="{{ $thumbnail->Id }}" data-filename="{{ $thumbnail->Image }}"
title="Delete Image"><i class="fa fa-trash"></i></button>
</td> </td>
</tr> </tr>
@endforeach @endforeach
@@ -300,16 +223,15 @@
</table> </table>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-primary" id="btn_save_thumbnail_sorting">Save <button type="button" class="btn btn-primary" id="btn_save_thumbnail_sorting">Save Changes</button>
Changes</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button> <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="modal fade" id="modal_add_thumbnail" role="dialog"> <div class="modal fade" id="modal_add_thumbnail" role="dialog">
<div class="modal-dialog modal-lg"> <div class="modal-dialog modal-lg">
<div class="modal-content"> <div class="modal-content">
<form id="frm_add_new_images"> <form id="frm_add_new_images">
@@ -324,12 +246,10 @@
<h3>Select Image(s)</h3> <h3>Select Image(s)</h3>
<div class="form-group"> <div class="form-group">
<input type="hidden" name="_id" value="{{ $product_array[0]->Id }}" /> <input type="hidden" name="_id" value="{{ $product_array[0]->Id }}" />
<input type="file" class="form-control" id="upload_images" <input type="file" class="form-control" id="upload_images" name="upload_images[]" multiple accept="image/*" />
name="upload_images[]" multiple accept="image/*" />
</div> </div>
<div class="form-group"> <div class="form-group">
<button type="button" id="clear_frm_add_new_images" <button type="button" id="clear_frm_add_new_images" class="btn btn-default btn-block">Clear</button>
class="btn btn-default btn-block">Clear</button>
</div> </div>
</div> </div>
</div> </div>
@@ -351,294 +271,7 @@
</form> </form>
</div> </div>
</div> </div>
</div> </div>
<div class="modal fade" id="modalAddRoster" role="dialog" data-backdrop="static" data-keyboard="false">
<div class="modal-dialog">
<div class="modal-content">
{{-- @{{ roster }} --}}
<form @submit.prevent="onRosterSubmit">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="modal-title">Add Roster</h4>
</div>
<div class="modal-body">
<table class="table">
<tr>
<th>#</th>
<th>Name</th>
<th>Number</th>
<th></th>
</tr>
<tr v-for="(item, i) in roster" :key="i">
<td style="padding: 0px 0px 0px 8px;">@{{ i + 1 }}</td>
<td style="padding: 0px 0px 0px 8px;"><input type="text" placeholder="Player Name"
v-model="roster[i]['Name']" class="form-control"></td>
<td style="padding: 0px 8px 0px 0px;"><input type="text"
placeholder="Player Number" v-model="roster[i]['Number']" maxlength="2"
class="form-control"></td>
<td style="padding: 0px 8px 0px 0px; text-align: end;">
<button type="button" @click="removeRosterRow(i)" :disabled="roster.length <= 1"
class="btn btn-danger"><i class="fa fa-times-circle-o"></i></button>
</td>
</tr>
<tr>
<td colspan="4" style="padding: 8px 8px 0px 8px; text-align: end;">
<button type="button" @click="addRosterRow" class="btn btn-primary">Add
Row</button>
</td>
</tr>
</table>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary" :disabled="isSubmit">
<i v-if="isSubmit" class="fa fa-spinner fa-spin"></i> Submit
</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</form>
</div>
</div>
</div>
<div class="modal fade" id="modalViewRoster" role="dialog" data-backdrop="static" data-keyboard="false">
<div class="modal-dialog">
<div class="modal-content">
<form @submit.prevent="onRosterUpdate">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="modal-title">Roster</h4>
</div>
<div class="modal-body">
<div style="text-align: end;">
<button type="button" @click.prevent="getRoster" class="btn btn-success" :disabled="isRefresh">
<i v-if="isRefresh" class="fa fa-spinner fa-spin"></i> Refresh
</button>
</div>
<table class="table">
<tr>
<th>#</th>
<th>Name</th>
<th>Number</th>
<th></th>
</tr>
<tr v-for="(item, i) in currentRoster" :key="i">
<td style="padding: 0px 0px 0px 8px;">@{{ i + 1 }}</td>
<td style="padding: 0px 0px 0px 8px;"><input type="text" placeholder="Player Name"
v-model="currentRoster[i]['Name']" class="form-control"></td>
<td style="padding: 0px 8px 0px 0px;"><input type="text" maxlength="2"
placeholder="Player Number" v-model="currentRoster[i]['Number']"
class="form-control"></td>
<td style="padding: 0px 8px 0px 0px; text-align: end;">
<button type="button" @click="deleteRoster(i, item)" class="btn btn-danger"><i
class="fa fa-trash-o"></i></button>
</td>
</tr>
{{-- <tr>
<td colspan="4" style="padding: 8px 8px 0px 8px; text-align: end;">
<button type="button" @click="addRosterRow" class="btn btn-primary">Add
Row</button>
</td>
</tr> --}}
</table>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary" :disabled="isSubmit">
<i v-if="isSubmit" class="fa fa-spinner fa-spin"></i> Update
</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</form>
</div>
</div>
</div>
</div>
<script>
new Vue({
el: '#viewStoreItem',
data: {
title: "Store Item", // Passing Laravel data to Vue
showAddRoster: false,
itemFormSelected: {!! json_encode($product_array[0]->ProductForm) !!},
roster: [{
Name: "",
Number: "",
ProductId: {!! json_encode($product_array[0]->Id) !!}
}],
currentRoster: {!! json_encode($roster) !!},
toBeDeletedRoster: [],
isSubmit: false,
isRefresh: false
},
methods: {
handleSelectItemForm() {
console.log('Selected option:', this.itemFormSelected);
},
addRosterRow() {
this.roster.push({
Name: "",
Number: "",
ProductId: {!! json_encode($product_array[0]->Id) !!}
})
},
deleteRoster(i, item) {
this.currentRoster.splice(i, 1);
this.toBeDeletedRoster.push(item.Id);
},
async onRosterUpdate() {
// this.isSubmit = true;
if (this.toBeDeletedRoster.length > 0) {
this.postDeleteRoster();
}
await this.updateRoster();
// this.isSubmit = false;
},
async postDeleteRoster() {
const token = $('meta[name="csrf_token"]').attr('content')
axios.post("{{ url('user/roster-delete') }}", {
data: this.toBeDeletedRoster
}, {
headers: {
"Content-Type": "application/json",
'X-CSRF-TOKEN': token
}
})
.then(response => {
this.isSubmit = false;
console.log(response.data);
// alert("Roster is successfully saved.");
// $('#modalAddRoster').modal('hide');
// this.roster = [{
// Name: "",
// Number: "",
// ProductId: {!! json_encode($product_array[0]->Id) !!}
// }]
})
.catch(error => {
console.error(error); // Handle error
this.isSubmit = false; // Hide loading indicator
});
},
async onRosterSubmit() {
this.isSubmit = true;
const token = $('meta[name="csrf_token"]').attr('content')
axios.post("{{ url('user/roster') }}", {
data: this.roster
}, {
headers: {
"Content-Type": "application/json",
'X-CSRF-TOKEN': token
}
})
.then(response => {
this.isSubmit = false;
console.log(response.data);
const res = response.data;
if (!res.status) {
alert(res.message);
return
}
alert(res.message);
$('#modalAddRoster').modal('hide');
this.roster = [{
Name: "",
Number: "",
ProductId: {!! json_encode($product_array[0]->Id) !!}
}]
})
.catch(error => {
console.error(error); // Handle error
this.isSubmit = false; // Hide loading indicator
});
},
async updateRoster() {
this.isSubmit = true;
const token = $('meta[name="csrf_token"]').attr('content')
axios.post("{{ url('user/roster-update') }}", {
data: this.currentRoster
}, {
headers: {
"Content-Type": "application/json",
'X-CSRF-TOKEN': token
}
})
.then(response => {
this.isSubmit = false;
console.log(response.data);
alert("Roster is successfully updated.");
$('#modalViewRoster').modal('hide');
})
.catch(error => {
console.error(error); // Handle error
this.isSubmit = false; // Hide loading indicator
});
},
async getRoster() {
this.isRefresh = true;
const productId = {!! json_encode($product_array[0]->Id) !!}
const token = $('meta[name="csrf_token"]').attr('content')
axios.get("{{ url('user/roster') }}", {
params: {
'product-id': productId
}
}, {
headers: {
"Content-Type": "application/json",
'X-CSRF-TOKEN': token
}
})
.then(response => {
this.isRefresh = false;
console.log("getRoster", response)
this.currentRoster = response.data;
})
.catch(error => {
console.error(error); // Handle error
this.isRefresh = false;
});
},
removeRosterRow(i) {
this.roster.splice(i, 1);
},
addRosterModal() {
$('#modalAddRoster').modal('show');
},
viewRosterModal() {
$('#modalViewRoster').modal('show');
// this.getRoster()
}
},
});
</script>
@endsection @endsection

66
start-local.sh Executable file
View File

@@ -0,0 +1,66 @@
#!/bin/bash
# Quick start script for local development and testing before Cloud Run deployment
set -e
echo "🚀 Starting Merchbay Laravel Application Setup..."
# Check if Docker is running
if ! docker info > /dev/null 2>&1; then
echo "❌ Docker is not running. Please start Docker and try again."
exit 1
fi
# Determine which docker compose command to use
if command -v docker-compose > /dev/null 2>&1; then
DOCKER_COMPOSE="docker-compose"
elif docker compose version > /dev/null 2>&1; then
DOCKER_COMPOSE="docker compose"
else
echo "❌ Neither 'docker-compose' nor 'docker compose' is available."
echo "Please install Docker Compose and try again."
exit 1
fi
echo " Using: $DOCKER_COMPOSE"
# Check if .env file exists
if [ ! -f .env ]; then
echo "📄 Creating .env file from .env.example..."
cp .env.example .env
echo "✅ .env file created. Please update it with your configuration."
fi
# Build and start services
echo "🔨 Building Docker images..."
$DOCKER_COMPOSE -f docker-compose.local.yml build
echo "🚀 Starting services..."
$DOCKER_COMPOSE -f docker-compose.local.yml up -d
echo "⏳ Waiting for services to be ready..."
sleep 30
# Run Laravel setup commands
echo "🔧 Setting up Laravel..."
$DOCKER_COMPOSE -f docker-compose.local.yml exec app php artisan key:generate
#$DOCKER_COMPOSE -f docker-compose.local.yml exec app php artisan migrate --force
$DOCKER_COMPOSE -f docker-compose.local.yml exec app php artisan config:cache
$DOCKER_COMPOSE -f docker-compose.local.yml exec app php artisan route:cache
$DOCKER_COMPOSE -f docker-compose.local.yml exec app php artisan view:cache
echo "✅ Setup complete!"
echo ""
echo "🌐 Your application is now running at:"
echo " Application: http://localhost:8080"
echo " phpMyAdmin: http://localhost:8081"
echo ""
echo "📊 Check logs with:"
echo " $DOCKER_COMPOSE -f docker-compose.local.yml logs -f app"
echo ""
echo "🛑 Stop the application with:"
echo " $DOCKER_COMPOSE -f docker-compose.local.yml down"
echo ""
echo "🚀 When ready to deploy to Cloud Run, use:"
echo " gcloud builds submit --config=cloudbuild.yaml"

0
storage/keys/crewsportswear_app.key Executable file → Normal file
View File

0
storage/keys/crewsportswear_app.ppk Executable file → Normal file
View File

View File

@@ -1,17 +0,0 @@
# 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

View File

@@ -1,54 +0,0 @@
<?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;