diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..79f23b1 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,23 @@ +.git +.github +.phpunit.cache +.worktrees +build +tests +vendor +.gitignore +.gitattributes +.php-cs-fixer.dist.php +Dockerfile +.dockerignore +phpunit.xml.dist +README.md +ARCHITECTURE.md +CONTRIBUTING.md +LICENSE +app.yaml +firebase-debug.log +.firebaserc +firebase.json +public/ + diff --git a/.firebaserc b/.firebaserc new file mode 100644 index 0000000..4be568b --- /dev/null +++ b/.firebaserc @@ -0,0 +1,5 @@ +{ + "projects": { + "default": "recaptcha-demo-php" + } +} diff --git a/.github/workflows/deploy-examples.yml b/.github/workflows/deploy-examples.yml new file mode 100644 index 0000000..7406000 --- /dev/null +++ b/.github/workflows/deploy-examples.yml @@ -0,0 +1,106 @@ +name: Deploy Examples to Cloud Run and Firebase + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + types: [opened, synchronize, reopened, closed] + +env: + PROJECT_ID: recaptcha-demo-php + SERVICE_NAME: recaptcha-examples + REGION: us-central1 + +jobs: + deploy: + if: github.event.action != 'closed' + runs-on: ubuntu-latest + permissions: + contents: 'read' + id-token: 'write' + pull-requests: 'write' + checks: 'write' + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - id: 'auth' + uses: 'google-github-actions/auth@v1' + with: + credentials_json: '${{ secrets.GCP_SA_KEY }}' + + - name: 'Set up Cloud SDK' + uses: 'google-github-actions/setup-gcloud@v1' + + - name: 'Build and Push Container' + run: | + BUILD_ID=$(gcloud builds submit --tag gcr.io/${{ env.PROJECT_ID }}/${{ env.SERVICE_NAME }}:${{ github.sha }} --async --format='value(id)') + echo "Build started with ID: $BUILD_ID" + while true; do + STATUS=$(gcloud builds describe $BUILD_ID --format='value(status)') + echo "Current status: $STATUS" + if [[ "$STATUS" == "SUCCESS" ]]; then + break + fi + if [[ "$STATUS" == "FAILURE" || "$STATUS" == "INTERNAL_ERROR" || "$STATUS" == "TIMEOUT" || "$STATUS" == "CANCELLED" ]]; then + echo "Build failed with status: $STATUS" + exit 1 + fi + sleep 10 + done + + - name: 'Deploy to Cloud Run' + id: deploy-cloud-run + uses: 'google-github-actions/deploy-cloudrun@v1' + with: + service: '${{ env.SERVICE_NAME }}' + image: 'gcr.io/${{ env.PROJECT_ID }}/${{ env.SERVICE_NAME }}:${{ github.sha }}' + region: '${{ env.REGION }}' + env_vars: | + RECAPTCHA_V2_SITE_KEY=${{ secrets.RECAPTCHA_V2_SITE_KEY }} + RECAPTCHA_V2_SECRET_KEY=${{ secrets.RECAPTCHA_V2_SECRET_KEY }} + RECAPTCHA_V3_SITE_KEY=${{ secrets.RECAPTCHA_V3_SITE_KEY }} + RECAPTCHA_V3_SECRET_KEY=${{ secrets.RECAPTCHA_V3_SECRET_KEY }} + flags: '--allow-unauthenticated' + # For PRs, we want to route to the new revision without affecting prod traffic + no_traffic: ${{ github.event_name == 'pull_request' }} + tag: ${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.number) || 'live' }} + + + - name: 'Setup Firebase Site and Config for PR' + if: github.event_name == 'pull_request' + run: | + npm install -g firebase-tools + SITE_ID="${{ env.PROJECT_ID }}-pr-${{ github.event.number }}" + TAG="pr-${{ github.event.number }}" + # Create the site (ignore error if it already exists) + firebase hosting:sites:create $SITE_ID --project ${{ env.PROJECT_ID }} || true + # Update firebase.json to deploy to the PR site and route to the specific PR tag + jq ".hosting.site = \"$SITE_ID\" | .hosting.rewrites[0].run.tag = \"$TAG\" | del(.hosting.rewrites[0].run.pinTag)" firebase.json > firebase.tmp.json && mv firebase.tmp.json firebase.json + + - name: 'Deploy to Firebase Hosting' + uses: FirebaseExtended/action-hosting-deploy@v0 + with: + repoToken: '${{ secrets.GITHUB_TOKEN }}' + firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_RECAPTCHA_DEMO_PHP }}' + projectId: ${{ env.PROJECT_ID }} + channelId: 'live' + + cleanup: + if: github.event_name == 'pull_request' && github.event.action == 'closed' + runs-on: ubuntu-latest + steps: + - id: 'auth' + uses: 'google-github-actions/auth@v1' + with: + credentials_json: '${{ secrets.GCP_SA_KEY }}' + + - name: 'Set up Cloud SDK' + uses: 'google-github-actions/setup-gcloud@v1' + + - name: 'Delete PR Tag from Cloud Run' + run: | + # We don't delete the service, just remove the tag or ignore + gcloud run services update ${{ env.SERVICE_NAME }} --remove-tags pr-${{ github.event.number }} --region ${{ env.REGION }} || true diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..5ae7f58 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,48 @@ +# Use the official PHP image with Apache +FROM php:8.5-apache + +# Install system dependencies for PHP extensions +RUN apt-get update && apt-get install -y \ + libzip-dev \ + zip \ + unzip \ + && rm -rf /var/lib/apt/lists/* + +# Install PHP extensions required for the library and examples +RUN docker-php-ext-install zip + +# Enable Apache mod_rewrite +RUN a2enmod rewrite + +# Install Composer +COPY --from=composer:latest /usr/bin/composer /usr/bin/composer + +# Set the working directory +WORKDIR /var/www/html + +# Copy composer files first +COPY composer.json composer.lock ./ + +# Install project dependencies +RUN composer install --no-dev --optimize-autoloader + +# Copy the library source and examples +COPY src/ ./src/ +COPY examples/ ./examples/ + +# Update Apache configuration to serve from the examples directory correctly +RUN sed -ri -e 's!/var/www/html!/var/www/html/examples!g' /etc/apache2/sites-available/*.conf +RUN sed -ri -e 's!/var/www/!/var/www/html/examples!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf + +# Set ServerName to localhost to avoid startup warnings +RUN echo "ServerName localhost" >> /etc/apache2/apache2.conf + +# Cloud Run requires Apache to listen on the port defined by the PORT environment variable +RUN sed -i 's/80/8080/g' /etc/apache2/sites-available/000-default.conf /etc/apache2/ports.conf + +# Set permissions for Apache +RUN chown -R www-data:www-data /var/www/html + +# Use the default PORT environment variable or fallback to 8080 +ENV PORT 8080 +EXPOSE 8080 diff --git a/app.yaml b/app.yaml deleted file mode 100644 index b6ccaf1..0000000 --- a/app.yaml +++ /dev/null @@ -1,8 +0,0 @@ -runtime: php -env: flex - -skip_files: -- tests - -runtime_config: - document_root: examples diff --git a/examples/recaptcha-content-security-policy.php b/examples/recaptcha-content-security-policy.php index 4edbf0e..2ea462d 100644 --- a/examples/recaptcha-content-security-policy.php +++ b/examples/recaptcha-content-security-policy.php @@ -66,8 +66,8 @@ ); // Register API keys at https://www.google.com/recaptcha/admin -$siteKey = ''; -$secret = ''; +$siteKey = getenv('RECAPTCHA_V2_SITE_KEY') ?: ''; +$secret = getenv('RECAPTCHA_V2_SECRET_KEY') ?: ''; // Copy the config.php.dist file to config.php and update it with your keys to run the examples if ('' == $siteKey && is_readable(__DIR__.'/config.php')) { diff --git a/examples/recaptcha-v2-checkbox-explicit.php b/examples/recaptcha-v2-checkbox-explicit.php index a8366cd..913509e 100644 --- a/examples/recaptcha-v2-checkbox-explicit.php +++ b/examples/recaptcha-v2-checkbox-explicit.php @@ -43,8 +43,8 @@ require_once __DIR__.'/../vendor/autoload.php'; // Register API keys at https://www.google.com/recaptcha/admin -$siteKey = ''; -$secret = ''; +$siteKey = getenv('RECAPTCHA_V2_SITE_KEY') ?: ''; +$secret = getenv('RECAPTCHA_V2_SECRET_KEY') ?: ''; // Copy the config.php.dist file to config.php and update it with your keys to run the examples if ('' == $siteKey && is_readable(__DIR__.'/config.php')) { diff --git a/examples/recaptcha-v2-checkbox.php b/examples/recaptcha-v2-checkbox.php index ac1b41e..c87eb5a 100644 --- a/examples/recaptcha-v2-checkbox.php +++ b/examples/recaptcha-v2-checkbox.php @@ -43,8 +43,8 @@ require_once __DIR__.'/../vendor/autoload.php'; // Register API keys at https://www.google.com/recaptcha/admin -$siteKey = ''; -$secret = ''; +$siteKey = getenv('RECAPTCHA_V2_SITE_KEY') ?: ''; +$secret = getenv('RECAPTCHA_V2_SECRET_KEY') ?: ''; // Copy the config.php.dist file to config.php and update it with your keys to run the examples if ('' == $siteKey && is_readable(__DIR__.'/config.php')) { diff --git a/examples/recaptcha-v2-invisible.php b/examples/recaptcha-v2-invisible.php index e54a249..1dfb404 100644 --- a/examples/recaptcha-v2-invisible.php +++ b/examples/recaptcha-v2-invisible.php @@ -43,8 +43,8 @@ require_once __DIR__.'/../vendor/autoload.php'; // Register API keys at https://www.google.com/recaptcha/admin -$siteKey = ''; -$secret = ''; +$siteKey = getenv('RECAPTCHA_V2_SITE_KEY') ?: ''; +$secret = getenv('RECAPTCHA_V2_SECRET_KEY') ?: ''; // Copy the config.php.dist file to config.php and update it with your keys to run the examples if ('' == $siteKey && is_readable(__DIR__.'/config.php')) { diff --git a/examples/recaptcha-v3-request-scores.php b/examples/recaptcha-v3-request-scores.php index 3d8785b..4e67fed 100644 --- a/examples/recaptcha-v3-request-scores.php +++ b/examples/recaptcha-v3-request-scores.php @@ -40,8 +40,8 @@ require_once __DIR__.'/../vendor/autoload.php'; // Register API keys at https://www.google.com/recaptcha/admin -$siteKey = ''; -$secret = ''; +$siteKey = getenv('RECAPTCHA_V3_SITE_KEY') ?: ''; +$secret = getenv('RECAPTCHA_V3_SECRET_KEY') ?: ''; // Copy the config.php.dist file to config.php and update it with your keys to run the examples if ('' == $siteKey && is_readable(__DIR__.'/config.php')) { diff --git a/examples/recaptcha-v3-verify.php b/examples/recaptcha-v3-verify.php index b4ffa43..1b669c6 100644 --- a/examples/recaptcha-v3-verify.php +++ b/examples/recaptcha-v3-verify.php @@ -43,8 +43,8 @@ require_once __DIR__.'/../vendor/autoload.php'; // Register API keys at https://www.google.com/recaptcha/admin -$siteKey = ''; -$secret = ''; +$siteKey = getenv('RECAPTCHA_V3_SITE_KEY') ?: ''; +$secret = getenv('RECAPTCHA_V3_SECRET_KEY') ?: ''; // Copy the config.php.dist file to config.php and update it with your keys to run the examples if ('' == $siteKey && is_readable(__DIR__.'/config.php')) { diff --git a/firebase.json b/firebase.json new file mode 100644 index 0000000..75ec6d5 --- /dev/null +++ b/firebase.json @@ -0,0 +1,20 @@ +{ + "hosting": { + "public": "public", + "ignore": [ + "firebase.json", + "**/.*", + "**/node_modules/**" + ], + "rewrites": [ + { + "source": "**", + "run": { + "serviceId": "recaptcha-examples", + "region": "us-central1", + "pinTag": true + } + } + ] + } +}